Android 11 でのストレージに関する更新

Android 11 ではプラットフォームをさらに強化し、外部ストレージ上のアプリとユーザーのデータの保護を改善しています。今回のプレビュー リリースでは、Google が Android Dev Summit で去年発表したように、メディアの raw ファイルパスへのオプトイン アクセス、メディアの一括編集操作、ストレージ アクセス フレームワークの UI の更新などの機能強化が行われています。

デベロッパーの便宜を図るため、対象範囲別ストレージへの移行を容易にする改善がプラットフォームに追加されています。アプリのユースケースに基づいて対象範囲別ストレージを使用するようにアプリを移行する方法については、このページの対象範囲別ストレージのセクション、Android ストレージのユースケースとおすすめの方法に関するガイド、Android 11 Storage FAQ と題された Medium の記事をご覧ください。

今後とも、Android の次期バージョンを方向づけるフィードバックをお寄せくださるようお願いいたします。Issue Tracker を使用してコメントを送信してください。

対象範囲別ストレージの適用

デベロッパーがテストに時間をかけられるように、Android 10(API レベル 29)をターゲットとするアプリであれば、引き続き requestLegacyExternalStorage 属性をリクエストできます。アプリでこのフラグを指定すると、異なるディレクトリや種類の異なるメディア ファイルへのアクセス権の付与など、対象範囲別ストレージに関連する変更を一時的に無効にできます。Android 11 をターゲットとするようにアプリを更新すると、requestLegacyExternalStorage フラグは無視されます。

Android 10 との互換性を維持する

Android 10 デバイスで稼働するアプリが対象範囲別ストレージを無効にする場合、アプリのマニフェスト ファイルで引き続き requestLegacyExternalStoragetrue に設定することが推奨されます。それにより、Android 10 を搭載するデバイスで引き続きアプリが想定どおりに動作できます。

対象範囲別ストレージを使用して表示できるディレクトリにデータを移行する

アプリが以前のストレージ モデルを使用していて、これまで Android 10 以下をターゲットにしていた場合、対象範囲別ストレージ モデルを有効にするとアプリからアクセスできなくなるディレクトリに、データを保存している可能性があります。Android 11 をターゲットとする前に、対象範囲別ストレージと互換性のあるディレクトリにデータを移行してください。ほとんどの場合、アプリ専用のディレクトリにデータを移行できます。

移行するデータがある場合、Android 11 をターゲットとするアプリの新しいバージョンにユーザーがアップグレードする際に、以前のストレージ モデルを保持することもできます。これにより、アプリがこれまでにデータを保存していたディレクトリ内のアプリデータに、ユーザーが引き続きアクセスできます。アップグレードに際して、以前のストレージ モデルを有効にするには、アプリのマニフェストで preserveLegacyExternalStorage 属性を true に設定してください。

注: ほとんどのアプリでは preserveLegacyExternalStorage を使用する必要はありません。このフラグは、対象範囲別ストレージと互換性のある場所にアプリデータを移行した後、アプリを更新するユーザーがデータへのアクセスを維持できるようにする状況でのみ使用するように設計されています。このフラグを使用すると、対象範囲別ストレージがアプリのユーザーにどのように影響を与えているかのテストが難しくなります。ユーザーがアプリを更新しても、アプリは以前のストレージ モデルを引き続き使用するからです。

preserveLegacyExternalStorage を使用する場合、以前のストレージ モデルはユーザーがアプリをアンインストールするまで、ずっと有効なままになります。Android 11 が稼働するデバイス上でユーザーがアプリをインストールまたは再インストールする場合は、preserveLegacyExternalStorage の値にかかわらず、アプリは対象範囲別ストレージ モデルを無効にできなくなります。

対象範囲別ストレージをテストする

アプリのターゲット SDK のバージョンとマニフェスト フラグの値にかかわらず、アプリ内で対象範囲別ストレージを有効にするには、以下のアプリの互換性フラグを有効にします。

対象範囲別ストレージを無効にして、以前のストレージ モデルを代わりに使用するには、両方のフラグの設定を解除してください。

