Android 11 デベロッパー プレビュー 2 が公開されました。ぜひお試しのうえ、フィードバックをお寄せください

アプリ パーミッションに関するベスト プラクティス

パーミッション リクエストは、デバイスで利用可能な機密情報を保護するためのものであるため、情報へのアクセスがアプリの動作に必要な場合に限り行うようにしてください。このドキュメントでは、こうした機密情報にアクセスせずに、同等またはそれ以上の機能を実現するための方法について説明します(Android オペレーティング システム内のパーミッションの仕組みについて包括的に説明するものではありません)。

Android パーミッションの概要については、パーミッションの概要をご覧ください。コード内でパーミッションを扱う方法については、アプリのパーミッションをリクエストするをご覧ください。

Android パーミッションを扱うときの原則

Android パーミッションを扱う際は、次の原則に従うことをおすすめします。

#1: アプリの動作に必要なパーミッションだけを使用する。パーミッションの使用方法によっては、機密情報にアクセスすることなく、別の方法(システム インテント、識別子、通話時のバックグラウンド移行)で必要な処理を実行できる場合があります。

#2: ライブラリが必要とするパーミッションに注意する。 ライブラリを追加すると、パーミッション要件も継承します。追加する要素、その要素が必要とするパーミッション、そのパーミッションの用途について注意する必要があります。

#3: 透明性を確保する。 パーミッションをリクエストするときは、アクセス対象とアクセス理由を明確にし、ユーザーが十分な情報に基づいて判断できるようにする必要があります。インストール時や実行時、パーミッション更新時のダイアログなど、パーミッションのリクエストとともにこの情報を表示してください。

#4: システム アクセスを明示的にする。 機密性の高い機能(カメラ、マイクなど)にアクセスするときは、継続的に通知を表示することで、データ収集のタイミングをユーザーに明確に示し、データをひそかに収集しているという疑いをもたれないようにします。

以下のセクションでは、Android アプリの開発時に上記のルールをどのように適用するのかについて詳細に説明します。

Android 6.0 以降のパーミッション

Android 6.0(Marshmallow)において、インストール前ではなく実行時にアプリがユーザーにパーミッションをリクエストする新しいパーミッション モデルが導入されました。この新しいモデルをサポートするアプリは、サービスや、サービスによって保護されるデータが実際に必要になったときにパーミッションをリクエストします。新しいモデルの導入により、必ずしもアプリの全体的な動作が変化するわけではありませんが、機密性の高いユーザーデータの取り扱いに関しては変更された部分があります。

状況に応じたリクエストを促進: アプリの実行中に、アプリ内で、対象のパーミッション グループがカバーする機能にアクセスするパーミッションを求めるメッセージがユーザーに表示されます。ユーザーはパーミッションがリクエストされる状況に敏感に反応するため、リクエストするパーミッションとアプリの用途が一致していない場合には、そのパーミッションをリクエストしている理由について、詳細な説明をユーザーに提供することが極めて重要になります。可能な限り、リクエストの時点において、そしてユーザーがリクエストを拒否した場合はフォローアップ ダイアログにおいて、リクエスト理由を説明してください。

パーミッションの付与方法の柔軟性が向上: ユーザーは、パーミッションがリクエストされたときやパーミッションの設定において、各パーミッションへのアクセスを拒否することができますが、その結果としてアプリが正常に機能しなくなると困惑する可能性があります。Google アナリティクスなどを使用して、パーミッションの付与を拒否したユーザーの数を確認し、そのパーミッションを使用しないようにアプリをリファクタリングするか、アプリが正常に動作するためにパーミッションが必要になる理由を明確にすることをおすすめします。また、ユーザーがパーミッション リクエストを拒否した場合や、設定でパーミッションをオフに切り替えた場合に生じる例外をアプリが処理できるようにすることも必要です。

