A/B system <二> update_engine_client二>
update_engine 复杂的代码结构让人有点无从下手,本篇简单介绍 update_engine_client 原理,通过 这个 debug 工具来了解 update_engine 的原理
升级执行命令
生成的 update.zip ,使用adb push 到 /data/ota_package/ 下,同时把 update.zip 中 payload_properties.txt 复制出来传给headers参数,执行以下命令
update_engine_client --update --follow --payload=file:///data/ota_package/update.zip --offset=7769 --size=580694079 --headers="FILE_HASH=ZSgofjc05i4zZyA1pjypMF5dIS+vKvuRIXkf6mTkKoo=
FILE_SIZE=580694079
METADATA_HASH=m++g6wd+wyMMZDVQgdmGIC7oqZg9Bpd2xt6TaL5mqr8=
METADATA_SIZE=87192
"
流程分析
update_engine_client 侧
system/update_engine/ 下有 update_engine_client_android.cc update_engine_client.cc两个文件,update_engine_client_android.cc是我们Android设备要使用的文件,另一个应该是 chrome 用的。
看下 main 函数,很简单
int main(int argc, char** argv) {
chromeos_update_engine::internal::UpdateEngineClientAndroid client(
argc, argv);
return client.Run();
}
Run 方法调用 external/libbrillo/brillo/daemons/daemon.cc 下的 Run,OnInit 是一个虚函数,所以又会调用子类的函数
int Daemon::Run() {
int exit_code = OnInit();//虚函数
if (exit_code != EX_OK)
return exit_code;
message_loop_.Run();
OnShutdown(&exit_code_);
// base::RunLoop::QuitClosure() causes the message loop to quit
// immediately, even if pending tasks are still queued.
// Run a secondary loop to make sure all those are processed.
// This becomes important when working with D-Bus since dbus::Bus does
// a bunch of clean-up tasks asynchronously when shutting down.
while (message_loop_.RunOnce(false /* may_block */)) {}
return exit_code_;
}
看下子类的实现 UpdateEngineClientAndroid::OnInit,省略了部分代码,前面都是一些处理命令行,初始话 logger 日志。来看下重点的地方,获取 android.os.UpdateEngineService 服务,这个在哪里注册的呢?其实是在 update_engine 侧进行注册的,咱们后面再说。还记得前面的命令行吗?是有 follow 和 update 参数的。follow 里面注册了回调函数,用于 update_engine 调用。applyPayload 函数是触发整个升级流程的关键函数了,实现是在 update_engine 端实现的。最后注册一个服务用于检测 server(update_client)端是否正常。到这里 update_engien_client 就分析完了,其实就是这么简单。下面是要根据 update_engine_client 来分析 server(update_engine) 端的做的工作了
android::status_t status = android::getService(
android::String16("android.os.UpdateEngineService"), &service_);
.......
if (FLAGS_follow) {
// Register a callback object with the service.
callback_ = new UECallback(this);
bool bound;
if (!service_->bind(callback_, &bound).isOk() || !bound) {
LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
return 1;
}
keep_running = true;
}
if (FLAGS_update) {
std::vector<std::string> headers = base::SplitString(
FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::vector<android::String16> and_headers;
for (const auto& header : headers) {
and_headers.push_back(android::String16{header.data(), header.size()});
}
Status status = service_->applyPayload(
android::String16{FLAGS_payload.data(), FLAGS_payload.size()},
FLAGS_offset,
FLAGS_size,
and_headers);
if (!status.isOk())
return ExitWhenIdle(status);
}
if (!keep_running)
return ExitWhenIdle(EX_OK);
// When following updates status changes, exit if the update_engine daemon
// dies.
android::BinderWrapper::Create();
android::BinderWrapper::Get()->RegisterForDeathNotifications(
android::os::IUpdateEngine::asBinder(service_),
base::Bind(&UpdateEngineClientAndroid::UpdateEngineServiceDied,
base::Unretained(this)));
update_engine 侧
注册服务,可以看到 BinderUpdateEngineAndroidService 是继承自 android::brillo::BnUpdateEngine ,而 BnUpdateEngine 刚好是 binder_bindings/android/os/IUpdateEngine.aidl,通过 aidl 文件,就实现了跨进程的多线程通信。
UpdateEngineDaemon::OnInit(){
binder_service_ = new BinderUpdateEngineAndroidService{
daemon_state_android->service_delegate()};
......
auto binder_wrapper = android::BinderWrapper::Get();
if (!binder_wrapper->RegisterService(binder_service_->ServiceName(),
binder_service_)) {
LOG(ERROR) << "Failed to register binder service.";
}
......
}
再来看一下 StartUpdater 函数,看名称有点像和更新相关的功能了,代码在 daemon_state_android.cc
bool DaemonStateAndroid::StartUpdater() {
// The DaemonState in Android is a passive daemon. It will only start applying
// an update when instructed to do so from the exposed binder API.
update_attempter_->Init();
return true;
}
最终调用 UpdateCompletedOnThisBoot 函数检测是否升级成功了,如果升级成功,则发出重启命令,若未升级则将系统更新状态设置为空闲状态。代码路径在 update_attempter_android.cc
void UpdateAttempterAndroid::Init() {
// In case of update_engine restart without a reboot we need to restore the
// reboot needed state.
if (UpdateCompletedOnThisBoot())
SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
else
SetStatusAndNotify(UpdateStatus::IDLE);
}
到这里 update_engine 就分析完了,怎么觉得太简单了一点,/system/update_engine 这么多的代码啊,其实别忘了我们前面分析的 applypayload。
执行升级
首先调用 binder_service_android.cc 里面的 applyPayload ,其实 binder_service_android.cc 不仅仅提供了 applypayload,像之前的 reset,suspend 等函数都是在这里面调用的。
bool UpdateAttempterAndroid::ApplyPayload(
const string& payload_url,
int64_t payload_offset,
int64_t payload_size,
const vector<string>& key_value_pair_headers,
brillo::ErrorPtr* error) {
.......
BuildUpdateActions(payload_url);
// Setup extra headers.
HttpFetcher* fetcher = download_action_->http_fetcher();
if (!headers[kPayloadPropertyAuthorization].empty())
fetcher->SetHeader("Authorization", headers[kPayloadPropertyAuthorization]);
if (!headers[kPayloadPropertyUserAgent].empty())
fetcher->SetHeader("User-Agent", headers[kPayloadPropertyUserAgent]);
SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
ongoing_update_ = true;
// Just in case we didn't update boot flags yet, make sure they're updated
// before any update processing starts. This will start the update process.
UpdateBootFlags();
LOG(INFO) << "chenan UpdateBootFlags end";
return true;
}
void DownloadAction::PerformAction() {
LOG(INFO) << "chenan DownloadAction::PerformAction"
http_fetcher_->set_delegate(this);
// Get the InstallPlan and read it
CHECK(HasInputObject());
install_plan_ = GetInputObject();
install_plan_.Dump();
bytes_received_ = 0;
bytes_total_ = 0;
for (const auto& payload : install_plan_.payloads)
bytes_total_ += payload.size;
if (install_plan_.is_resume) {
int64_t payload_index = 0;
if (prefs_->GetInt64(kPrefsUpdateStatePayloadIndex, &payload_index) &&
static_cast<size_t>(payload_index) < install_plan_.payloads.size()) {
// Save the index for the resume payload before downloading any previous
// payload, otherwise it will be overwritten.
resume_payload_index_ = payload_index;
for (int i = 0; i < payload_index; i++)
install_plan_.payloads[i].already_applied = true;
}
}
// TODO(senj): check that install plan has at least one payload.
if (!payload_)
payload_ = &install_plan_.payloads[0];
LOG(INFO) << "Marking new slot as unbootable";
if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
LOG(WARNING) << "Unable to mark new slot "
<< BootControlInterface::SlotName(install_plan_.target_slot)
<< ". Proceeding with the update anyway.";
}
StartDownloading();
}
小结:
- update_engine_client 调用关系 update_engine_client_android.cc (applyPayload)-> binder_service_android.cc -> update_attempter_android.cc (UpdateAttempterAndroid::ApplyPayload)
- 服务注册 main.cc (update_engine_daemon.Run();)-> daemon.cc (UpdateEngineDaemon::OnInit)->