Adoptable Storage

Android では、これまで常に外部ストレージ アクセサリ(SD カードなど)がサポートされてきました。ただし、従来の外部ストレージには永続性が見込めず、データ保護も最小限であったことから、シンプルなファイル ストレージに限定されてきたという経緯がありました。Android 6.0 では、外部ストレージ メディアを取り込み、内部ストレージのように動作させる機能が導入されています。

外部ストレージ メディアを取り込むと、一度に 1 つの Android デバイスでのみ動作するようにフォーマットと暗号化が行われます。取り込まれたメディアは、Android デバイスと強く結合されるため、すべてのユーザーのアプリと個人データを安全に格納できます。

ユーザーが新しいストレージ メディア(SD カードなど)を取り込み可能な場所に挿入すると、Android 上でそのメディアの使用方法の確認が求められます。ユーザーは、メディアを取り込んでフォーマットと暗号化を行うか、シンプルなファイル ストレージとしてそのまま使用するかを選択できます。取り込みを選択すると、プラットフォームによって、プライマリの共有ストレージ コンテンツ(通常は /sdcard にマウント)が取り込まれたメディアに移行され、内部ストレージの貴重なスペースが解放されます。従来のストレージでは MBR が使用されるため、ストレージの上限が 2 TB になりますが、Adoptable Storage では GPT が使用されるため、ファイル ストレージの上限は最大 9 ZB になります。

アプリは、デベロッパーが android:installLocation 属性でサポートを指定した場合に限り、Adopted Storage メディアに配置できます。サポート対象のアプリを新規にインストールする場合は、空き領域が最も多いストレージ デバイスに自動的に配置されます。ユーザーは設定アプリ上でサポート対象アプリのストレージデ バイス間での移動を行えます。取り込まれたメディアに移されたアプリは、メディアが取り外されている間は記録され、再度挿入された時点でメディアに戻ります。

セキュリティ

プラットフォームでは、取り込まれたデバイスごとに暗号鍵をランダムに生成し、その鍵を Android デバイスの内部ストレージに格納します。そうすることで、取り込まれたメディアは内部ストレージと同様に安全に保護されます。鍵は、取り込まれたパーティションの GUID に基づいて、各 Adopted Storage デバイスに関連付けられます。Adopted Storage デバイスは、aes-cbc-essiv:sha256 アルゴリズムと 128 ビットの鍵サイズで構成された dm-crypt で暗号化されます。

Adopted Storage デバイスのディスク上のレイアウトは、SELinux ラベルなどの内部データ パーティションとほぼ同じです。Android デバイスでマルチユーザーがサポートされている場合、Adopted Storage デバイスでもマルチユーザーをサポートし、内部ストレージと同水準の分割が行われます。

Adopted Storage デバイスに格納されたデータは、そのデバイスを取り込んだ Android デバイスと強く結合されるため、親デバイスから暗号鍵を抽出することはできません。したがって、ストレージ デバイスを別の場所にマウントすることはできません。

デフォルトの暗号化アルゴリズムは、コンテンツ モードの場合は aes-256-xts で、ファイル名の場合は aes-256-heh です。これを変更するには、device.mkPRODUCT_PROPERTY_OVERRIDES で、プロパティ ro.crypto.volume.contents_mode および ro.crypto.volume.filenames_mode の値をそれぞれ変更します。

カーネルが HEH ファイル名暗号化に対応していない場合は、代わりに以下の行を device.mk に追加して、CTS モードを使用します。

PRODUCT_PROPERTY_OVERRIDES += \
    ro.crypto.volume.filenames_mode=aes-256-cts

パフォーマンスと安定性

Adopted Storage に使用するメディアについては、偶発的なデータの損失や破損を防ぐため、バッテリー収納部のスロットや保護カバーの内側など、安定した場所にある外部ストレージ メディアのみとすることを検討してください。特に、スマートフォンやタブレットに接続した USB デバイスの使用は控えるようにしてください。ただし、一般的には、テレビとして使用されるデバイスに接続する外付け USB ドライブは除外できます。通常、テレビ自体は安定した場所に設置されているためです。

ユーザーが新しいストレージ デバイスを取り込むと、プラットフォームではベンチマークが実行され、内部ストレージとパフォーマンスが比較されます。取り込まれたデバイスが内部ストレージより著しく低速である場合、プラットフォームはエクスペリエンスが低下する可能性についてユーザーに警告を発します。このベンチマークは、よく利用される Android アプリの実際の I/O 動作をベースとしています。現在の AOSP の実装では、しきい値は 1 つで超過するとユーザーに警告が発せられるだけですが、デバイス メーカーはこれをさらに発展させて、カードが極端に遅い場合は取り込み自体を拒否するなどの実装を採用することができます。

取り込まれたデバイスは、POSIX 権限と拡張属性をサポートするファイルシステム(ext4f2fs など)でフォーマットする必要があります。フラッシュ ベースのストレージ デバイスの場合、パフォーマンスを最適化するには f2fs ファイルシステムが推奨されます。

定期的なアイドル メンテナンスを実施する際には、内部ストレージの場合と同様に、プラットフォームが取り込まれたメディアに対して FI_TRIM を発行します。現在の SD カード仕様は DISCARD コマンドに対応していませんが、カーネルは代わりに ERASE コマンドを使用します。SD カードのファームウェアでは、最適化を目的としてこのコマンドの使用を選択できます。

二重暗号化の解決

Adoptable Storage は、Android 8.x 以前では FBE で動作しませんでした。Adoptable Storage を利用する既存のデバイスは、すべてフルディスク暗号化(FDE)を使用していました。Android 9 では、Adoptable Storage が FBE で動作します。ただし、Adoptable Storage には FDE レイヤと FBE レイヤがあるため、デフォルトではファイル コンテンツが二重に暗号化されます。両方のレイヤでファイル コンテンツがデフォルトで暗号化されるため、デバイスのパフォーマンスが低下します。この二重暗号化の問題を解決してデバイスのパフォーマンスを高速化するには、次の手順を実施します。

  1. パッチをカーネルに追加します。
  2. vold を使用して変更内容を伝達するために、device.mk に次の行を追加します。
    PRODUCT_PROPERTY_OVERRIDES += ro.crypto.allow_encrypt_override=true

この設定を行っても、カーネルパッチが存在しない場合は Adoptable Storage が機能せず、dm デバイスを作成できなかったというエラーが vold ログに記録されます。

テスト

Adoptable Storage が機能しているかどうかをテストするには、次の CTS テストを実行します。

cts-tradefed run commandAndExit cts-dev \
        -m CtsAppSecurityHostTestCases \
        -t android.appsecurity.cts.AdoptableHostTest

デバイスに内蔵スロットがない場合、または USB コネクタをアクティブな adb 接続に使用している場合に、USB ドライブと SD カードの動作を確認するには、次のコマンドを使用します。

    adb shell sm set-virtual-disk true