トランザクションの負担が増大: ユーザーは、一括ではなく、個々にパーミッション グループへのアクセス権限を付与するよう求められます。このため、リクエストするパーミッションの数を最小限に抑えることが極めて重要になります。リクエストするパーミッションの数が多いと、ユーザーがパーミッションを付与する際の負担が増え、リクエストが拒否される可能性が高くなります。

デフォルト ハンドラでなければ付与されないパーミッション

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

デフォルト ハンドラ プロンプトをユーザーに表示する方法を含め、デフォルト ハンドラの詳細については、デフォルト ハンドラに限り使用できるパーミッションに関するガイドをご覧ください。

不要なパーミッションのリクエストを避ける

パーミッションを求めるたびに、ユーザーに決断する負担を強いることになります。そのため、パーミッション リクエストの回数は最小限に抑える必要があります。Android 6.0(API レベル 23)以降を搭載しているデバイスをユーザーが使用している場合、パーミッションを必要とする新しいアプリ機能をユーザーが試すたびに、アプリが求めるパーミッション リクエストが表示され、ユーザーの作業が中断されることになります。それ以前のバージョンの Android デバイスを使用している場合、ユーザーはアプリのインストール時にアプリのパーミッションをすべて付与する必要があります。リストが長すぎる場合や表示が不適切な場合、ユーザーはアプリをインストールしない可能性があります。そのため、アプリが必要とするパーミッションの数は最小限に抑える必要があります。

以下のセクションでは、一般的なユースケースの代替手段として、パーミッション リクエストの数を抑えるうえで役に立つ方法について説明します。パーミッションのリクエストが少ない類似アプリと比べた場合、ユーザーにリクエストするパーミッションの数とタイプはダウンロード数に影響するため、不要な機能のパーミッション リクエストは避けることをおすすめします。

パーミッションではなくインテントを使用する

多くの場合、アプリがタスクを実行する方法には 2 つの選択肢があります。アプリ自身がタスクを実行するパーミッションを求める方法と、インテントを使用して別のアプリにタスクを実行してもらう方法です。

たとえば、デバイスのカメラを使用して写真を撮る機能がアプリに必要だとします。アプリは CAMERA パーミッションをリクエストできます。このパーミッションにより、アプリはカメラに直接アクセスできます。この場合、アプリはカメラ API を使用してカメラの操作や写真の撮影を行います。この方法の場合、アプリが写真撮影プロセスを完全に制御できるため、アプリにカメラの UI を組み込むことができます。

ただし、ユーザーデータへのアクセスを求める頻度が低い場合、つまり、データへのアクセスが必要になるたびにユーザーに実行時ダイアログを表示することが許容範囲内の場合は、インテント ベースのリクエストを使用できます。Android に用意されているシステム インテントを利用した場合、アプリはパーミッションを求める必要がありません。インテント ベースのリクエストの発行時に、アプリと共有する要素(存在する場合)をユーザーが選択します。

たとえば、インテント アクション タイプの MediaStore.ACTION_IMAGE_CAPTURE または MediaStore.ACTION_VIDEO_CAPTURE を使用すると、Camera オブジェクトを直接使用することなく(つまり、パーミッションを要求することなく)、画像や動画を撮影することができます。この場合は、画像を撮影するたびに、アプリに代わってシステム インテントがユーザーのパーミッションを求めます。

同様に、電話をかける処理やユーザーの連絡先にアクセスする処理などが必要な場合も、適切なインテントを作成することで、その処理を行うことができます。もちろん、パーミッションをリクエストして、対象オブジェクトに直接アクセスすることもできます。各方法には、それぞれ長所と短所があります。

パーミッションを使用した場合:

  • 処理を実行する際、アプリがユーザー エクスペリエンスを完全に制御します。ただし、このような広範な制御を行うには適切な UI を設計する必要があるため、コードが複雑化します。
  • ユーザーの Android バージョンに応じて、実行時またはインストール時のいずれかに 1 回、ユーザーにパーミッションの付与を求めるメッセージが表示されます。その後は、ユーザーに追加操作を求めることなく、アプリは処理を実行できます。ただし、ユーザーがパーミッションを付与しなかった場合(あるいは、後でパーミッションを取り消した場合)、アプリは処理を一切実行できなくなります。

