4、OTA升级包制作(上):差分包与整包概念、bsdiff与imgdiff算法、ota_from_target_files工具使用、增量包生成流程

各位同学,今天我们来聊聊OTA升级包制作的上半部分。说实话,这部分内容在车载系统里属于「看着简单,做起来坑多」的类型。我最早接触OTA时,以为就是把新固件打个包扔给车机就行,结果第一次做增量升级就把系统搞成了砖——嗯,从那以后我再也不敢小看这个环节了。

咱们先理清一个核心问题:升级包到底有几种?说白了就两类——整包和差分包。整包好理解,就是把整个系统镜像完整打包,不管车机当前是什么版本,直接全量覆盖。差分包呢?只包含新旧版本之间的差异部分,体积小、下载快,但依赖车机当前版本必须匹配。

核心观点: 量产车99%的场景用差分包,开发调试阶段用整包。别搞反了。

4.1 整包与差分包:什么时候选哪个?

我在项目中遇到过这样一个场景:某款车型的T-Box(远程通信模块)需要从Android 10升级到Android 11。第一次做整包,镜像大小1.8GB,通过4G网络下载,用户等得直骂娘。后来改成差分包,大小只有280MB,体验瞬间好了。

但差分包不是万能的。你想想看,如果车机当前版本是A,你要升到C,中间跳过了B版本,那差分包就废了。所以选型时要记住:

  • 整包适用场景: 首次刷机、版本跨度太大、系统分区损坏后的恢复、开发调试阶段
  • 差分包适用场景: 小版本迭代、安全补丁、功能增量更新、量产车常规OTA
对比项 整包 差分包
包体大小 大(通常1-3GB) 小(通常100-500MB)
升级成功率 高(不依赖旧版本) 中等(依赖旧版本完整性)
制作复杂度 低(直接打包) 高(需要差分算法)
网络消耗

4.2 bsdiff与imgdiff算法:差分背后的两个「工人」

差分包的核心技术就是差分算法。Android系统里主要用两个:bsdiff和imgdiff。我刚开始做OTA时,以为它们差不多,后来踩了坑才发现——完全不是一回事。

bsdiff:通用二进制差分

bsdiff是Colin Percival大神写的算法,专门处理二进制文件的差异。它的原理说白了就是:把旧文件和新文件切成小块,找到相同或相似的部分,只记录变化的部分。这个算法对普通文件(比如APK、so库、jar包)效果很好。

我记得有一次,我们升级一个系统服务APK,旧版本20MB,新版本22MB,用bsdiff做出来的差分包只有1.2MB。用户下载体验非常好。

个人习惯: 对于非稀疏格式的普通文件,我优先用bsdiff。压缩率高,而且稳定。

imgdiff:专为Android镜像设计

imgdiff是Google在bsdiff基础上做的改进版,专门处理Android的稀疏镜像(sparse image)和文件系统镜像。你想想看,Android的system.img、vendor.img这些镜像文件,内部有大量的空洞(未使用的块),如果直接用bsdiff去处理,会把空洞也当成差异,导致差分包巨大。

imgdiff的聪明之处在于:它会先解析镜像的稀疏格式,跳过空洞区域,只对实际有数据的块做差分。我曾经用bsdiff和imgdiff分别处理同一个system.img(新旧版本差异很小),结果bsdiff生成了800MB的差分包,imgdiff只生成了50MB——差距就是这么夸张。

避坑指南: 千万不要用bsdiff去处理Android的稀疏镜像!我曾经在项目里犯过这个错,差分包比整包还大,被测试同事笑了好久。

4.3 ota_from_target_files工具:制作升级包的核心武器

在Android源码里,有一个专门制作OTA升级包的工具——ota_from_target_files。它位于build/tools/releasetools/目录下,是一个Python脚本。我个人觉得,这个工具是Android OTA体系里设计得最精巧的部分之一。

它的输入是什么?是target_files——也就是编译产物中的*-target_files-*.zip文件。这个zip包包含了系统所有的镜像文件、分区表、签名信息等。输出呢?就是最终的OTA升级包(整包或差分包)。

基本用法如下:

# 制作整包
ota_from_target_files \
  -k vendor/xxx/security/releasekey \
  -i old_target_files.zip \
  new_target_files.zip \
  full_ota_update.zip

# 制作差分包
ota_from_target_files \
  -k vendor/xxx/security/releasekey \
  -i old_target_files.zip \
  new_target_files.zip \
  incremental_ota_update.zip

这里-k参数指定签名密钥,-i参数指定旧版本target_files(制作差分包时必须)。如果不加-i,默认生成整包。

