Dangerous Permissionとは、アプリ実行中にランタイムダイアログを通じてユーザーの明示的な同意を必要とするAndroidの権限カテゴリです。Androidデベロッパーガイド(2024年)によると、危険な権限はProtectionLevel dangerousを持ち、カメラ、マイク、位置情報、連絡先などの機密データへのアクセスを提供します。ユーザーの明示的な同意なしに、アプリはこれらの機能を使用できません。
重要なポイント
Dangerous Permissionは、ユーザーの機密データへのアクセスを提供するAndroidシステム権限のカテゴリです。通常の権限とは異なり、危険な権限はインストール時に自動的に付与されません。アプリはAndroid 6.0 Marshmallow(API 23)で導入されたランタイムメカニズムを通じて、実行時に明示的にリクエストする必要があります。
明示的なリクエストが必要な理由は、これらの権限が保護するデータの性質にあります。ユーザーの位置情報、個人の連絡先、カメラとマイクのコンテンツ、通話履歴、SMSなどです。Androidはこれらのデータを機密とみなし、ユーザーが意識的にアクセスを許可することを要求します。Android Privacy Sandbox(2024年)によると、ユーザーは平均して約30パーセントのランタイムリクエストを拒否しています。
Dangerous Permissionの重要な特徴は、いつでも撤回できることです。ユーザーは設定→アプリ→権限に移動して、任意の危険な権限をオフにできます。アプリは、以前に付与された権限が再起動なしでいつでも撤回される可能性があることを想定しておく必要があります。
dangerous保護レベルは、OSレベルのシステム権限定義で設定されます。アプリがこのprotectionLevelでuses-permissionを宣言すると、システムはその権限をランタイムリクエストが必要なものとしてマークします。normalとは異なり、dangerous権限は常にシステムの権限管理UIに表示され、撤回できます。
すべての危険な権限は、機能カテゴリに従ってPermission Groupsにグループ化されます。たとえば、CAMERAとCAMERA2はCAMERAグループに、ACCESS_FINE_LOCATIONとACCESS_COARSE_LOCATIONはLOCATIONグループにあります。ユーザーがグループ内の1つの権限を許可した場合、同じグループの残りの権限は追加のダイアログなしで自動的に付与されます。
ランタイムリクエストは、アプリがシステムAPIを呼び出して権限リクエストダイアログを表示するメカニズムです。ユーザーは権限名と許可/拒否ボタンがあるモーダルウィンドウを表示します。応答後、システムは結果とともにonRequestPermissionsResultコールバックを呼び出します。
完全なサイクルには3つのステップが含まれます。checkSelfPermissionによるステータス確認、権限がない場合のrequestPermissionsの呼び出し、onRequestPermissionsResultでの結果処理です。ステータス確認は必須です。ユーザーが設定を通じていつでも権限を撤回している可能性があり、確認せずに関数を呼び出すとSecurityExceptionが発生するからです。
fun checkAndRequestCameraPermission() {
when {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
openCamera()
}
else -> {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_CODE
)
}
}
}
結果の処理はActivityResultLauncherまたはonRequestPermissionsResultで行われます。推奨される最新のアプローチはActivityResultContracts.RequestPermissionを使用することです。これは明示的なリクエストコードなしでよりクリーンなAPIを提供します。このコントラクトはBoolean(権限が付与されたかどうか)を返します。
危険な権限は、アプリの起動時ではなく、機能の使用コンテキストで厳密にリクエストしてください。ユーザーがカメラボタンを押したらCAMERAをリクエストします。マップを開いたらLOCATIONをリクエストします。コンテキストに応じたリクエストは、最初の起動時にすべての権限をリクエストするよりも2倍の許可率を得られます。また、ユーザーがどの機能にアクセスが必要かを理解できるよう、一度に1つ以上の権限をリクエストしないことをお勧めします。
Androidは危険な権限のグループをいくつか定義しており、それぞれに1つ以上の定数が含まれています。最も完全なリストはManifest.permissionクラスで利用できます。以下は、開発で使用される主要なグループと権限です。
| 権限グループ | 権限 | APIアクセス |
|---|---|---|
| CAMERA | CAMERA | Camera API, CameraX |
| LOCATION | ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION | FusedLocationProvider, Geofence |
| MICROPHONE | RECORD_AUDIO | MediaRecorder, AudioRecord |
| PHONE | READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG | TelephonyManager |
| CONTACTS | READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS | ContactsContract |
| SMS | READ_SMS, SEND_SMS, RECEIVE_SMS | SmsManager |
| STORAGE | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | MediaStore, File API |
| CALENDAR | READ_CALENDAR, WRITE_CALENDAR | CalendarContract |
Android 12以降、Googleは一部の権限の要件を厳格化しました。たとえば、BLUETOOTH_CONNECTとBLUETOOTH_SCANは危険な権限になり、ランタイムリクエストが必要です。また、センサーへのバックグラウンドアクセスのためにBODY_SENSORS_BACKGROUND権限が追加されました。デベロッパーはtargetSdkVersionを更新し、現在のOSバージョンでリクエストをテストする必要があります。
Android 13(API 33)では、通知(POST_NOTIFICATIONS)とメディアファイル(READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO)の新しい権限が導入され、一般的なREAD_EXTERNAL_STORAGEが置き換えられました。現在、写真、ビデオ、オーディオへのアクセスは、単一のダイアログなしで専門の権限を通じて個別にリクエストされます。
DangerousとNormal Permissionは、付与方法、撤回可能性、UXにおいて根本的に異なります。Normalはインストール時に自動的に付与され、Dangerousは明示的なランタイムダイアログが必要です。Normalは設定から撤回できませんが、Dangerousはいつでも無効にできます。この非対称性が異なる開発パターンを生み出します。
コードの観点から見ると、危険な権限にはより多くの作業が必要です。checkSelfPermission、requestPermissions、拒否処理などです。通常の権限にはAndroidManifest.xmlの1行で十分です。ただし、Dangerous Permissionはユーザーに制御権を与えるため、特にカメラや位置情報などの機密機能において信頼性が向上します。
カテゴリの選択はデベロッパーではなくシステムによって決定されます。デベロッパーはuses-permissionを宣言するだけで、システムがprotectionLevelに基づいてカテゴリを決定します。ただし、危険な権限のリクエスト戦略はユーザーエクスペリエンスに影響を与えます。頻繁または不適切なダイアログはアプリの評価を下げます。
最新の方法でKotlinの権限をリクエストするには、ActivityResultContracts.RequestMultiplePermissionsまたはRequestPermissionを使用します。これらのコントラクトはandroidx.activityライブラリの一部であり、onRequestPermissionsResultをオーバーライドする必要なしに、ラムダベースのクリーンなAPIを提供します。
class CameraActivity : AppCompatActivity() {
private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
openCamera()
} else {
showPermissionDeniedMessage()
}
}
fun requestCamera() {
when {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED ->
openCamera()
ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.CAMERA
) ->
showRationaleDialog()
else ->
requestPermissionLauncher.launch(
Manifest.permission.CAMERA
)
}
}
}
アプリが同時に複数の危険な権限を必要とする場合は、RequestMultiplePermissionsを使用します。コントラクトはMap<String, Boolean>を返し、キーは権限名、値は結果です。これは、ビデオ録画のためにCAMERAとRECORD_AUDIOをリクエストする必要がある最初の起動時に便利です。
ユーザーがリクエストを拒否した場合、shouldShowRequestPermissionRationaleメソッドはtrueを返します。これは、権限が必要な理由の説明を表示する必要があることを示します。ベストプラクティスは、説明と再試行ボタンを備えたカスタムダイアログを表示することです。ユーザーがNever Ask Againチェックボックスをオンにして再度リクエストを拒否した場合、shouldShowRequestPermissionRationaleはfalseを返し、設定にリダイレクトする必要があります。
Never Ask Againは、ユーザーが2回目のランタイムダイアログ拒否時に設定できるフラグです。これ以降、その権限の標準ダイアログは表示されなくなります。アクセスを許可する唯一の方法は、ユーザーをシステムのアプリ設定にリダイレクトすることです。
デベロッパーは2つの拒否シナリオを区別する必要があります。1つ目はshouldShowRequestPermissionRationaleがtrueを返す場合(ユーザーは拒否したがダイアログはまだ表示可能)、2つ目はメソッドがfalseを返す場合(Never Ask Againがアクティブ、またはポリシーで権限がブロックされている)です。2つ目のケースでは、設定を開くボタンを表示する必要があります。
fun handlePermissionDenied(permission: String) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this, permission
)) {
showRationaleDialog(permission)
} else {
showSettingsRedirectDialog(permission)
}
}
private fun showSettingsRedirectDialog(permission: String) {
AlertDialog.Builder(this)
.setTitle("アクセスが拒否されました")
.setMessage(
"権限がブロックされました。設定を開いてください。"
)
.setPositiveButton("設定") { _, _ ->
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts(
"package", packageName, null
)
)
startActivity(intent)
}
.show()
}
shouldShowRequestPermissionRationaleがfalseを返した場合、権限を再度リクエストしないことが重要です。この場合のrequestPermissionsの繰り返し呼び出しはダイアログを表示せず、結果は説明なしですぐにDENIEDで返されます。ユーザーは不明瞭な動作に遭遇し、アプリのエクスペリエンスに悪影響を及ぼします。
よくある質問
危険な権限には、ProtectionLevel dangerousを持つ権限(CAMERA、RECORD_AUDIO、ACCESS_FINE_LOCATION、READ_CONTACTS、READ_SMS、READ_CALENDARなど)が含まれます。完全なリストはManifest.permissionクラスで入手できます。
ContextCompat.checkSelfPermissionを使用し、コンテキストと権限名を渡します。メソッドはPERMISSION_GRANTEDまたはPERMISSION_DENIEDを返します。危険な権限を必要とするAPI呼び出しの前に毎回確認を実行する必要があります。
Permission Groupは、関連する危険な権限をグループ化します。ユーザーがグループ内の1つの権限を許可すると、残りは自動的に付与されます。たとえば、LOCATIONにはACCESS_FINE_LOCATIONとACCESS_COARSE_LOCATIONが含まれます。
拒否後にshouldShowRequestPermissionRationaleを確認します。メソッドがfalseを返し、権限がまだ付与されていない場合、Never Ask Againがアクティブです。ACTION_APPLICATION_DETAILS_SETTINGSを含むIntentを介してユーザーを設定にリダイレクトします。
はい、引き続き必須です。Android 13+では、一部の権限が変更されました。POST_NOTIFICATIONSは個別のランタイム権限になり、READ_EXTERNAL_STORAGEはメディアファイルへの詳細アクセスのためにREAD_MEDIA_IMAGESに置き換えられました。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。