安卓 update_engine 启动机制深度解析:从属性触发到 Binder 就绪
1. 引言:为什么关注 update_engine 的启动?
在采用 A/B(无缝)更新 的安卓设备上,update_engine 是一个至关重要的守护进程。它负责在后台安全地下载、验证并应用系统更新,确保用户设备能够平滑升级。
与常规应用不同,update_engine 需要极高的权限来直接读写系统分区(如 boot、system、vendor)。因此,它的启动并非由应用框架层发起,而是由更底层的 init 进程在特定时机触发。理解其启动时序,对于深入掌握安卓系统启动流程、进行系统定制或排查OTA相关问题都至关重要。
2. 核心启动流程总览
简单来说,update_engine 的启动遵循一个清晰的链条:Bootloader -> Kernel -> Init(属性系统) -> 执行二进制 -> 注册Binder服务。
下图描绘了这一完整流程的核心环节与时序:
3. 详细启动时序与代码解析
阶段一:Init 进程与属性触发器
安卓用户空间的第一个进程是 init。它负责解析 .rc 配置文件来启动和管理服务。update_engine 的启动定义在一个典型的 update_engine.rc 文件中。
关键配置文件 (system/update_engine/update_engine.rc):
# update_engine.rc
service update_engine /system/bin/update_engine
class main
user root
group root cache inet
seclabel u:r:update_engine:s0
disabled # 关键:不随 class main 自动启动
shutdown critical
# 属性触发器:当系统属性 ro.boot.slot_suffix 被设置时启动
on property:ro.boot.slot_suffix=*
setprop update_engine.slot_suffix ${ro.boot.slot_suffix}
start update_engine # 启动服务的命令
解析:
disabled:表明该服务不会在class main启动时自动运行。on property:ro.boot.slot_suffix=*:这是一个属性触发器。它监听系统属性ro.boot.slot_suffix。一旦该属性被设置(无论值是什么),触发器内的命令就会执行。start update_engine:这是真正启动服务进程的命令。
阶段二:属性的起源与触发时机
那么,关键的 ro.boot.slot_suffix 属性从何而来?它源于 Bootloader。
- Bootloader 传递:支持 A/B 分区的 Bootloader 在启动内核时,会通过内核命令行传递当前激活的槽位信息,例如
androidboot.slot_suffix=_a。 - Init 解析并设置属性:
init进程在早期的on init或on early-init阶段,会调用property_init和process_kernel_cmdline等函数来解析内核命令行。当它遇到androidboot.slot_suffix时,会将其转换为ro.boot.slot_suffix系统属性并设置。 - 触发器激活:属性被设置的瞬间,
init进程中监听此属性的触发器被激活,随即执行start update_engine。
阶段三:服务进程的执行与初始化
start 命令导致 init 进程 fork() 并 exec() 执行 /system/bin/update_engine 这个 ELF 二进制文件。
程序入口 (system/update_engine/update_engine_main.cc):
int main(int argc, char** argv) {
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr);
auto update_engine_daemon = update_engine::UpdateEngineDaemon{};
return update_engine_daemon.Run(); // 核心逻辑在此
}
守护进程初始化 (system/update_engine/common/update_engine_daemon.cc):
UpdateEngineDaemon::Run() 方法是核心,其主要逻辑如下:
int UpdateEngineDaemon::Run() {
// 1. 初始化Binder框架
auto binder_wrapper = AndroidBinderWrapper::Create();
// 2. 创建核心业务对象(依赖HAL)
auto system_state = std::make_unique<SystemState>(...);
// 3. 实例化主服务对象
main_service_ = new UpdateEngineService(std::move(system_state));
// 4. 注册为Binder系统服务
auto res = android::defaultServiceManager()->addService(
android::String16("android.os.UpdateEngineService"),
main_service_);
// 5. 加入Binder线程池,等待调用
android::IPCThreadState::self()->joinThreadPool();
return 0;
}
至此,update_engine 服务已经启动完毕,并作为系统服务 android.os.UpdateEngineService 等待来自系统其他部分(如 SystemUpdateManager)的 Binder 调用。
4. 关键点与厂商定制差异
启动机制的核心特点
- 精确触发:依赖 A/B 分区属性,确保只在支持的设备上启动。
- 权限与隔离:以
root权限运行在独立进程,通过 Binder 接口与外界通信,保证了安全边界。 - 强依赖:依赖于 Bootloader 传递的正确槽位信息。
为什么我在源码里找不到?
重要提示:这是安卓开发中的常见困惑。标准路径 (system/update_engine/) 存在于 AOSP(Android开源项目)。但在实际设备(尤其是手机)的源码中,它很可能被厂商修改。
| 可能性 | 说明 | 查找建议 |
|---|---|---|
| 厂商深度定制 | 代码被重命名、移动至 vendor/ 或 hardware/ 目录下,或完全被私有实现替换。 | 使用 find . -name \"*update_engine*\" 全局搜索。 |
| Android Mainline 模块化 | 在更新版本的 AOSP 中,它可能被移至 packages/modules/update_engine/。 | 检查该路径。 |
| 源码不完整 | 同步代码时未包含此仓库。 | 检查 .repo/manifests 中的清单文件。 |
启动时序小结
整个启动过程可以概括为以下几个步骤:
- Bootloader 启动内核,并传递当前槽位信息。
- 内核 启动
init进程。 - Init 解析内核命令行,设置
ro.boot.slot_suffix属性。 - 属性触发器 被激活,执行
start update_engine。 - Init 创建进程,执行
/system/bin/update_engine二进制文件。 - update_engine 运行
main(),初始化后通过UpdateEngineDaemon::Run()注册 Binder 服务,进入主循环等待指令。
5. 总结
update_engine 的启动是安卓属性系统与 init 服务管理机制协同工作的一个典范。它展示了安卓如何通过简单的属性键值对,来精确控制一个高权限系统服务的生命周期。理解这个过程,不仅能帮助开发者深入系统底层,也为处理OTA更新、系统升级等高级任务打下了坚实基础。
下次当你研究系统启动优化,或者为设备移植A/B更新功能时,希望这篇分析能为你提供清晰的路线图。
你是否在定制ROM或研究系统启动时遇到过与update_engine相关的问题?欢迎在评论区分享你的经历和发现。

6008

被折叠的 条评论
为什么被折叠?