インテントを使用した場合:

  • 処理用の UI を設計する必要はありません。インテントを処理するアプリが UI を提供します。
  • ユーザーは、自分が好きなアプリを使用してタスクを実行できます。たとえば、お気に入りの写真アプリを選択して写真を撮影できます。
  • 対象の処理に対してデフォルト アプリが指定されていない場合、アプリの選択を求めるプロンプトがユーザーに表示されます。ユーザーがデフォルト ハンドラを指定しないと、処理のたびに選択ダイアログが表示される可能性があります。

ユーザーに負担を与えない

Android 6.0(API レベル 23)以降を使用しているユーザーは、アプリの実行中に、アプリにパーミッションを付与する必要があります。ユーザーは一度に多くのパーミッションをリクエストされると、負担を感じてアプリを放棄する場合があります。必要なパーミッションだけをユーザーにリクエストするようにしてください。

アプリによっては、1 つまたは複数のパーミッションが必須の場合もあります。その場合、アプリの起動後すぐに、すべてのパーミッションをリクエストすることをおすすめします。たとえば、写真アプリを作成する場合、そのアプリはデバイスのカメラにアクセスする必要があります。アプリを初めて起動するときにカメラを使用するパーミッションを求められてもユーザーは驚きません。ただし、ユーザーの連絡先と写真を共有する機能がアプリにある場合は、最初の起動時に READ_CONTACTS パーミッションを求めるのではなく、ユーザーが「共有」機能を使ったときに、初めてパーミッションを求めるようにしてください。

アプリにチュートリアルが用意されている場合は、チュートリアル シーケンスの最後に、アプリに必須のパーミッションをリクエストすることをおすすめします。

音声フォーカスを失った後にメディアを一時停止する

アプリによっては、ユーザーに電話がかかってきたらバックグラウンドに移行し、通話が終了したらリフォーカスする必要があります。

たとえば、通話中にメディア プレーヤーがミュートや一時停止を行う場合など、このようなケースでは一般的に、PhoneStateListener を使用して通話状態の変化をリッスンするか、android.intent.action.PHONE_STATE のブロードキャストをリッスンします。この解決方法の問題点は、READ_PHONE_STATE パーミッションが必要になることです。ユーザーは、デバイスや SIM ハードウェアの ID、着信した電話番号など、機密性の高いさまざまなデータに対するアクセス権限を付与する必要があります。また、Android 10(API レベル 29)以降でアプリが実行された場合、LISTEN_CELL_LOCATION イベントと LISTEN_CELL_INFO イベントは、LOCATION パーミッションが必須になります(特に、アプリが Android 10 以降をターゲットにしている場合は ACCESS_FINE_LOCATION)。

ユーザーが通話中かどうかは、READ_PHONE_STATE パーミッションや MODIFY_PHONE_STATE パーミッションがなくても、明示的パーミッションを必要としない AudioFocus をリクエストすることで検出できます(機密情報にアクセスしないため、明示的パーミッションは不要です)。必要なのは、オーディオをバックグラウンドに移行するために必要なコードを onAudioFocusChange() イベント ハンドラに追加することだけです。OS が音声フォーカスをシフトすると、このコードが自動的に実行されます。この方法の詳細については、こちらをご覧ください。

インスタンスが稼働しているデバイスを判別する

アプリのインスタンスが稼働しているデバイスを判別するために一意の ID が必要になることがあります。

アプリがデバイス別の設定やメッセージに対応している場合があります(たとえば、ユーザーが車内と自宅で別々のプレイリストを利用できるように、デバイス別のプレイリストをクラウドに保存する場合など)。一般的な解決策は、Device IMEI などのデバイス ID を利用する方法ですが、これには Device ID and call information パーミッション グループ(M 以降の場合は PHONE)が必要になります。また、この方法の場合、すべてのアプリ間で共有される再設定不可能な ID を使用することになります。

