update_engine简介
AB升级(谷歌官网叫 无缝更新)是自android7.0开始新增的一种android设备升级方式,这种方式对设备存储要求高
简而言之:系统同时存在两套system分区,一套处于休眠状态不可使用,一套处于使用状态,两者通过slot的概念来做区分,在设备启动引导阶段通过特殊标记位确定启动哪个system,当有可用升级版本时候,客户端将升级包下载下来,或者将下载地址请求下来,然后通过update_engine将当前没有在使用的一套system升级到最新版本,然后修改启动标志位,在下次启动的时候,就进入升级到最新版本的那套system系统,这样做的好处就是省去了recovery升级过程的耗时,规避了recovery升级过程中发生意外中断导致设备无法开机的风险。现将其主要逻辑做简单整理归纳
AB升级的主要实现过程在update_engine中,主要代码集中在/system/update_engine路径下
主要分为两部分:
1.update_engine的初始化过程
2.java层调用升级接口执行升级过程
1.update_engine的初始化过程
首先查看目录下的Android.bp
cc_binary { name: "update_engine", defaults: [ "ue_defaults", "libupdate_engine_android_exports", ], static_libs: ["libupdate_engine_android"], required: [ "cacerts_google", "otacerts", ], srcs: ["main.cc", "aosp/metrics_reporter_android.cc"], init_rc: ["update_engine.rc"], }
update_engine.rc内容如下:update_engine服务在init进程中启动,启动后运行main.cc中的main函数:
update_engine 默认是不启动的,当设置了属性ro.boot.slot_suffix时,就enable update_engine了,也就是启动Update_eingine了。如果使用AB分区的模式的,这个属性是一定要设置的。此属性一般也是bootloader启动时,从misc分区中读取了哪个Slot是当前可以启动的,然后设置此属性。
service update_engine /system/bin/update_engine --logtostderr --logtofile --foreground class late_start user root group root system wakelock inet cache media_rw writepid /dev/cpuset/system-background/tasks /dev/blkio/background/tasks disabled on property:ro.boot.slot_suffix=* enable update_engine
main.cc 在Main.cc的main函数里主要做了两个事,一个是启动logging, 第二个就是运行update_engine_daemon。
int main(int argc, char** argv) { DEFINE_bool(logtofile, false, "Write logs to a file in log_dir."); DEFINE_bool(logtostderr, false, "Write logs to stderr instead of to a file in log_dir."); DEFINE_bool(foreground, false, "Don't daemon()ize; run in foreground."); 终端初始化 chromeos_update_engine::Terminator::Init(); brillo::FlagHelper::Init(argc, argv, "A/B Update Engine"); // We have two logging flags "--logtostderr" and "--logtofile"; and the logic // to choose the logging destination is: // 1. --logtostderr --logtofile -> logs to both // 2. --logtostderr -> logs to system debug // 3. --logtofile or no flags -> logs to file bool log_to_system = FLAGS_logtostderr; bool log_to_file = FLAGS_logtofile || !FLAGS_logtostderr;
设置log 在update_engine.rc里,启动项里有带–logtostderr --logtofile,所以当前log_to_system 和 log_to_file 都是true。
chromeos_update_engine::SetupLogging(log_to_system, log_to_file); if (!FLAGS_foreground) PLOG_IF(FATAL, daemon(0, 0) == 1) << "daemon() failed"; LOG(INFO) << "A/B Update Engine starting"; // xz-embedded requires to initialize its CRC-32 table once on startup. xz_crc32_init(); // Ensure that all written files have safe permissions. // This is a mask, so we _block_ all permissions for the group owner and other // users but allow all permissions for the user owner. We allow execution // for the owner so we can create directories. // Done _after_ log file creation. umask(S_IRWXG | S_IRWXO); 初始化并实例化 更新引擎 auto daemon = chromeos_update_engine::DaemonBase::CreateInstance(); int exit_code = daemon->Run(); chromeos_update_engine::Subprocess::Get().FlushBufferedLogsAtExit(); LOG(INFO) << "A/B Update Engine terminating with exit code " << exit_code; return exit_code; }
启动logging
void SetupLogging(bool log_to_system, bool log_to_file) { logging::LoggingSettings log_settings; log_settings.lock_log = logging::DONT_LOCK_LOG_FILE; log_settings.logging_dest = static_cast<logging::LoggingDestination>( (log_to_system ? logging::LOG_TO_SYSTEM_DEBUG_LOG : 0) | (log_to_file ? logging::LOG_TO_FILE : 0)); log_settings.log_file = nullptr; string log_file; if (log_to_file) { log_file = SetupLogFile(kSystemLogsRoot); //先删除此目录下的log文件 log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE; #if BASE_VER < 780000 log_settings.log_file = log_file.c_str(); #else log_settings.log_file_path = log_file.c_str(); #endif } //调用libchrome里的logging logging::InitLogging(log_settings); }
logging::InitLogging(log_settings) 调用的并不是Android 自带的logger机制里的方法,而是libchrome里的logging。因为本身update_engine 这个工程就是最早用于chrome里的,后面才移植到Android里来升级AB分区的。这里说明下保存log的文件路径kSystemLogsRoot为/data/misc/update_engine_log/下。
external\libchrome\base\logging.h external\libchrome\base\logging.cc inline bool InitLogging(const LoggingSettings& settings) { return BaseInitLoggingImpl(settings); }
bool BaseInitLoggingImpl(const LoggingSettings& settings) { #if defined(OS_NACL) // Can log only to the system debug log. CHECK_EQ(settings.logging_dest & ~LOG_TO_SYSTEM_DEBUG_LOG, 0); #endif if (base::CommandLine::InitializedForCurrentProcess()) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); // Don't bother initializing |g_vlog_info| unless we use one of the // vlog switches. if (command_line->HasSwitch(switches::kV) || command_line->HasSwitch(switches::kVModule)) { // NOTE: If |g_vlog_info| has already been initialized, it might be in use // by another thread. Don't delete the old VLogInfo, just create a second // one. We keep track of both to avoid memory leak warnings. CHECK(!g_vlog_info_prev); g_vlog_info_prev = g_vlog_info; g_vlog_info = new VlogInfo(command_line->GetSwitchValueASCII(switches::kV), command_line->GetSwitchValueASCII(switches::kVModule), &g_min_log_level); } } g_logging_destination = settings.logging_dest; // ignore file options unless logging to file is set. if ((g_logging_destination & LOG_TO_FILE) == 0) return true; #if defined(OS_POSIX) || defined(OS_FUCHSIA) LoggingLock::Init(settings.lock_log, settings.log_file); LoggingLock logging_lock; #endif // Calling InitLogging twice or after some log call has already opened the // default log file will re-initialize to the new options. CloseLogFileUnlocked(); if (!g_log_file_name) g_log_file_name = new PathString(); *g_log_file_name = settings.log_file; if (settings.delete_old == DELETE_OLD_LOG_FILE) DeleteFilePath(*g_log_file_name); //初始化log文件句柄 return InitializeLogFileHandle(); }
在InitLogging的函数的系列调用 中,最后通过fopen的方式打开g_log_file_name, 后面再通过追加写入的方式,去保存update的log。
运行update_engine_daemon
DaemonBase继承自 brillo::Daemon (external\libbrillo\brillo\daemons\daemon.cc)会运行到DaemonChromeOS::OnInit中:
这里需要注意Android.bp文件中 引用的源文件,因为实现DaemonBase有两个地方,而Android.bp 使用的是其中一个
cc_library_static { name: "libupdate_engine_android", defaults: [ "ue_defaults", "libupdate_engine_android_exports", ], // TODO(deymo): Remove external/cros/system_api/dbus once the strings are moved // out of the DBus interface. include_dirs: ["external/cros/system_api/dbus"], aidl: { local_include_dirs: ["binder_bindings"], export_aidl_headers: true, }, srcs: [ ":libupdate_engine_aidl", "common/system_state.cc", "aosp/apex_handler_android.cc", "aosp/binder_service_android.cc", "aosp/binder_service_stable_android.cc", "aosp/daemon_android.cc", 重点关注 "aosp/daemon_state_android.cc", "aosp/hardware_android.cc", "aosp/logging_android.cc", "aosp/network_selector_android.cc", "aosp/update_attempter_android.cc", "certificate_checker.cc", "download_action.cc", "libcurl_http_fetcher.cc", "metrics_utils.cc", "update_boot_flags_action.cc", "update_status_utils.cc", ], }
class DaemonBase : public brillo::Daemon { public: DaemonBase() = default; virtual ~DaemonBase() = default; // Creates an instance of the daemon. static std::unique_ptr<DaemonBase> CreateInstance(); private: DISALLOW_COPY_AND_ASSIGN(DaemonBase); };
unique_ptr<DaemonBase> DaemonBase::CreateInstance() { LOG(INFO) << "daemon_android.cc:DaemonBase::CreateInstance()"; return std::make_unique<DaemonAndroid>(); } OnInit方法里 初始化了update_engine的全局状态 并创建了DBUS
int DaemonAndroid::OnInit() { // Register the |subprocess_| singleton with this Daemon as the signal // handler. subprocess_.Init(this); //父类初始化 int exit_code = brillo::Daemon::OnInit(); if (exit_code != EX_OK) return exit_code; //Binder初始化 android::BinderWrapper::Create(); binder_watcher_.Init(); //DaemonStateAndroid的初始化 DaemonStateAndroid* daemon_state_android = new DaemonStateAndroid(); daemon_state_.reset(daemon_state_android); LOG_IF(ERROR, !daemon_state_android->Initialize()) << "Failed to initialize system state."; auto binder_wrapper = android::BinderWrapper::Get(); //初始化BinderUpdateEngineAndroidService // Create the Binder Service. binder_service_ = new BinderUpdateEngineAndroidService{ daemon_state_android->service_delegate()}; if (!binder_wrapper->RegisterService(binder_service_->ServiceName(), binder_service_)) { LOG(ERROR) << "Failed to register binder service."; } daemon_state_->AddObserver(binder_service_.get()); // Create the stable binder service. stable_binder_service_ = new BinderUpdateEngineAndroidStableService{ daemon_state_android->service_delegate()}; if (!binder_wrapper->RegisterService(stable_binder_service_->ServiceName(), stable_binder_service_)) { LOG(ERROR) << "Failed to register stable binder service."; } daemon_state_->AddObserver(stable_binder_service_.get()); //启动updater daemon_state_->StartUpdater(); return EX_OK; }
updateEngineDaemon的onInit()里大致做了三件事情:1. 初始化DaemonStateAndroid 2. 初始化BinderUpdateEngineAndroidService 和 BinderUpdateEngineAndroidStableService 3.启动startUpdater.
初始化DaemonStateAndroid
bool DaemonStateAndroid::Initialize() { //初始化boot_control boot_control_ = boot_control::CreateBootControl(); if (!boot_control_) { LOG(WARNING) << "Unable to create BootControl instance, using stub " << "instead. All update attempts will fail."; boot_control_.reset(new BootControlStub()); } //创建硬件相关接口,主要以属性操作为主 hardware_ = hardware::CreateHardware(); if (!hardware_) { LOG(ERROR) << "Error initializing the HardwareInterface."; return false; } LOG_IF(INFO, !hardware_->IsNormalBootMode()) << "Booted in dev mode."; LOG_IF(INFO, !hardware_->IsOfficialBuild()) << "Booted non-official build."; //初始化存取key的存储接口 // Initialize prefs. base::FilePath non_volatile_path; if (!hardware_->GetNonVolatileDirectory(&non_volatile_path)) { prefs_.reset(new MemoryPrefs()); LOG(WARNING) << "Could not get a non-volatile directory, fall back to memory prefs"; } else { Prefs* prefs = new Prefs(); prefs_.reset(prefs); if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) { LOG(ERROR) << "Failed to initialize preferences."; return false; } } //校验类的初始化 // The CertificateChecker singleton is used by the update attempter. certificate_checker_.reset( new CertificateChecker(prefs_.get(), &openssl_wrapper_)); certificate_checker_->Init(); //update_attempter的实始化 // Initialize the UpdateAttempter before the UpdateManager. update_attempter_.reset(new UpdateAttempterAndroid(this, prefs_.get(), boot_control_.get(), hardware_.get(), CreateApexHandler())); return true; }
DaemonStateAndroid的初始化主要做了如下事件
boot_control的初始化,为后面切换A/B slot做准备。
hardware的初始化,为了后续获取版本信息,OOB的状态,编译时间等信息
prefs的初始化,主要为了从文件中读写key
certificateChecker的初始化,主要为后面验证升级包
updateAttempterAndroid的初始化, 基于前面获取的boot_control, perfs, hardware的实类初始化
初始化BinderUpdateEngineAndroidService
BinderUpdateEngineAndroidService::BinderUpdateEngineAndroidService(
ServiceDelegateAndroidInterface* service_delegate)
: service_delegate_(service_delegate) {}
从上面代码看,初始化BinderUpdateEngineAndroidService时,会把DaemonStateAndroid 的实例指针传给BinderUpdateEngineAndroidService。然后DaemonStateAndroid 会调用 AddObserver,将这个加入到service_observers_的列表中,后续通过调用service_observers()来获取这个列表,从而获取这个函数指针来调用其函数。
启动startUpdater
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; }
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); UpdatePrefsAndReportUpdateMetricsOnReboot(); #ifdef _UE_SIDELOAD LOG(INFO) << "Skip ScheduleCleanupPreviousUpdate in sideload because " << "ApplyPayload will call it later."; #else ScheduleCleanupPreviousUpdate(); #endif } }
bool UpdateAttempterAndroid::UpdateCompletedOnThisBoot() { // In case of an update_engine restart without a reboot, we stored the boot_id // when the update was completed by setting a pref, so we can check whether // the last update was on this boot or a previous one. string boot_id; TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id)); string update_completed_on_boot_id; //判断配置升级完成的bootid 与当前的bootId,是不是同一个 return (prefs_->Exists(kPrefsUpdateCompletedOnBootId) && prefs_->GetString(kPrefsUpdateCompletedOnBootId, &update_completed_on_boot_id) && update_completed_on_boot_id == boot_id); }
void UpdateAttempterAndroid::SetStatusAndNotify(UpdateStatus status) { status_ = status; size_t payload_size = install_plan_.payloads.empty() ? 0 : install_plan_.payloads[0].size; UpdateEngineStatus status_to_send = {.status = status_, .progress = download_progress_, .new_size_bytes = payload_size}; for (auto observer : daemon_state_->service_observers()) { observer->SendStatusUpdate(status_to_send); } last_notify_time_ = TimeTicks::Now(); }
上面注释与逻辑中可以看到,我们需要先去看当前的bootid 是否需要等待重启切换slot, 如果需要,则通知上面的调用方去上报状态。不需要重启,也需要上报空闲状态,同时更新perfs配置。下面来看如何更新配置:
void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() { string current_boot_id; TEST_AND_RETURN(utils::GetBootId(¤t_boot_id)); // Example: [ro.build.version.incremental]: [4292972] string current_version = android::base::GetProperty("ro.build.version.incremental", ""); TEST_AND_RETURN(!current_version.empty()); const auto current_slot = boot_control_->GetCurrentSlot(); // If there's no record of previous version (e.g. due to a data wipe), we // save the info of current boot and skip the metrics report. if (!prefs_->Exists(kPrefsPreviousVersion)) { prefs_->SetString(kPrefsBootId, current_boot_id); prefs_->SetString(kPrefsPreviousVersion, current_version); prefs_->SetInt64(std::string{kPrefsPreviousSlot}, boot_control_->GetCurrentSlot()); ClearMetricsPrefs(); return; } int64_t previous_slot = -1; prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot); string previous_version; // update_engine restarted under the same build and same slot. // TODO(xunchang) identify and report rollback by checking UpdateMarker. if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) && previous_version == current_version && previous_slot == current_slot) { string last_boot_id; bool is_reboot = prefs_->Exists(kPrefsBootId) && (prefs_->GetString(kPrefsBootId, &last_boot_id) && last_boot_id != current_boot_id); // Increment the reboot number if |kPrefsNumReboots| exists. That pref is // set when we start a new update. if (is_reboot && prefs_->Exists(kPrefsNumReboots)) { prefs_->SetString(kPrefsBootId, current_boot_id); int64_t reboot_count = metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_); metrics_utils::SetNumReboots(reboot_count + 1, prefs_); } return; }
到这里update_engine_daemon的启动信息就升级完了