四个action的执行过程

四个action的执行过程

  • 前言
  • InstallPlanAction
  • DownloadAction
  • FilesystemVerifierAction
  • PostinstallRunnerAction

前言

一,咱们继续跟着上一篇文章来看,UpdateAttempterAndroid及action机制, 来看四个action的执行过程。 还是放一张几个action的关系图,便于理解
四个action的执行过程_第1张图片

InstallPlanAction

我们前面讲过,updateEngine的核心都是围绕,UpdateAttempterAndroid类,在应用调用
UpdateAttempterAndroid::ApplyPayload() 进行升级后,在这个方法里面就会调用BuildUpdateActions()方法来构建
首个action ->InstallPlanAction,这个InstallPlanAction并没有什么实际功能,就是负责记录当前升级的进行状态及当前
参数什么的。随后的DownloaderAction, FilesystemVerifierAction和PostinstallRunnerAction也都使用InstallPlanAction来
存储升级的基本信息
void UpdateAttempterAndroid::BuildUpdateActions(const string& url) {
  // 检查ActionProcessor是否已经处于Running状态
  CHECK(!processor_->IsRunning());
  //将自己设置为ActionProcessor的代理对象,ActionProcessor只需要通过代理对象就可以向外发送通知
  processor_->set_delegate(this);

  // Actions:
  // 构建 InstallPlanAction
  shared_ptr<InstallPlanAction> install_plan_action(
      new InstallPlanAction(install_plan_));

  HttpFetcher* download_fetcher = nullptr;
  if (FileFetcher::SupportedUrl(url)) { // 判断是url是 file还是http
    DLOG(INFO) << "Using FileFetcher for file URL.";
    download_fetcher = new FileFetcher(); //url 是 file 就是FileFetcher负责read
  } else {
#ifdef _UE_SIDELOAD
    LOG(FATAL) << "Unsupported sideload URI: " << url;
#else
    LibcurlHttpFetcher* libcurl_fetcher =
        new LibcurlHttpFetcher(&proxy_resolver_, hardware_); // url是http LibcurlHttpFetcher负责下载
    libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
    download_fetcher = libcurl_fetcher;
#endif  // _UE_SIDELOAD
  }
  // 构建 DownloadAction
  shared_ptr<DownloadAction> download_action(
      new DownloadAction(prefs_,
                         boot_control_,
                         hardware_,
                         nullptr,           // system_state, not used.
                         download_fetcher,  // passes ownership
                         true /* is_interactive */));
  //构建 FilesystemVerifierAction
  shared_ptr<FilesystemVerifierAction> filesystem_verifier_action(
      new FilesystemVerifierAction());

 //构建 PostinstallRunnerAction
  shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
      new PostinstallRunnerAction(boot_control_, hardware_));

  download_action->set_delegate(this);
  download_action->set_base_offset(base_offset_);
  download_action_ = download_action;
  postinstall_runner_action->set_delegate(this);

  // 将四个action都set到ActionProcessor的 std::deque actions_中。
  actions_.push_back(shared_ptr<AbstractAction>(install_plan_action));
  actions_.push_back(shared_ptr<AbstractAction>(download_action));
  actions_.push_back(shared_ptr<AbstractAction>(filesystem_verifier_action));
  actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));

  // Bond them together. We have to use the leaf-types when calling
  // BondActions().
  //通过ActionPipe将4个Action连接起来,前面action的结果作为输入到下一个action。
  BondActions(install_plan_action.get(), download_action.get());
  BondActions(download_action.get(), filesystem_verifier_action.get());
  BondActions(filesystem_verifier_action.get(),
              postinstall_runner_action.get());

  // Enqueue the actions.
  //将Action添加到ActionProcessor的管理队列中
  for (const shared_ptr<AbstractAction>& action : actions_)
    processor_->EnqueueAction(action.get());
}

DownloadAction

我们从上面的代码可以看到BondActions方法,将四个action做了一个前后连接,这里就是前面一个action执行完自己的任务之后,就会紧接着执行下一个action。

