【Android】他のアプリの上に重ねて表示まとめ
Androidの世界では基本的に1アプリのみが前面に出る。
しかし android.permission.SYSTEM_ALERT_WINDOW
を宣言することで他のアプリが前面の場合でも Viewを表示できる
Android 6.0未満
制限はなく、 android.permission.SYSTEM_ALERT_WINDOW
を宣言するだけで利用可能
Android 6.0以上
攻撃者でもこの権限を悪用し、ランサムウェアなどで常に画面表示することができてしまうため、ユーザの明示的な許可が必要になった。 ただしAndroid 6.0.1以上でGoogle Playアプリ6.0.5以上が入っている場合、Google Playからインストールしたアプリは「他のアプリの上に重ねて表示」の権限がデフォルトオンになる。恐らくGoogle的にはGoogle Playにあるアプリは安全という前提がある。 https://stackoverflow.com/questions/36016369/system-alert-window-how-to-get-this-permission-automatically-on-android-6-0-an/36019034
この権限はRuntime Perissionではなく設定の奥にあるのでIntentを飛ばし、 onActivityResult()
でハンドリングする。
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName")) startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMIISSION)
設定が有効かどうかは Settings.canDrawOverlays()
を使う。
Android 8.0以上
targetSdkVersion 26以上にてViewを表示するときの指定でTYPE_APPLICATION_OVERLAY
を使わなければいけなくなった。これによりシステムUIなどの重要なViewの上に描画できなくなり、アプリインストール時に権限の上にViewを表示して隠すなどの攻撃が不可能になった。
https://developer.android.com/about/versions/oreo/android-8.0-changes
Android 8.0では権限取得にバグがあり、変更がすぐに反映されないので監視する。 https://stackoverflow.com/questions/46173460/why-in-android-o-method-settings-candrawoverlays-returns-false-when-user-has
Android 8.1では修正済み。https://issuetracker.google.com/issues/66072795
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager appOpsManager.startWatchingMode(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, packageName, object : AppOpsManager.OnOpChangedListener { override fun onOpChanged(op: String?, packageName: String?) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { appOpsManager.stopWatchingMode(this) onOverlayPermissionResult(Settings.canDrawOverlays()) } } }) }