2012/10/22

Android:JBで追加されたREAD_EXTERNAL_STORAGE

●はじめに

JellyBeanでREAD_EXTERNAL_STORAGE権限が追加されました。
READ_EXTERNAL_STORAGE権限は外部SDカードの読み込みを制御するものです。

JBにおいて、この権限は将来有効化される"予約された権限"という位置付けです。
(SDカードを読み込むアプリは、将来READ_EXTERNAL_STORAGE権限が必要になるということです)
そのため、JBでのREAD_EXTERNAL_STORAGE権限は何の効力も持ちません。

宣言を見ても、テスト用であることがわかります。
<!-- Allows an application to read from external storage -->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
    android:label="@string/permlab_sdcardRead"
    android:description="@string/permdesc_sdcardRead"
    android:protectionLevel="normal" />
http://tools.oesf.biz/android-4.1.2_r1.0/xref/frameworks/base/core/res/AndroidManifest.xml#692

将来のためにも、SDカードの読み込みが必要なアプリはREAD_EXTERNAL_STORAGE権限を要求するようにしましょう。


●動作確認

JBでは、開発者オプションでSDカードの読み込み時にREAD_EXTERNAL_STORAGE権限を必須にすることが可能です。
これを活用すれば、将来READ_EXTERNAL_STORAGE権限が必須になった場合の予行練習が可能です。

これを設定するには、
 [設定]>[開発者向けオプション]>[SDカードの保護]
にチェックを付けます。 ※Nexus7の場合は[USBストレージの保護]になっています。
これで、SDカードの読み込み時にREAD_EXTERNAL_STORAGE権限がチェックされるようになります。

# 2012/10/22現在、最新のエミュレータ(SDK Platform 16 Rev.2)ではこの確認を実施できない可能性があります。
# この環境では何度試してもエラーが発生しませんでした。

あとは、下記のようなコードでSDにアクセスすれば動作確認できます。
File sdFile = new File(Environment.getExternalStorageDirectory(),
        "/memo.txt");
try {
    FileInputStream fis = new FileInputStream(sdFile);
    fis.read();
    fis.close();  // TODO: move finally section.
} catch (Exception e) {
}
READ_EXTERNAL_STORAGE権限を持っていないと、下記のようなエラーが発生します。
java.io.FileNotFoundException: /storage/sdcard0/memo.txt: open failed: EACCES (Permission denied)
  at libcore.io.IoBridge.open(IoBridge.java:416)
  at java.io.FileInputStream.<init>(FileInputStream.java:78)
  at com.example.sdcardread.MainActivity.onResume(MainActivity.java:28)
  ...
Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
  at libcore.io.Posix.open(Native Method)
  at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
  at libcore.io.IoBridge.open(IoBridge.java:400)
  ... 18 more

ただし、WRITE_EXTERNAL_STORAGE権限を持っていると、READ_EXTERNAL_STORAGE権限を持っていなく
ても、SDカードの読み込みは可能です。
これは、WRITE_EXTERNAL_STORAGE権限がREAD_EXTERNAL_STORAGE権限を内包しているためです。

この辺りはPackageParserの128行目にSplitPermissionとして定義されています。
new PackageParser.SplitPermissionInfo[] {
  // READ_EXTERNAL_STORAGE is always required when an app requests
  // WRITE_EXTERNAL_STORAGE, because we can't have an app that has
  // write access without read access.  The hack here with the target
  // target SDK version ensures that this grant is always done.
  new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
    new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE },
    android.os.Build.VERSION_CODES.CUR_DEVELOPMENT+1),
http://tools.oesf.biz/android-4.1.2_r1.0/xref/frameworks/base/core/java/android/content/pm/PackageParser.java#128


●そのほか

GBでのSDマウントポイントは /mnt/sdcard が主流でした。
ICSでは /mnt/sdcard/external_sd。
しかし、JBでは /storage/sdcard0/ が主流となりつつあるようです。

この辺はキャリアによって様々です。
動作確認の際、試験ファイルのpush先には注意しましょう。

以上です。