2. OTA 基础架构
理解 Android OTA 的稳定性问题,第一步不是看日志,而是把“升级到底改了哪些分区、由谁执行、失败后如何回退”这条链路建立起来。Android 的 OTA 并不是简单替换一个系统镜像,而是围绕分区布局、启动控制、升级执行器和校验器共同构成的一套可恢复机制。分区体系决定了升级的物理边界,A/B 与 A-only 决定了回退策略,Recovery 与 update_engine 决定了执行路径,update_verifier 则决定了升级后能否被确认“真正可用”。
一、Android 分区体系:升级的物理边界
Android 设备通常包含 boot、system、vendor、product、dtbo、vbmeta、userdata、misc、metadata 等分区。其中与 OTA 强相关的核心分区如下:
- boot:包含内核与 ramdisk,决定启动第一阶段;
- system / vendor / product:构成 Android 运行时的主要只读镜像;
- vbmeta:存放 AVB 校验元数据,校验失败会直接阻断启动;
- misc:A-only 方案中用于写入 recovery 命令和升级状态;
- userdata:用户数据区,通常不参与整包替换,但可能涉及 metadata 加密与回退;
- slot 元数据:A/B 方案中记录当前活动槽位、成功标记与重试次数。
OTA 包本质上是一组针对这些分区的操作指令集合。整包升级会重写完整镜像,差分包则基于旧镜像做块级或文件级补丁。无论哪种形式,升级失败往往不是“包坏了”这么简单,而是某个分区的写入、校验或启动标记没有按预期完成。
二、A/B 与 A-only:两种回退哲学
A-only 方案只有一个系统槽位,升级时通常进入 Recovery 模式,把新镜像写入 system、vendor 等分区。它的优点是分区占用小、实现简单;缺点是升级过程中设备不可用,且一旦写入中断或新系统无法启动,回退能力有限,往往依赖 Recovery 中的备份或重新刷写。
A/B 方案则维护两套可启动槽位(slot_a、slot_b)。当前运行在 slot_a 时,OTA 把新版本写入 slot_b,完成后把 slot_b 标记为 active,重启后尝试从 slot_b 启动。如果启动失败,bootloader 会根据重试计数自动切回 slot_a。这种“后台写入 + 重启切换 + 失败回退”的机制,是 A/B OTA 稳定性的核心。
| 对比项 | A-only | A/B |
|---|---|---|
| 槽位数量 | 1 套 | 2 套 |
| 升级执行环境 | Recovery 模式 | 正常系统内 update_engine |
| 升级期间可用性 | 不可用 | 可继续使用 |
| 失败回退 | 依赖 Recovery / 重刷 | 自动切回旧槽位 |
| 存储开销 | 较低 | 较高 |
| 典型状态记录 | misc 分区 | slot 元数据 + bootloader |
三、Recovery 模式与 update_engine 的角色分工
Recovery 是一个独立的最小系统,负责在 A-only 方案中解析 OTA 包、校验签名、写入分区并更新 misc 中的命令状态。它不依赖主系统,因此即使 system 损坏也能进入。常见命令包括 --update_package、--wipe_data 等,执行结果会写回 misc,供下次启动判断。
update_engine 是 A/B 方案中的升级执行器,运行在正常系统内。它从 OTA 服务端下载或读取本地升级包,按 payload 中的操作逐块写入非活动槽位,同时维护进度、校验和槽位标记。它的关键状态包括当前 slot、目标 slot、payload 校验结果、写入偏移和 markBootSuccessful 调用时机。
# A/B 升级关键状态示例
current_slot: _a
target_slot: _b
payload_url: https://ota.example.com/payload.bin
payload_size: 1073741824
payload_hash: 9f2c...e81a
status: INSTALLING
progress: 0.62
update_verifier 则出现在升级完成后的首次启动阶段。它读取 dm-verity 或 AVB 相关元数据,对已写入的分区做抽样或完整校验,确认新槽位内容与预期一致。如果校验失败,系统会标记该槽位不可启动,触发回退。因此 update_verifier 不是“升级执行者”,而是“升级可信度的确认者”。
四、典型场景与排障切入点
场景一:A/B 设备升级后卡在开机动画。此时应优先确认 slot 元数据中的 successful 标记是否写入、retry_count 是否耗尽,以及 update_verifier 是否在启动日志中报出校验失败。若校验失败,问题通常出在 payload 写入不完整或 vbmeta 与 boot 不匹配。
场景二:A-only 设备进入 Recovery 后提示 status 7。这通常表示 OTA 包中的设备断言与当前设备不匹配,或 system 分区剩余空间不足。需要检查 misc 中的命令状态和 Recovery 日志中的具体分区写入错误。
场景三:差分包升级失败但整包成功。差分包依赖旧镜像的精确哈希,若设备曾做过局部修改或分区被重新刷写,源数据不匹配就会导致 patch 失败。此时应核对源版本号、分区哈希和 payload 属性。
排障顺序建议:先确认方案类型(A/B 还是 A-only),再确认执行角色(update_engine 还是 Recovery),最后确认校验角色(update_verifier / AVB / dm-verity)。顺序错了,日志会看得很乱。
五、常见误区与复盘
- 误区一:把 update_engine 当成校验器。update_engine 负责写入和进度管理,真正的启动可信校验由 update_verifier 与 AVB 完成。
- 误区二:认为 A/B 一定不会变砖。如果 bootloader 的槽位控制逻辑或 vbmeta 校验被破坏,回退同样可能失败。
- 误区三:忽略 misc 分区。A-only 的升级命令和结果都记录在 misc,misc 异常会导致 Recovery 反复进入或无法继续。
- 误区四:只看升级日志,不看首次启动日志。很多 OTA 稳定性问题发生在升级完成后的第一次启动,update_verifier 的输出才是关键证据。
把分区、方案、执行器和校验器四者串起来,才能在实际排障中快速定位问题属于“写入阶段”“切换阶段”还是“启动校验阶段”。这也是后续分析 OTA 失败日志、回退异常和升级卡死问题的基础框架。