系统更新与 OTA:OTA 升级架构、AB 分区与无缝升级、更新策略与回滚机制

各位同学,今天我们来聊聊车载系统里一个非常关键的话题——OTA 升级。说实话,在手机行业,OTA 已经是个很成熟的技术了。但在车上,情况要复杂得多。你想想看,手机升级失败大不了重启一下,或者去售后刷个机。车要是升级失败,停在半路动不了,那可是要出大事的。

所以,车载 OTA 的核心目标就两个字:安全。怎么保证安全?靠的就是我们今天要讲的 AB 分区、无缝升级和回滚机制。我个人习惯把这三者比作「双保险」——一个保升级过程,一个保升级结果。

OTA 升级架构:从云端到车端

先看整体架构。车载 OTA 不是手机那样「下载-安装-重启」这么简单。它涉及云端、车机端、甚至多个 ECU 的协同。

核心架构分层:

  • 云端 OTA 平台:负责版本管理、差分包生成、策略下发、设备认证。
  • 车端 OTA 客户端:负责下载、校验、安装、状态上报。
  • 更新代理(Update Engine):负责执行具体的分区写入操作。
  • Bootloader & Recovery:负责启动引导和恢复模式下的更新。

我在项目中遇到过一个问题:云端下发了更新包,车端也下载成功了,但安装时发现校验失败。后来排查发现,是下载过程中网络波动导致包体损坏。所以,校验机制一定要做在安装之前,这是血的教训。

下面这张图展示了 OTA 升级的完整流程:

云端 OTA 平台 车端 OTA 客户端 下发更新包 Update Engine 调用安装 Slot A (当前) system_a / vendor_a Slot B (备用) system_b / vendor_b 写入备用分区 Bootloader 切换 Slot 回滚机制 启动失败回滚

AB 分区与无缝升级

AB 分区,说白了就是准备两套系统分区。一套叫 Slot A,一套叫 Slot B。当前运行的是 Slot A,升级时就把新系统写到 Slot B。写完之后,重启时 Bootloader 判断一下,如果 Slot B 没问题,就切过去。如果出问题了,还能切回 Slot A。

这样做的好处很明显:用户几乎感觉不到升级过程。下载和安装都在后台进行,只有最后重启那一下会中断使用。而且,就算升级失败,系统也能自动回滚,不会变砖。

我的经验:AB 分区不是简单的「复制一份」。你要确保两个分区的 fstabinit.rc 等关键文件都正确指向各自的分区。我曾经见过一个案例,开发人员只改了 system 分区,忘了改 vendor 分区,结果升级后摄像头驱动加载失败。嗯,这种低级错误其实很容易犯。

AB 分区的核心配置在 BoardConfig.mk 中:

# 启用 AB 分区
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor \
    product \
    odm

# 分区大小建议保持一致
BOARD_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 2147483648
BOARD_VENDORIMAGE_PARTITION_SIZE := 536870912

这里要注意,AB_OTA_PARTITIONS 列表里列出的分区,每个都要有对应的 _a 和 _b 后缀。比如 system_asystem_b。分区大小最好保持一致,否则升级时可能因为空间不足而失败。

更新策略:增量 vs 全量

更新策略主要分两种:全量更新和增量更新。

策略 优点 缺点 适用场景
全量更新 简单可靠,不依赖旧版本 包体大,下载时间长 首次升级、跨版本升级
增量更新 包体小,下载快 依赖旧版本,差分算法复杂 小版本迭代、安全补丁

我个人更倾向于增量更新,毕竟车载网络环境不比家里 Wi-Fi。但增量更新有个坑:差分包的生成必须基于精确的源版本。如果用户跳版本升级,或者某个文件被修改过,差分包就打不上。

避坑指南:我曾经遇到过一个问题:增量包打不上,原因是用户 root 过车机,修改了 system 分区里的某个文件。差分工具比对时发现文件哈希不匹配,直接拒绝安装。所以,增量更新一定要做版本校验和文件完整性校验。如果校验失败,应该自动回退到全量更新。

增量更新的核心是 bsdiff 算法。Android 的 update_engine 使用 delta_generator 工具生成差分包:

# 生成差分包
delta_generator \
  --out_file=/tmp/update.zip \
  --partition_names=system \
  --new_partition=/tmp/system_new.img \
  --old_partition=/tmp/system_old.img

# 生成完整包
delta_generator \
  --out_file=/tmp/full_update.zip \
  --partition_names=system \
  --new_partition=/tmp/system_new.img

回滚机制:最后的防线

回滚机制是 OTA 安全的最后一道防线。说白了,就是升级失败后,系统能自动恢复到升级前的状态。

AB 分区天然支持回滚。Bootloader 会维护一个 slot_suffix 变量,记录当前启动的是哪个 Slot。如果新 Slot 启动失败(比如内核 panic、关键服务 crash),Bootloader 就会切回旧 Slot。

回滚的触发条件一般包括:

  • 内核启动超时:比如 30 秒内没进入 init 进程
  • 关键服务崩溃:比如 surfaceflinger、audioserver 连续重启
  • 用户手动触发:通过 Recovery 模式选择回滚

回滚策略配置:

// 在 BoardConfig.mk 中配置回滚次数
AB_OTA_RETRY_COUNT := 3
// 表示新 Slot 最多尝试启动 3 次
// 如果 3 次都失败,则标记为坏 Slot

这里有个细节:回滚不是无限次的。如果新 Slot 启动失败,Bootloader 会尝试重新启动它几次(比如 3 次)。如果每次都失败,就彻底标记为坏 Slot,不再尝试。这样做是为了避免「反复重启-回滚-重启」的死循环。

我记得有一次,客户反馈说车机升级后频繁重启。排查后发现,是某个硬件驱动在新版本里不兼容,导致内核启动时 panic。Bootloader 尝试了 3 次都失败,自动回滚到旧版本,问题解决。嗯,这就是回滚机制的价值所在。

总结

OTA 升级在车载系统里是个「牵一发而动全身」的活。AB 分区保证了升级过程的安全性,增量更新优化了用户体验,回滚机制则提供了最后的保障。这三者缺一不可。

最后送大家一句话:永远不要相信升级过程不会出错。设计系统时,把「出错」当作常态,把「回滚」当作默认行为。这样,你的 OTA 系统才能真正做到安全可靠。


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