Android 10 (API 29) 対応

# 概要

Android 10 が公開され、 document も日本語化されたので、そろそろ対応しておこうと思います。
https://developer.android.com/about/versions/10/behavior-changes-10?hl=JA

Android の バージョン履歴はこちら。
https://ja.wikipedia.org/wiki/Androidのバージョン履歴

## いつ対応するのが良いのか
いままではGoogle Playの要件がそこまで厳しくなかったので放置しがちでしたが、
最近、 [Google Play の targetSdkVersion 要件が厳しくなって](https://developer.android.com/distribute/best-practices/develop/target-sdk?hl=ja)、わりと早めに対応が必要になってきました。
しかし、次のバージョン (Android 11) が出てきたときも、 targetSdkVersion=29の状態でAndroid 11でのテストもしなければいけません。
そのため、Android 11 が出たタイミングで、一緒に対応してしまうのがコスト的には低いように思います。

## テスト
重要なことなので先に書きます。

ドキュメントに記載されている項目以外にも実は隠れ変更点とかがあったりもします。
また、一般的に影響範囲はアプリ全域に及びます。対応したつもりでも対応漏れとかもあり得るため、いずれにしても**デグレしていないか必ずテストしましょう！**

最低限、以下はテスト観点に含めましょう。

- Android 10 の端末でテストを行う  
target になっているバージョンでしか発生しない問題もあります。  
Android 11 は 10 を含んでいるから 11 でやれば良いというような判断は危険です。  
また、後で出てきますが、今回は*折りたたみ式のサポート*が結構大変そうです。
可能なら折りたたみ式デバイスを1台用意しておいたほうがよさそうです。
- Android 10 未満でもテストを行う  
target になっているバージョン以外にも影響がある可能性があります。  
最低でも minSdkVersion ~ Android 9 のうち1つ以上のバージョンでも確認を行いましょう。


## リリースは慎重に。
OS level の対応なので、いくら慎重に対応しても想定外の問題が発生することがあります。
いきなり100%リリースすると問題が顕在化してからでは対応が大変です。
リリース直後はいつでも公開停止できるようにしておきましょう。


# targetSdkVersion=29 の対応項目一覧

targetSdkVersion を 29 に設定すると、こちらの変更点に対応が必要になります。
https://developer.android.com/about/versions/10/behavior-changes-10?hl=JA

以下、各項目の要約です。


## 非 SDK インターフェースに対する制限の更新
> アプリで使用できる非 SDK インターフェースが制限されています。

非 SDK インターフェースとは、要はSDKのうち、 `@hide` とかで隠蔽されているようなやつですかね。
リフレクションやjniで無理やり使うのはやめましょうということですね。

リフレクションとか使っている場合は注意しましょう。

[非 SDK インターフェースの制限](https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces?hl=JA)
[非 SDK インターフェースの使用頻度を減らすことで安定性を向上させる方法](https://android-developers.googleblog.com/2018/02/improving-stability-by-reducing-usage.html)

## 共有メモリ
>ashmem は /proc/<pid>/maps 内の dalvik マップの形式を変更しました。マップファイルを直接解析するアプリが影響を受けます。

low layer な話ですね。
マップファイルを直接解析するアプリなんてあるのか..?
よほどのことがなければマップファイルを直接解析なんてしないと思うので、対応した記憶がなければ無視して大丈夫でしょう。

## アプリのホーム ディレクトリに対する実行権限の削除
>アプリのホーム ディレクトリ内のファイルで exec() を呼び出すことはできません。

ホームディレクトリは package 名で作られたディレクトリのことでしょうか。
こちらも `exec` を直接呼び出すことなんてほぼ無いと思うので、対応した記憶がなければ無視して大丈夫でしょう。

## Android ランタイムが受け入れるのはシステムが生成した OAT ファイルに限られる
ちょっと意味がわからないので分かり次第書きます。

## ART が行う AOT の正確性を強化
ちょっと意味がわからないので分かり次第書きます。

## 全画面表示インテントに関するアクセス権限の変更
>全画面表示インテントを含む通知を使用する場合、アプリのマニフェスト ファイル内で USE_FULL_SCREEN_INTENT 権限をリクエストする必要があります。
これは normal 権限であり、リクエストしたアプリに自動で付与されます。

`全画面表示インテントを含む通知を使用する場合` なので、すべての全画面表示が対象ではないのかな・・？
通知から全画面表示する場合は USE_FULL_SCREEN_INTENT permission を manifest に書きましょう。
https://developer.android.com/reference/android/Manifest.permission.html?hl=JA#USE_FULL_SCREEN_INTENT

```xml:AndroidManifest.xml
    <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
```

## 折りたたみ式のサポート
>Android 10 は、折りたたみ式デバイスと大画面デバイスをサポートするように変更されています。

Android4系の頃にもNECから挑戦的な折りたたみが出ましたけどね。
時代を先取りしすぎましたね。

>onResume() メソッドと onPause() メソッドの動作が変わります。

えっ..

>複数のアプリが複数のウィンドウに表示される複数表示モードの場合、表示されるスタック内のフォーカス可能な最上部のアクティビティはすべて再開状態ですが、実際にフォーカスされるのは、そのうちの 1 つである「一番に再開される」アクティビティだけです。
Android 10 より前のバージョンで実行されている場合、一度に再開できるのはシステム内の 1 つのアクティビティだけで、その他の表示されているアクティビティはすべて一時停止されます。

なるほど。 Android 10ではそれぞれ別の画面に表示される Activity でいずれも onResume が呼ばれますよっていうことですね。

>「フォーカス」と、「一番に再開される」アクティビティを混同しないようにしてください。システムは z オーダーに応じて、アクティビティに優先度を付け、ユーザーが最後に操作したアクティビティほど高い優先度を与えます。一番に再開されるアクティビティにフォーカスがない場合もあります（たとえば、通知シェードが展開されている場合）。

先の説明と完全に矛盾していますが :sweat: 、ユーザーが最後に操作した Activity にフォーカスが与えられるということですかね。

>Android 10（API レベル 29）以降では、onTopResumedActivityChanged() コールバックに登録して、アプリのアクティビティが一番に再開される位置を獲得したときと失ったときに通知を受けることができます。

うわぁ.. クソ面倒なものが追加されましたね。。
これは Android 10 の端末で要検証ですね。
[Activity#onTopResumedActivityChanged(boolean)](https://developer.android.com/reference/android/app/Activity.html?hl=JA#onTopResumedActivityChanged(boolean))

詳しくは、[折りたたみ式デバイスに対応したアプリの作成](https://developer.android.com/guide/topics/ui/foldables?hl=JA)をご覧ください。 

※ ちなみに Fragment には onTopResumedActivityChanged はありません。


## java.io.FileChannel.map() の変更点
>FileChannel.map() は /dev/zero のような非標準ファイルではサポートされません。非標準ファイルのサイズを truncate() を使って変更することはできません。

もし `/dev/zero` のような非標準ファイルを利用している場合は確認しておきましょう。