上一篇概括介绍了Apex包管理器的整体,本篇主要讲一下Boot阶段Apex包的安装过程,上篇文章说到onStart过程最后会安装所有需要安装的Apex包,主要使用了scanStagedSessionsDirAndStage和scanPackagesDirAndActivate两个函数对Apex包,本文将介绍上面两个函数具体的流程,用以简单了解Apex包是如何mount到/apex目录下并可以被使用。
这一部分主要介绍了如何处理将要安装的session,因为Apex在安装之后需要reboot才能使新的版本生效,所以在boot阶段需要检测是否有已经通过Apex管理器接收到的session等待进一步安装挂载;
其主要有下面几个步骤:
其主要目的就是为了boot阶段将session中的Apex包链接到Active对应的目录下,而session中存在的则是reboot之前通过adb或者play store拷贝到此目录的Apex包。更新状态后会在scanPackagesDirAndActivate中进行之后的程序。当然其中会有多次的校验过程,这里不再详细介绍了。主要是对Apex的文件结构和数据进行校验。整个启动阶段的校验过程相当多,应该会降低效率,估计Google在后期应该会作出处理。
下面列出处理过程的几个主要函数,其主要还是为了后面的安装过程做前期的准备。
//对Apex包进行校验
auto verify_status = verifyPackages(tmpPaths, VerifyPackageBoot);
//创建active对应的文件目录
auto create_dir_status = createDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0750);
//过滤掉不需要的package,即已经被link的和已经是active状态的;
std::vector<std::string> paths_to_stage = FilterUnnecessaryStagingPaths(tmpPaths);
//链接Apex包
if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0)
该函数用以扫描参数对应的每一个文件目录,并将文件目录中的apex包进行挂载以及绑定挂载目录;在onStart阶段,针对active目录会执行一次,针对内置分区中的Apex包会执行一次。
Status scanPackagesDirAndActivate(const char* apex_package_dir) {
//找到文件目录下所有的apex包文件
StatusOr<std::vector<std::string>> scan =
FindApexFilesByName(apex_package_dir, scanBuiltinApexes);
...
//获取已经是active状态的package,开机过程则为空
const auto& packages_with_code = GetActivePackagesMap();
...
for (const std::string& name : *scan) {
StatusOr<ApexFile> apex_file = ApexFile::Open(name);
//循环确认每个apex包是否有已经安装过更高的版本,有的话则不需要再次安装
uint64_t new_version =
static_cast<uint64_t>(apex_file->GetManifest().version());
const auto& it = packages_with_code.find(apex_file->GetManifest().name());
if (it != packages_with_code.end() && it->second >= new_version) {
LOG(INFO) << "Skipping activation of " << name
<< " same package with higher version " << it->second
<< " is already active";
skipped_cnt++;
continue;
}
...
Status res = activatePackageImpl(*apex_file);
...
}
}
从代码段可知,主要的装载过程是在activatePackageImpl完成,前面是为了找到所需要的Apex包,并将对应的数据存储到ApexFile类对象中,接着activatePackageImpl函数将会执行,完成了挂载和绑定的动作,将原始的apex包挂载到/apex/PackageNane@version目录下,挂载结束后会将挂载的结果存到apexd进程的本地数据库,最后如果没有绑定则将/apex/PackageNane@version绑定到/apex/PackageName/目录。
Status activatePackageImpl(const ApexFile& apex_file) {
const ApexManifest& manifest = apex_file.GetManifest();
...
//获取挂载点目录
const std::string& mountPoint = apexd_private::GetPackageMountPoint(manifest);
if (!version_found_mounted) {
//完成挂载过程
Status mountStatus = apexd_private::MountPackage(apex_file, mountPoint);
if (!mountStatus.Ok()) {
return mountStatus;
}
}
bool mounted_latest = false;
if (is_newest_version) {
//如果没有绑定更高的版本,则完成绑定过程
const Status& update_st = apexd_private::BindMount(
apexd_private::GetActiveMountPoint(manifest), mountPoint);
mounted_latest = update_st.Ok();
if (!update_st.Ok()) {
return Status::Fail(StringLog()
<< "Failed to update package " << manifest.name()
<< " to version " << manifest.version() << " : "
<< update_st.ErrorMessage());
}
}
}
//挂载结束后会进行挂在信息存储
gMountedApexes.AddMountedApex(apex.GetManifest().name(), false,
std::move(*ret));
挂载过程还是通过mount函数完成,mount之前都会创建一个环回设备,将apex包的信息对应到块设备上,对于内置分区(主要是system)的apex包,因为已经是通过dm-verify校验的,所以mount loop设备,而对于data分区则会mount dm设备,进而完成dm-verify校验,主要针对apex升级包;mount成功后会再次进行校验。
if (mount(blockDevice.c_str(), mountPoint.c_str(), "ext4",
MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY, nullptr) == 0) {
LOG(INFO) << "Successfully mounted package " << full_path << " on "
<< mountPoint;
auto status = VerifyMountedImage(apex, mountPoint);
if (!status.Ok()) {
umount2(mountPoint.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH);
return StatusM::Fail(StringLog() << "Failed to verify " << full_path
<< ": " << status.ErrorMessage());
}
// Time to accept the temporaries as good.
if (mountOnVerity) {
verityDev.Release();
}
loopbackDevice.CloseGood();
return StatusM(std::move(apex_data));
} else {
return StatusM::Fail(StringLog() << "Mounting failed for package "
<< full_path << " : " << strerror(errno));
}
绑定过程同样是mount函数,使用MS_BIND标志完成,这样就可以保证/apex/PackageName/下永远是最新的版本,因为只有最新版本可以绑定到该目录下,客户端可以使用绑定装载路径从 APEX 读取或执行文件。
if (mount(source.c_str(), target.c_str(), nullptr, MS_BIND, nullptr) == 0) {
return Status::Success();
}
所有的安装工作都结束了,那肯定是要告诉系统我已经完成了,你可以随时请求来使用,因此最后会写一个标志来告诉系统其他组件,Apex包管理器已经ready,其他组件可以使用了。
void onAllPackagesReady() {
// Set a system property to let other components to know that APEXs are
// correctly mounted and ready to be used. Before using any file from APEXs,
// they can query this system property to ensure that they are okay to
// access. Or they may have a on-property trigger to delay a task until
// APEXs become ready.
LOG(INFO) << "Marking APEXd as ready";
if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusReady)) {
PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to "
<< kApexStatusReady;
}
}
以上就是boot阶段所有的安装过程,当然,因为Apex是一个服务,所以该进程需要一直存在,并且可以通过aidl接口找到对应的实现,所以要申请线程池,由线程完成其他工作,主线程防止进程退出;