Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

アプリの権限をリクエストする

すべての Android アプリは、アクセスが制限されたサンドボックス内で実行されます。アプリ自体のサンドボックスの外部にあるリソースや情報を使用する必要がある場合は、権限を宣言し、このアクセスを提供する権限リクエストを設定できます。この手順は、権限を使用するためのワークフローの一部です。

危険な権限を宣言し、Android 6.0(API レベル 23)以降を搭載しているデバイスにアプリがインストールされている場合は、このガイドの手順に従って実行時に危険な権限をリクエストする必要があります。

危険な権限を宣言していない場合、または Android 5.1(API レベル 22)以前を搭載しているデバイスにアプリがインストールされている場合は、権限が自動的に付与されるため、このページの残りの手順を完了する必要はありません。

基本方針

実行時に権限をリクエストする際に適用される基本方針は次のとおりです。

  • 権限を必要とする機能をユーザーが操作し始めたら、その状況に応じた権限をリクエストすること。
  • ユーザーをブロックしないこと。権限に関連した説明を表示する UI フローは、常にキャンセルできるようにしてください。
  • 機能に必要な権限をユーザーが拒否または取り消した場合は、グレースフル デグラデーションを行いアプリの使用を続けられるようにすること。その際、権限を必要とする機能が無効になる場合もあります。
  • システム動作を前提としないこと。たとえば、すべての権限が同じ権限グループにあるとは限りません。権限グループが役立つのは、アプリが密接に関連する権限をリクエストしたときに、ユーザーに表示されるシステム ダイアログの数を最小限に抑えられることだけです。

権限をリクエストする場合のワークフロー

アプリで実行時の権限を宣言してリクエストする前に、アプリでその権限が必要かどうかを検討してください。権限の宣言を必要とすることなく、写真の撮影、メディア再生の一時停止、関連性の高い広告の掲載など、アプリのさまざまなユースケースに対応できます。

アプリで実行時の権限を宣言してリクエストする必要があると判断した場合は、次の手順を行ってください。

  1. アプリのマニフェスト ファイル内で、アプリがリクエストする必要がある権限を宣言します。
  2. アプリの特定のアクションが特定の実行時の権限に関連付けられるようアプリの UX を設計します。アプリが非公開のユーザーデータにアクセスするための権限の付与をどのアクションで必要となるかをユーザーが認識できるようにします。
  3. 特定の非公開のユーザーデータへのアクセスを必要とするアプリ内のタスクやアクションをユーザーが起動するまで待ちます。ユーザーが起動した時点で、アプリはそのデータへのアクセスに必要となる実行時の権限をリクエストできます。
  4. アプリが必要とする実行時の権限をユーザーがすでに付与しているかどうかを確認します。すでに付与している場合、アプリは非公開のユーザーデータにアクセスできます。付与されていない場合は、次のステップに進みます。

    権限を必要とする操作を実行するたびに、その権限の有無を確認する必要があります。

  5. アプリがユーザーに根拠を示す必要があるかどうかを確認します。この根拠によって、特定の実行時の権限を付与してもらう必要がある理由を説明します。アプリが根拠を示す必要がないとシステムで判断された場合は、UI 要素を表示せずに、直接次のステップに進みます。

    ただし、アプリが根拠を示す必要があるとシステムで判断された場合は、UI 要素でその根拠をユーザーに提示します。この根拠によって、アプリがアクセスしようとしているデータと、実行時の権限を付与した場合にアプリからユーザーにもたらされるメリットを明確に説明する必要があります。ユーザーが根拠を確認したら、次のステップに進みます。

  6. アプリが非公開のユーザーデータにアクセスするために必要な実行時の権限をリクエストします。システムによって、実行時の権限を求めるメッセージが表示されます(権限の概要ページに表示されるメッセージなど)。

  7. ユーザーの応答で、ユーザーが実行時の権限を付与するか、拒否するかを確認します。

  8. ユーザーがアプリに権限を付与した場合は、非公開のユーザーデータにアクセスできます。ユーザーが権限を拒否した場合は、アプリ エクスペリエンスのグレースフル デグラデーションを行って、その権限で保護される情報がなくてもユーザーに機能が提供されるようにします。

図 1 は、このプロセスに関連するワークフローと一連の判断を示します。

図 1. Android で実行時の権限を宣言してリクエストする場合のワークフローを示す図

アプリに権限が付与されているかどうかを確認する

ユーザーがすでに特定の権限をアプリに付与しているかどうかを確認するには、その権限を 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 以降のライブラリを使用してください。

次のクラスのいずれかを使用できます。

RequestPermission コントラクトを使用する手順を以下に示します。RequestMultiplePermissions コントラクトを使用する手順もこれとほぼ同じです。

  1. アクティビティまたはフラグメントの初期化ロジックで、registerForActivityResult() の呼び出しに ActivityResultCallback の実装を渡します。ActivityResultCallback は、権限リクエストに対するユーザーの応答の処理方法を定義します。

    ActivityResultLauncher 型の戻り値 registerForActivityResult() への参照を保持します。

  2. 必要に応じてシステム権限ダイアログを表示するには、前の手順で保存した 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 回だけのアクセス許可

[今回のみ] というオプションは、ダイアログにある 3 つのボタンのうち 2 つ目のオプションです。
図 2. アプリが 1 回だけのアクセス許可をリクエストしたときに表示されるシステム ダイアログ

