[この記事は Laurence Moroney、デベロッパー アドボケートによる Android Developers Blog の記事 "Google Play services 8.1 and Android 6.0 Permissions" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]

新しいプラットフォーム機能に加え、Android 6.0 Marshmallow では、アプリのインストールと自動アップデート プロセスを効率化する新たなパーミッション モデルが提供されます。Google Play 開発者サービス 8.1 は、今回初めて、Android 6.0 が稼働するデバイスでの実行時パーミッションに対応できるようになり、デバイス上の API 対応で必要なパーミッションすべてを取得します。このため、アプリを使用するためのパーミッションのリクエストが原則不要になります。しかしアプリが API Level 23 をターゲットとした場合、チェックおよび実行時パーミッションのリクエストが必要とされることもあります。

最新のパーミッション モデルに対処できるよう Google Play 開発者サービス アプリをアップデートするにあたり、ランタイムに必要なパーミッション設定に対するユーザーの期待をうまく管理するようにしてください。以下に、ベスト プラクティスをいくつかご紹介します。

はじめに...

この投稿の目的に適うよう、API Level と Target SDK を最低 23 に設定します。また、下位互換のため、パーミッションの検証とリクエストに V4 サポートライブラリを使用していることを確認してください。まだお持ちでない場合、gradle ファイルに追加します。
 
com.android.support:support-v4:23.0.1
また、AndroidManifest.xml でパーミッションを宣言する必要もあります。ここの部分は以前と同じです。アプリが必要としてきたパーミッションに関係なく、AndroidManifest.xml のユーザーパーミッションのタグで宣言する必要があります。以下に例を示します。
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
接続の詳しい手順を含む、地図と位置に関するドキュメントはこちらです。

ステップ 1. GoogleApiClient への接続の管理

GoogleApiClient での接続障害を適切に処理していることと、こちらで説明されているとおり、プロセスに適した解像度を使用していることを確認してください。Google Play 開発者サービス自体にパーミッションがない場合、この方法に従っていれば、ユーザーフローが自動的に補正します。

以下に例を示します。
 @Override
public void onConnectionFailed(ConnectionResult result) {
      if (mResolvingError) {
             // Already attempting to resolve an error.
             return;
      } else if (result.hasResolution()) {
             try {
                   mResolvingError = true;
                   result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
             } catch (SendIntentException e) {
                   // There was an error with the resolution intent. Try again.
                   mGoogleApiClient.connect();
             }
      } else {
             // Show dialog using GooglePlayServicesUtil.getErrorDialog()
             showErrorDialog(result.getErrorCode());
             mResolvingError = true;
      }
}

ステップ 2. API を呼び出す前にパーミッションを検証

接続を確立し、AndroidManifest.xml ファイルにある使用する API に必要なパーミッションが宣言されていれば、今後の呼び出しに問題はないと考えられます。しかし、必ず必要なパーミッションがあることを確認してから、API の呼び出しまたはGoogleApiClient への接続を実行してください。これは、ActivityCompatFragmentContextCompat のいずれかの checkSelfPermission メソッドを使用して実行できます。

呼び出しに失敗した、つまりパーミッションが付与されない場合、requestPermissions を使用してリクエストします。以下のステップのようなコールバックが返されます。

以下に例を示します。
 private static final int REQUEST_CODE_LOCATION = 2;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
 // Request missing location permission.
 ActivityCompat.requestPermissions(this, 
    new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 
    REQUEST_CODE_LOCATION);
} else {
 // Location permission has been granted, continue as usual.
 Location myLocation = 
             LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}

ステップ 3. パーミッションのコールバックリクエストを実装

ステップ 2 では、ユーザーによりパーミッションが付与されていない場合、ユーザーに対してパーミッションを付与するようリクエストするため requestPermissions メソッドが呼び出されました。ユーザーからのレスポンスは、コールバック onRequestPermissionsResult に取り込まれます。これを実装する必要があります。また、リクエストは拒否またはキャンセルされることもあるので、戻り値を確認します。ここでは、複数のパーミッションのリクエストが必要になることもあります。このサンプルでは、1 つのパーミッションだけをチェックしますが、場合によっては複数のパーミッションのチェックが必要です。
 public void onRequestPermissionsResult(int requestCode, 
                                      String[] permissions,
                                      int[] grantResults) {
     if (requestCode == REQUEST_CODE_LOCATION) {
          if(grantResults.length == 1 
       && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
          // success!
          Location myLocation =
               LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
     } else {
     // Permission was denied or request was cancelled
     }
}

ステップ 4. パーミッションの理由を提示

ユーザーが以前にもパーミッションのリクエストを拒否したことのある場合、再リクエストを実行する前に、アプリでユーザーに補足説明をする必要があります。例えば、パーミッションがアプリのコア機能にとって重要であるにも関わらず、ユーザーがその必要性を理解していない場合には、その意味を説明するべきです。

この場合、requestPermissions を呼び出す前に (上記 ステップ 2)、shouldShowRequestPermissionRationale を呼び出します。TRUE が返された場合、パーミッションに関する追加情報を表示する UI をいくつか作成すべきです。

ステップ 2 からのコードはこのようになります。
private static final int REQUEST_CODE_LOCATION = 2;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
 // Check Permissions Now

  if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {
        // Display UI and wait for user interaction
  } else {
 ActivityCompat.requestPermissions(
             this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 
                                     REQUEST_CODE_LOCATION);
  }
} else {
     // permission has been granted, continue as usual
     Location myLocation = 
        LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}
この場合でも、ユーザーはパーミッションを拒否できることに注意してください。アプリを構築する際には、このパーミッションの拒否がアプリに悪影響を及ぼさないよう考慮する必要があります。詳細とガイダンスは Android Developers サイトの「Best Practices」セクション をご覧ください。

Google Play 開発者サービスを利用するアプリケーションをビルドしたことがある場合、Google Play 開発者サービス 8.1 SDK をダウンロードし、それを使ってアプリケーションをリビルドし、Android Developers サイトからダウンロードできる Android 6.0 の最新バージョンで試行することを推奨します。

便利なリソース:

Android 6.0 用ビルドのスタート ガイド

Android パーミッションのデザイン ガイドライン
Android M パーミッションに関するGoogle I/O 2015 セッション
コーディングのベストプラクティスを含む Google Play 開発者サービス 8.1 の サンプル


Posted by Yoshifumi Yamaguchi - Developer Relations Team