APK 快取

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

原始設備製造商 (OEM) 可以在新款A/B 分割裝置的空白 B 分割區中儲存 APK 快取,並在其中放置預先載入內容和熱門應用程式,不會影響任何面向使用者的資料空間。只要在裝置上提供 APK 快取,新裝置或最近恢復原廠設定的裝置就能立即使用,無須從 Google Play 下載 APK 檔案。

用途

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

必要條件

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

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

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

因此,APK 快取無法透過 OTA 更新,只能在工廠預先載入。工廠重置只會影響 /data 分割區。在 OTA 映像檔下載之前,系統 B 分區仍會保留預先載入的內容。恢復原廠設定後,系統會再次進行首次啟動。也就是說,如果 OTA 映像檔下載至 B 分區,然後裝置恢復原廠設定,則無法使用 APK 快取功能。

實作

方法 1. system_other 分割區的內容

Pro:恢復原廠設定後,預先載入的內容不會遺失,系統會在重新啟動後從 B 分區複製這些內容。

Con:需要 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/+/main/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 變更的範例,可從供應商的 Git 存放區 (在本例中為 vendor/google_devices/marlin/preloads) 複製 APK 快取資源,並將其複製至 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. 模擬檔案快取目錄及其內容 (也需要 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 應用程式。應用程式應會顯示一個檔案 test.txt 及其內容。請參閱這張螢幕截圖,瞭解這些結果在使用者介面中的顯示方式。

    圖 1. ApkCacheTest 結果。