Xamarin.Android でのアクセス許可

概要

Android アプリケーションは、独自のサンドボックスで実行され、セキュリティ上の理由でデバイス上の特定のシステム リソースまたはハードウェアへのアクセス権がありません。 ユーザーは、そのようなリソースの使用前に、アプリにアクセス許可を明示的に付与する必要があります。 たとえば、アプリケーションは、ユーザーからの明示的なアクセス許可がないと、デバイス上の GPS にアクセスできません。 アプリがアクセス許可なしで、保護されたリソースにアクセスしようとすると、Android は Java.Lang.SecurityException をスローします。

アクセス許可は、アプリケーション開発者がアプリの開発時に AndroidManifest.xml で宣言します。 Android には、それらのアクセス許可に対するユーザーの同意を取得するための、2 つの異なるワークフローがあります。

  • Android 5.1 (API レベル 22) 以下を対象とするアプリの場合、アプリのインストール時にアクセス許可要求が行われていました。 ユーザーがアクセス許可を付与しない場合、アプリはインストールされませんでした。 アプリがインストールされた後は、アプリをアンインストールする以外にアクセス許可を取り消す方法はありません。
  • Android 6.0 (API レベル 23) 以降、ユーザーはアクセス許可をより柔軟に制御できるようになりました。アプリがデバイスにインストールされている限り、アクセス許可を付与したり取り消したりできます。 このスクリーンショットは、Google 連絡先アプリのアクセス許可設定を示しています。 さまざまなアクセス許可が一覧表示され、ユーザーはアクセス許可を有効または無効にすることができます。

Sample Permissions screen

Android アプリは、実行時に、保護されたリソースへのアクセス許可があるかどうかを確認するチェックを行う必要があります。 アプリは、アクセス許可がない場合、Android SDK で提供されている新しい API を使用して、ユーザーにアクセス許可を付与してもらうための要求を行う必要があります。 アクセス許可は、次の 2 つのカテゴリに分けられます。

  • 標準のアクセス許可 – ユーザーのセキュリティまたはプライバシーに対するセキュリティ リスクがほとんどないアクセス許可です。 Android 6.0 は、インストール時に標準のアクセス許可を自動的に付与します。 標準のアクセス許可の詳細な一覧については、Android のドキュメントを参照してください。
  • 危険なアクセス許可 – 標準のアクセス許可とは対照的に、危険なアクセス許可は、ユーザーのセキュリティまたはプライバシーを保護するものです。 これらは、ユーザーが明示的に付与する必要があります。 SMS メッセージの送受信は、危険なアクセス許可を必要とするアクションの一例です。

重要

アクセス許可が属するカテゴリは、やがて変更される可能性があります。 "標準" のアクセス許可として分類されていたアクセス許可が、将来の API レベルで、危険なアクセス許可に引き上げられる可能性があります。

危険なアクセス許可は、さらにアクセス許可グループに細分化されます。 アクセス許可グループは、論理的に関連する複数のアクセス許可を保持します。 ユーザーがアクセス許可グループの 1 つのメンバーにアクセス許可を付与すると、Android はそのグループのすべてのメンバーにアクセス許可を自動的に付与します。 たとえば、STORAGE アクセス許可グループは、WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE の両方のアクセス許可を保持します。 ユーザーが READ_EXTERNAL_STORAGE へのアクセス許可を付与すると、同時に WRITE_EXTERNAL_STORAGE アクセス許可も自動的に付与されます。

1 つ以上のアクセス許可を要求する際には、アクセス許可の要求前に、アプリがアクセス許可を必要とする理由を説明することがベスト プラクティスです。 ユーザーが理由を理解したら、アプリはユーザーにアクセス許可を要求することができます。 ユーザーは理由を理解すると、アクセス許可を付与するかどうかについて情報に基づいた決定を下すことができ、付与しない場合の影響について理解できます。

アクセス許可のチェックと要求のワークフロー全体は、"実行時のアクセス許可" チェックと呼ばれ、次の図にまとめることができます。

Run-time permission check flow chart

Android サポート ライブラリは、アクセス許可用の新しい API の一部を、古いバージョンの Android にバックポートします。 これらのバックポートされた API は、デバイス上の Android のバージョンを自動的にチェックするため、API レベルのチェックを毎回実行する必要はありません。

このドキュメントでは、Xamarin.Android アプリケーションにアクセス許可を追加する方法と、Android 6.0 (API レベル 23) 以上を対象とするアプリが実行時のアクセス許可チェックを実行する方法について説明します。

