Android 13でのストレージ権限の扱い
問題の概要
Android 13では、ストレージ関連の権限モデルが大幅に変更されました。従来のREAD_EXTERNAL_STORAGE
とWRITE_EXTERNAL_STORAGE
権限に代わり、メディアタイプごとの詳細な権限が導入されました:
- 画像:
READ_MEDIA_IMAGES
- 動画:
READ_MEDIA_VIDEO
- 音声:
READ_MEDIA_AUDIO
しかし、PDFやExcelなどのドキュメントファイル、その他のメディア以外のファイルタイプにアクセスする場合はどうすれば良いでしょうか?この変更により、開発者は新しい権限モデルへの適応が必要です。
ソリューション
Android バージョン別の対応
重要
適切な権限戦略は、ターゲットとするAndroidバージョンによって異なります。
Android 10以下 (API 29以下)
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Android 11-12 (API 30-32)
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:requestLegacyExternalStorage="true"
...
/>
Android 13以上 (API 33以上)
メディアファイルへのアクセス:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
その他のファイルタイプへのアクセスには、以下の方法を使用します。
権限リクエストの実装
fun requestStoragePermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Android 11以上
if (!Environment.isExternalStorageManager()) {
val uri = Uri.parse("package:${applicationContext.packageName}")
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri)
startActivity(intent)
}
} else {
// Android 10以下
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
), REQUEST_CODE_STORAGE)
}
}
}
private void requestStoragePermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Uri uri = Uri.parse(String.format(Locale.ENGLISH,
"package:%s", getApplicationContext().getPackageName()));
startActivity(new Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
uri
));
}
} else {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, REQUEST_CODE_STORAGE);
}
}
}
Future<void> _requestPermissions() async {
if (Platform.isAndroid) {
if (await _getAndroidVersion() >= 33) {
var status = await Permission.manageExternalStorage.status;
if (!status.isGranted) {
status = await Permission.manageExternalStorage.request();
if (!status.isGranted) {
throw Exception("Manage External Storage permission not granted");
}
}
} else {
var status = await Permission.storage.status;
if (!status.isGranted) {
status = await Permission.storage.request();
if (!status.isGranted) {
throw Exception("Storage permission not granted");
}
}
}
}
}
別のアプローチ: システムピッカーを使用する
権限を要求せずにファイルにアクセスする方法として、システムのファイルピッカーを使用することが推奨されています:
fun openFilePicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*" // すべてのファイルタイプ
}
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT)
}
メリット
- 権限のリクエストが不要
- ユーザーが明示的にファイルを選択するため、プライバシー保護に優れる
- すべてのファイルタイプに対応
MANAGE_EXTERNAL_STORAGE権限について
注意
MANAGE_EXTERNAL_STORAGE
権限は「すべてのファイルアクセス」権限であり、Google Playでの厳格な審査が必要です。この権限は本当に必要な場合にのみ使用してください。
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
この権限を使用するアプリは、Google Playコンソールで「高リスク権限の宣言」を行う必要があります。正当な理由なくこの権限を使用すると、アプリの公開が拒否される可能性があります。
ベストプラクティス
- 最小権限の原則: 必要な権限のみを要求する
- ランタイム権限: 必要な時にのみ権限をリクエストする
- フォールバック処理: 権限が拒否された場合の代替手段を提供する
- ユーザー教育: 権限が必要な理由を明確に説明する
まとめ
Android 13では、メディアファイルへのアクセスには新しい詳細な権限を使用し、それ以外のファイルタイプには以下の方法を選択できます:
- システムファイルピッカーを使用(推奨)
- MANAGE_EXTERNAL_STORAGE権限を使用(制限あり)
- 従来のストレージ権限をAndroid 10以下用に維持
アプリの要件に応じて最適なアプローチを選択し、常にユーザーのプライバシーを尊重する実装を心がけましょう。