Android Q のプライバシーに関する変更点: アプリに付与するデバイス位置情報アクセス権限に関するユーザー制御

Android Q ベータ版 5 の時点で、この変更により次のような影響があります。

  • 影響を受けるアプリ: バックグラウンドにおけるデバイス位置情報へのアクセス権限をリクエストするアプリ
  • 通知の表示: バックグラウンドでデバイスの位置情報にアクセスする権限がアプリに付与された後に表示される場合があります。
  • 対応策: バックグラウンドで位置情報にアクセスするための新しいアクセス権限を使用します。バックグラウンドで位置情報が更新されない場合でも、グレースフル デグラデーションを確保します。
  • この動作は Android Q では常に有効になっています。

ご意見、ご感想をお待ちしております。 Android Q ベータ プログラムでこの機能の使用中に見つかった問題を報告してくださるようお願いいたします。

ユーザー向けダイアログのスクリーンショット
図 1: 位置情報に関してユーザーの同意をリクエストするダイアログ

Android Q では、デバイスの位置情報にアプリがいつアクセスできるのかをユーザーが詳細に制御できます。Android Q 上で稼働しているアプリが位置情報アクセス権限をリクエストすると、ユーザーには図 1 に示すダイアログが表示されます。このダイアログで、ユーザーは「アプリの使用中」(フォアグラウンドのみ)または「常時」(フォアグラウンドとバックグラウンド)という 2 つの範囲のいずれかで、位置情報アクセス権限を付与できます。

位置情報に対するアプリのアクセス権限をユーザーが詳細に制御できるようにするため、Android Q では、新しい位置情報権限 ACCESS_BACKGROUND_LOCATION が導入されています。既存の ACCESS_FINE_LOCATION 権限や ACCESS_COARSE_LOCATION 権限とは異なり、新しい権限が位置情報へのアクセスを制御するのは、アプリがバックグラウンドで稼働しているときのみです。アプリのアクティビティの 1 つが可視状態であるか、アプリがフォアグラウンド サービスを実行していない限り、そのアプリはバックグラウンドにあると見なされます。

バックグラウンドから位置情報をリクエストする

Android Q をターゲットにしているアプリが、バックグラウンドで稼働しているときにデバイス位置情報にアクセスする必要がある場合、アプリのマニフェスト ファイル内で、次のように新しいパーミッションを宣言する必要があります。

    <manifest>
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    </manifest>
    

Android 9(API レベル 28)以前をターゲットにしているアプリが Android Q 上で稼働している場合は、次の動作が適用されます。

  • アプリが ACCESS_FINE_LOCATION または ACCESS_COARSE_LOCATION のいずれかの <uses-permission> 要素を宣言している場合、インストール中に ACCESS_BACKGROUND_LOCATION<uses-permission> 要素が自動的に追加されます。
  • アプリが ACCESS_FINE_LOCATION または ACCESS_COARSE_LOCATION のいずれかをリクエストした場合、そのリクエストに対して ACCESS_BACKGROUND_LOCATION が自動的に追加されます。

バックグラウンド位置情報アクセス権限をリクエストする

アプリのユースケースで、バックグラウンドで実行しているときに位置情報へのアクセスが必要な場合、このアクセス権限が必要となる範囲を考慮することが重要です。

  • ナビゲーションやスマートホーム アクションなど、アプリのユースケースが、ユーザー開始型のアクションが継続することに依存している場合。この場合、アプリがフォアグラウンドで稼働しているときに限りデバイス位置情報へのアクセス権限を付与することをユーザーがリクエストしていたとしても、フォアグラウンド サービスを設定することで、ユーザーがデバイスのホームボタンを押した後やデバイスのディスプレイをオフにした後でも、デバイス位置情報へのアクセス権限を維持することができます。
  • ジオフェンスや現在地の共有など、アプリのユースケースが、常にデバイス位置情報を定期的にチェックすることに依存している場合。この場合、「アプリが正常に機能するためには、常にデバイス位置情報にアクセスできる必要があること」をユーザーに説明したうえで、バックグラウンド位置情報アクセス権限をリクエストする必要があります。

ユーザー開始型アクションの継続

注: アプリがバックグラウンドで稼働しているときに位置情報へのアクセス権限を必要としない場合は、Android Q をターゲットにして、新しいバックグラウンド位置情報パーミッションをリクエストしないことをおすすめします。このようにアプリを設定することで、ユーザーがアプリを使用しているときに限り位置情報の更新データを受け取るようになります。

該当するアプリの例については、GitHub の LocationUpdatesForegroundService プロジェクトをご覧ください。