Note

ハードウェアに対するアクセス許可は、Google Play でのアプリのフィルター処理に影響を与える可能性があります。 たとえば、アプリがカメラに対するアクセス許可を必要とする場合、Google Play は、カメラがインストールされていないデバイス上の Google Play ストアでそのアプリを表示しません。

要件

Xamarin.Android プロジェクトに Xamarin.Android.Support.Compat NuGet パッケージを含めることを強くお勧めします。 このパッケージは、アクセス許可固有の API を古いバージョンの Android にバックポートし、アプリが実行されている Android のバージョンを常にチェックする必要のない 1 つの共通インターフェイスを提供します。

システムのアクセス許可を要求する

Android のアクセス許可を処理する最初の手順は、Android マニフェスト ファイルでアクセス許可を宣言することです。 これは、アプリが対象としている API レベルに関係なく行う必要があります。

Android 6.0 以上を対象とするアプリは、ユーザーが過去のある時点でアクセス許可を付与したことを理由に、次回もそのアクセス許可が有効であると想定することはできません。 Android 6.0 を対象とするアプリは、実行時のアクセス許可チェックを常に実行する必要があります。 Android 5.1 以下を対象とするアプリは、実行時のアクセス許可チェックを実行する必要はありません。

Note

アプリケーションは、必要とするアクセス許可のみを要求する必要があります。

マニフェストでアクセス許可を宣言する

アクセス許可は、uses-permission 要素を使用して AndroidManifest.xml に追加します。 たとえば、アプリケーションがデバイスの位置を特定する必要がある場合は、細かい、および粗い位置情報へのアクセス許可が必要です。 次の 2 つの要素をマニフェストに追加します。

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

Visual Studio に組み込まれているツール サポートを利用して、アクセス許可を宣言できます。

  1. ソリューション エクスプローラー[プロパティ] をダブルクリックし、プロパティ ウィンドウで [Android マニフェスト] タブを選択します。

    Required permissions in the Android Manifest tab

  2. アプリケーションにまだ AndroidManifest.xml がない場合は、次をクリックします: [AndroidManifest.xml が見つかりません。クリックして 1 つ追加してください]。以下に示すとおりです。

    No AndroidManifest.xml message

  3. [必要なアクセス許可] の一覧から、アプリケーションが必要とするアクセス許可を選択し、保存します。

    Example CAMERA permissions selected

Xamarin.Android は、ビルド時にいくつかのアクセス許可をデバッグ ビルドに自動的に追加します。 これにより、アプリケーションのデバッグが容易になります。 特に、INTERNETREAD_EXTERNAL_STORAGE という 2 つのアクセス許可は重要です。 自動的に設定されるこれらのアクセス許可は、[必要なアクセス許可] の一覧で、有効なものとして表示されていません。 一方、リリース ビルドでは、[必要なアクセス許可] の一覧で明示的に設定したアクセス許可のみが使用されます。

Android 5.1 (API レベル 22) 以下を対象とするアプリの場合、さらに行う必要のあることはありません。 Android 6.0 (API 23 レベル 23) 以上で実行されるアプリの場合、実行時のアクセス許可チェックを実行する方法に関する次のセクションに進む必要があります。

Android 6.0 での実行時のアクセス許可チェック

特定のアクセス許可が付与されているかどうかをチェックするために、ContextCompat.CheckSelfPermission メソッド (Android サポート ライブラリで利用可能) を使用します。 このメソッドは、次の 2 つの値のいずれかを持つ、Android.Content.PM.Permission 列挙型を返します。

  • Permission.Granted – 指定したアクセス許可が付与されています。
  • Permission.Denied – 指定したアクセス許可が付与されていません。

このコード スニペットは、アクティビティでカメラのアクセス許可をチェックする方法の例です。

if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == (int)Permission.Granted) 
{
    // We have permission, go ahead and use the camera.
} 
else 
{
    // Camera permission is not granted. If necessary display rationale & request.
}

アプリケーションにアクセス許可が必要な理由をユーザーに知らせて、アクセス許可の付与のために情報に基づく決定を下せるようにすることがベスト プラクティスです。 この例として、写真を撮影し、それに位置情報タグを付けるアプリがあります。 ユーザーにとって、カメラのアクセス許可が必要であることは明らかであっても、アプリがデバイスの位置情報も必要とする理由は明らかでない可能性があります。 理由を示すメッセージは、位置情報のアクセス許可が望ましい理由と、カメラのアクセス許可が必須であることをユーザーが理解するのに役立つようにする必要があります。