Android 11(API レベル 30)以降では、アプリが位置情報、マイク、またはカメラに関連する権限をリクエストするたびに、図 2 に示すように、ユーザー向けの権限ダイアログに [今回のみ] というオプションが表示されます。ユーザーがダイアログでこのオプションを選択した場合、アプリには一時的な「1 回だけのアクセス許可」が付与されます。

アプリの動作とユーザーの操作に応じて、アプリは該当するデータに一定時間アクセスできます。

  • アプリのアクティビティが表示されている間、アプリはデータにアクセスできます。
  • ユーザーがアプリをバックグラウンドに移行した場合、アプリは短時間だけ引き続きデータにアクセスできます。
  • アクティビティが表示されている間にフォアグラウンド サービスを起動した場合、ユーザーがアプリをバックグラウンドに移動しても、そのフォアグラウンド サービスが停止するまで、アプリは引き続きそのデータにアクセスできます。
  • ユーザーがシステム設定などで 1 回だけのアクセス許可を取り消した場合、アプリはフォアグラウンドサービスを開始したかどうかにかかわらず、データにアクセスできなくなります。他の権限と同様に、ユーザーがアプリの 1 回だけのアクセス許可を取り消すと、アプリのプロセスは終了します。

ユーザーが次にアプリを開いて、アプリ内の機能が位置情報、マイク、またはカメラへのアクセスをリクエストすると、ユーザーに再度権限の付与が求められます。

使用していないアプリの権限を自動リセットする

Android 11(API レベル 30)以上をターゲットとするアプリを数か月間使用しないと、ユーザーデータを保護するため、システムはユーザーがアプリに付与した機密情報に関する実行時の権限を自動的にリセットします。このアクションは、ユーザーがシステム設定で権限を表示してアプリのアクセスレベルを [拒否] に変更するのと同じ効果があります。

アプリがおすすめの方法に従って実行時の権限をリクエストする場合、アプリに変更を加える必要はありません。

自動リセットを無効にするようにユーザーにリクエストする

必要に応じて、システムがアプリの権限をリセットしないように設定することをユーザーに依頼します。これは、以下のユースケースのように、ユーザーがアプリを操作しなくても主にバックグラウンドで動作することが前提となっている場合に役立ちます。

図 3. 特定のアプリについてユーザーが権限の自動リセットを無効化
  • 家族の安全を確保する
  • データを同期する
  • スマート デバイスと通信する
  • コンパニオン デバイスとペア設定する

ユーザーにアプリ内のシステム設定のページを表示するには、Intent.ACTION_AUTO_REVOKE_PERMISSIONS インテント アクションを含むインテントを呼び出します。ユーザーはこの画面で次の手順に沿って、システムがアプリの権限をリセットしないように設定できます。

  1. [権限] をタップすると、[アプリの権限] 設定画面が読み込まれます。
  2. 図 3 に示されているように、[アプリが使用されていない場合に権限を削除] をオフにします。

自動リセットが無効になっているかどうかを確認する

アプリで自動リセット機能が無効になっているかどうかを確認するには、isAutoRevokeWhitelisted() を呼び出します。 この方法で true が返された場合、アプリの権限は自動リセットされません。

必要に応じてデフォルト ハンドラになるようリクエストする

アプリによっては、通話履歴や SMS メッセージに関するユーザーの機密情報にアクセスする必要があります。通話履歴や SMS メッセージにのみ使用される権限をリクエストするアプリを Play ストアに公開する場合は、実行時の権限をリクエストする前に、アプリを中核的システム機能のデフォルト ハンドラとして設定することをユーザーに求める必要があります。

デフォルト ハンドラの詳細(デフォルト ハンドラのプロンプトをユーザーに表示する方法など)については、デフォルト ハンドラでのみ使用される権限のガイドをご覧ください。

実行時の権限をテストする

このセクションでは、実行時の権限のいくつかの要素をテストする方法について説明します。

すべての実行時の権限を付与する

エミュレータまたはテストデバイスにアプリをインストールする際にすべての実行時の権限を自動的に付与するには、次のコード スニペットに示すように、adb shell install コマンドで -g オプションを使用します。

adb shell install -g PATH_TO_APK_FILE

アプリの権限の自動リセットを実行する

システムがアプリの権限をリセットすることを確認するには、次のようにします。

  1. システムがアプリの権限をリセットするまでのデフォルトの時間を保持します。それにより、テスト後の復元が可能になります。

    threshold=$(adb shell device_config get permissions \
      auto_revoke_unused_threshold_millis2)
    
  2. システムが権限をリセットするまでの時間を短縮します。次の例では、アプリの操作を中断して 1 秒後にアプリの権限がリセットされるように変更されています。

    adb shell device_config put permissions \
      auto_revoke_unused_threshold_millis2 1000
    
  3. 次のスニペットに示すように、自動リセット プロセスを手動で呼び出します。 テストデバイスを短時間(約 45 秒間)オンにしてから、このコマンドを実行します。

    adb shell cmd jobscheduler run -u 0 -f \
      com.google.android.permissioncontroller 2
    
  4. アプリが自動リセット イベントを処理できることを確認します。

  5. システムがアプリの権限を自動リセットするまでのデフォルトの時間を元に戻します。

    adb shell device_config put permissions \
      auto_revoke_unused_threshold_millis2 $threshold
    

参考情報

権限について詳しくは、以下の記事をご覧ください。

権限のリクエストについて詳しくは、以下のサンプルアプリをダウンロードしてください。

  • Android RuntimePermissionsBasic サンプル Java | Kotlin