365連休

にわかのandroidとかの開発メモ。

Android 10 Q 対象範囲別ストレージ(Scoped Storage)とは

2020/9/14追記

Android 11の登場とともにこの記事は陳腐化しています。

公式情報(日本語)をご確認ください。

developer.android.com

 

 

黒い太字はAndroid Developersドキュメント原文です。

緑字は解釈です。

アプリのストレージアクセス一覧表(意訳)へワープ

ページ下部へワープ

 

ユーザーがファイルを詳細に管理して整理できるように、Android 10(API レベル 29)以降をターゲットとするアプリには、外部ストレージ デバイスに対する特別アクセス権限がデフォルトで付与されます(対象範囲別ストレージ)。

つまりtargetSDK 28以下ならこれまで通りで、targetSDK 29以上なら対象範囲別ストレージを使用することができる。ということ?

 

このようなアプリで認識できるのは、Context.getExternalFilesDir() を使用してアクセスするアプリ固有ディレクトと、特定のメディアタイプだけに限られます。アプリ固有ディレクトリ内や MediaStore 内に存在していないファイルにアクセスする必要がない限り、対象範囲別ストレージを使用することをおすすめします。

このようなアプリ=特別なアクセス権限が付与されたアプリ=targetSDK 29

targetSDK 29以上のアプリは、アプリ固有ディレクトリと特定のメディアタイプ以外にアクセスできない。ということ?

それ以外のファイルにアクセスするには、対象範囲別ストレージを使用しなければできる。ということ?だって"使用することをおすすめ"してるだけだから。

 

ファイルの場所 必要なパーミッション アクセス方法(*)

アプリを
アンインストールすると
ファイルも削除されるか?

アプリ固有ディレクト なし getExternalFilesDir()
メディア コレクション
(写真、動画、音声)
READ_EXTERNAL_STORAGE
他のアプリのファイルにアクセスする場合のみ

たぶん嘘、後述