デバイスのストレージを管理する

Android 11 では、対象範囲別ストレージ モデルを使用するアプリは、アプリ固有のキャッシュ ファイルだけにアクセスできます。アプリでデバイスのストレージを管理する必要がある場合は、次のようにします。

  1. ACTION_MANAGE_STORAGE インテントのアクションを呼び出して空き容量を確認します。
  2. デバイスに十分な空き容量がない場合、キャッシュの削除に同意するようユーザーに求めます。そのために、ACTION_CLEAR_APP_CACHE インテントのアクションを呼び出します。

外部ストレージ内のアプリ固有のディレクトリ

Android 11 では、アプリは外部ストレージでアプリ固有のディレクトリを作成できません。お使いのアプリ専用のディレクトリにアクセスするには、getExternalFilesDirs() を呼び出します。

メディア ファイルへのアクセス

Android 11 では、ユーザーのプライバシーを維持しながらメディアにアクセスしやすくするために、以下の機能が追加されています。

一括操作を行う

デバイス間の一貫性を保ち、ユーザーの利便性を高めるため、Android 11 では MediaStore API に複数のメソッドを追加しています。これらのメソッドは、写真の編集をその場で行うなど、特定のメディア ファイルの変更を合理化したフローで行おうとするアプリに特に便利です。

追加されるメソッドは次のとおりです。

createWriteRequest()
メディア ファイルの指定のグループへの書き込みアクセス権をユーザーがアプリに付与するためのリクエスト。
createFavoriteRequest()
ユーザーが指定したメディア ファイルをデバイス上の「お気に入り」のメディアの一部としてマークするためのリクエスト。このファイルに読み取りアクセス権を持つアプリは、ユーザーがそのファイルを「お気に入り」としてマークしたことを確認できます。
createTrashRequest()

ユーザーが指定したメディア ファイルをデバイスのゴミ箱に入れるためのリクエスト。ゴミ箱内のアイテムは、システムが規定する期間後に完全に削除されます。

createDeleteRequest()

前もってゴミ箱に入れずに、ユーザーが指定したメディア ファイルを完全に削除するためのリクエスト。

これらのメソッドのいずれかを呼び出すと、システムは PendingIntent オブジェクトを作成します。アプリがこのインテントを呼び出すと、指定のメディア ファイルをアプリが更新または削除する同意を求めるダイアログがユーザーに表示されます。

たとえば、createWriteRequest() の呼び出しは次のように作成します。

Kotlin

val urisToModify = /* A collection of content URIs to modify. */
val editPendingIntent = MediaStore.createWriteRequest(contentResolver,
        urisToModify)

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE,
    null, 0, 0, 0)

Java

List<Uri> urisToModify = /* A collection of content URIs to modify. */
PendingIntent editPendingIntent = MediaStore.createWriteRequest(contentResolver,
                  urisToModify);

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.getIntentSender(),
    EDIT_REQUEST_CODE, null, 0, 0, 0);

ユーザーのレスポンスを評価して、同意があれば手続きを進めます。ユーザーが同意しない場合は、アプリに権限が必要である理由をユーザーに説明します。

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int,
                 data: Intent?) {
    ...
    when (requestCode) {
        EDIT_REQUEST_CODE ->
            if (resultCode == Activity.RESULT_OK) {
                /* Edit request granted; proceed. */
            } else {
                /* Edit request not granted; explain to the user. */
            }
    }
}

Java

@Override
protected void onActivityResult(int requestCode, int resultCode,
                   @Nullable Intent data) {
    ...
    if (requestCode == EDIT_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            /* Edit request granted; proceed. */
        } else {
            /* Edit request not granted; explain to the user. */
        }
    }
}

createFavoriteRequest()createTrashRequest()createDeleteRequest()でも、この一般的なパターンを使用できます。

直接ファイルパスとネイティブ ライブラリを使用してファイルにアクセスする

