22、OTA 升级:OTA 架构、AB 分区升级、升级包制作与签名、回滚机制

OTA 升级,说白了就是让车机系统能「空中换血」。你想想看,车已经卖出去了,总不能每回修个 Bug 都让用户跑 4S 店刷机吧?那体验也太差了。

我最早接触 OTA 是在做手机 ROM 的时候,后来转到车载领域,发现 Android Automotive OS 的 OTA 逻辑虽然底层跟手机差不多,但多了很多车规级的要求——比如升级不能中断、失败必须能回滚、还要考虑网络不稳定等等。

嗯,这一章我们就来把 OTA 的整个链路拆开看看。

OTA 架构:谁在管这件事?

整个 OTA 升级,核心涉及三个角色:

  • 云端服务器:存放升级包,管理版本号,下发策略。
  • 车机端 Update Engine:Android 系统里的升级引擎,负责下载、校验、安装。
  • Bootloader 与 Recovery:负责在重启后真正切换系统分区。

我在项目中遇到过一种情况:云端已经下发了升级包,但车机端因为存储空间不足,下载到一半就失败了。所以后来我们加了一个前置检查——空间不够,直接弹提示,不让用户点「立即升级」。

核心流程:

  1. 云端推送升级包 URL 和签名信息。
  2. 车机端下载升级包到 /data/ota_package/。
  3. 校验签名和哈希值。
  4. 调用 Update Engine 写入非活跃分区。
  5. 重启后 Bootloader 切换槽位。

这里有个细节:下载过程是支持断点续传的。如果网络断了,下次启动会接着下,不用从头来。嗯,这个设计很贴心。

AB 分区升级:为什么要有两套系统?

传统的 OTA 升级是「原地更新」——直接往当前系统分区里写新数据。但这样有个致命问题:如果写到一半断电了,系统就变砖了。

AB 分区升级,说白了就是准备两套系统分区:

  • Slot A:当前运行的系统。
  • Slot B:空闲的备份分区。

升级的时候,Update Engine 往 非活跃的 Slot 里写入新系统。写入完成后,Bootloader 把启动槽位切换到新分区。下次重启,车机就跑在新系统上了。

如果新系统启动失败,Bootloader 会自动切回旧槽位。整个过程用户几乎无感。

我个人的习惯:在开发阶段,我会用 adb shell getprop ro.boot.slot_suffix 来查看当前启动的是哪个槽位。调试 AB 升级时,这个命令几乎是每天都要敲的。

AB 分区的布局大致如下:

分区表:
  system_a   (当前运行)
  system_b   (空闲)
  vendor_a
  vendor_b
  product_a
  product_b
  ...

每个分区都有对应的 A/B 两份。升级时只写 B 侧,不影响 A 侧的正常运行。

升级包制作与签名:包是怎么来的?

升级包不是随便打个 zip 就行的。它需要经过严格的制作流程,否则车机端不会认。

制作工具是 Android 源码里的 ota_from_target_files 脚本。流程大概是:

  1. 编译出完整的 target_files 包(包含所有分区镜像)。
  2. ota_from_target_files 生成增量包或完整包。
  3. 对包进行签名,确保来源可信。

签名这一步特别重要。我记得有一次,测试同事拿了一个没签名的包去刷机,结果 Update Engine 直接拒绝安装,日志里报的是 signature verification failed。嗯,这个保护机制是必须的——否则谁都能伪造升级包,那安全性就崩了。

签名流程:

  • 使用 releasekey 对升级包进行签名。
  • 车机端预置了公钥,用于验证签名。
  • 签名算法通常是 RSA + SHA256。

制作增量包的命令示例:

./build/tools/releasetools/ota_from_target_files \
  -i PREVIOUS-target_files.zip \
  -k build/target/product/security/releasekey \
  NEW-target_files.zip \
  incremental-ota.zip

这里 -i 指定了旧版本,-k 指定了签名密钥。生成的 incremental-ota.zip 就是最终要下发给车机的升级包。

回滚机制:万一翻车了怎么办?

升级失败是不可避免的。网络断了、存储写坏了、新系统有 Bug……什么情况都可能发生。所以回滚机制是 OTA 系统的最后一道防线。

Android Automotive OS 的回滚主要靠两个东西:

  • Bootloader 的槽位切换:如果新系统启动超时或崩溃,Bootloader 自动切回旧槽位。
  • 回滚版本号保护:系统会记录一个「最小可接受版本号」。如果新系统的版本号低于这个值,Bootloader 也会拒绝启动。

我曾经遇到过一个问题:测试团队刷了一个有 Bug 的版本,系统反复重启。但 Bootloader 没有自动回滚,因为那个版本虽然有问题,但启动过程没有超时。后来我们加了一个「健康检查」机制——系统启动后,如果某个关键服务(比如 CAN 通信)没有在 30 秒内上报心跳,就主动触发回滚。

注意:回滚不是万能的。如果升级过程中写坏了 Bootloader 分区,那车机就真的变砖了。所以 Bootloader 分区一般不做 AB 升级,而是用单独的防护机制——比如写之前先备份,写完之后做双重校验。

回滚的触发条件通常包括:

  • 新系统启动超时(Bootloader 检测)。
  • 新系统关键服务未启动(用户态检测)。
  • 用户手动选择回滚(通过诊断工具)。

SVG 流程图:OTA 升级核心逻辑

下面这张图展示了从云端下发到车机端切换槽位的完整流程。我建议你多看几遍,把每个环节的职责记清楚。

云端服务器 推送升级包 URL 车机端下载 校验签名 签名校验 写入非活跃分区 写入 Slot B 重启切换槽位 重启 → 新系统

你看,整个链路其实不复杂。关键就三点:下载、校验、切换。但每个环节都有坑,尤其是签名校验和回滚触发条件,稍不注意就会翻车。

避坑指南:我曾经在调试 AB 升级时,发现升级包写完后重启,系统还是进了旧分区。查了半天,原来是 Bootloader 里的 slot_suffix 没有正确更新。后来加了一行 update_engine_client --update 的日志输出,才定位到问题。嗯,调试 OTA 时,日志是你的好朋友。

好了,这一章的内容就到这里。OTA 升级是车载系统里最核心的能力之一,也是用户感知最强的功能。把 AB 分区和回滚机制搞明白,你就能应对大部分升级场景了。


公众号:蓝海资料掘金营,微信 deep3321