ActivityCompat.ShouldShowRequestPermissionRationale メソッドは、ユーザーに対して理由を表示する必要があるかどうかを判別するために使用します。 特定のアクセス許可の理由を表示する必要がある場合、このメソッドは true を返します。 このスクリーンショットは、アプリケーションで表示されるスナックバーに、アプリがデバイスの位置情報を把握する必要がある理由が説明されている例を示しています。

Rationale for location

ユーザーがアクセス許可を付与した場合は、ActivityCompat.RequestPermissions(Activity activity, string[] permissions, int requestCode) メソッドを呼び出す必要があります。 このメソッドには、次のパラメーターが必要です。

  • activity – これは、アクセス許可を要求していて、Android に結果を通知してもらう必要があるアクティビティです。
  • permissions – 要求されているアクセス許可の一覧。
  • requestCodeRequestPermissions 呼び出しへのアクセス許可要求の結果と照合するために使用する整数値。 この値は、ゼロより大きい値である必要があります。

このコード スニペットは、説明された 2 つのメソッドの例です。 まず、アクセス許可の理由を表示する必要があるかどうかを判別するチェックが行われます。 理由を表示する必要がある場合は、スナックバーに理由が表示されます。 ユーザーがスナックバーで [OK] をクリックすると、アプリはアクセス許可を要求します。 ユーザーが理由を受け入れない場合、アプリはアクセス許可の要求に進んではなりません。 理由が表示されない場合、アクティビティはアクセス許可を要求します。

if (ActivityCompat.ShouldShowRequestPermissionRationale(this, Manifest.Permission.AccessFineLocation)) 
{
    // Provide an additional rationale to the user if the permission was not granted
    // and the user would benefit from additional context for the use of the permission.
    // For example if the user has previously denied the permission.
    Log.Info(TAG, "Displaying camera permission rationale to provide additional context.");

    var requiredPermissions = new String[] { Manifest.Permission.AccessFineLocation };
    Snackbar.Make(layout, 
                   Resource.String.permission_location_rationale,
                   Snackbar.LengthIndefinite)
            .SetAction(Resource.String.ok, 
                       new Action<View>(delegate(View obj) {
                           ActivityCompat.RequestPermissions(this, requiredPermissions, REQUEST_LOCATION);
                       }    
            )
    ).Show();
}
else 
{
    ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.Camera }, REQUEST_LOCATION);
}

RequestPermission は、ユーザーが既にアクセス許可を付与している場合でも呼び出すことができます。 後続の呼び出しは必要ではありませんが、アクセス許可を確認 (または取り消す) 機会をユーザーに提供できます。 RequestPermission が呼び出されると、オペレーティング システムに制御が渡され、アクセス許可を受け入れるための UI が表示されます。

Permssion Dialog

ユーザーが完了すると、Android はコールバック メソッド OnRequestPermissionResult を使用して結果をアクティビティに返します。 このメソッドは、アクティビティが実装する必要があるインターフェイス ActivityCompat.IOnRequestPermissionsResultCallback の一部です。 このインターフェイスには 1 つのメソッド OnRequestPermissionsResult があります。Android はこれを呼び出して、ユーザーの選択についてアクティビティに通知します。 ユーザーがアクセス許可を付与した場合、アプリは処理を進め、保護されたリソースを使用できます。 OnRequestPermissionResult を実装する方法の例を次に示します。

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
    if (requestCode == REQUEST_LOCATION) 
    {
        // Received permission result for camera permission.
        Log.Info(TAG, "Received response for Location permission request.");

        // Check if the only required permission has been granted
        if ((grantResults.Length == 1) && (grantResults[0] == Permission.Granted)) {
            // Location permission has been granted, okay to retrieve the location of the device.
            Log.Info(TAG, "Location permission has now been granted.");
            Snackbar.Make(layout, Resource.String.permission_available_camera, Snackbar.LengthShort).Show();            
        } 
        else 
        {
            Log.Info(TAG, "Location permission was NOT granted.");
            Snackbar.Make(layout, Resource.String.permissions_not_granted, Snackbar.LengthShort).Show();
        }
    } 
    else 
    {
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

まとめ

このガイドでは、Android デバイスでアクセス許可の追加とチェックを行う方法について説明しました。 古い Android アプリ (API レベル < 23) と新しい Android アプリ (API レベル> 22) での、アクセス許可が機能するしくみの違いを示しました。 Android 6.0 で実行時のアクセス許可チェックを実行する方法について説明しました。