DownloadAction获取InstallPlanAction传递过来的install_plan_信息,并构建http_fetcher_用于升级数据下载,
http_fetcher_下载部分数据后会通过ReceivedBytes()通知DownloadAction收到了数据,DownloadAction会使用
DeltaPerformer类对象writer_的Write()方法解析下载的数据,并将其更新到相应分区中。所以http_fetcher_下载数据,
writer_将解析下载的数据并更新,然后http_fetcher_继续下载数据,writer_继续解析新下载的数据并更新到分区,这样
的操作一直到所有数据下载完成,此时所有的升级数据也会被写入相应分区。DownloadAction任务结束后,ActionProcessor会挑选下一个任务FilesystemVerifierAction来执行。

参考
DownloadAction过程(一)
DownloadAction过程(二)

FilesystemVerifierAction

FilesystemVerifierAction获取DownloadAction传递过来的install_plan_信息,这里接收到的install_plan_数据同   
InstallPlanAction传递给DownloadAction的数据已经不一样了。因为DownloadAction在接收到升级数据后,会解析数
据,并更新install_plan_,例如有哪些分区需要更新,每个分区的volume多大,每个分区的更新操作是如何的,每个分
区更新完成后的期望Hash值等等。FilesystemVerifierAction根据install_plan_中需要校验的分区,读取相应分区的所有数
据计算Hash,并同分区期望的Hash数据进行比较,如果一致,则说明DownloadAction中的下载更新是成功的,否则会报错。FilesystemVerifierAction任务结束后,ActionProcessor继续挑选下一个任务PostinstallRunnerAction来执行。
oid FilesystemVerifierAction::PerformAction() {
  // Will tell the ActionProcessor we've failed if we return.
  ScopedActionCompleter abort_action_completer(processor_, this);

  if (!HasInputObject()) {
    LOG(ERROR) << "FilesystemVerifierAction missing input object.";
    return;
  }
  // 从 DownloadAction获取升级状态
  install_plan_ = GetInputObject();

// 没有分区需要去做升级验证了
  if (install_plan_.partitions.empty()) {
    LOG(INFO) << "No partitions to verify.";
    if (HasOutputPipe())
      SetOutputObject(install_plan_);
    abort_action_completer.set_code(ErrorCode::kSuccess);
    return;
  }

// 开始验证刷写分区的hash指
  StartPartitionHashing();
  abort_action_completer.set_should_complete(false);
}
void FilesystemVerifierAction::StartPartitionHashing() {
// 如果所有分区都验证ok了,代表hash验证成功,返回kSuccess。
  if (partition_index_ == install_plan_.partitions.size()) {
    Cleanup(ErrorCode::kSuccess);
    return;
  }
  // 获取一个分区
  InstallPlan::Partition& partition =
      install_plan_.partitions[partition_index_];

// 根据当前的操作是验证Source包还是target包
  string part_path;
  switch (verifier_step_) {
    case VerifierStep::kVerifySourceHash:
      part_path = partition.source_path;
      remaining_size_ = partition.source_size;
      break;
    case VerifierStep::kVerifyTargetHash:
      part_path = partition.target_path;
      remaining_size_ = partition.target_size;
      break;
  }
  LOG(INFO) << "Hashing partition " << partition_index_ << " ("
            << partition.name << ") on device " << part_path;
  if (part_path.empty())
    return Cleanup(ErrorCode::kFilesystemVerifierError);

  brillo::ErrorPtr error;
  src_stream_ = brillo::FileStream::Open(
      base::FilePath(part_path),
      brillo::Stream::AccessMode::READ,
      brillo::FileStream::Disposition::OPEN_EXISTING,
      &error);

  if (!src_stream_) {
    LOG(ERROR) << "Unable to open " << part_path << " for reading";
    return Cleanup(ErrorCode::kFilesystemVerifierError);
  }

  buffer_.resize(kReadFileBufferSize);
  read_done_ = false;
  hasher_.reset(new HashCalculator());

  // Start the first read.
  // 开始读分区
  ScheduleRead();
}
void FilesystemVerifierAction::ScheduleRead() {
  size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
                                  remaining_size_);
  if (!bytes_to_read) {
    OnReadDoneCallback(0);
    return;
  }

  bool read_async_ok = src_stream_->ReadAsync(
    buffer_.data(),
    bytes_to_read,
    // 持续读直至读完
    base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
               base::Unretained(this)),
    // 有问题回调
    base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
               base::Unretained(this)),
    nullptr);

  if (!read_async_ok) {
    LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
    Cleanup(ErrorCode::kError);
  }
}

