一,咱们继续跟着上一篇文章来看,UpdateAttempterAndroid及action机制, 来看四个action的执行过程。 还是放一张几个action的关系图,便于理解
我们前面讲过,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());
}
我们从上面的代码可以看到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获取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();
}
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