前面几篇分析了update_engine的启动,update_engine_client的使用,boot_control的AB切换,以及update_engine的4个Action,但始终没有看到核心的部分,如何将payload.bin如何下载到目标分区的。之前一直以为是在PostinstallRunnerAction的做的,后面发现错了,升级包里根本没有Postinstall脚本。重新分析之后发现,核心的升级操作都在DownloadAction 里做的,从而牵扯出一个核心的升级类DeltaPerformer。那我们还是从我们之前分析漏了的DownloadAction内容里开始。
Writer的初始化和使用
在DownloadAction的StartDonwloading中,已经初始化了Delta_performer_和wrter_。
void DownloadAction::StartDownloading() {
...
if (writer_ && writer_ != delta_performer_.get()) {
LOG(INFO) << "Using writer for test.";
} else {
delta_performer_.reset(new DeltaPerformer(prefs_,
boot_control_,
hardware_,
delegate_,
&install_plan_,
payload_,
is_interactive_));
writer_ = delta_performer_.get();
}
...
http_fetcher_->BeginTransfer(install_plan_.download_url);
}
void UpdateAttempterAndroid::BuildUpdateActions(const string& url) {
...
HttpFetcher* download_fetcher = nullptr;
if (FileFetcher::SupportedUrl(url)) {
DLOG(INFO) << "Using FileFetcher for file URL.";
download_fetcher = new FileFetcher();
} else {
#ifdef _UE_SIDELOAD
LOG(FATAL) << "Unsupported sideload URI: " << url;
#else
LibcurlHttpFetcher* libcurl_fetcher =
new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
download_fetcher = libcurl_fetcher;
#endif
}
shared_ptr<DownloadAction> download_action(
new DownloadAction(prefs_,
boot_control_,
hardware_,
nullptr,
download_fetcher,
true ));
...
}
bool FileFetcher::SupportedUrl(const string& url) {
return base::StartsWith(
url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
}
因为我们升级更多的是将升级包下载后,使用U盘或SD卡在安装,调用的方式一般使用file:///开头的,所以download_fetcher使用的是FileFetcher类。同步download_action的初始化也是用FileFetcher初始化,那我们继续分析FileFetcher.BeginTransfer(…)。
FileFetcher传输
void FileFetcher::BeginTransfer(const string& url) {
CHECK(!transfer_in_progress_);
if (!SupportedUrl(url)) {
LOG(ERROR) << "Unsupported file URL: " << url;
http_response_code_ = 0;
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, false);
return;
}
string file_path = url.substr(strlen("file://"));
stream_ =
brillo::FileStream::Open(base::FilePath(file_path),
brillo::Stream::AccessMode::READ,
brillo::FileStream::Disposition::OPEN_EXISTING,
nullptr);
if (!stream_) {
LOG(ERROR) << "Couldn't open " << file_path;
http_response_code_ = kHttpResponseNotFound;
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, false);
return;
}
http_response_code_ = kHttpResponseOk;
if (offset_)
stream_->SetPosition(offset_, nullptr);
bytes_copied_ = 0;
transfer_in_progress_ = true;
ScheduleRead();
}
void FileFetcher::ScheduleRead() {
if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
return;
buffer_.resize(kReadBufferSize);
size_t bytes_to_read = buffer_.size();
if (data_length_ >= 0) {
bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
data_length_ - bytes_copied_);
}
if (!bytes_to_read) {
OnReadDoneCallback(0);
return;
}
ongoing_read_ = stream_->ReadAsync(
buffer_.data(),
bytes_to_read,
base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)),
base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)),
nullptr);
if (!ongoing_read_) {
LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, false);
}
}
使用文件流的方式打开升级包文件,使用buffer作为下载的接收缓存,然后开始下载,每次默认下载16K,下载完成之后就会回调onReadDoneCallback。
void FileFetcher::OnReadDoneCallback(size_t bytes_read) {
ongoing_read_ = false;
if (bytes_read == 0) {
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, true);
} else {
bytes_copied_ += bytes_read;
if (delegate_)
delegate_->ReceivedBytes(this, buffer_.data(), bytes_read);
ScheduleRead();
}
}
void DownloadAction::PerformAction() {
http_fetcher_->set_delegate(this);
...
}
在DownloadAction的PerformAction中,已经设置了delegate回调,将DownloadAction对象本身做为回调delegate设置给FileFetcher。上面的ReceivedBytes调用就是调用DownloadAction的ReceivedBytes
void DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
const void* bytes,
size_t length) {
if (!p2p_file_id_.empty()) {
WriteToP2PFile(bytes, length, bytes_received_);
}
bytes_received_ += length;
uint64_t bytes_downloaded_total =
bytes_received_previous_payloads_ + bytes_received_;
if (delegate_ && download_active_) {
delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_);
}
if (writer_ && !writer_->Write(bytes, length, &code_)) {
if (code_ != ErrorCode::kSuccess) {
LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
<< ") in DeltaPerformer's Write method when "
<< "processing the received payload -- Terminating processing";
}
....
}
void UpdateAttempterAndroid::BytesReceived(uint64_t bytes_progressed,
uint64_t bytes_received,
uint64_t total) {
double progress = 0;
if (total)
progress = static_cast<double>(bytes_received) / static_cast<double>(total);
if (status_ != UpdateStatus::DOWNLOADING || bytes_received == total) {
download_progress_ = progress;
SetStatusAndNotify(UpdateStatus::DOWNLOADING);
} else {
ProgressUpdate(progress);
}
int64_t current_bytes_downloaded =
metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
int64_t total_bytes_downloaded =
metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
prefs_->SetInt64(kPrefsCurrentBytesDownloaded,
current_bytes_downloaded + bytes_progressed);
prefs_->SetInt64(kPrefsTotalBytesDownloaded,
total_bytes_downloaded + bytes_progressed);
}
DownloadAction的BytesReceived 一方面回调给UpdateAttempterAndroid的BytesReceived,将升级进度上报给上层调用者,同时将数据传给DeltaPerformer的write。 那这个函数是不是实际的升级动作呢,我们继续往下分析:
bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode *error) {
*error = ErrorCode::kSuccess;
const char* c_bytes = reinterpret_cast<const char*>(bytes);
total_bytes_received_ += count;
UpdateOverallProgress(false, "Completed ");
while (!manifest_valid_) {
const bool do_read_header = !IsHeaderParsed();
CopyDataToBuffer(&c_bytes, &count,
(do_read_header ? kMaxPayloadHeaderSize :
metadata_size_ + metadata_signature_size_));
MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
if (result == MetadataParseResult::kError)
return false;
if (result == MetadataParseResult::kInsufficientData) {
if (do_read_header && IsHeaderParsed())
continue;
return true;
}
if ((*error = ValidateManifest()) != ErrorCode::kSuccess)
return false;
manifest_valid_ = true;
DiscardBuffer(false, metadata_size_);
if (!ParseManifestPartitions(error))
return false;
if (payload_->already_applied)
return false;
num_total_operations_ = 0;
for (const auto& partition : partitions_) {
num_total_operations_ += partition.operations_size();
acc_num_operations_.push_back(num_total_operations_);
}
LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize,
metadata_size_))
<< "Unable to save the manifest metadata size.";
LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestSignatureSize,
metadata_signature_size_))
<< "Unable to save the manifest signature size.";
if (!PrimeUpdateState()) {
*error = ErrorCode::kDownloadStateInitializationError;
LOG(ERROR) << "Unable to prime the update state.";
return false;
}
if (!OpenCurrentPartition()) {
*error = ErrorCode::kInstallDeviceOpenError;
return false;
}
if (next_operation_num_ > 0)
UpdateOverallProgress(true, "Resuming after ");
LOG(INFO) << "Starting to apply update payload operations";
}
while (next_operation_num_ < num_total_operations_) {
if (download_delegate_ && download_delegate_->ShouldCancel(error))
return false;
while (next_operation_num_ >= acc_num_operations_[current_partition_]) {
CloseCurrentPartition();
current_partition_++;
if (!OpenCurrentPartition()) {
*error = ErrorCode::kInstallDeviceOpenError;
return false;
}
}
const size_t partition_operation_num = next_operation_num_ - (
current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);
const InstallOperation& op =
partitions_[current_partition_].operations(partition_operation_num);
CopyDataToBuffer(&c_bytes, &count, op.data_length());
if (!CanPerformInstallOperation(op))
return true;
if (!payload_->metadata_signature.empty()) {
*error = ValidateOperationHash(op);
if (*error != ErrorCode::kSuccess) {
if (install_plan_->hash_checks_mandatory) {
LOG(ERROR) << "Mandatory operation hash check failed";
return false;
}
LOG(WARNING) << "Ignoring operation validation errors";
*error = ErrorCode::kSuccess;
}
}
ScopedTerminatorExitUnblocker exit_unblocker =
ScopedTerminatorExitUnblocker();
base::TimeTicks op_start_time = base::TimeTicks::Now();
bool op_result;
switch (op.type()) {
case InstallOperation::REPLACE:
case InstallOperation::REPLACE_BZ:
case InstallOperation::REPLACE_XZ:
op_result = PerformReplaceOperation(op);
OP_DURATION_HISTOGRAM("REPLACE", op_start_time);
break;
case InstallOperation::ZERO:
case InstallOperation::DISCARD:
op_result = PerformZeroOrDiscardOperation(op);
OP_DURATION_HISTOGRAM("ZERO_OR_DISCARD", op_start_time);
break;
case InstallOperation::MOVE:
op_result = PerformMoveOperation(op);
OP_DURATION_HISTOGRAM("MOVE", op_start_time);
break;
case InstallOperation::BSDIFF:
op_result = PerformBsdiffOperation(op);
OP_DURATION_HISTOGRAM("BSDIFF", op_start_time);
break;
case InstallOperation::SOURCE_COPY:
op_result = PerformSourceCopyOperation(op, error);
OP_DURATION_HISTOGRAM("SOURCE_COPY", op_start_time);
break;
case InstallOperation::SOURCE_BSDIFF:
case InstallOperation::BROTLI_BSDIFF:
op_result = PerformSourceBsdiffOperation(op, error);
OP_DURATION_HISTOGRAM("SOURCE_BSDIFF", op_start_time);
break;
case InstallOperation::PUFFDIFF:
op_result = PerformPuffDiffOperation(op, error);
OP_DURATION_HISTOGRAM("PUFFDIFF", op_start_time);
break;
default:
op_result = false;
}
if (!HandleOpResult(op_result, InstallOperationTypeName(op.type()), error))
return false;
if (!target_fd_->Flush()) {
return false;
}
next_operation_num_++;
UpdateOverallProgress(false, "Completed ");
CheckpointUpdateProgress();
}
if (major_payload_version_ == kBrilloMajorPayloadVersion &&
manifest_.has_signatures_offset() && manifest_.has_signatures_size() &&
signatures_message_data_.empty()) {
if (manifest_.signatures_offset() != buffer_offset_) {
LOG(ERROR) << "Payload signatures offset points to blob offset "
<< manifest_.signatures_offset()
<< " but signatures are expected at offset "
<< buffer_offset_;
*error = ErrorCode::kDownloadPayloadVerificationError;
return false;
}
CopyDataToBuffer(&c_bytes, &count, manifest_.signatures_size());
if (buffer_.size() < manifest_.signatures_size())
return true;
if (!ExtractSignatureMessage()) {
LOG(ERROR) << "Extract payload signature failed.";
*error = ErrorCode::kDownloadPayloadVerificationError;
return false;
}
DiscardBuffer(true, 0);
CheckpointUpdateProgress();
}
return true;
}
Write 函数很关键,首先分清楚是metadata的数据,下载到分区的数据,还是metadata签名数据。我们主要关注要下载到分区的数据,再根据installOperation的tyte类型,做对应的操作。我们先来看REPLACE的对应操作:
bool DeltaPerformer::PerformReplaceOperation(
const InstallOperation& operation) {
CHECK(operation.type() == InstallOperation::REPLACE ||
operation.type() == InstallOperation::REPLACE_BZ ||
operation.type() == InstallOperation::REPLACE_XZ);
TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
if (ExtractSignatureMessageFromOperation(operation)) {
DiscardBuffer(true, 0);
return true;
}
std::unique_ptr<ExtentWriter> writer = std::make_unique<ZeroPadExtentWriter>(
std::make_unique<DirectExtentWriter>());
if (operation.type() == InstallOperation::REPLACE_BZ) {
writer.reset(new BzipExtentWriter(std::move(writer)));
} else if (operation.type() == InstallOperation::REPLACE_XZ) {
writer.reset(new XzExtentWriter(std::move(writer)));
}
TEST_AND_RETURN_FALSE(
writer->Init(target_fd_, operation.dst_extents(), block_size_));
TEST_AND_RETURN_FALSE(writer->Write(buffer_.data(), operation.data_length()));
TEST_AND_RETURN_FALSE(writer->End());
DiscardBuffer(true, buffer_.size());
return true;
}
终于看到下载的数据实际要写入目标区域的操作了,其他几种type也是类似的,包括差分升级,原始拷贝,移动等,都是在这里处理的。 那到这里,我们的AB升级分区就分析完了。
|