void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
  if (bytes_read == 0) {
    read_done_ = true;
  } else {
    remaining_size_ -= bytes_read;
    CHECK(!read_done_);
    if (!hasher_->Update(buffer_.data(), bytes_read)) {
      LOG(ERROR) << "Unable to update the hash.";
      Cleanup(ErrorCode::kError);
      return;
    }
  }

  // We either terminate the current partition or have more data to read.
  if (cancelled_)
    return Cleanup(ErrorCode::kError);

// remaining_size_ 保存了整个包的大小,验证一个byte,减去一个byte,直到为0,表示验证结束
  if (read_done_ || remaining_size_ == 0) {
    if (remaining_size_ != 0) {
      LOG(ERROR) << "Failed to read the remaining " << remaining_size_
                 << " bytes from partition "
                 << install_plan_.partitions[partition_index_].name;
      return Cleanup(ErrorCode::kFilesystemVerifierError);
    }
    // 没什么问题就会有回到StartPartitionHashing(),然后在StartPartitionHashing()中return Cleanup(ErrorCode::kSuccess);
    return FinishPartitionHashing();
  }
  // 读完一个分区继续下一个读
  ScheduleRead();
}

四个action的执行过程_第2张图片

PostinstallRunnerAction

 DownloadAction下载更新的install_plan_会包含每个分区在更新完成后需要执行的post install script脚本,	
 PostinstallRunnerAction对每个分区执行相应的post install script脚本。执行完所有分区的post install script脚本后,
 PostInstallRunnerAction结束。
