4、OTA升级包制作(上):差分包与整包概念、bsdiff与imgdiff算法、ota_from_target_files工具使用、增量包生成流程
各位同学,今天我们来聊聊OTA升级包制作的上半部分。说实话,这部分内容在车载系统里属于「看着简单,做起来坑多」的类型。我最早接触OTA时,以为就是把新固件打个包扔给车机就行,结果第一次做增量升级就把系统搞成了砖——嗯,从那以后我再也不敢小看这个环节了。
咱们先理清一个核心问题:升级包到底有几种?说白了就两类——整包和差分包。整包好理解,就是把整个系统镜像完整打包,不管车机当前是什么版本,直接全量覆盖。差分包呢?只包含新旧版本之间的差异部分,体积小、下载快,但依赖车机当前版本必须匹配。
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。用户下载体验非常好。
imgdiff:专为Android镜像设计
imgdiff是Google在bsdiff基础上做的改进版,专门处理Android的稀疏镜像(sparse image)和文件系统镜像。你想想看,Android的system.img、vendor.img这些镜像文件,内部有大量的空洞(未使用的块),如果直接用bsdiff去处理,会把空洞也当成差异,导致差分包巨大。
imgdiff的聪明之处在于:它会先解析镜像的稀疏格式,跳过空洞区域,只对实际有数据的块做差分。我曾经用bsdiff和imgdiff分别处理同一个system.img(新旧版本差异很小),结果bsdiff生成了800MB的差分包,imgdiff只生成了50MB——差距就是这么夸张。
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,默认生成整包。
4.4 增量包生成流程:从编译到交付
好了,理论讲完了,咱们来走一遍实际流程。我建议你把这个流程记下来,因为每个项目都会用到。
增量包生成的完整流程如下:
- 准备新旧版本target_files:确保两个版本都是同一个项目的编译产物,分区表、签名配置一致
- 运行ota_from_target_files:指定旧版本为输入,新版本为目标,生成差分包
- 工具内部处理:
- 解析新旧版本的分区列表
- 对每个分区文件,判断使用bsdiff还是imgdiff
- 生成差分数据,打包成OTA格式
- 添加签名和校验信息
- 验证差分包:在模拟环境或测试车上进行升级验证
- 签名和发布:使用正式签名密钥重新签名,发布到OTA服务器
这里我画了一张流程图,帮你理解整个逻辑:
流程看起来简单,但实际执行时有很多细节。我举几个常见的坑:
- 分区表不一致:新旧版本的target_files如果分区数量或大小不同,工具会报错。我曾经因为调整了分区大小,导致差分包制作失败,排查了半天才发现是分区表没同步。
- 签名密钥不匹配:制作差分包时用的签名密钥必须和旧版本一致,否则车机验证会失败。这个坑我踩过两次,后来养成了每次制作前先检查密钥指纹的习惯。
- 文件权限问题:target_files里的文件权限如果被修改过(比如在Windows环境下解压再打包),会导致差分失败。建议全程在Linux环境下操作。
4.5 实战:手把手制作一个差分包
光说不练假把式。咱们来一个实际例子。假设你手头有target_files_v1.0.zip和target_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-build和post-build分别对应旧版本和新版本的构建指纹。如果这两个值不对,车机升级时会直接拒绝安装。我在项目里遇到过因为构建指纹写错导致升级失败的情况,排查起来非常痛苦。
好了,关于OTA升级包制作的上半部分就讲到这里。差分包和整包的概念、bsdiff与imgdiff的区别、ota_from_target_files的使用方法、增量包的生成流程,这些内容你掌握了吗?下一节我们会继续深入,讲升级包的签名验证、分区脚本编写、以及如何应对升级失败的回滚策略。到时候见。
公众号:蓝海资料掘金营,微信deep3321