update_engine简介

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(

(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 CreateInstance();

private:

DISALLOW_COPY_AND_ASSIGN(DaemonBase);

};

unique_ptr DaemonBase::CreateInstance() {

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的启动信息就升级完了

相关文章

excel怎么把相同内容去掉
365betasia

excel怎么把相同内容去掉

🕒 07-10 👁️ 4818
荠菜素馅蒸饺的详细做法
365网站打不开了

荠菜素馅蒸饺的详细做法

🕒 09-01 👁️ 5095
中国这条河,每个月流出20万黄金供游客淘金,淘多少拿多少