void PostinstallRunnerAction::PerformAction() {
  CHECK(HasInputObject());
  install_plan_ = GetInputObject();
  //确认下是否需要powerwash_, 如果有设置,一般在ApplyPayload的那个hashKey中会带有POWERWASH的key值
  if (install_plan_.powerwash_required) {
    if (hardware_->SchedulePowerwash()) {
      powerwash_scheduled_ = true;
    } else {
      return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
    }
  }

  // 初始化所有升级分区的权重
  partition_weight_.resize(install_plan_.partitions.size());
  total_weight_ = 0;
  for (size_t i = 0; i < install_plan_.partitions.size(); ++i) {
    partition_weight_[i] = install_plan_.partitions[i].run_postinstall;
    total_weight_ += partition_weight_[i];
  }
  accumulated_weight_ = 0;
  ReportProgress(0);

  PerformPartitionPostinstall();
}
void PostinstallRunnerAction::PerformPartitionPostinstall() {
  //判断是否在升级过程中
  if (!install_plan_.run_post_install) {
    LOG(INFO) << "Skipping post-install according to install plan.";
    return CompletePostinstall(ErrorCode::kSuccess);
  }
  //判断下载路么为空
  if (install_plan_.download_url.empty()) {
    LOG(INFO) << "Skipping post-install during rollback";
    return CompletePostinstall(ErrorCode::kSuccess);
  }

  //检查当前分区是否需要升级(升级文件大小与当前分区比较以及当前分区是否需要升级)
  while (current_partition_ < install_plan_.partitions.size() &&
         !install_plan_.partitions[current_partition_].run_postinstall) {
    VLOG(1) << "Skipping post-install on partition "
            << install_plan_.partitions[current_partition_].name;
    current_partition_++;
  }
  //当前要升给的分区等于分区数据,表示已经遍历完一遍分区了
  if (current_partition_ == install_plan_.partitions.size())
    return CompletePostinstall(ErrorCode::kSuccess);
  //获取当前分区,同时挂载当前需要升级分区,挂载到/postinstall 目录下
  const InstallPlan::Partition& partition =
      install_plan_.partitions[current_partition_];

  const string mountable_device =
      utils::MakePartitionNameForMount(partition.target_path);
  if (mountable_device.empty()) {
    LOG(ERROR) << "Cannot make mountable device from " << partition.target_path;
    return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
  }

  // Perform post-install for the current_partition_ partition. At this point we
  // need to call CompletePartitionPostinstall to complete the operation and
  // cleanup.
#ifdef __ANDROID__
  fs_mount_dir_ = "/postinstall";
#else   // __ANDROID__
  base::FilePath temp_dir;
  TEST_AND_RETURN(base::CreateNewTempDirectory("au_postint_mount", &temp_dir));
  fs_mount_dir_ = temp_dir.value();
#endif  // __ANDROID__

  //再次确认,挂载路径是不是已经被挂载了
  if (utils::IsMountpoint(fs_mount_dir_)) {
    LOG(INFO) << "Found previously mounted filesystem at " << fs_mount_dir_;
    utils::UnmountFilesystem(fs_mount_dir_);
  }
  //将升级包分区设备赋值
  base::FilePath postinstall_path(partition.postinstall_path);
  if (postinstall_path.IsAbsolute()) {
    LOG(ERROR) << "Invalid absolute path passed to postinstall, use a relative"
                  "path instead: "
               << partition.postinstall_path;
    return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
  }
  //设置挂载点的绝对路径,并检查
  string abs_path =
      base::FilePath(fs_mount_dir_).Append(postinstall_path).value();
  if (!base::StartsWith(
          abs_path, fs_mount_dir_, base::CompareCase::SENSITIVE)) {
    LOG(ERROR) << "Invalid relative postinstall path: "
               << partition.postinstall_path;
    return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
  }

#ifdef __ANDROID__
  // In Chromium OS, the postinstall step is allowed to write to the block
  // device on the target image, so we don't mark it as read-only and should
  // be read-write since we just wrote to it during the update.

  // Mark the block device as read-only before mounting for post-install.
  //在挂载前,将要升级包的设备设置为只读
  if (!utils::SetBlockDeviceReadOnly(mountable_device, true)) {
    return CompletePartitionPostinstall(
        1, "Error marking the device " + mountable_device + " read only.");
  }
#endif  // __ANDROID__

  if (!utils::MountFilesystem(mountable_device,
                              fs_mount_dir_,
                              MS_RDONLY,
                              partition.filesystem_type,
                              constants::kPostinstallMountOptions)) {
    return CompletePartitionPostinstall(
        1, "Error mounting the device " + mountable_device);
  }

  LOG(INFO) << "Performing postinst (" << partition.postinstall_path << " at "
            << abs_path << ") installed on device " << partition.target_path
            << " and mountable device " << mountable_device;

  // Logs the file format of the postinstall script we are about to run. This
  // will help debug when the postinstall script doesn't match the architecture
  // of our build.
  LOG(INFO) << "Format file for new " << partition.postinstall_path
            << " is: " << utils::GetFileFormat(abs_path);

  //组合command:abs_path target_slot kPostinstallStatusFd 
  vector<string> command = {abs_path};
#ifdef __ANDROID__
  // In Brillo and Android, we pass the slot number and status fd.
  command.push_back(std::to_string(install_plan_.target_slot));
  command.push_back(std::to_string(kPostinstallStatusFd));
#else
  // Chrome OS postinstall expects the target rootfs as the first parameter.
  command.push_back(partition.target_path);
#endif  // __ANDROID__
//执行command指令,错误信息输出kRedirectStderrToStdout,输出管道使用kPostinstallStatusFd
  current_command_ = Subprocess::Get().ExecFlags(
      command,
      Subprocess::kRedirectStderrToStdout,
      {kPostinstallStatusFd},
      base::Bind(&PostinstallRunnerAction::CompletePartitionPostinstall,
                 base::Unretained(this)));
  // Subprocess::Exec should never return a negative process id.
  CHECK_GE(current_command_, 0);

  if (!current_command_) {
    CompletePartitionPostinstall(1, "Postinstall didn't launch");
    return;
  }

  //查看kPostinstallStatusFd是否可以打开
  progress_fd_ =
      Subprocess::Get().GetPipeFd(current_command_, kPostinstallStatusFd);
  int fd_flags = fcntl(progress_fd_, F_GETFL, 0) | O_NONBLOCK;
  if (HANDLE_EINTR(fcntl(progress_fd_, F_SETFL, fd_flags)) < 0) {
    PLOG(ERROR) << "Unable to set non-blocking I/O mode on fd " << progress_fd_;
  }
//监听progress_fd_, 从这里面读出升级的状态和进度
  progress_task_ = MessageLoop::current()->WatchFileDescriptor(
      FROM_HERE,
      progress_fd_,
      MessageLoop::WatchMode::kWatchRead,
      true,
      base::Bind(&PostinstallRunnerAction::OnProgressFdReady,
                 base::Unretained(this)));
}

由于这部分代码网上做分享的挺多的,借用一下参考
https://www.freesion.com/article/395632549/#PostInstallSummary

你可能感兴趣的:(Android升级,update,Engine,前端,javascript,python)