このようなタイプの ID の代わりとなる方法が 2 つあります。

  1. com.google.android.gms.iid InstanceID API を使用する。getInstance(Context context).getID() はアプリ インスタンス用の一意のデバイス ID を返します。このアプリ インスタンス別の ID は、アプリに関する情報を保存する際にキーとして使用することが可能で、ユーザーがアプリを再インストールするとリセットされます。
  2. randomUUID() などの基本的なシステム機能を使用して、独自のアプリ ストレージ別 ID を作成する。

広告やユーザー分析用の一意の ID を作成する

アプリにログインしていないユーザーのプロファイルを作成するために一意の ID が必要になることがあります(たとえば、広告のターゲティングやコンバージョンの測定などのため)。

広告やユーザー分析用にプロファイルを作成する場合、他のアプリと共有できる ID が必要になることがあります。一般的な解決策は、Device IMEI などのデバイス ID を利用する方法ですが、これには Device ID and call information パーミッション グループ(API レベル 23 以降の場合は PHONE)が必要になります。また、この ID をユーザーがリセットすることはできません。いずれの場合も、再設定不可能な ID を使用し、ユーザーによっては不自然に感じられるパーミッションをリクエストすることに加え、Play デベロッパー プログラム ポリシーに違反することにもなります。

アプリ間で ID を共有することが必要になる可能性があるため、残念ながら、com.google.android.gms.iid InstanceID API やシステム機能を使用してアプリ別 ID を作成する方法は、適切な解決策ではありません。もう 1 つの解決策は、Advertising Identifier を使用する方法です。この ID は、 AdvertisingIdClient.Info クラスから getId() メソッド経由で利用できます。AdvertisingIdClient.Info オブジェクトを作成し(getAdvertisingIdInfo(Context) メソッドを使用)、getId() メソッドを呼び出すことで、この ID を利用することができます。ただし、これはブロッキング メソッドであるため、メインスレッドからこのメソッドを呼び出さないでください。このメソッドの詳細については、こちらをご覧ください。

使用するライブラリを把握する

アプリ内で使用するライブラリにパーミッションが必要となることがあります。たとえば、広告や分析用のライブラリは、必要な機能を実装するうえで LOCATION パーミッション グループへのアクセスを必要とする場合があります。ただし、ユーザーの観点からすると、パーミッションをリクエストしているのはアプリであって、ライブラリではありません。

一般的に、複数のアプリが同じ機能を実現する場合、ユーザーは、使用するパーミッションが少ない方のアプリを選択します。それと同じように、デベロッパーは、ライブラリを調査して、不要なパーミッションを使用していないサードパーティ SDK を選択する必要があります。たとえば、位置情報機能を提供するライブラリを使用する場合、位置情報ベースのターゲティング機能を使用するのでなければ、FINE_LOCATION パーミッションをリクエストしないようにします。

パーミッションが必要な理由を説明する

requestPermissions() を呼び出したときに表示されるパーミッション ダイアログには、アプリが必要とするパーミッションの内容は表示されますが、その理由は表示されません。そのため、ユーザーが困惑する場合があります。requestPermissions() を呼び出す前に、アプリがそのパーミッションを必要とする理由をユーザーに説明することをおすすめします。

調査によると、ユーザーは、アプリがパーミッションを必要とする理由を知っていると、パーミッション リクエストを快く受け入れるようになることが判明しています。あるユーザー調査は、次のように指摘しています。

...ユーザーが特定のパーミッションを特定のモバイルアプリに付与するかどうかは、そのパーミッションに関連付けられている目的に大きく左右されます。たとえば、ユーザーが位置情報に対するアクセス権限を付与するかどうかは、パーミッション リクエストがアプリの中核的機能を実現するうえで必要なのか、広告掲載ネットワークや分析会社に位置情報を提供するために必要なのかによって変わります。1

