APK キャッシュ

このドキュメントでは、A/B パーティションをサポートするデバイスにプリロード済みアプリを迅速にインストールする APK キャッシュ ソリューションの設計について説明します。

OEM は、ユーザー向けのデータスペースに影響を及ぼすことなく、新しい A/B パーティション デバイスのほとんど空の B パーティションに格納された APK キャッシュに、プリロード コンテンツと一般的なアプリを配置できます。デバイスで APK キャッシュを使用可能にすると、Google Play から APK ファイルをダウンロードしなくても、新しいデバイスまたは出荷時の設定にリセットしたばかりのデバイスをすぐに使用できます。

使用場面

  • プリロードされたアプリを B パーティションに保存して迅速な設定を可能にする
  • 一般的なアプリを B パーティションに保存して迅速な復元を可能にする

前提条件

デバイスでこの機能を使用するための前提条件は次のとおりです。

  • Android 8.1(O MR1)リリースがインストールされている
  • A/B パーティションが実装されている

プリロード済みコンテンツは初回起動時にのみコピーできます。これは、A/B システム アップデートをサポートするデバイスの B パーティションには、システム イメージ ファイルは実際には保存されておらず、その代わりに販売店デモのリソース、OAT ファイル、APK キャッシュなどのプリロード済みコンテンツが保存されているためです。初回起動時にリソースが /data パーティションにコピーされると、B パーティションは、システム イメージの更新版をダウンロードするために、無線(OTA)アップデートによって使用されます。

したがって、APK キャッシュを OTA で更新することはできません。工場出荷時にプリロードできるだけです。出荷時設定へのリセットは、/data パーティションにのみ影響します。OTA イメージがダウンロードされるまで、システムの B パーティションにはプリロード済みコンテンツがそのまま存在します。出荷時の設定にリセットすると、システムの初回起動が再度行われます。つまり、OTA イメージが B パーティションにダウンロードされた後でデバイスを出荷時の設定にリセットした場合、APK キャッシュは使用できなくなります。

実装

アプローチ 1. system_other パーティションのコンテンツ

メリット: 出荷時の設定にリセットした後もプリロード済みコンテンツは失われず、再起動後に B パーティションからコピーされます。

デメリット: B パーティションにスペースが必要です。出荷時の設定にリセットした後の起動で、プリロード済みコンテンツをコピーするための時間が余計にかかります。

システムは、初回起動時にプリロード済みコンテンツをコピーするために、/system/bin/preloads_copy.sh にあるスクリプトを呼び出します。このスクリプトは、単一の引数(system_b パーティションの読み取り専用マウント ポイントへのパス)を指定して呼び出されます。

この機能を実装するには、デバイス固有の変更を行います。Marlin の例を以下に示します。

  1. 次のように、device-common.mk ファイル(この場合は device/google/marlin/device-common.mk)へのコピーを行うスクリプトを追加します。
    # Script that copies preloads directory from system_other to data partition
    PRODUCT_COPY_FILES += \
        device/google/marlin/preloads_copy.sh:system/bin/preloads_copy.sh
    
    サンプル スクリプトのソース: device/google/marlin/preloads_copy.sh
  2. 必要な /data/preloads ディレクトリとサブディレクトリを作成するために、init.common.rc ファイルを編集します。
    mkdir /data/preloads 0775 system system
    mkdir /data/preloads/media 0775 system system
    mkdir /data/preloads/demo 0775 system system
    
    サンプル init ファイルのソース: device/google/marlin/init.common.rc
  3. preloads_copy.te ファイルで新しい SELinux ドメインを定義します。
    type preloads_copy, domain, coredomain;
    type preloads_copy_exec, exec_type, vendor_file_type, file_type;
    
    init_daemon_domain(preloads_copy)
    
    allow preloads_copy shell_exec:file rx_file_perms;
    allow preloads_copy toolbox_exec:file rx_file_perms;
    allow preloads_copy preloads_data_file:dir create_dir_perms;
    allow preloads_copy preloads_data_file:file create_file_perms;
    allow preloads_copy preloads_media_file:dir create_dir_perms;
    allow preloads_copy preloads_media_file:file create_file_perms;
    
    # Allow to copy from /postinstall
    allow preloads_copy system_file:dir r_dir_perms;
    
    サンプル SELinux ドメイン ファイル: device/google/marlin/+/master/sepolicy/preloads_copy.te
  4. 新しい /sepolicy/file_contexts ファイルでドメインを登録します。
    /system/bin/preloads_copy\.sh     u:object_r:preloads_copy_exec:s0
    
    サンプル SELinux コンテキスト ファイル: device/google/marlin/sepolicy/preloads_copy.te
  5. ビルド時に、コンテンツがプリロードされたディレクトリを system_other パーティションにコピーする必要があります。
    # Copy contents of preloads directory to system_other partition
    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,vendor/google_devices/marlin/preloads,system_other/preloads)
    
    これは、ベンダーの Git リポジトリ(この例では vendor/google_devices/marlin/preloads にあります)から system_other パーティション上の領域(後でデバイスを初回起動したときに /data/preloads にコピーされます)に、APK キャッシュ リソースをコピーするために使用できる Makefile の変更例です。このスクリプトは、system_other イメージを準備するためにビルド時に実行されます。プリロード済みコンテンツが vendor/google_devices/marlin/preloads で使用可能になると想定しています。OEM は、実際のリポジトリ名とパスを自由に選択できます。
  6. APK キャッシュは /data/preloads/file_cache にあり、レイアウトは次のとおりです。
    /data/preloads/file_cache/
        app.package.name.1/
              file1
              fileN
        app.package.name.N/
    
    これは、デバイス上の最終的なディレクトリ構造です。OEM は、最終的なファイル構造で上記の構造が再現される限り、どのような実装方法でも選択できます。