サードパーティのメディア ライブラリでアプリをよりスムーズに動作させるため、Android 11 では MediaStore API 以外の API を使用して共有ストレージからメディア ファイルにアクセスすることが可能です。次のいずれかの API を使用してメディア ファイルに直接アクセスできます。

  • File API
  • ネイティブ ライブラリ(fopen() など)。

アプリにストレージ権限がない場合は、直接ファイルパスを使用して、アプリに紐付けされたメディア ファイルにアクセスできます。アプリに READ_EXTERNAL_STORAGE 権限がある場合は、ファイルがアプリに紐付けされているかどうかにかかわらず、直接ファイルパスを使用してすべてのメディア ファイルにアクセスできます。

メディア ファイルに直接アクセスする場合は、アプリのマニフェスト ファイルで requestLegacyExternalStoragetrue に設定して、対象範囲別ストレージをオプトアウトすることをおすすめします。そうすれば、Android 10 を実行するデバイスでアプリが想定どおりに動作します。

パフォーマンス

直接ファイルパスを使用してメディア ファイルの順次読み取りを実行する場合は、MediaStore API と同等のパフォーマンスが得られます。

一方、直接ファイルパスを使用してメディア ファイルのランダム読み取りと書き込みを実行する場合は、処理が最大で 2 倍遅くなる可能性があります。そのような場合は、代わりに MediaStore API を使用することをおすすめします。

メディアストアから取得できる値

既存のメディア ファイルにアクセスする場合は、アプリのロジックで DATA 列の値を使用できます。これは、この値に有効なファイルパスが含まれているためです。ただし、ファイルが常に使用可能であるとは限りません。ファイルベースの I/O エラーが発生した場合の処理を準備してください。

一方、メディア ファイルを作成または更新する場合は、DATA 列の値を使用しないでください。代わりに、DISPLAY_NAME 列と RELATIVE_PATH 列の値を使用します。

他のアプリからのデータへのアクセス

Android 11 ではユーザーのプライバシーを保護するために、アプリから他のアプリのプライベート ディレクトリへのアクセスを制限しています。

内部ストレージのデータ ディレクトリへのアクセス

変更の詳細

変更の名前: APP_DATA_DIRECTORY_ISOLATION

変更 ID: 143937733

切り替え方法

アプリと Android 11 との互換性をテストする際に、以下の ADB コマンドを使用して、この変更をオンまたはオフに切り替えることができます。

adb shell am compat enable (143937733|APP_DATA_DIRECTORY_ISOLATION) PACKAGE_NAME
adb shell am compat disable (143937733|APP_DATA_DIRECTORY_ISOLATION) PACKAGE_NAME

互換性フレームワークと変更の切り替えについて詳しくは、Android 11 とのアプリの互換性をテストするをご覧ください。

Android 9(API レベル 28)以降、内部ストレージのデータ ディレクトリにあるファイルを他のアプリから誰でもアクセスできるようにするアプリは制限されています。Android 9 以降をターゲットとするアプリでは、データ ディレクトリにあるファイルを誰でもアクセスできるようにはできません

Android 11 では、この制限が拡張されています。Android 11 をターゲットとするアプリでは、他のアプリが Android 8.1(API レベル 27)以前をターゲットとしており、データ ディレクトリにあるファイルを誰でも読み取り可能としている場合であっても、他のアプリのデータ ディレクトリにあるファイルにアクセスできません。

外部ストレージ上のアプリ固有のディレクトリへのアクセス

Android 11 では、アプリは外部ストレージにある別のどのアプリの専用ディレクトリにもアクセスできなくなりました。

ドキュメントへのアクセス制限

デベロッパーがテストする時間を設けるため、ストレージ アクセス フレームワーク(SAF)に関連する変更は、アプリが Android 11 をターゲットとした場合のみ有効になります。

ディレクトリへのアクセス

