开启AB升级方案的项目,因为很多需要升级的镜像都有两份,所以存储空间比较浪费。为缓解此问题,有个针对odex的优化方案。
编译版本会生成两个system镜像:system.img和system_other.img,其中,system_other.img中存储的就是odex文件,这样system.img就能小很多,意味着可以为system分区划分较小的空间。
在首次开机时,假设system.img镜像存储在A slot,那么此时的B slot是闲置的。所以可以把system.img刷入A slot的system分区,把system_other.img刷入B slot的system分区。在首次开机时,再把system_other.img中的odex文件拷贝到data分区。
https://source.android.com/devices/tech/dalvik/configure.html#other_odex
开机后copy odex文件的相关代码
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
// 首次开机时才执行
if (!mOnlyCore && mFirstBoot) {
requestCopyPreoptedFiles();
}
...
}
/**
* Requests that files preopted on a secondary system partition be copied to the data partition
* if possible. Note that the actual copying of the files is accomplished by init for security
* reasons. This simply requests that the copy takes place and awaits confirmation of its
* completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
*/
private static void requestCopyPreoptedFiles() {
final int WAIT_TIME_MS = 100;
final String CP_PREOPT_PROPERTY = "sys.cppreopt";
if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
// We will wait for up to 100 seconds.
final long timeStart = SystemClock.uptimeMillis();
final long timeEnd = timeStart + 100 * 1000;
long timeNow = timeStart;
while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
try {
Thread.sleep(WAIT_TIME_MS);
} catch (InterruptedException e) {
// Do nothing
}
timeNow = SystemClock.uptimeMillis();
if (timeNow > timeEnd) {
SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
Slog.wtf(TAG, "cppreopt did not finish!");
break;
}
}
Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
}
}
这里是在PSM初始化时,PMS_scan_START之前进行copy动作,且只在首次开机执行。
有开关控制此功能:ro.cp_system_other_odex=1则打开,默认为0
如注释中提到的,PMS并不负责真正的copy,因为它没有相关权限,而是由init进程来执行。
PMS和init的交互是通过系统属性:sys.cppreopt
PMS将其设置为requested,来请求init去copy;每隔一段时间检测下是否已经变更为finished。
当sys.cppreopt=requested时,触发copy;结束后,设置为finished
如下是对应rc文件,相关操作会在init进程中执行。
这个代码给我们提供了一个普通进程和init进程交互的方法:透过property通信
/system/extras/cppreopts/cppreopts.rc
on property:sys.cppreopt=requested && property:ro.boot.slot_suffix=_a
mount ext4 /dev/block/by-name/system_b /postinstall ro nosuid nodev noexec
exec - root -- /system/bin/cppreopts.sh /postinstall
# Optional script to copy additional preloaded content to data directory
exec - system system -- /system/bin/preloads_copy.sh /postinstall
umount /postinstall
setprop sys.cppreopt finished
on property:sys.cppreopt=requested && property:ro.boot.slot_suffix=_b
mount ext4 /dev/block/by-name/system_a /postinstall ro nosuid nodev noexec
exec - root -- /system/bin/cppreopts.sh /postinstall
# Optional script to copy additional preloaded content to data directory
exec - system system -- /system/bin/preloads_copy.sh /postinstall
umount /postinstall
setprop sys.cppreopt finished
具体copy流程:
把system_other.img镜像mount到/postinstall,然后运行/system/bin/cppreopts.sh,之后umount。这个sh脚步在/system/extras/cppreopts/cppreopts.sh
在copy时,需要用到preopt2cachename来生成目标文件路径和名称:system/extras/preopt2cachename/,这个bin档会输出一个string
std::cout << output_file_location;
一般地,system/app和system/priv-app/目录下的odex文件会生成到system_other.img
/build/make/core/dex_preopt.mk
# The default filter for which files go into the system_other image (if it is
# being used). To bundle everything one should set this to '%'
SYSTEM_OTHER_ODEX_FILTER ?= app/% priv-app/%
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)