原文:https://source.android.com/devices/tech/perf/apk-caching
本文介绍了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更新用来下载系统映像的更新版本。
因此,无法通过OTA更新APK缓存; 它只能在出厂时预装。恢复出厂设置仅影响/ data分区。系统B分区一直保存预加载的内容直到OTA映像被下载。恢复出厂设置后,系统将再次进行首次启动。这意味着如果将OTA映像下载到B分区后,再将设备恢复出厂设置,则APK缓存不可用。
实现
方法1. system_other分区上的内容
赞成:恢复出厂设置后预加载的内容不会丢失(重启后将从B分区复制)。
反对:B分区需要空间。恢复出厂设置后启动需要额外的时间来复制预加载的内容。
为了在首次引导期间复制预加载,系统将调用脚本/system/bin/preloads_copy.sh
。使用单参数(system_b
分区的只读挂载点的路径)来调用该脚本:
要实现此功能,请进行这些特定设备的更改。以下是Marlin
的一个例子:
- 将执行复制的脚本添加到
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
- 编辑
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域:
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
- 在新
/sepolicy/file_contexts
文件中注册域:
/system/bin/preloads_copy\.sh u:object_r:preloads_copy_exec:s0
查找示例SELinux上下文文件:device/google/marlin/sepolicy/preloads_copy.te
- 在构建时,必须将具有预加载内容的目录复制到
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可以自由选择实际的仓库名称/路径。
- 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可能并不总是有效。
新@SystemApi方法,getPreloadsFileCache()
加入到 android.content.Context
。它返回预加载缓存中特定于应用程序的目录的绝对路径。
添加了一个新方法,IPackageManager.deletePreloadsFileCache
允许删除preloads目录以回收所有空间。该方法只能由具有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
文件及其内容。请参阅此屏幕截图,了解这些结果在用户界面中的显示方式。