ACTION_OPEN_DOCUMENT_TREE インテントのアクションを使って以下のディレクトリへのアクセスをリクエストすることはできなくなりました。

  • 内部ストレージ ボリュームのルート ディレクトリ。
  • エミューレートされているカードかリムーバブル カードかによらず、デバイス メーカーが信頼できるとみなす各 SD カード ボリュームのルート ディレクトリ。信頼できるボリュームとは、ほとんどの場合にアプリが正常にアクセスできるボリュームです。
  • Download ディレクトリ。

ファイルへのアクセス

ACTION_OPEN_DOCUMENT_TREE または ACTION_OPEN_DOCUMENT インテントのアクションを使って、以下のディレクトリから個々のファイルをユーザーに選択してもらうようリクエストすることはできなくなりました。

  • Android/data/ ディレクトリとすべてのサブディレクトリ。
  • Android/obb/ ディレクトリとすべてのサブディレクトリ。

変更をテストする

この動作変更は、以下の手順でテストします。

  1. ACTION_OPEN_DOCUMENT アクションを設定してインテントを呼び出します。Android/data/ ディレクトリと Android/obb/ ディレクトリが両方とも表示されないことを確認します。
  2. 次のいずれかを行います。
  3. ACTION_OPEN_DOCUMENT_TREE アクションを設定してインテントを呼び出します。Download ディレクトリが表示され、そのディレクトリに関連付けられたアクション ボタンがグレー表示になっていることを確認します。

権限

Android 11 では、ストレージの権限に関連する以下の変更が導入されました。

ターゲットとするバージョンによらない

最初のダイアログに「[設定] で許可してください」と表示される
図 1. 対象範囲別ストレージを使用するアプリが READ_EXTERNAL_STORAGE 権限をリクエストするときに表示されるダイアログ

Android 11 では、アプリのターゲット SDK のバージョンによらず、以下の変更が有効になります。

  • ストレージのランタイム権限が [ファイルとメディア] に変更されました。
  • アプリが対象範囲別ストレージをオプトアウトせずに READ_EXTERNAL_STORAGE 権限をリクエストすると、Android 10 とは異なるダイアログがユーザーに表示されます。図 1 に示すように、アプリが写真やメディアへのアクセスをリクエストしていることがダイアログに表示されます。

    ユーザーは READ_EXTERNAL_STORAGE 権限を持つアプリをシステム設定で確認できます。設定アプリ > [プライバシー] > [権限マネージャ] > [ファイルとメディア] のページで、その権限を持つアプリは [すべてのファイルで許可] の下に表示されます。

    注: アプリが Android 11 をターゲットとしている場合、「すべてのファイル」へのアクセスは読み取り専用となります。このアプリを使用して共有ストレージのすべてのファイルに対して読み取りと書き込みを行うには、[すべてのファイルへのアクセス] 権限を持つ必要があります。

Android 11 をターゲットとする

アプリが Android 11 をターゲットとしている場合、WRITE_EXTERNAL_STORAGE 権限と WRITE_MEDIA_STORAGE 特権の両方で、追加のアクセス権を提供しなくなりました。

Android 10(API レベル 29)以上を実行するデバイスでは、ストレージ関連の権限をリクエストしなくても、アプリは MediaStore.Downloads などの明確に定義されたメディア コレクションにデータを書き込むことができます。詳しくは、アプリ内のメディア ファイルを操作する場合に必要な権限だけをリクエストする方法をご確認ください。

すべてのファイルへのアクセス

共有ストレージへのアクセスを必要とするアプリの大半は、ストレージ アクセス フレームワークや MediaStore API などの対象範囲別ストレージのおすすめの方法に従うことが可能です。しかし、一部のアプリでは、重要なユースケースでデバイス上のファイルへの幅広いアクセスが必要な場合に、プライバシーに配慮したストレージのおすすめの方法を効率的に使用することができません。

たとえば、ウィルス対策アプリの主要なユースケースでは、さまざまなディレクトリで多数のファイルを定期的にスキャンする必要があります。このスキャンで、ユーザーがシステム ファイル選択ツールを使用して繰り返しディレクトリを選択する必要があるとすれば、ユーザー エクスペリエンスは低下します。他のユースケース(ファイル マネージャー アプリ、バックアップと復元を行うアプリ、ドキュメント管理アプリなど)でも、同様の状況を考慮する必要があります。