关键点: target_files.zip必须保留!很多团队编译完就删了中间产物,等到要做差分包时才发现旧版本的target_files没了,只能重新编译——浪费时间。

4.4 增量包生成流程:从编译到交付

好了,理论讲完了,咱们来走一遍实际流程。我建议你把这个流程记下来,因为每个项目都会用到。

增量包生成的完整流程如下:

  1. 准备新旧版本target_files:确保两个版本都是同一个项目的编译产物,分区表、签名配置一致
  2. 运行ota_from_target_files:指定旧版本为输入,新版本为目标,生成差分包
  3. 工具内部处理
    • 解析新旧版本的分区列表
    • 对每个分区文件,判断使用bsdiff还是imgdiff
    • 生成差分数据,打包成OTA格式
    • 添加签名和校验信息
  4. 验证差分包:在模拟环境或测试车上进行升级验证
  5. 签名和发布:使用正式签名密钥重新签名,发布到OTA服务器

这里我画了一张流程图,帮你理解整个逻辑:

增量包生成流程 旧版本target_files old_target_files.zip 新版本target_files new_target_files.zip ota_from_target_files 处理 解析分区 → 选择算法(bsdiff/imgdiff) → 生成差分数据 生成差分包 incremental_ota_update.zip 验证升级 签名发布

流程看起来简单,但实际执行时有很多细节。我举几个常见的坑:

  • 分区表不一致:新旧版本的target_files如果分区数量或大小不同,工具会报错。我曾经因为调整了分区大小,导致差分包制作失败,排查了半天才发现是分区表没同步。
  • 签名密钥不匹配:制作差分包时用的签名密钥必须和旧版本一致,否则车机验证会失败。这个坑我踩过两次,后来养成了每次制作前先检查密钥指纹的习惯。
  • 文件权限问题:target_files里的文件权限如果被修改过(比如在Windows环境下解压再打包),会导致差分失败。建议全程在Linux环境下操作。
我的建议: 在CI/CD流水线里,每次编译完成后自动归档target_files.zip,并按照版本号命名保存。这样后续做差分包时,随时可以找到旧版本,不用重新编译。

4.5 实战:手把手制作一个差分包

光说不练假把式。咱们来一个实际例子。假设你手头有target_files_v1.0.ziptarget_files_v1.1.zip,要生成从1.0到1.1的差分包。

# 第一步:检查两个版本的分区列表
unzip -l target_files_v1.0.zip | grep "IMAGES/"
unzip -l target_files_v1.1.zip | grep "IMAGES/"

# 确保分区一致,比如都有 system.img、vendor.img、boot.img

# 第二步:制作差分包
ota_from_target_files \
  -k build/target/product/security/releasekey.x509.pem \
  -i target_files_v1.0.zip \
  target_files_v1.1.zip \
  incremental_v1.0_to_v1.1.zip

# 第三步:验证差分包
# 查看包内文件列表
unzip -l incremental_v1.0_to_v1.1.zip

# 重点关注:
# - META-INF/com/android/otacert  (OTA证书)
# - META-INF/com/android/metadata (元数据,包含版本信息)
# - patch/ 目录下的差分数据文件

制作完成后,我建议你检查一下metadata文件,里面记录了新旧版本的指纹信息:

# 查看metadata
unzip -p incremental_v1.0_to_v1.1.zip META-INF/com/android/metadata

# 输出示例:
# post-build=xxx/yyy/zzz:11/RP1A.200720.009/12345678:user/release-keys
# pre-build=xxx/yyy/zzz:10/QP1A.190711.020/87654321:user/release-keys
# post-timestamp=1595000000
# pre-timestamp=1560000000

这里pre-buildpost-build分别对应旧版本和新版本的构建指纹。如果这两个值不对,车机升级时会直接拒绝安装。我在项目里遇到过因为构建指纹写错导致升级失败的情况,排查起来非常痛苦。

重要提醒: 差分包制作完成后,一定要在测试车上做一次完整的升级验证。不要只看包体大小和文件列表就发布。我曾经有一次差分包制作成功,但升级到一半就失败了,原因是某个分区文件在差分时出现了校验错误。

好了,关于OTA升级包制作的上半部分就讲到这里。差分包和整包的概念、bsdiff与imgdiff的区别、ota_from_target_files的使用方法、增量包的生成流程,这些内容你掌握了吗?下一节我们会继续深入,讲升级包的签名验证、分区脚本编写、以及如何应对升级失败的回滚策略。到时候见。


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