APEX包管理器简述(二)

上一篇概括介绍了Apex包管理器的整体,本篇主要讲一下Boot阶段Apex包的安装过程,上篇文章说到onStart过程最后会安装所有需要安装的Apex包,主要使用了scanStagedSessionsDirAndStage和scanPackagesDirAndActivate两个函数对Apex包,本文将介绍上面两个函数具体的流程,用以简单了解Apex包是如何mount到/apex目录下并可以被使用。

scanStagedSessionsDirAndStage

这一部分主要介绍了如何处理将要安装的session,因为Apex在安装之后需要reboot才能使新的版本生效,所以在boot阶段需要检测是否有已经通过Apex管理器接收到的session等待进一步安装挂载;
其主要有下面几个步骤:

APEX包管理器简述(二)_第1张图片
其主要目的就是为了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)

scanPackagesDirAndActivate

该函数用以扫描参数对应的每一个文件目录,并将文件目录中的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接口找到对应的实现,所以要申请线程池,由线程完成其他工作,主线程防止进程退出;

你可能感兴趣的:(Apex,Android)