APK 快取

本文說明 APK 快取解決方案的設計,方便在支援 A/B 分區的裝置上快速安裝預先載入的應用程式。

在新的A/B 分割裝置上,OEM 可以將預先載入的應用程式和熱門應用程式放在 APK 快取中,而這些快取儲存在 B 分割區 (通常為空白),不會影響任何面向使用者的資料空間。裝置上備有 APK 快取,因此新裝置或最近恢復原廠設定的裝置幾乎可以立即使用,不必從 Google Play 下載 APK 檔案。

用途

  • 將預先載入的應用程式儲存在 B 分割區,加快設定速度
  • 將常用應用程式儲存在 B 分割區,加快還原速度

必要條件

如要使用這項功能,裝置必須符合下列條件:

  • 已安裝 Android 8.1 (O MR1) 版本
  • 已實作 A/B 分區

預先載入的內容只能在首次開機時複製。這是因為在支援 A/B 系統更新的裝置上,B 分割區實際上不會儲存系統映像檔,而是預先載入內容,例如零售展示資源、OAT 檔案和 APK 快取。資源複製到 /data 分區後 (首次開機時會發生此情況),無線 (OTA) 更新會使用 B 分區下載系統映像檔的更新版本。

因此無法透過 OTA 更新 APK 快取,只能在工廠預先載入。恢復原廠設定只會影響 /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. 編輯 init.common.rc 檔案,建立必要的 /data/preloads 目錄和子目錄:
    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/+/android16-release/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)
    
    以下是 Makefile 的變更範例,可將 APK 快取資源從供應商的 Git 存放區 (在本例中為 vendor/google_devices/marlin/preloads) 複製到 system_other 分割區的位置,裝置首次啟動時,這些資源會複製到 /data/preloads。這個指令碼會在建構時執行,準備 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/ 目錄中的內容。

使用 ApkCacheTest 應用程式範例測試 APK 快取。

  1. 從根目錄執行下列指令,建構應用程式:
    make ApkCacheTest
    
  2. 以具備特殊權限的應用程式身分安裝應用程式 (請注意,只有具備特殊權限的應用程式才能存取 APK 快取)。 這需要已解鎖裝置:
    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. 視需要模擬檔案快取目錄及其內容 (也需要根層級權限):
    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 應用程式。應用程式應會顯示一個檔案 test.txt 及其內容。請參閱這張螢幕截圖,瞭解這些結果在使用者介面中的顯示方式。

    圖 1. ApkCacheTest 結果。