カーネギー メロン大学の Jason Hong 教授は、研究チームの調査結果に基づいて次のように述べています。

...ターゲット広告の場合など、位置情報のような機密性の高い情報をアプリが使用する場合、その理由を知っていると、アプリが位置情報を使用することを知らされただけのときよりも、ユーザーの信頼感は高まります。1

そのため、パーミッション グループに属する一部の API 呼び出しだけを使用する場合は、使用するパーミッションと、そのパーミッションを使用する理由について、明示的なリストを作成することをおすすめします。たとえば、次のように対応します。

  • 低精度の位置情報だけを使用する場合は、その点について、アプリの説明やアプリに関するヘルプ記事でユーザーに知らせます。
  • 不正行為からユーザーを保護する認証コードを取得するうえで、SMS メッセージにアクセスする必要がある場合は、その点について、アプリの説明か、データに最初にアクセスするタイミングでユーザーに知らせます。

    注: アプリが Android 8.0(API レベル 26)以降をターゲットとしている場合は、ユーザーの認証情報を検証するプロセスの一環として READ_SMS パーミッションをリクエストしないでください。代わりに、createAppSpecificSmsToken() を使用してアプリ固有のトークンを生成し、検証用の SMS メッセージを送信できる別のアプリやサービスにこのトークンを渡してください。

状況によっては、機密データに対するアクセスをリアルタイムでユーザーに知らせることをおすすめします。たとえば、カメラやマイクにアクセスする場合は、アプリ内や通知トレイ内(アプリがバックグラウンドで稼働している場合)に通知アイコンを表示してユーザーに知らせることで、データをひそかに収集しているわけではないことを示すことをおすすめします。

最後に、アプリを正常に機能させるためにパーミッションをリクエストする必要があるが、ユーザーにとってその理由が明白ではない場合は、最も機密性の高いパーミッションを必要とする理由をユーザーに知らせる方法を探してください。

両方のパーミッション モデルでテストする

Android 6.0(API レベル 23)以降、ユーザーはアプリのインストール時ではなく、アプリの実行時にパーミッションの付与や取り消しを行うようになりました。そのため、さまざまな条件下でアプリをテストする必要があります。Android 6.0 より前のバージョンでは、アプリが実行されていれば、アプリ マニフェスト内で宣言したすべてのパーミッションが付与されていると見なすことができました。Android 6.0 以降の場合、ユーザーは、すべてのアプリでパーミッションのオンとオフを切り替えることができます(API レベル 22 以下をターゲットとしているアプリであっても同様です)。パーミッションの付与状況にかかわらず、アプリが正常に機能するかテストする必要があります。

API レベル 23 以上を搭載しているデバイスを対象として、パーミッション関連のコードの問題を検出するうえで役立つおすすめの方法を以下に示します。

  • アプリの現在のパーミッションと関連コードパスを確認します。
  • 権限で保護されているサービスやデータでユーザーフローをテストします。
  • パーミッション付与とパーミッション取り消しのさまざまな組み合わせをテストします。たとえば、カメラアプリのマニフェスト内に CAMERAREAD_CONTACTSACCESS_FINE_LOCATION がリストされているとします。各パーミッションのオンとオフを切り替えてアプリをテストし、すべてのパーミッション構成を適切に処理できるか確認する必要があります。
  • コマンドラインからパーミッションを管理するには、adb ツールを使用します。
    • グループごとにパーミッションとステータスをリスト表示するには:
      $ adb shell pm list permissions -d -g
    • 1 つまたは複数のパーミッションの付与や取り消しを行うには:
      $ adb shell pm [grant|revoke] <permission-name> ...
  • アプリを分析して、パーミッションを使用しているサービスを特定します。

参考リンク

出典

[1] Modeling Users' Mobile App Privacy Preferences: Restoring Usability in a Sea of Permission Settings(J. Lin、B. Liu、N. Sadeh、J. Hong 著。SOUPS 2014 紀要)。