アプローチ 2. 工場出荷時にフラッシュされるユーザーデータ イメージのコンテンツ

このアプローチは、プリロード済みコンテンツが /data パーティションの /data/preloads ディレクトリにすでに含まれていることを前提とします。

メリット: 初回起動時にファイルをコピーするためにデバイスをカスタマイズする必要がなく、すぐに使用できます。コンテンツは /data パーティションにすでに含まれています。

デメリット: 出荷時の設定にリセットすると、プリロード済みコンテンツが失われます。これは許容される場合もありますが、品質管理検査を実施した後でデバイスを出荷時の設定にリセットする OEM では、許容されない場合もあります。

android.content.Context に追加された新しい @SystemApi メソッド getPreloadsFileCache() は、プリロード済みキャッシュに含まれるアプリ固有のディレクトリへの絶対パスを返します。

新しいメソッドである IPackageManager.deletePreloadsFileCache が追加され、プリロード ディレクトリを削除してすべての領域を再要求できるようになりました。このメソッドは、SYSTEM_UID を持つアプリ(つまり、システム サーバーまたは設定アプリ)のみが呼び出すことができます。

アプリの準備

プリロード キャッシュ ディレクトリにアクセスできるのは特権アプリのみです。このアクセスを行うには、アプリを /system/priv-app ディレクトリにインストールする必要があります。

検証

  • 初回起動後、デバイスの /data/preloads/file_cache ディレクトリにコンテンツが含まれている必要があります。
  • デバイスの空き容量が不足している場合は、file_cache/ ディレクトリのコンテンツを削除してください。

APK キャッシュをテストするには、ApkCacheTest サンプルアプリを使用します。

  1. ルート ディレクトリから次のコマンドを実行して、アプリをビルドします。
    make ApkCacheTest
    
  2. アプリを特権アプリとしてインストールします(APK キャッシュにアクセスできるのは特権アプリのみです)。これには、ユーザーに root 権限のあるデバイスが必要です。
    adb root && adb remount
    adb shell mkdir /system/priv-app/ApkCacheTest
    adb push $ANDROID_PRODUCT_OUT/data/app/ApkCacheTest/ApkCacheTest.apk /system/priv-app/ApkCacheTest/
    adb shell stop && adb shell start
    
  3. 必要に応じて、ファイル キャッシュ ディレクトリとその内容をシミュレートします(これにも root 権限が必要です)。
    adb shell mkdir -p /data/preloads/file_cache/com.android.apkcachetest
    adb shell restorecon -r /data/preloads
    adb shell "echo "Test File" > /data/preloads/file_cache/com.android.apkcachetest/test.txt"
    
  4. アプリをテストします。アプリをインストールしてテスト用の file_cache ディレクトリを作成した後、ApkCacheTest アプリを開きます。そうすると、1 つのファイル(test.txt)とその内容が表示されます。次のスクリーンショットは、ユーザー インターフェースに表示されたテスト結果を示しています。

    図 1. ApkCacheTest の結果