ユーザーがアプリに対し、フォアグラウンド限定の位置情報アクセス権限を付与している場合でも、ユーザーがデバイスのホームボタンを押した後やデバイスの表示をオフにした後に、アプリが位置情報にアクセスすることが必要となるワークフローをユーザーが起動することがあります。

このような特定のユースケースでデバイス位置情報へのアクセス権限を維持するには、アプリのマニフェスト内でフォアグラウンド サービスのタイプ"location" であると宣言して、そのフォアグラウンド サービスを起動します。

    <service
        android:name="MyNavigationService"
        android:foregroundServiceType="location" ... >
        ...
    </service>
    

フォアグラウンド サービスを起動する前に、アプリがまだデバイス位置情報にアクセスできることを確認する必要があります。

Kotlin

    val permissionAccessCoarseLocationApproved = ActivityCompat
        .checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED

    if (permissionAccessCoarseLocationApproved) {
       // App has permission to access location in the foreground. Start your
       // foreground service that has a foreground service type of "location".
    } else {
       // Make a request for foreground-only location access.
       ActivityCompat.requestPermissions(this,
           arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
           your-permission-request-code
       )
    }
    

Java

    boolean permissionAccessCoarseLocationApproved =
        ActivityCompat.checkSelfPermission(this,
            permission.ACCESS_COARSE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED;

    if (permissionAccessCoarseLocationApproved) {
        // App has permission to access location in the foreground. Start your
        // foreground service that has a foreground service type of "location".
    } else {
       // Make a request for foreground-only location access.
       ActivityCompat.requestPermissions(this, new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION},
           your-permission-request-code);
    }
    

デバイス位置情報の定期的なチェック

アプリのユースケースによっては、デバイス位置情報に対するアクセス権限が常に必要となることがあります。たとえば、ジオフェンスや、友だちや家族との位置情報の共有などのユースケースが該当します。

このような条件が当てはまるアプリの場合、ユーザーがアプリに対してデバイス位置情報への常時アクセス権限を付与している場合に限り、変更を加えずに位置情報の更新をリクエストし続けることができます。

デバイス位置情報への常時アクセス権限をリクエストするサービスを宣言する方法を、次のコード スニペットに示します。

    <!-- It's unnecessary to include a foreground service type for services that must have
         access to the device's location "all the time" in order to run successfully.-->
    <service
        android:name="MyFamilyLocationSharingService" ... >
        ...
    </service>
    

該当するアプリの例については、GitHub の LocationUpdatesPendingIntent プロジェクトをご覧ください。

システム通知のスクリーンショット
図 2. デバイスの位置情報への「常時」アクセス権限がアプリに付与されていることを知らせる通知

ユーザーがアプリにデバイスの位置情報への「常時」アクセス権限を付与するたびに、ユーザーに送信される通知がスケジュールされます。この通知は、アプリによるデバイスの位置情報への常時アクセスが許可されていることをユーザーに知らせます。図 2 に示す通知の例をご覧ください。

アプリはバックグラウンドからでも位置情報へのアクセス権限をリクエストできますが、ユーザーは、アプリのアクセス権限をフォアグラウンドだけに制限したり、アクセス権限全体を取り消したりすることができます。そのため、アプリはサービスを起動するたびに、バックグラウンドで位置情報にアクセスすることが引き続き許可されているかを確認します。

ユーザーがアプリの位置情報アクセス権限をフォアグラウンドのみに限定するようリクエストしている場合は、カスタム ダイアログで「位置情報に常時アクセスできないとアプリ内のワークフローが正常に機能しない」というアラートを表示することをおすすめします。ユーザーがこのダイアログを承認すると、図 3 に示すシステム ダイアログが表示されて、バックグラウンドでの位置情報アクセス権限をリクエストできます。

ユーザー向けダイアログのスクリーンショット
図 3. 位置情報への常時アクセス権限に関してユーザーの同意をリクエストするダイアログ

このアクセス権限確認ロジックの例を、次のコード スニペットに示します。

Kotlin

    val permissionAccessCoarseLocationApproved = ActivityCompat
        .checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED

    if (permissionAccessCoarseLocationApproved) {
       val backgroundLocationPermissionApproved = ActivityCompat
           .checkSelfPermission(this, permission.ACCESS_BACKGROUND_LOCATION) ==
           PackageManager.PERMISSION_GRANTED

       if (backgroundLocationPermissionApproved) {
           // App can access location both in the foreground and in the background.
           // Start your service that doesn't have a foreground service type
           // defined.
       } else {
           // App can only access location in the foreground. Display a dialog
           // warning the user that your app must have all-the-time access to
           // location in order to function properly. Then, request background
           // location.
           ActivityCompat.requestPermissions(this,
               arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
               your-permission-request-code
           )
       }
    } else {
       // App doesn't have access to the device's location at all. Make full request
       // for permission.
       ActivityCompat.requestPermissions(this,
           arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
                   Manifest.permission.ACCESS_BACKGROUND_LOCATION),
           your-permission-request-code
       )
    }
    

