Androidの権限自動削除でプッシュ通知はどうなるか調べてみました

Androidアプリの開発を行っているsinoです。
アプリが長期間利用されなかった場合にアプリの権限が自動削除されますが、プッシュ通知についてどういった動作をするか調べたので結果を残しておきます。
結論を先に記載しておくと、何もしなくてもアプリを起動したらプッシュ通知を受信するようになります。

Googleのアナウンスは　[こちら](https://developer.android.com/topic/performance/app-hibernation?hl=ja)　です。

Android 12以上でプッシュ通知に関する内容を抜粋すると

`アプリの実行時の権限はリセットされます。`
`アプリはプッシュ通知（Firebase Cloud Messaging を介して送信される優先度の高いメッセージなど）を受信できません。`

と記載されているので、プッシュ通知は届かなくなります。そして

`ユーザーが次にアプリを操作すると、アプリは休止状態を終了し、再びジョブ、アラート、通知を作成できるようになります。`

とありますが

`ただし、以下の処理がアプリに対して行われることはありません。`
`　1.アプリの実行時の権限を再付与する。`
`　　ユーザーがアプリに再付与する必要があります。`
`　2.アプリが休止状態になる前にスケジュールされたジョブ、アラート、通知のスケジュールを再設定する。`

と書かれているので、権限は再付与されません。
プッシュ通知の権限を削除されたら自力で再付与しないと通知が届かないのでは？
という疑問が湧いたので、試してみることにしました。

まずは色々な権限を付与したアプリを作成しました。
AndroidManifest.xmlに適当に30個以上の宣言を追加しました。

```
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.NFC"/>
    <uses-permission android:name="android.permission.NFC_PREFERRED_PAYMENT_INFO"/>
    <uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT"/>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.SET_ALARM"/>
    <uses-permission android:name="android.permission.TRANSMIT_IR"/>
    <uses-permission android:name="android.permission.USE_BIOMETRIC"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
```

Activityでユーザーの許諾を求める部分

```
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayList<String> permissionList = new ArrayList<String>();

        addPermissionList(permissionList, Manifest.permission.ACCESS_NETWORK_STATE);
        //addPermissionList(permissionList, Manifest.permission.ACCESS_BACKGROUND_LOCATION);
        addPermissionList(permissionList, Manifest.permission.ACCESS_COARSE_LOCATION);
        addPermissionList(permissionList, Manifest.permission.ACCESS_FINE_LOCATION);
        addPermissionList(permissionList, Manifest.permission.ANSWER_PHONE_CALLS);
        addPermissionList(permissionList, Manifest.permission.ACCESS_WIFI_STATE);
        addPermissionList(permissionList, Manifest.permission.BLUETOOTH);
        addPermissionList(permissionList, Manifest.permission.BODY_SENSORS);
        addPermissionList(permissionList, Manifest.permission.CALL_PHONE);
        addPermissionList(permissionList, Manifest.permission.CAMERA);
        addPermissionList(permissionList, Manifest.permission.CHANGE_NETWORK_STATE);
        addPermissionList(permissionList, Manifest.permission.CHANGE_WIFI_STATE);
        addPermissionList(permissionList, Manifest.permission.FOREGROUND_SERVICE);
        addPermissionList(permissionList, Manifest.permission.INTERNET);
        addPermissionList(permissionList, Manifest.permission.KILL_BACKGROUND_PROCESSES);
        addPermissionList(permissionList, Manifest.permission.MODIFY_AUDIO_SETTINGS);
        addPermissionList(permissionList, Manifest.permission.NFC);
        addPermissionList(permissionList, Manifest.permission.NFC_PREFERRED_PAYMENT_INFO);
        addPermissionList(permissionList, Manifest.permission.NFC_TRANSACTION_EVENT);
        addPermissionList(permissionList, Manifest.permission.QUERY_ALL_PACKAGES);
        addPermissionList(permissionList, Manifest.permission.POST_NOTIFICATIONS);
        addPermissionList(permissionList, Manifest.permission.READ_CONTACTS);
        addPermissionList(permissionList, Manifest.permission.READ_EXTERNAL_STORAGE);
        addPermissionList(permissionList, Manifest.permission.READ_PHONE_STATE);
        addPermissionList(permissionList, Manifest.permission.READ_SMS);
        addPermissionList(permissionList, Manifest.permission.SET_ALARM);
        addPermissionList(permissionList, Manifest.permission.TRANSMIT_IR);
        addPermissionList(permissionList, Manifest.permission.USE_BIOMETRIC);
        addPermissionList(permissionList, Manifest.permission.VIBRATE);
        addPermissionList(permissionList, Manifest.permission.WAKE_LOCK);
        addPermissionList(permissionList, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (!permissionList.isEmpty()) {
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[0]), 1);
        }
    }

    private void addPermissionList(ArrayList<String> permissionList, String permission) {
        if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(permission);
        }
    }
```

ACCESS_BACKGROUND_LOCATIONを入れると許諾ダイアログが一切出なくなったので外してあります。
これだけ入れても7回しか許諾は求められませんでした。
尚、動作確認はAndroid 13のPixel 7で行っています。
全て許可した状態がこちら。

![Screenshot_20231223-211603.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38008/9e0ebb1c-2695-a243-ad4e-94152a160074.png)

手動で休止状態の動作を呼び出す方法が　[こちら](https://developer.android.com/topic/performance/app-hibernation?hl=ja#manually-invoke)　に記載されているので実施します。

何をするかと言うと、自動削除されるまで何か月も待っていられないので、一時的に自動削除が発動するのを1秒後にしてしまいましょうということです。

自分の環境はWindowsなのと元の値を環境変数に保存しておくとコマンドプロンプトを終了したらわからなくなってしまうので、環境変数を使わずコピペして保存する方針に改変しています。
又、コマンドが反映されているかの確認も適宜追加してあります。

```
>adb shell device_config put app_hibernation app_hibernation_enabled true

>adb shell device_config get permissions auto_revoke_unused_threshold_millis2
7776000000
```

確認した機種では7776000000msに設定されていましたので、この値をどこかに記録しておきます。
7776000000ms / (1000ms × 60s × 60m × 24h) = 90d となるので90日で自動削除されるようです。
これを1秒に変更し、本当に変わっているか確認します。

```
>adb shell device_config put permissions auto_revoke_unused_threshold_millis2 1000

>adb shell device_config get permissions auto_revoke_unused_threshold_millis2
1000
```

次のコマンドで起動時のブロードキャストされるとのことですが、調べても詳しいことはわかりませんでしたので、とりあえず実行します。

```
>adb shell am wait-for-broadcast-idle
All broadcast queues are idle!
```

`All broadcast queues are idle!` と表示されるまで待つそうです。
自分の環境では瞬時に表示されました。
次のコマンドで休止状態のプロセスが発動します。

```
>adb shell cmd jobscheduler run -u 0 -f com.google.android.permissioncontroller 2
Running job [FORCED]
```

これでアプリが休止状態になりましたので、確認してみます。

```
>adb shell cmd app_hibernation get-state tokyo.zyyx.sino.pushtest
true
```

`true` なので休止状態になっています。
実機ではこのような通知が表示されていました。
権限の自動削除を無効化していないアプリは全て巻き込まれてますね・・・・・

![Screenshot_20231223-221306.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38008/36d8d8bc-b462-d94f-d230-e2d792045a64.png)

実際の権限はどうなっているか。

![Screenshot_20231223-221527.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38008/e3ec993a-ca5d-3259-1ed9-5e98c5d4d525.png)

「通知」だけは削除されていませんでした。
「権限」をタップすると「通知」以外は「許可しない」に移動していることがわかります。

![Screenshot_20231223-221541.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38008/03c9c72d-da17-cf3c-61a7-cd73fceb48b6.png)

***この状態でプッシュ通知を送りましたが、受信はしませんでした。***

この後、アプリを起動するとプッシュ通知を除いた6個の許諾を求められました。
***プッシュ通知については何もしなくても受信できるようになっていました。***
念のため休止状態が解除されていることをコマンドで確認しておきます。

```
>adb shell cmd app_hibernation get-state tokyo.zyyx.sino.pushtest
false
```

`false` なので休止状態から復旧しています。
確認が終わったので自動削除までの時間を元に戻し、戻っていることを確認しておきます。

```
>adb shell device_config put permissions auto_revoke_unused_threshold_millis2 7776000000

>adb shell device_config get permissions auto_revoke_unused_threshold_millis2
7776000000
```

以上でアプリの権限が自動削除された場合にプッシュ通知がどうなるかの確認は完了となります。
最後まで読んでいただきありがとうございました。