Android11から導入されたAuto-reset permissionsを調べてみてわかったこと
はじめに
本記事はAndroid Advent Calendar 2020 14日目の記事です
本記事では、Android11から導入されたAuto-reset permissionsを調べてみてわかったことを書いていきます
Auto-reset permissionsとは?
長期間アプリを使わなかった場合、ユーザーがアプリに付与した機密情報に関わるPermission(権限)が自動的にリセットされる仕組みです Protection LevelがDangerousに分類されるPermissionが自動リセットの対象です つまり、すべてのPermissionが自動リセットの対象ではないということです
公式ドキュメントの説明はこのあたりか、ブログを読むといいかと思います
アプリに対してAuto-reset permissionsが適用されるかどうかは、ユーザの設定次第になります 設定アプリの各アプリケーション Permission画面下部に、長期間アプリを使わなかった場合にPermissionを自動的にリセットするかどうかの設定があります
targetSDKVersionが30以上のアプリはこの設定値が初期状態で有効、targetSDKVersionが30未満のアプリこの設定値が初期状態で無効になります 公式ドキュメントでは、targetSdkVersionが30以上のアプリにAuto-reset permissionsが適用されるっぽい記載になっていますが、targetSDKVersionが30未満のアプリにも適用されることがあります
最終的には、ユーザの設定次第なので、targetSdkVersionが30以上のアプリでもユーザが設定値をOFFにすれば、Auto-reset permissionsは適用されなくなりますし、targetSDKVersionが30未満のアプリでもユーザが設定値をONにすれば、Auto-reset permissionsは適用されます
Auto-reset permissionsが実行され、Permissionが自動リセットされると通知が来ます 通知タップで遷移する画面でアプリをアンインストールできるのは便利ですね(自動リセットされる = 長期間使用していないアプリなのでアンインストールも検討してねってことだろう)
Auto-reset permissionsでPermissionが自動リセットされると通知が来る | 通知タップで遷移する画面 |
---|---|
ここからは、Auto-reset permissionsの内部動作等について調べてわかったことなどを書いていきます
調べた理由
- アプリをAndroid 11対応する上で、Auto-reset permissionsがどれだけ影響度がある機能なのかを理解したかったため
- Android Advent Calendarのネタにちょうど良さそうだと思ったため
- Android Code Searchを使ってCode Readingしてみたかったため
わかったこと要約
- アプリ実装への影響度そこまで大きくない
- 思ったよりAuto-reset permissionsが実行される頻度が少ない
Android Code Searchを使って内部実装を調べる
Android Code Searchを使ってAuto-reset permissionsの内部実装を調べました
Auto-reset permissionsに関する実装はだいたいAutoRevokePermissionsクラスにまとまってます
長期間アプリを使わなかった場合の「長期間」ってどれくらいの期間なのか
疑問に思ったのが、長期間アプリを使わなかった場合の「長期間」ってどれくらいの期間なの?ってところです
期間に関するしきい値の定数がありました デフォルトでは90日間らしいです
private val DEFAULT_UNUSED_THRESHOLD_MS = if (AUTO_REVOKE_ENABLED) DAYS.toMillis(90) else Long.MAX_VALUE
期間のしきい値を変更する
デフォルトってことは、つまり期間のしきい値を変更する方法が存在します 期間のしきい値にどの値を使うかを決めているコードを抜粋したのが以下です
fun getUnusedThresholdMs(context: Context) = when { DEBUG_OVERRIDE_THRESHOLDS -> SECONDS.toMillis(1) TeamfoodSettings.get(context) != null -> TeamfoodSettings.get(context)!!.unusedThresholdMs else -> DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_AUTO_REVOKE_UNUSED_THRESHOLD_MILLIS, DEFAULT_UNUSED_THRESHOLD_MS) }
private data class TeamfoodSettings( val enabledForPreRApps: Boolean, val unusedThresholdMs: Long, val checkFrequencyMs: Long ) { companion object { private var cached: TeamfoodSettings? = null fun get(context: Context): TeamfoodSettings? { if (cached != null) return cached return Settings.Global.getString(context.contentResolver, "auto_revoke_parameters" /* Settings.Global.AUTO_REVOKE_PARAMETERS */)?.let { str -> if (DEBUG) { DumpableLog.i(LOG_TAG, "Parsing teamfood setting value: $str") } str.split(",") .mapNotNull { val keyValue = it.split("=") keyValue.getOrNull(0)?.let { key -> key to keyValue.getOrNull(1) } } .toMap() .let { pairs -> TeamfoodSettings( enabledForPreRApps = pairs["enabledForPreRApps"] == "true", unusedThresholdMs = pairs["unusedThresholdMs"]?.toLongOrNull() ?: DEFAULT_UNUSED_THRESHOLD_MS, checkFrequencyMs = pairs["checkFrequencyMs"]?.toLongOrNull() ?: DEFAULT_CHECK_FREQUENCY_MS) } }.also { cached = it if (DEBUG) { Log.i(LOG_TAG, "Parsed teamfood setting value: $it") } } } }
Settings.Global.AUTO_REVOKE_PARAMETERSから設定値を読み込んでいます(Settings.Global.AUTO_REVOKE_PARAMETERS はhide APIなので、通常のアプリケーションからは定数を参照できません)
Settings.Global.AUTO_REVOKE_PARAMETERSの設定値にunusedThresholdMsが存在すれば、未使用期間のしきい値にそれを使うような実装になっています
つまり、adb shellから以下のコマンドを実行することで、未使用期間のしきい値を変更できます
// 未使用期間のしきい値を1分に変更 adb shell settings put global auto_revoke_parameters enabledForPreRApps=false,unusedThresholdMs=60000,checkFrequencyMs=60000 // 設定できているかを確認 adb shell settings get global auto_revoke_parameters
adbではなくコードからSettings.Global.AUTO_REVOKE_PARAMETERSを設定しようと試みましたが、これはさすがにセキュリティ的に無理でした
// java.lang.SecurityException: Permission denial: writing to settings requires:android.permission.WRITE_SECURE_SETTINGS Settings.Global.putString(contentResolver, "auto_revoke_parameters", "enabledForPreRApps=false,unusedThresholdMs=60000,checkFrequencyMs=60000")
設定は無理でしたが、設定値の読み込みはコードからできました
Settings.Global.getString(contentResolver,"auto_revoke_parameters")
Settings.Globalを設定する以外に、DeviceConfigを設定する方法もあります adb shellから以下のコマンドを実行することで、未使用期間のしきい値を変更できます
// 未使用期間のしきい値を1分に変更 adb shell device_config put permissions auto_revoke_unused_threshold_millis2 60000 // 設定できているかを確認 adb shell device_config get permissions auto_revoke_unused_threshold_millis2
どのくらいの周期でAuto-reset permissionsが実行されるのか
Auto-reset permissionsが実行される周期に関する定数がありました デフォルトでは15日間隔らしいです(広くね...?🤔)
private val DEFAULT_CHECK_FREQUENCY_MS = DAYS.toMillis(15)
内部ではJobSchedulerを使って定期実行しています BOOT_COMPLETEDでAutoRevokeOnBootReceiverを起動させて、そこでJobSchedulerを登録して、AutoRevokeServiceが定期的に実行されるようにしてます
Auto-reset permissionsの実行周期を変更する
こちらも期間のしきい値と同様、Settings.GlobalかDeviceConfigを設定することで変更できます
private fun getCheckFrequencyMs(context: Context) = when { TeamfoodSettings.get(context) != null -> TeamfoodSettings.get(context)!!.checkFrequencyMs else -> DeviceConfig.getLong( DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_AUTO_REVOKE_CHECK_FREQUENCY_MILLIS, DEFAULT_CHECK_FREQUENCY_MS) }
変更する際の注意点として、JobSchedulerの関係上 実行周期を15分以下にできません
Settings.Global
adb shellから以下のコマンドを実行することで、実行周期を変更できます
// 実行周期を15分に変更 adb shell settings put global auto_revoke_parameters enabledForPreRApps=false,unusedThresholdMs=60000,checkFrequencyMs=900000 // 設定できているかを確認 adb shell settings get global auto_revoke_parameters
DeviceConfig
adb shellから以下のコマンドを実行することで、実行周期を変更できます
// 実行周期を15分に変更 adb shell device_config put permissions auto_revoke_check_frequency_millis 900000 // 設定できているかを確認 adb shell device_config get permissions auto_revoke_check_frequency_millis
Auto-reset permissionsをすぐに実行したい!!
定期実行にJobSchedulerを使ってるので、adb shellから以下のコマンドを実行することで、Auto-reset permissionsをすぐに実行させることができます
adb shell cmd jobscheduler run -u 0 -f com.google.android.permissioncontroller 2
Auto-reset permissionsの動作確認をしたい時に便利です
ACTIVITY_RECOGNITION Permissionは自動リセットの対象外
Protection levelがDangerousに分類されるPermissionでも、android.Manifest.permission.ACTIVITY_RECOGNITIONのPermissionだけは自動リセットの対象外みたいです
private val EXEMPT_PERMISSIONS = listOf( android.Manifest.permission.ACTIVITY_RECOGNITION)
アプリの未使用期間はどのように調べているのか
UsageStatsManagerを利用してゴニョゴニョ調べているようです 詳細はコードを読んでみてください
Auto-reset permissionsの動作ログを見る
以下のlogcatで見れます
adb logcat -v time -s AutoRevokePermissions
dumpsysから過去書き込んだログは見れます
adb shell dumpsys permissionmgr
Auto-reset permissionsの内部ステータスを見たい
dumpsysから色々見れます
adb shell dumpsys permissionmgr
自身のアプリのAuto-reset permissionsの設定値が知りたい
PackageManager#isAutoRevokeWhitelistedメソッドで知ることができます
applicationContext.packageManager.isAutoRevokeWhitelisted
自身のアプリのAuto-reset permissions設定画面に遷移させたい
Intent.ACTION_AUTO_REVOKE_PERMISSIONSで遷移できます
val i = Intent(Intent.ACTION_AUTO_REVOKE_PERMISSIONS).apply { data = Uri.fromParts("package", packageName, null) } startActivity(i)
おわりに
調べてみると公式ドキュメントには記載されていないことがいくつかわかったので面白かったです Permissionの実装を普通に行っていれば、Auto-reset permissionsは何も面倒なことではないのですが、詳細な動作が知りたくて調べてみました 久々にAndroidの一機能の内部実装を読んでみたのですが、Android Code Searchがとにかく便利だった!
今回Auto-reset permissionsを調べる上で、ちょっと書いたコードをGitHubで公開してます