Java

    boolean permissionAccessCoarseLocationApproved =
        ActivityCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION)
            == PackageManager.PERMISSION_GRANTED;

    if (permissionAccessCoarseLocationApproved) {
       boolean backgroundLocationPermissionApproved =
               ActivityCompat.checkSelfPermission(this,
                   permission.ACCESS_BACKGROUND_LOCATION)
                   == PackageManager.PERMISSION_GRANTED;

       if (backgroundLocationPermissionApproved) {
           // App can access location both in the foreground and in the background.
           // Start your service that doesn't have a foreground service type
           // defined.
       } else {
           // App can only access location in the foreground. Display a dialog
           // warning the user that your app must have all-the-time access to
           // location in order to function properly. Then, request background
           // location.
           ActivityCompat.requestPermissions(this, new String[] {
               Manifest.permission.ACCESS_BACKGROUND_LOCATION},
               your-permission-request-code);
       }
    } else {
       // App doesn't have access to the device's location at all. Make full request
       // for permission.
       ActivityCompat.requestPermissions(this, new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
            },
            your-permission-request-code);
    }
    

デバイスのアップグレード シナリオの設計

ユーザーが以下の手順を実施します。

  1. Android 9(API レベル 28)を実行しているデバイスにアプリをインストールします。
  2. アプリに、デバイス位置情報へのアクセス権限を付与します(ACCESS_COARSE_LOCATION または ACCESS_FINE_LOCATION)。
  3. デバイスを Android 9 から Android Q にアップグレードします。

その後、次の表に示すように、ターゲット SDK のバージョンと定義済みのアクセス権限に基づいて、アプリに対するデフォルトの位置情報の利用許可が自動的に更新されます。

表 1. デバイスの Android Q へのアップグレード後に変更される位置情報の利用許可の状態

ターゲット SDK のバージョン アクセス権限が
付与されている
バックグラウンドでのアクセス権限が
マニフェストで定義されている
更新されたデフォルトのアクセス権限の状態
Android Q フォアグラウンドとバックグラウンドでのアクセス権限
Android Q × フォアグラウンドでのアクセス権のみ
Android Q × (システムによって無視される) アクセス権限なし
Android 9 以前 デバイスのアップグレード時に自動的に追加される フォアグラウンドとバックグラウンドでのアクセス権限
Android 9 以前 × (システムによって無視される) アクセス権限なし

注: デバイスの位置情報へのアクセス権限が自動的に更新された後でも、ユーザーはこのアクセスレベルを変更できます。アプリのアクセス権限をフォアグラウンドのみに制限することも、アクセス権限を完全に取り消すことも可能です。

このため、デバイスの位置情報の更新にアクセスする前に、アプリによる位置情報の受信が引き続き許可されているかどうか、おすすめの方法を使用して確認してください。

位置情報に関するおすすめの方法に沿って設定する

このガイドに記載されている方法に沿って位置情報パーミッションをチェック、リクエストすることで、デバイス位置情報に関するアクセス権限レベルをアプリが正しく把握できるようになります。

ユーザーのデータを安全に保つ方法については、パーミッションに関するおすすめの設定をご覧ください。

必要なアクセス権限だけをリクエストする

必要なアクセス権限だけをリクエストするようにしてください。以下に例を示します。

  • どうしても必要な場合を除き、アプリの起動時には位置情報アクセス権限をリクエストしないでください。
  • Android Q をターゲットにしているアプリが、フォアグラウンドで稼働しているときにのみ位置情報にアクセスする必要がある場合は、ACCESS_BACKGROUND_LOCATION をリクエストしないでください。

アクセス権限が付与されない場合はグレースフル デグラデーションをサポートする

優れたユーザー エクスペリエンスを維持するために、次の状況を適切に処理できるようにアプリを設計してください。

  • アプリが位置情報にアクセスできない。
  • アプリがバックグラウンドで稼働しているときは位置情報にアクセスできない。

参考情報

アプリによるデバイスの位置情報へのアクセスに関する変更について詳しくは、次の参考情報をご覧ください。

サンプル