本文档介绍了 APK 缓存解决方案的设计,该解决方案用于在支持 A/B 分区的设备上快速安装预加载的应用。
原始设备制造商 (OEM) 可以将预加载的应用和热门应用放置在 APK 缓存中,该缓存存储在新的 A/B 分区设备的几乎为空的 B 分区中,而不会影响任何面向用户的数据空间。通过在设备上提供 APK 缓存,新的或最近恢复出厂设置的设备几乎可以立即使用,而无需从 Google Play 下载 APK 文件。
用例
- 将预加载的应用存储在 B 分区中,以便更快地进行设置
- 将热门应用存储在 B 分区中,以便更快地进行恢复
前提条件
要使用此功能,设备需要满足以下条件:
- 已安装 Android 8.1 (O MR1) 版本
- 已实现 A/B 分区
预加载的内容只能在首次启动期间复制。这是因为在支持 A/B 系统更新的设备上,B 分区实际上不存储系统映像文件,而是存储预加载的内容,例如零售演示资源、OAT 文件和 APK 缓存。在资源已复制到 /data 分区(这在首次启动时发生)之后,B 分区将由 无线下载 (OTA) 更新用于下载系统映像的更新版本。
因此,APK 缓存无法通过 OTA 更新;它只能在工厂预加载。恢复出厂设置只会影响 /data 分区。系统 B 分区仍然具有预加载的内容,直到 OTA 映像下载完成。恢复出厂设置后,系统将再次经历首次启动。这意味着,如果 OTA 映像下载到 B 分区,然后设备恢复出厂设置,则 APK 缓存将不可用。
实现
方法 1:系统其他分区上的内容
优点:预加载的内容在恢复出厂设置后不会丢失 - 重启后会从 B 分区复制。
缺点:需要 B 分区上的空间。恢复出厂设置后的启动需要额外的复制预加载内容的时间。
为了在首次启动期间复制预加载,系统会在 /system/bin/preloads_copy.sh
中调用脚本。该脚本使用单个参数(system_b
分区的只读挂载点路径)调用。
要实现此功能,请进行以下设备特定的更改。以下是来自 Marlin 的示例
- 将执行复制的脚本添加到
device-common.mk
文件(在本例中为device/google/marlin/device-common.mk
),如下所示 在以下位置查找示例脚本源代码:device/google/marlin/preloads_copy.sh# 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
- 修改
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 - 在文件
preloads_copy.te
中定义一个新的 SELinux 域 在以下位置找到 SELinux 域文件示例: /device/google/marlin/+/main/sepolicy/preloads_copy.tetype 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;
- 在新的
文件中注册该域/sepolicy/file_contexts 在以下位置找到 SELinux 上下文文件示例: device/google/marlin/sepolicy/preloads_copy.te/system/bin/preloads_copy\.sh u:object_r:preloads_copy_exec:s0
- 在构建时,包含预加载内容的目录必须复制到
system_other
分区 这是一个 Makefile 中更改的示例,该更改允许从供应商的 Git 存储库(在我们的例子中是 vendor/google_devices/marlin/preloads)复制 APK 缓存资源到 system_other 分区上的位置,该位置稍后将在设备首次启动时复制到 /data/preloads。此脚本在构建时运行以准备 system_other 镜像。它期望预加载内容在 vendor/google_devices/marlin/preloads 中可用。OEM 可以自由选择实际的存储库名称/路径。# 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)
- APK 缓存位于
/data/preloads/file_cache
中,并具有以下布局 这是设备上的最终目录结构。只要最终文件结构与上述结构相同,OEM 可以自由选择任何实现方法。/data/preloads/file_cache/ app.package.name.1/ file1 fileN app.package.name.N/
方法 2:工厂刷写的用户数据镜像上的内容
这种替代方法假设预加载内容已包含在 /data
分区上的 /data/preloads
目录中。
优点:开箱即用 - 无需进行设备自定义即可在首次启动时复制文件。内容已在 /data
分区上。
缺点:预加载内容在恢复出厂设置后会丢失。虽然这对某些人来说可能是可以接受的,但对于在进行质量控制检查后恢复出厂设置的 OEM 来说,可能并不总是适用。
一个新的 @SystemApi 方法 getPreloadsFileCache()
已添加到 android.content.Context
中。它返回预加载缓存中特定于应用程序的目录的绝对路径。
添加了一个新方法 IPackageManager.deletePreloadsFileCache
,该方法允许删除预加载目录以回收所有空间。该方法只能由具有 SYSTEM_UID 的应用程序调用,即系统服务器或“设置”。
应用准备
只有特权应用才能访问预加载缓存目录。为了获得该访问权限,应用必须安装在 /system/priv-app
目录中。
验证
- 首次启动后,设备应在
/data/preloads/file_cache
目录中包含内容。 - 如果设备存储空间不足,则必须删除
file_cache/
目录中的内容。
使用示例 ApkCacheTest 应用来测试 APK 缓存。
- 通过从根目录运行此命令来构建应用
make ApkCacheTest
- 将应用作为特权应用安装。(请记住,只有特权应用才能访问 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
- 如果需要,模拟文件缓存目录及其内容(也需要 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"
- 测试应用。安装应用并创建测试
file_cache
目录后,打开 ApkCacheTest 应用。它应该显示一个文件test.txt
及其内容。请参阅此屏幕截图,以了解这些结果在用户界面中的显示方式。
图 1. ApkCacheTest 结果。