3. 生成增量OTA升级包

任务目标

在Android系统OTA升级开发中,增量升级包(Incremental OTA Package)只包含两个版本之间的差异数据,体积远小于全量包。本案例使用bsdiff差分工具,基于给定系统镜像文件生成增量升级包,并验证生成包的完整性。

环境与依赖

  • 操作系统:Ubuntu 18.04+ / CentOS 7+(x86_64)
  • 差分工具:bsdiff 4.3(apt install bsdiff 或 yum install bsdiff)
  • 校验工具:md5sum(系统自带)

实现原理

增量升级包的核心是二进制差分算法。bsdiff基于后缀排序算法找出新旧文件的最长公共子串,将差异编码为补丁文件。生成增量包时,以旧版本镜像为基准(oldfile),新版本镜像为输入(newfile),输出补丁文件(patch)。该补丁即为增量升级包的核心内容,后续可被恢复工具(bspatch)用于将旧版本还原为新版本。

关键完整代码


#!/bin/bash
# generate_incremental_ota.sh
# 用法: ./generate_incremental_ota.sh <old_image> <new_image> <output_patch>

set -e

OLD_IMG="$1"
NEW_IMG="$2"
PATCH_FILE="$3"

if [ $# -ne 3 ]; then
    echo "错误: 需要3个参数"
    echo "用法: $0 <old_image> <new_image> <output_patch>"
    exit 1
fi

if [ ! -f "$OLD_IMG" ] || [ ! -f "$NEW_IMG" ]; then
    echo "错误: 输入文件不存在"
    exit 1
fi

echo "=== 生成增量升级包 ==="
echo "旧版本: $OLD_IMG ($(stat -c%s "$OLD_IMG") bytes)"
echo "新版本: $NEW_IMG ($(stat -c%s "$NEW_IMG") bytes)"

# 调用bsdiff生成差分补丁
bsdiff "$OLD_IMG" "$NEW_IMG" "$PATCH_FILE"

echo "增量包已生成: $PATCH_FILE ($(stat -c%s "$PATCH_FILE") bytes)"

# 计算校验值用于验证
echo "=== 校验信息 ==="
echo "旧版本 MD5: $(md5sum "$OLD_IMG" | cut -d' ' -f1)"
echo "新版本 MD5: $(md5sum "$NEW_IMG" | cut -d' ' -f1)"
echo "增量包 MD5: $(md5sum "$PATCH_FILE" | cut -d' ' -f1)"

# 验证增量包可恢复性
echo "=== 验证增量包正确性 ==="
TEMP_RESTORED="/tmp/restored_$$.img"
bspatch "$OLD_IMG" "$TEMP_RESTORED" "$PATCH_FILE"

if cmp -s "$NEW_IMG" "$TEMP_RESTORED"; then
    echo "✓ 验证通过: 增量包可完整恢复新版本"
    rm -f "$TEMP_RESTORED"
    exit 0
else
    echo "✗ 验证失败: 恢复结果与新版本不一致"
    rm -f "$TEMP_RESTORED"
    exit 1
fi

运行与验证

准备测试镜像:若无现成镜像,可用以下命令生成两个测试文件模拟新旧版本:


# 生成10MB随机测试文件作为旧版本
dd if=/dev/urandom of=old_v1.img bs=1M count=10

# 复制并修改部分内容作为新版本
cp old_v1.img new_v2.img
# 修改文件末尾1KB数据模拟版本更新
printf '\x00\x01\x02\x03' | dd of=new_v2.img bs=1 seek=$((10*1024*1024-4)) conv=notrunc

执行生成脚本:


chmod +x generate_incremental_ota.sh
./generate_incremental_ota.sh old_v1.img new_v2.img update_patch.bin

通过条件:脚本输出包含以下内容即判定成功:

  • 增量包文件已生成,且体积明显小于新版本镜像(本例中应远小于10MB)
  • 三行MD5校验值正常输出
  • 最后一行显示 ✓ 验证通过: 增量包可完整恢复新版本

预期输出示例:


=== 生成增量升级包 ===
旧版本: old_v1.img (10485760 bytes)
新版本: new_v2.img (10485760 bytes)
增量包已生成: update_patch.bin (296 bytes)
=== 校验信息 ===
旧版本 MD5: 3a7b...
新版本 MD5: 8f2c...
增量包 MD5: b1e9...
=== 验证增量包正确性 ===
✓ 验证通过: 增量包可完整恢复新版本

注意:由于新旧镜像仅4字节差异,增量包极小(约几百字节),这正体现了差分升级在版本迭代中的带宽节省优势。实际OTA场景中,增量包通常为全量包的5%~20%。

常见问题

  • bsdiff内存不足:处理超大镜像(>2GB)时可能耗尽内存,建议使用 bsdiff4 或分块处理。
  • 恢复验证失败:确认新旧镜像未被修改,且bspatch版本与bsdiff匹配。
  • 实际OTA集成:本案例生成的patch文件需封装为Android OTA包格式(含META-INF签名等),该封装流程在后续案例中展开。