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
(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
private:
DISALLOW_COPY_AND_ASSIGN(DaemonBase);
};
unique_ptr
LOG(INFO) << "daemon_android.cc:DaemonBase::CreateInstance()";
return std::make_unique
}
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.
1. 初始化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的初始化,主要为了从文件中读写keycertificateChecker的初始化,主要为后面验证升级包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的启动信息就升级完了