すべての Android アプリは、アクセスが制限されたサンドボックス内で実行されます。アプリ自体のサンドボックスの外部にあるリソースや情報を使用する必要がある場合は、権限を宣言し、このアクセスを提供する権限リクエストを設定できます。この手順は、権限を使用するためのワークフローの一部です。
危険な権限を宣言し、Android 6.0(API レベル 23)以降を搭載しているデバイスにアプリがインストールされている場合は、このガイドの手順に従って実行時に危険な権限をリクエストする必要があります。
危険な権限を宣言していない場合、または Android 5.1(API レベル 22)以前を搭載しているデバイスにアプリがインストールされている場合は、権限が自動的に付与されるため、このページの残りの手順を完了する必要はありません。
基本方針
実行時に権限をリクエストするための基本方針は次のとおりです。
- 権限を必要とする機能をユーザーが操作し始めたら、その状況に応じた権限をリクエストすること。
- ユーザーをブロックしないこと。権限に関連した説明を表示する UI フローは、常にキャンセルできるようにしてください。
- 機能に必要な権限をユーザーが拒否または取り消した場合は、グレースフル デグラデーションを行って、アプリの使用を続けられるようにしてください。このために、権限を必要とする機能を無効にする場合もあります。
- システム動作を前提としないこと。
権限を確認する
アプリに dangerous 権限が必要な場合は、dangerous 権限が必要な操作を実行するたびに、権限の有無を確認する必要があります。Android 6.0(API レベル 23)以降では、ユーザーは任意のアプリから dangerous 権限をいつでも取り消すことができます。
アプリに権限が付与されているかどうかを確認する
ユーザーがすでに特定の権限をアプリに付与しているかどうかを確認するには、その権限を ContextCompat.checkSelfPermission()
メソッドに渡します。このメソッドは、アプリに権限があるかどうかに応じて、PERMISSION_GRANTED
または PERMISSION_DENIED
のいずれかを返します。
アプリが権限を必要とする理由を説明する
ContextCompat.checkSelfPermission()
メソッドが PERMISSION_DENIED
を返した場合は、shouldShowRequestPermissionRationale()
を呼び出します。このメソッドが true
を返した場合は、ユーザーに説明 UI を表示します。この UI で、ユーザーが有効にしようとしている機能が特定の権限を必要とする理由を説明します。
権限をリクエストする
ユーザーに説明 UI を表示した後、または shouldShowRequestPermissionRationale()
の戻り値が今回は説明 UI を表示する必要がないことを示したときは、権限をリクエストします。システム権限ダイアログがユーザーに表示され、アプリに特定の権限を付与するかどうかの選択をユーザーに促します。
従来からある方法では、権限リクエストの一部として開発者自身がリクエスト コードを管理し、このリクエスト コードを権限コールバックのロジックに含めます。別の方法として、AndroidX ライブラリに含まれる RequestPermission
コントラクトを使用して、システムに権限リクエスト コードの管理を許可することもできます。RequestPermission
コントラクトを使用するとロジックが簡素化されるため、使用できる場合は使用することをおすすめします。
システムに権限リクエスト コードの管理を許可する
権限リクエストに関連付けられたリクエスト コードの管理をシステムに許可するには、モジュールの build.gradle
ファイルに androidx.activity
ライブラリへの依存関係を追加します。バージョン 1.2.0 以降のライブラリを使用してください。
次のクラスのいずれかを使用できます。
- 1 つの権限だけをリクエストするには、
RequestPermission
を使用します。 - 複数の権限を同時にリクエストするには、
RequestMultiplePermissions
を使用します。
RequestPermission
コントラクトを使用する手順を以下に示します。RequestMultiplePermissions
コントラクトを使用する手順もこれとほぼ同じです。
アクティビティまたはフラグメントの初期化ロジックで、
registerForActivityResult()
の呼び出しにActivityResultCallback
の実装を渡します。ActivityResultCallback
は、権限リクエストに対するユーザーの応答の処理方法を定義します。ActivityResultLauncher
型の戻り値registerForActivityResult()
への参照を保持します。必要に応じてシステム権限ダイアログを表示するには、前の手順で保存した
ActivityResultLauncher
のインスタンスでlaunch()
メソッドを呼び出します。launch()
が呼び出されると、システム権限ダイアログが表示されます。ユーザーが選択すると、前のステップで定義したActivityResultCallback
の実装が非同期で呼び出されます。注:
launch()
を呼び出したときに表示されるダイアログをアプリでカスタマイズすることはできません。ユーザーに詳細な情報やコンテキストを提供するには、アプリの UI を変更して、アプリの機能に特定の権限が必要な理由をユーザーにわかりやすく説明します。たとえば、機能を有効にするボタンのテキストを変更します。また、権限をリクエストするシステム ダイアログのテキストでは、リクエストする権限に関連付けられた権限グループを参照します。この権限グループはシステムを使いやすくするために設計されたものです。アプリでは、権限が特定の権限グループに属しているかどうかに依存するロジックを使用するべきではありません。
次のコード スニペットは、権限に関する応答の処理方法を示しています。
Kotlin
// Register the permissions callback, which handles the user's response to the // system permissions dialog. Save the return value, an instance of // ActivityResultLauncher. You can use either a val, as shown in this snippet, // or a lateinit var in your onAttach() or onCreate() method. val requestPermissionLauncher = registerForActivityResult(RequestPermission() ) { isGranted: Boolean -> if (isGranted) { // Permission is granted. Continue the action or workflow in your // app. } else { // Explain to the user that the feature is unavailable because the // features requires a permission that the user has denied. At the // same time, respect the user's decision. Don't link to system // settings in an effort to convince the user to change their // decision. } }
Java
// Register the permissions callback, which handles the user's response to the // system permissions dialog. Save the return value, an instance of // ActivityResultLauncher, as an instance variable. private ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(new RequestPermission(), isGranted -> { if (isGranted) { // Permission is granted. Continue the action or workflow in your // app. } else { // Explain to the user that the feature is unavailable because the // features requires a permission that the user has denied. At the // same time, respect the user's decision. Don't link to system // settings in an effort to convince the user to change their // decision. } });
このコード スニペットは、権限を確認し、必要に応じてユーザーに権限をリクエストする場合の推奨されるプロセスを示しています。
Kotlin
when { ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION ) == PackageManager.PERMISSION_GRANTED -> { // You can use the API that requires the permission. } shouldShowRequestPermissionRationale(...) -> { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected. In this UI, // include a "cancel" or "no thanks" button that allows the user to // continue using your app without granting the permission. showInContextUI(...) } else -> { // You can directly ask for the permission. // The registered ActivityResultCallback gets the result of this request. requestPermissionLauncher.launch( Manifest.permission.REQUESTED_PERMISSION) } }
Java
if (ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION) == PackageManager.PERMISSION_GRANTED) { // You can use the API that requires the permission. performAction(...); } else if (shouldShowRequestPermissionRationale(...)) { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected. In this UI, // include a "cancel" or "no thanks" button that allows the user to // continue using your app without granting the permission. showInContextUI(...); } else { // You can directly ask for the permission. // The registered ActivityResultCallback gets the result of this request. requestPermissionLauncher.launch( Manifest.permission.REQUESTED_PERMISSION); }
開発者自身がリクエスト コードを管理する
システムに権限リクエスト コードの管理を許可する代わりに、開発者自身がリクエスト コードを管理することもできます。そのためには、requestPermissions()
の呼び出しにリクエスト コードを含めます。
次のコード スニペットは、リクエスト コードを使用して権限をリクエストする方法を示しています。
Kotlin
when { ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION ) == PackageManager.PERMISSION_GRANTED -> { // You can use the API that requires the permission. performAction(...) } shouldShowRequestPermissionRationale(...) -> { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected. In this UI, // include a "cancel" or "no thanks" button that allows the user to // continue using your app without granting the permission. showInContextUI(...) } else -> { // You can directly ask for the permission. requestPermissions(CONTEXT, arrayOf(Manifest.permission.REQUESTED_PERMISSION), REQUEST_CODE) } }
Java
if (ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION) == PackageManager.PERMISSION_GRANTED) { // You can use the API that requires the permission. performAction(...); } else if (shouldShowRequestPermissionRationale(...)) { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected. In this UI, // include a "cancel" or "no thanks" button that allows the user to // continue using your app without granting the permission. showInContextUI(...); } else { // You can directly ask for the permission. requestPermissions(CONTEXT, new String[] { Manifest.permission.REQUESTED_PERMISSION }, REQUEST_CODE); }
ユーザーがシステム権限ダイアログに応答すると、システムはアプリの onRequestPermissionsResult()
の実装を呼び出します。システムは、ユーザーの応答と、開発者が定義したリクエスト コードを権限ダイアログに渡します。次のコード スニペットをご覧ください。
Kotlin
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { when (requestCode) { PERMISSION_REQUEST_CODE -> { // If request is cancelled, the result arrays are empty. if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // Permission is granted. Continue the action or workflow // in your app. } else { // Explain to the user that the feature is unavailable because // the features requires a permission that the user has denied. // At the same time, respect the user's decision. Don't link to // system settings in an effort to convince the user to change // their decision. } return } // Add other 'when' lines to check for other // permissions this app might request. else -> { // Ignore all other requests. } } }
Java
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case PERMISSION_REQUEST_CODE: // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission is granted. Continue the action or workflow // in your app. } else { // Explain to the user that the feature is unavailable because // the features requires a permission that the user has denied. // At the same time, respect the user's decision. Don't link to // system settings in an effort to convince the user to change // their decision. } return; } // Other 'case' lines to check for other // permissions this app might request. } }
権限の拒否を処理する
ユーザーが権限のリクエストを拒否した場合は、その影響をユーザーが理解できるようにする必要があります。特に、権限がないために動作しない機能をユーザーに知らせる必要があります。その際は、次のベスト プラクティスを念頭に置いてください。
ユーザーの注意を引く。アプリに必要な権限がないために機能が制限されている UI を強調します。次のような方法が考えられます。
- 機能の結果やデータが表示される場所にメッセージを表示する。
- エラーアイコンとエラーを示す色を含んだ別のボタンを表示する。
具体的に記述する。一般的なメッセージは表示しないでください。代わりに、アプリに必要な権限がないためにどの機能が利用できないかを記載します。
ユーザー インターフェースをブロックしない。具体的には、アプリの使用を妨げるような全画面の警告メッセージは表示しないでください。
同時に、アプリは権限のリクエストを拒否するというユーザーの決定を尊重する必要があります。Android 11(API レベル 30)以降では、デバイスにインストールされたアプリの全期間に、同じ権限に対してユーザーが何度も [許可しない] をタップした場合、アプリがその権限を再度リクエストしても、ユーザーにシステム権限ダイアログが表示されることはありません。このユーザーのアクションにより、[次回から表示しない] が選択されたことになります。以前のバージョンでは、ユーザーが以前に [次回から表示しない] チェックボックスまたはオプションをオンにしていない限り、アプリが権限をリクエストするたびに、システム権限ダイアログが表示されていました。
状況によっては、権限が自動的に拒否され、ユーザーによるアクションを必要としない場合があります(同じように、権限が自動的に付与される場合もあります)。自動的な動作を想定しないことが重要です。権限を必要とする機能にアプリがアクセスするたびに、その権限がアプリに付与されていることを確認する必要があります。
アプリの権限を求めるときのユーザー エクスペリエンスを向上させるには、アプリの権限に関するベスト プラクティスもご覧ください。
1 回だけのアクセス許可
Android 11(API レベル 30)以降では、アプリが位置情報、マイク、またはカメラに関連する権限をリクエストするたびに、図 1 に示すように、ユーザー向けの権限ダイアログに [今回のみ] というオプションが表示されます。ユーザーがダイアログでこのオプションを選択した場合、アプリには一時的な「1 回だけのアクセス許可」が付与されます。
アプリの動作とユーザーの操作に応じて、一定期間アプリは該当するデータにアクセスできます。
- アプリのアクティビティが表示されている間、アプリはデータにアクセスできます。
- ユーザーがアプリをバックグラウンドに移行した場合、アプリは短時間だけ引き続きデータにアクセスできます。
- アクティビティが表示されている間にフォアグラウンド サービスを起動した場合、ユーザーがアプリをバックグラウンドに移動しても、そのフォアグラウンド サービスが停止するまで、アプリは引き続きそのデータにアクセスできます。
- ユーザーがシステム設定などで 1 回だけのアクセス許可を取り消した場合、アプリはフォアグラウンドサービスを開始したかどうかにかかわらず、データにアクセスできなくなります。他の権限と同様に、ユーザーがアプリの 1 回だけのアクセス許可を取り消すと、アプリのプロセスは終了します。
ユーザーが次にアプリを開いて、アプリ内の機能が位置情報、マイク、またはカメラへのアクセスをリクエストすると、ユーザーに再度権限の付与が求められます。
使用していないアプリの権限を自動リセットする
Android 11(API レベル 30)以上をターゲットとするアプリが数か月間使用されていない場合、システムはユーザーデータを保護するために、ユーザーがアプリに付与した機密情報に関する実行時の権限を自動的にリセットします。このアクションは、ユーザーがシステム設定で権限を表示してアプリのアクセスレベルを [拒否] に変更するのと同じ効果があります。
アプリが実行時に権限をリクエストするためのおすすめの方法に従っている場合、アプリに変更を加える必要はありません。
自動リセットを無効にするようにユーザーにリクエストする
必要に応じて、システムがアプリの権限をリセットしないように設定することをユーザーに依頼します。これは、以下のユースケースのように、ユーザーがアプリを操作しなくても主にバックグラウンドで動作することが前提となっている場合に役立ちます。
- 家族の安全を確保する
- データを同期する
- スマート デバイスと通信する
- コンパニオン デバイスとペア設定する
アプリ内のシステム設定のページにユーザーを誘導するには、Intent.ACTION_AUTO_REVOKE_PERMISSIONS
インテント アクションを含むインテントを呼び出します。ユーザーはこの画面で次の手順に沿って、システムがアプリの権限をリセットしないように設定できます。
- [権限] をタップすると、[アプリの権限] 設定画面が読み込まれます。
- 図 2 に示されているように、[アプリが使用されていない場合に権限を削除] をオフにします。
自動リセットが無効になっているかどうかを確認する
アプリで自動リセット機能が無効になっているか確認するには、isAutoRevokeWhitelisted()
を呼び出します。
この方法で true
が返された場合、アプリの権限は自動リセットされません。
必要に応じてデフォルト ハンドラになるようリクエストする
アプリによっては、通話履歴や SMS メッセージに関するユーザーの機密情報にアクセスする必要があります。通話履歴や SMS メッセージに固有の権限をリクエストするアプリを Play ストアに公開する場合は、実行時の権限をリクエストする前に、アプリを中核的システム機能のデフォルト ハンドラとして設定することをユーザーに求める必要があります。
デフォルト ハンドラの詳細(デフォルト ハンドラのプロンプトをユーザーに表示する方法など)については、デフォルト ハンドラでのみ使用される権限のガイドをご覧ください。
実行時の権限をテストする
このセクションでは、実行時の権限のいくつかの要素をテストする方法について説明します。
すべての実行時の権限を付与する
エミュレータまたはテストデバイスにアプリをインストールする際にすべての実行時の権限を自動的に付与するには、次のコード スニペットに示すように、adb shell install
コマンドで -g
オプションを使用します。
adb shell install -g PATH_TO_APK_FILE
アプリの権限の自動リセットを実行する
システムがアプリの権限をリセットすることを確認するには、次のようにします。
システムがアプリの権限をリセットするまでのデフォルトの時間を保持します。それにより、テスト後の復元が可能になります。
threshold=$(adb shell device_config get permissions \ auto_revoke_unused_threshold_millis2)
システムが権限をリセットするまでの時間を短縮します。次の例では、アプリの操作を中断して 1 秒後にアプリの権限がリセットされるように変更されています。
adb shell device_config put permissions \ auto_revoke_unused_threshold_millis2 1000
次のスニペットに示すように、自動リセット プロセスを手動で呼び出します。 テストデバイスを短時間(約 45 秒間)オンにしてから、このコマンドを実行します。
adb shell cmd jobscheduler run -u 0 -f \ com.google.android.permissioncontroller 2
アプリが自動リセット イベントを処理できることを確認します。
システムがアプリの権限を自動リセットするまでのデフォルトの時間を元に戻します。
adb shell device_config put permissions \ auto_revoke_unused_threshold_millis2 $threshold
参考情報
パーミッションについて詳しくは、以下の記事をご覧ください。
権限のリクエストについて詳しくは、以下のサンプルアプリをダウンロードしてください。