MediaStore ×
ダウンロード
(ドキュメント、
電子書籍
なし ストレージ アクセス フレームワーク
(システムのファイル選択ツールを読み込みます)
×

 

targetSDK 29以上の場合、その他の無数に存在するファイルにアクセスする方法が無い。ということ?

targetSDK 29以上の場合、任意の場所に任意のファイルを生成できないから、それを読み取ることも必要無い。ということ?

targetSDK 29以上の場合、ファイルエクスプローラの類は全滅。ということ?

 

新しめのAndroid Developersドキュメントは英語版と整合が取れていないケースがあるので念のために確認する。※すると同じURLの英語版(https://developer.android.com/training/data-storage/files/external-scoped?hl=en)は存在せず、https://developer.android.com/training/data-storage#scoped-storageへリダイレクトされた。

  Type of content Access method Permissions needed

Can other apps access?

他アプリがアクセスできる?

Files removed on app uninstall?
App-specific files

Files meant for your app's use only

アプリ専用ファイル

From internal storage, getFilesDir() or getCacheDir()

内部ストレージは上記メソッド

From external storage, getExternalFilesDir() or getExternalCacheDir()

外部ストレージは上記メソッド

Never needed for internal storage

内部ストレージには不要


Not needed for external storage when your app is used on devices that run Android 4.4 (API level 19) or higher

外部ストレージはAndroid4.4以上には不要

No, if files are in a directory within internal storage

いいえ、ファイルが内部ストレージにある場合


Yes, if files are in a directory within external storage

はい、ファイルが外部ストレージにある場合

Yes
Media

Shareable media files (images, audio files, videos)

共有可能なメディアファイル

MediaStore API

メディアストアAPI

READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE when accessing other apps' files on Android 10 (API level 29) or higher

Android 10以上の場合、他のアプリのファイルにアクセスする場合、READまたはWRITE権限が必要。

Permissions are required for all files on Android 9 (API level 28) or lower

Android 9以下の場合、全てのファイルにアクセス許可が必要。

Yes, though the other app needs the READ_EXTERNAL_STORAGE permission

 

はい

しかし、他アプリはREAD権限が必要。

No
Documents and other files

Other types of shareable content, including downloaded files

ダウンロードしたファイルを含む、その他の種類の共有可能なコンテンツ

Storage Access Framework

ユーザにとっても、
開発者にとっても、
使いづらいストレージアクセスフレームワーク

None

Yes, through the system file picker

はい

しかし、使いづらいシステムファイルピッカーを使用しなければならない。

No
App preferences Key-value pairs Jetpack Preferences library None No Yes
Database Structured data Room persistence library None No Yes

 

う~ん。

結構違う。

日本語ドキュメントは簡略化し過ぎて必要な情報が欠落している。

もしや、Scoped Storageの説明も違うのかも。

 

To give users more control over their files and to limit file clutter, apps that target Android 10 (API level 29) and higher are given scoped access into external storage, or scoped storage, by default.

ユーザがファイルを詳細に制御し、乱雑さを制限するため、Android 10以上を対象とするアプリには、既定で対象範囲別ストレージへのアクセスが許可される。

 

Such apps have access only to the app-specific directory on external storage, as well as specific types of media that the app has created.

このようなアプリは、アプリ固有ディレクトリと、アプリが作成した特定のメディアにのみアクセスできる。

 

Note: If your app requests a storage-related permission at runtime, the user-facing dialog indicates that your app is requesting broad access to external storage, even when scoped storage is enabled.

注: アプリが実行時にストレージ関連のアクセス許可を要求する場合、ユーザー向けのダイアログは、スコープ指定ストレージが有効になっている場合でも、アプリが外部ストレージへの広範なアクセスを要求していることを示す。

これまでと同じようにユーザへダイアログ が出るということは、これまで通りのアクセスもできそうに感じるけど、どうなんでしょう?

 

Use scoped storage unless your app needs access to a file that's stored outside of an app-specific directory and outside of a directory that the MediaStore APIs can access.
If you store app-specific files on external storage, you can make it easier to adopt scoped storage by placing these files in an app-specific directory on external storage.
That way, your app maintains access to these files when scoped storage is enabled.

アプリ固有ディレクトリ以外やMediaStore管理外のファイルにアクセスする場合を除き、対象範囲別ストレージを使用する。
アプリ固有のファイルを外部ストレージへ保存する場合、外部ストレージのアプリ固有ディレクトリへ配置することで、対象範囲別ストレージを採用できる。
これにより対象範囲別ストレージが有効である場合、アプリはこれらのファイルへのアクセスを維持する。

対象範囲別ストレージアクセスを使用しないアクセスができないとは言っていない気がする。

 

If your app has another use case that isn't covered by scoped storage, file a feature request and use the app compatibility feature that the platform provides.

対象範囲別ストレージでカバーできない別のユース ケースがアプリにある場合は、機能要求を提出し、プラットフォームが提供するアプリ互換性機能を使用します。

機能要求を提出とは、バグレポートの事で、特例とかじゃない。

アプリ互換性機能とは、旧来の方法でファイルアクセスするようオプトアウトすること。

言葉尻から対象範囲別ストレージじゃない方法がありそうな気がしてたけど、つまりオプトアウトのことっぽい。

 

Temporarily opt-out of scoped storage.
Before your app is fully compatible with scoped storage, you can temporarily opt out by using one of the following methods:

一時的な対象範囲別ストレージのオプトアウト
アプリが対象範囲別ストレージと完全に互換性を持つ前に、次のいずれかの方法で一時的にオプトアウトできる。

  • Target Android 9 (API level 28) or lower.
    targetSDK 28以下に設定する。
  • If you target Android 10 (API level 29) or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file:
    targetSDK 29以上の場合、マニフェストでrequestLegacyExternalStorageにtrueを指定する。
    <manifest ... >
      <!-- This attribute is "false" by default on apps targeting
           Android 10 or higher. -->
        <application android:requestLegacyExternalStorage="true" ... >
          ...
        </application>
    </manifest>

To test how an app targeting Android 9 or lower behaves when using scoped storage, you can opt in to the behavior by setting the value of requestLegacyExternalStorage to false.

対象範囲別ストレージを適用する "Android 9 以下を対象とするアプリ"の動作をテストするには、requestLegacyExternalStorageにfalseを指定して動作をオプトインできる。
targetSDK28以下でもrequestLegacyExternalStorage="false"に指定すれば対象範囲別ストレージを使用できるってこと?

 

警告: 来年度のメジャー プラットフォーム リリースでは、ターゲット SDK レベルに関係なく、すべてのアプリで対象範囲別ストレージが必須となります。そのため、対象範囲別ストレージを使用してアプリが正常に機能するか、必ず事前に確認するようにしてください。そのためには、Android 10(API レベル 29)以降を搭載しているデバイス上で、対象範囲別ストレージ機能が有効になっているか確認する必要があります。

来年度=たぶん2020年度
メジャープラットフォームリリース=新しくリリースされるAndroid≒Android9以下で新たなリリースが出るとは考えにくいので、Android 10.nあるいはAndroid 11以降
Android 10以降のリリースで対象範囲別ストレージが必須になるので早く対応しろ。ってこと?

日本語のドキュメントで警告は出されたものの、オプトアウトに関する英語のドキュメントでは、"一時的"がいつまで可能かは明言されていない。

また、ファイルエクスプローラの類のアプリは、対象範囲別ストレージをオプトアウトできないと実装できないし存在価値も無い。

 

ちなみに、targetSDK29以上で対象範囲別ストレージが有効の時、範囲外のアクセスをしようとするとFileNotFoundExceptionが発生するらしい。

stackoverflow.com

 

上記から検討した結果、選択肢。

  • targetSDK28以下でアプリをリリースする。
    新しい端末に対応しないのは現実的ではない。

  • targetSDK29以上に設定し、対象範囲別ストレージに準拠する。
    多くのアプリで問題なさそうだが、任意の場所の任意のファイルにアクセスしたい場合、StorageAccessFrameworkでは実用に耐えられない。
    ちなみに、MediaStore.MediaColumns.RELATIVE_PATHがAPI28以下で使えないせいで、Environment.・・PublicDirectoryのサブディレクトリに保存できないっぽい。対象範囲別ストレージを有効にしちゃうとAndroid9以下でもアクセスが制限されるのが問題。制限...されるよね?要検証。

  • targetSDK29以上に設定し、requestLegacyExternalStorage="true"を指定し、旧来の方法でアクセスできるようにする。
    一番簡単そう。ただし、これまで通り適切なパーミッションを取得しなければならない。※たぶん嘘だが、日本語ドキュメントによると対象範囲別ストレージが有効であればREADパーミッションのみでファイル操作できるらしい。英語ドキュメントではREAD or WRITEと書いてあるので変更する場合WRITEっぽい。
    いつまでオプトアウトできるかは不透明。恒久的かもしれないし、次期Androidのタイミングで使えなくなるかもしれない。
    ※targetSdkVersion 29でrequestLegacyExternalStorage属性を使用するとUnusedAttribute Lint警告がでるが、脳内無視して構わない。💡(インスペクション)から「Suppress With tools:targetApi Attribute」や「抑止」することもできるが、維持管理の観点からあまりお勧めはしない。

 

 

ところで、対象範囲別ストレージなのか対象範囲別外部ストレージなのか、公式の表記に揺らぎがあって、どっちなんでしょ?いっそスコープストレージでよくね?

 

developer.android.com

 

developer.android.com

developer.android.com

 

MediaStoreのDATA列が削除されるってま?

日本スマートフォンセキュリティ協会『Androidアプリのセキュア設計・セキュアコーディングガイド』より

www.jssec.org

 

※上記は、Android Developersドキュメントの転載や解釈、および英語版ドキュメントの意訳である。

意訳元https://developer.android.com/ライセンス解釈
ソースコードから抽出されたドキュメントやコードはAndroid Open Source Projectの優先ライセンスであるApache 2.0 licenseが適用され、それ以外のコンテンツはCC BY 2.5が適用されている。
よって、ページ全体の意訳についてはCC BY 2.5における翻案行為、ページ内のコード転載についてはApache 2.0 licenseにおける頒布行為にあたると解釈している。