APEX(Android Pony EXpress ) 是Google在Android 10中引进的一种用于管理较低级别系统模块的安装包管理器,用来更新一些不适用APK安装流程的系统组件,比如Bionic 、libnativebridge、libnativehelper、libnativeloader以及一些运行时类库。
使用APEX后,Google就可以通过Google play store对系统组件进行更新,加速Google系统升级,当然也可以通过adb进行本地升级。
adb install apex_file_name
adb reboot
APEX文件是一个zip压缩包,类似于一个APK安装包文件,其内部主要包含四个文件:
1. apex_manifest.json
2. AndroidManifest.xml
3. apex_payload.img
4. apex_pubkey
apex_manifest.json内部包含PackageName和Version;
AndroidManifest.xml与APK的对应文件类似,包含其他一些信息;
apex_payload.img是一个ext4的文件系统镜像;
apex_pubkey是用于为文件系统映像签名的公钥;
apexd的源码位于/system/apex /apexd/目录下,是一个本地守护进程,由init.rc负责启动,下面是apexd.rc中的启动程序,里面包含了两个对应的service,分别是apexd和apexd-bootstrap,对应的进程都是apexd,只是后者接受了bootstrap的参数,这个在main函数中会做不同的处理,后面的部分会讲到;
service apexd /system/bin/apexd
class core
critical
user root
group system
shutdown critical
service apexd-bootstrap /system/bin/apexd --bootstrap
critical
user root
group system
oneshot
disabled
在对应的Android.bp可以看到apexd.rc会注入到init.rc中通过init启动,并且source文件是apexd_main.cpp,所以可以确定入口函数位于此文件中;
cc_binary {
name: "apexd",
defaults: [
"apex_defaults",
"libapexservice-deps",
],
srcs: [
"apexd_main.cpp",
],
static_libs: [
"libapex",
"libapexd",
"libapexd_checkpoint_vold",
"libapexservice",
"libavb",
"libdm",
"libvold_binder",
],
shared_libs: [
"libselinux",
],
init_rc: ["apexd.rc"],
// Just like the init, apexd should be able to run without
// any APEX activated. To do so, it uses the bootstrap linker
// and the bootstrap bionic libraries.
bootstrap: true,
target: {
android: {
ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
},
},
}
进入到main函数,根据rc文件的定义,两个进程通过是否携带参数而做不同的处理,apexd-bootstrap会直接在HandleSubcommand函数中处理结束后被return掉,而apexd则会将service注册到SM中作为守护进程;
int main(int /*argc*/, char** argv) {
// Use CombinedLogger to also log to the kernel log.
android::base::InitLogging(argv, CombinedLogger());
if (argv[1] != nullptr) {
return HandleSubcommand(argv); //处理启动进程携带的参数
}
// TODO: add a -v flag or an external setting to change LogSeverity.
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
//创建vold对象用于存储方面的需要
android::apex::StatusOr<android::apex::VoldCheckpointInterface>
vold_service_st = android::apex::VoldCheckpointInterface::Create();
android::apex::VoldCheckpointInterface* vold_service = nullptr;
...
android::apex::onStart(vold_service); //真正执行扫描安装的过程
android::apex::binder::CreateAndRegisterService(); //向SM注册该service
android::apex::binder::StartThreadPool();
...
android::apex::binder::JoinThreadPool();
return 1;
}
int HandleSubcommand(char** argv) {
if (strcmp("--pre-install", argv[1]) == 0) {
LOG(INFO) << "Preinstall subcommand detected";
return android::apex::RunPreInstall(argv);
}
if (strcmp("--post-install", argv[1]) == 0) {
LOG(INFO) << "Postinstall subcommand detected";
return android::apex::RunPostInstall(argv);
}
if (strcmp("--bootstrap", argv[1]) == 0) {
LOG(INFO) << "Bootstrap subcommand detected";
return android::apex::onBootstrap();
}
LOG(ERROR) << "Unknown subcommand: " << argv[1];
return 1;
}
不管是否携带参数,最终都会走到android::apex命名空间中,对应apexd.cpp文件;两次调用分别对应onStart和onBootStrap成员函数;
int onBootstrap() {
...
Status preAllocate = preAllocateLoopDevices();
...
Status status = collectApexKeys();
...
// Activate built-in APEXes for processes launched before /data is mounted.
status = scanPackagesDirAndActivate(kApexPackageSystemDir);
...
return 0;
}
onBootstrap中主要做了三件事:
开机过程中Apexd service主要的工作还是在onStart完成,包括解析、校验、挂载和绑定等过程,最终挂载到/apex/目录下,系统其他组件使用过程中会直接使用该目录中的可执行文件,而不需要访问system目录;
void onStart(CheckpointInterface* checkpoint_service) {
LOG(INFO) << "Marking APEXd as starting";
...
Status status = collectApexKeys();
gMountedApexes.PopulateFromMounts();
// Activate APEXes from /data/apex. If one in the directory is newer than the
// system one, the new one will eclipse the old one.
scanStagedSessionsDirAndStage();
status = resumeRollbackIfNeeded();
if (!status.Ok()) {
LOG(ERROR) << "Failed to resume rollback : " << status.ErrorMessage();
}
status = scanPackagesDirAndActivate(kActiveApexPackagesDataDir);
if (!status.Ok()) {
LOG(ERROR) << "Failed to activate packages from "
<< kActiveApexPackagesDataDir << " : " << status.ErrorMessage();
Status rollback_status = rollbackActiveSessionAndReboot();
if (!rollback_status.Ok()) {
// TODO: should we kill apexd in this case?
LOG(ERROR) << "Failed to rollback : " << rollback_status.ErrorMessage();
}
}
for (const auto& dir : kApexPackageBuiltinDirs) {
// TODO(b/123622800): if activation failed, rollback and reboot.
status = scanPackagesDirAndActivate(dir.c_str());
if (!status.Ok()) {
// This should never happen. Like **really** never.
// TODO: should we kill apexd in this case?
LOG(ERROR) << "Failed to activate packages from " << dir << " : "
<< status.ErrorMessage();
}
}
}
onStart()函数中主要完成了以下6件事情:
前三步主要是做一些预处理,后三步完成了所有apex包的安装,包括有升级的话需要重新安装。其中的区别就是对不同状态的apex包进行不同的处理,然后进入到下一个阶段,分别是stage阶段处理session、active阶段处理安装以及确定最后的内置应用是否全部都安装成功,如果没有的话需要继续安装,具体的安装过程下一篇文章详细介绍。
APEX模块主要还是Google为了加速适配新系统,而不必取决于OEM厂商系统更新的速度,这样用户就可以最快使用到Google最新的系统。其类似于APK包管理模块,同样是为了完成模块安装、更新等动作,可以等同理解。
Android Q中Google已经有部分系统组件强制使用APEX包,这样的话OEM厂商就无法再对其进行修改,极大受限与Google的更新,当然现在只是初步实施此模块,所以仅仅是个别组件被强制,其他的只是Google推荐使用,也许在接下来的版本Google会将推荐使用改为强制使用吧?