アプリでは、以下の手順を使用して、「すべてのファイルへのアクセス」という特別なアプリアクセスをユーザーにリクエストできます。

  1. マニフェストで MANAGE_EXTERNAL_STORAGE 権限を宣言します。
  2. ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION インテントのアクションを使用してユーザーをシステム設定のページに移動します。そのページでユーザーは [すべてのファイルを管理できるアクセス権を付与] オプションを有効にすることができます。

アプリに MANAGE_EXTERNAL_STORAGE 権限が付与されているかどうかを判断するには、Environment.isExternalStorageManager() を呼び出します。

MANAGE_EXTERNAL_STORAGE 権限により、以下の権限が付与されます。

  • 共有ストレージ内のすべてのファイルへの読み取りアクセス権と書き込みアクセス権。

  • MediaStore.Files テーブルの内容へのアクセス権。

  • USB On The Go(OTG)ドライブと SD カードの両方のルート ディレクトリへのアクセス権。

  • /Android/data//sdcard/Android、および /sdcard/Android のほとんどのサブディレクトリを除く、すべての内部ストレージ ディレクトリへの書き込みアクセス権。この書き込みアクセス権には、ファイルパスのアクセス権が含まれています。

    この権限を付与されたアプリでも、他のアプリに属するアプリ専用ディレクトリにはアクセスできません。このようなディレクトリは、ストレージ ボリューム上で Android/data/ のサブディレクトリとして表示されるためです。

アプリに MANAGE_EXTERNAL_STORAGE 権限がある場合は、MediaStore API またはファイルパスを使用して、これらの追加のファイルとディレクトリにアクセスできます。ただし、ストレージ アクセス フレームワークを使用している場合は、MANAGE_EXTERNAL_STORAGE 権限がなくてもアクセスできるファイルまたはディレクトリにしかアクセスできません。

テストのために有効にする

「すべてのファイルへのアクセス」権限がアプリに及ぼす影響を調べるには、テスト目的で権限を有効にします。これを行うには、テストデバイスに接続されているマシンで次のコマンドを実行します。

adb shell appops set --uid PACKAGE_NAME MANAGE_EXTERNAL_STORAGE allow

Google Play に関する注意事項

このセクションでは、Google Play でアプリを公開するデベロッパー向けの注意事項について説明します。

共有ストレージへの広範なアクセスを制限するため、Google Play ストアでは、Android 11 をターゲットとするアプリを評価し、MANAGE_EXTERNAL_STORAGE 権限を介してすべてのファイルへのアクセスをリクエストするようにポリシーをアップデートしました。

ストレージ アクセス フレームワークMedia Store API など、よりプライバシーに配慮した API をアプリで効果的に利用できない場合にのみ、MANAGE_EXTERNAL_STORAGE 権限をリクエストしてください。また、アプリによる権限の用途は、許容される用途に該当し、アプリのコア機能に直接関連している必要があります。次の例に類似したユースケースがアプリに含まれる場合は、MANAGE_EXTERNAL_STORAGE 権限をリクエストできる可能性が高くなります。

  • ファイル マネージャー
  • バックアップと復元
  • ウイルス対策アプリ
  • ドキュメント管理アプリ

COVID-19(新型コロナウイルス感染症)の影響を踏まえ、Android 11 をターゲットとし、MANAGE_EXTERNAL_STORAGE 権限を必要とするアプリについては、2021 年初頭まで Google Play へのアップロードができなくなりました。新しいアプリのほか、既存のアプリのアップデートも対象となります。詳細については、ポリシー ヘルプセンターの更新されたポリシーをご覧ください。

現時点で、MANAGE_EXTERNAL_STORAGE 権限が必要と思われるアプリについては、ターゲット SDK を Android 11(API レベル 30)に更新しないことをおすすめします。Android 10 をターゲットとする場合は、requestLegacyExternalStorage フラグの使用をご検討ください。