19、车载OTA升级:OTA系统架构(update_engine、recovery);AB分区与无缝升级;OTA包生成与签名;OTA升级流程与回滚机制

OTA升级,说白了就是让车“自己给自己治病”。

我最早接触车载OTA是在2018年,那时候很多车还得开到4S店插线刷机。你想想看,一辆车卖出去几万台,要是每个系统bug都得召回,那成本得多吓人?所以,Android Automotive从设计之初就把OTA当成核心能力来做的。

19.1 OTA系统架构:update_engine 与 recovery

整个OTA体系,其实就两个核心角色:update_enginerecovery

  • update_engine:运行在正常系统里,负责下载、校验、写入升级包。它是个常驻服务,开机就启动。
  • recovery:一个独立的小系统,运行在单独的分区里。当主系统挂了,或者需要做关键操作时,由它来接管。

我习惯把update_engine比作“前台接待”,recovery比作“手术室”。前台接待处理日常事务,真到了动刀子的环节,就得进手术室。

核心架构图:OTA升级流程

云端OTA服务器 update_engine recovery OTA包(.zip) 签名验证 AB分区写入 回滚机制 下载 失败时调用 写入

19.2 AB分区与无缝升级

AB分区,这是Android从7.0开始引入的机制。说白了就是准备两套系统分区:slot Aslot B

你当前在A分区跑着,升级包就往B分区写。写完了,下次重启切到B分区。整个过程你完全无感——这就是所谓的“无缝升级”。

我的经验:曾经有个项目,客户非要搞“单分区升级”,说是省存储空间。结果升级到一半断电,车机直接变砖。后来老老实实改回AB分区。省那几百MB空间,真不值得冒这个险。

特性 AB分区 单分区
升级时可用 是(后台静默升级) 否(必须重启进recovery)
回滚能力 强(切回旧slot即可) 弱(需重新刷写)
存储占用 高(两套系统)
升级失败风险

分区布局大概是这样的:

/dev/block/by-name/
├── boot_a
├── boot_b
├── system_a
├── system_b
├── vendor_a
├── vendor_b
├── userdata        # 用户数据不分AB
└── misc            # 存储启动信息

嗯,这里要注意:userdata不分AB。因为用户数据是持续增长的,你不可能维护两套。所以升级时,系统分区随便换,但用户数据始终是同一份。

19.3 OTA包生成与签名

OTA包本质上就是一个zip文件。但里面的内容是有严格结构的。

生成工具是Android源码里的 ota_from_target_files 脚本。我一般这么用:

# 先生成 target files
make target-files-package

# 然后生成 OTA 包
./build/tools/releasetools/ota_from_target_files \
    -k build/target/product/security/releasekey \
    -i previous_target_files.zip \
    output_ota.zip

这里 -i 参数表示增量包。如果你不加,生成的就是完整包。完整包体积大,但兼容性好;增量包体积小,但只能从特定版本升级。

警告:签名密钥一定要妥善保管!我曾经见过一个团队把release key直接提交到git仓库里,结果被第三方拿到了,伪造了OTA包。那后果……嗯,你懂的。

签名流程其实就三步:

  1. 计算所有文件的SHA256哈希
  2. 用私钥对哈希列表签名
  3. 把签名和公钥证书打包进OTA包

车机收到包后,会用内置的公钥验证签名。验证通过才允许安装。这就是整个信任链的基础。

19.4 OTA升级流程与回滚机制

完整的升级流程,我习惯分成五个阶段:

  1. 检测阶段:update_engine定时向云端查询新版本。有的话,返回包信息。
  2. 下载阶段:下载OTA包到缓存分区。边下载边校验哈希,防止中途损坏。
  3. 验证阶段:校验签名、校验包完整性、校验版本号是否合法。
  4. 写入阶段:把包内容解压,写入非活跃slot。比如当前是A,就写B。
  5. 切换阶段:更新misc分区里的启动计数器,然后重启。bootloader根据计数器决定启动哪个slot。

回滚机制,说白了就是“启动失败了怎么办”。

Android Automotive里有个启动次数计数器。每次切到新slot,计数器+1。如果系统正常启动,计数器清零。如果启动失败,计数器不变。当计数器超过阈值(通常是7次),bootloader就自动切回旧slot。

回滚触发条件:

  • 新系统连续启动失败(计数器超限)
  • 用户手动选择回滚(通过诊断工具)
  • 升级过程中断电(下次启动自动回退)

我遇到过最坑的一次:升级后系统能启动,但蓝牙模块死活不工作。计数器没超限,所以不会自动回滚。最后是远程推送了一个修复包才解决。所以你看,回滚机制也不是万能的——它只能检测“能不能启动”,检测不了“功能是否正常”。

后来我们在项目中加了一个健康检查服务。升级完成后,系统会跑一遍自检流程:检查蓝牙、Wi-Fi、GPS、音频等核心模块。如果自检失败,主动触发回滚。这个做法我建议你也加上。

嗯,关于OTA升级,核心就是这些。AB分区是骨架,签名验证是锁,回滚机制是安全绳。三者缺一不可。


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