Android 差分包制作流程分析

整包与差分包生成流程

差分包生成指令

make otapackage 将编译生成的(xxx项目为例

out/target/product/xxxxxxx/full_xxx_hxxxx-target_files-1527715386.zip

此时生成的是base.zip

 

在代码中做一些修改,产生一些差异第二次make otapackage将编译生成的

out/target/product/xxxxxxx/full_xxx_hxxxx-target_files-1533195243.zip

 此时生成的是target.zip

两者之间的差分包生成  ./build/tools/releasetools/ota_from_target_files -i base.zip target.zip update.zip

注:-i指定制作差分包,update.zip  就是升级用的差分包,这个脚本要在Android源码的根目录下执行。


/build/tools/releasetools/ota_from_target_files.py

  -v  (--verify)

      Remount and verify the checksums of the files written to the

      system and vendor (if used) partitions.  Incremental builds only.

  -i  (--incremental_from)  <file>

      Generate an incremental OTA using the given target-files zip as

      the starting build.

  -t  (--worker_threads) <int>

      Specifies the number of worker-threads that will be used when

      generating patches for incremental updates (defaults to 3).

 ......................

全包WriteFullOTAPackage函数主要功能

从ota_from_target_files.py脚本中WriteFullOTAPackage()和WriteBlockIncrementalOTAPackage这两个函数(分别用来生成全包和差分包)实现主要功能。

WriteFullOTAPackage将整包所需要的文件从差分资源包中读出并写入到整包中。

script = edify_generator.EdifyGenerator(target_api_version, target_info)

edify_generator对象,其FormatPartition、UnpackPackageDir等方法分别是向脚本文件update-script中写入格式化分区、解压包等指令。

.......

同时,它还会向整包中的META-INFO/com/google/android/updater-script文件中写入一些操作命令。而update-binary则是一个二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作。

差分包WriteBlockIncrementalOTAPackage函数主要功能

   Boot分区相关

def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):在函数中

  .......

  source_boot = common.GetBootableImage(

      "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",

      OPTIONS.source_info_dict)

  target_boot = common.GetBootableImage(

      "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")

  updating_boot = (not OPTIONS.two_step and

                   (source_boot.data != target_boot.data))

 在之前已经得到了source target_file.zip中的boot.img和dest target_file.zip的boot.img数据分别为source_boot 与 target_boot,这里只是判断source_boot 与 target_boot是否相同来决定是否需要升级boot分区

System数据来源

............

  system_src = GetImage("system", OPTIONS.source_tmp)

  system_tgt = GetImage("system", OPTIONS.target_tmp)

 

分别从source target_file.zip和dest target_file.zip中获取system数据,为后面生成system.new.dat、system.patch.dat、system.transfer.list三个文件提供数据来源

生成差分包system dat,list文件

........

  system_diff = common.BlockDifference("system", system_tgt, system_src,

                                       check_first_block,

                                       version=blockimgdiff_version,

                                       disable_imgdiff=disable_imgdiff)

生成差分包中的system.new.dat、system.patch.dat、system.transfer.list三个文件 

   ..........

d = common.Difference(target_boot, source_boot)

当boot.img有变化时生成boot.img.p文件,在updater-script脚本中对应下面命令:

apply_patch("EMMC:/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/boot:8306944:eec9f25c26fa112a037e15b265445ae142c5cca6:8308992:9bfb4708a965363ec53cd8b15f8a0d640f490dbc",

            "-", 9bfb4708a965363ec53cd8b15f8a0d640f490dbc, 8308992,

            eec9f25c26fa112a037e15b265445ae142c5cca6,

            package_extract_file("patch/boot.img.p"))

校验system分区

...............

# Verify the existing partitions.

system_diff.WriteVerifyScript(script, touched_blocks_only=True)

校验升级前的system分区是否为基准版本,如果不是的话当然无法升级,因为system.new.dat、system.patch.dat、system.transfer.list是基于基准版本生成的。在updater-script脚本中对应下面命令

if (range_sha1("/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/system",”156,1,280,281,575,1256........”) == "faaa340bde5fccec0374da568463286d9454ae5b" || block_image_verify("/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat")) then

ui_print("Verified system image...");

生成升级system命令

.............

  system_diff.WriteScript(script, output_zip,

                          progress=0.8 if vendor_diff else 0.9)

生成升级system的命令,在updater-script脚本中对应下面命令:

block_image_update("/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||

  abort("E1001: Failed to update system image.");

对应recovery中的BlockImageUpdateFn函数 

update-binary

........

script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)

把updater可执行程序放进升级包中,重命名为update-binary。update-binary是完成升级的程序,负责解析updater-script脚本中的命令并执行对应的C函数

元数据

.........

FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)

把元数据输出到升级包的META-INF/com/android/metadata中

差分包目录

差分升级包就是比较现存基础包与原来的基础包的差异而生成的,即该OTA包有特定的应用背景(用于两个差分包之间)用差分包升级不会格式化system分区,只是对其中部分存储段的内容进行重写。升级过程中,升级脚本(打开该升级包)会检测fingerprint,确保该升级包被正确应用。fingerprint这个属性存在于build.prop,可通过adb shell进入根路径,通过cat build.prop查看这个属性(或getprop)。内容在差分包的脚本META-INF/com/google/android/updater-script中。

getprop("ro.xxx.projectname") == "xxx_h5312_c1_in" || abort("E3004: This package is for \"x572_h5312_c1_in\" devices; this is a \"" + getprop("ro.rlk.projectname") + "\".");

ui_print("Source: XXX/HXXX/XXX-XXX:8.1.0/O11019/XXX-XXX-O-IN-180531V42:user/release-keys");

ui_print("Target: XXX/HXXX/XXX-XXX:8.1.0/O11019/XXX-XXX-O-IN-180802V69:user/release-keys");

ui_print("Verifying current system...");

getprop("ro.xxx.fingerprint") == "XXX/HXXX/XXX-XXX:8.1.0/O11019/XXX-XXX-O-IN-180531V42:user/release-keys" ||

getprop("ro.xxx.fingerprint") == "XXX/HXXX/XXX-XXX:8.1.0/O11019/XXX-XXX-O-IN-180802V69:user/release-keys" ||

    abort("E3001: Package expects build fingerprint of XXX/HXXX/XXX-XXX:8.1.0/O11019/XXX-HXXX-O-IN-180531V42:user/release-keys or XXX/HXXX/XXX-XXX:8.1.0/O11019/XXX-HXXX-O-IN-180802V69:user/release-keys; this device has " + getprop("ro.xxx.fingerprint") + ".");

apply_patch_check("EMMC:/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/boot:8306944:eec9f25c26fa112a037e15b265445ae142c5cca6:8308992:9bfb4708a965363ec53cd8b15f8a0d640f490dbc") || abort("E3005: \"EMMC:/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/boot:8306944:eec9f25c26fa112a037e15b265445ae142c5cca6:8308992:9bfb4708a965363ec53cd8b15f8a0d640f490dbc\" has unexpected contents.");

apply_patch_space(81530880) || abort("E3006: Not enough free space on /cache to apply patches.");

ui_print("Verified system image...");

差分包解压后主要包含了如下信息,如下截图是一个差分包的目录结构:

META-INF 该目录升级包的升级脚本

system.new.dat (升级包新数据)

system.new.dat文件实际上是由system.transfer.list描述的一个稀疏数组,这个过程的主要目的是降低ota.zip的大小,将system.img转换成为稀疏数组描述。
system.patch.dat(升级包中用于patch的数据)’
system.transfer.list(升级命令执行列表)

system.transfer.list

system.transfer.list是由build/tools/releasetools/blockimgdiff.py生成的,如下是文件头部的生成信息

    out.insert(0, "%d\n" % (self.version,))   # format version number

    out.insert(1, "%d\n" % (total,))

    # v3+: the number of stash slots is unused.

    out.insert(2, "0\n")

out.insert(3, str(max_stashed_blocks) + "\n")

升级参数命令解释(此部分摘自网络)

整个升级过程中的数据操作,包括数据转移(move)、临时存储(stash)、差分目标数据写入(bsdiff/imgdiff)、新数据写入(new)、数据删除(free/erase)是一个极为复杂且严格依照顺序执行的过程,有的命令是上下相关甚至是一环扣着一环的。这些命令的执行顺序是在生成升级包时即已确定。

 

在一个system.transfer.list文件中:

4

787044

0

19905

erase 10,197764,228864,263267,294400,654312,654345,1051592,1080832,1081858,1113600

第一行1表示该transfer文件的版本为4;

第二行表示new命令总共要写入787044个block;

第三行表示同时并存的stash文件数;

第四行表示最大tash文件所占用的block空间数;

第五行表示删除的range是从197764到1113600,10表示range的区间描述数目是10个数值,即197764,228864,263267,294400,654312,654345,1051592,1080832,1081858,1113600;


上述参数之后,就是具体的操作命令,逐条仔细分析(相同的命令将略过):

move

1.   move 3b8219cff7a8035c5cfe4e82a5e718c32f7439e8 2,289578,289582 4 2,287350,287354

格式:"cmdname" + "source hash"  + "tg block pos" +"sourceblock num" + "source block pos"

解析:将源block 287350至block 287354(共4 block,hash值为3b8219cff7a8035c5cfe4e82a5e718c32f7439e8)的数据移动至目标block 289578至block 289582的空间。

2.   move 1e100337aa3a13fd57fdd76ff3923aaff3c3c597 2,285947,288845 2898 2,283719,286617

本命令执行格式及目标与命令1相同,但由于源块区间(283719,286617)与目标块区间(285947,288845)产生重叠,所以在命令执行的过程中会先将读出的源数据临时存储于cache目录下文件名为1e100337aa3a13fd57fdd76ff3923aaff3c3c597的stash文件中,然后再将stash的数据读出写入目标块区间,写完之后再删除这个stash 文件。

3.   move c182ec1c52cb74fd5eb8c9ae87df62d5778d91c4 2,229351,229363 12 -c182ec1c52cb74fd5eb8c9ae87df62d5778d91c4:2,0,12

格式:"cmdname" + "source hash"  + "tg block pos" + "sourceblocknum" + "source stash file";

解析:将存储于cache目录下文件名为c182ec1c52cb74fd5eb8c9ae87df62d5778d91c4的stash文件中从0至12 block的数据读出,写入目标块区间(229351,229363)。

free

4.   free c182ec1c52cb74fd5eb8c9ae87df62d5778d91c4

解析: 删除存储于cache/reovery目录下指定的stash文件;

bsdiff

5.   bsdiff 0 3169086 ffc645d03fa4f56e72fefa872b792a76c26f9bb4 4bc0c9ddf123d54a04c800824c0951873e195ef1 2,295583,311859 16290 2,295583,311873

格式:"cmdname" +"patch offset" + "patch length" + "source hash" +"target hash" + "tg block pos" + "sourceblocknum" + "source block pos"

解析:读取源block区间数据(block 295583至block 311873,hash值为 ffc645d03fa4f56e72fefa872b792a76c26f9bb4),与system.patch.dat文件里偏移量为0,长度为3169086的数据进行bsdiff差分算法运算,生成的新数据存储至目标块区间blokc 295583至block 311859(hash值为4bc0c9ddf123d54a04c800824c0951873e195ef1)。

 

6.   bsdiff 30447439 8190 624a0bae2be2c5c40d562a7f3d26e7d7ef343329 6da9fb7c288322db82365bcafbe1371b3441a444 2,210082,210263 181 -624a0bae2be2c5c40d562a7f3d26e7d7ef343329:2,0,181

格式:"cmdname" +"patch offset" + "patch length" + "source hash" +"target hash" + "tg block pos" + "stash hash" +"stash range"

解析:读取stash文件624a0bae2be2c5c40d562a7f3d26e7d7ef343329数据(block 0至block 181,hash值为624a0bae2be2c5c40d562a7f3d26e7d7ef343329),与system.patch.dat文件里偏移量为30447439,长度为8190字节的数据进行bsdiff差分算法运算,生成的新数据存储至目标块区间block 210082至block 210263(hash值为6da9fb7c288322db82365bcafbe1371b3441a444)。

imgdiff

7.   imgdiff 6249859 22259bc3809a88c9f9b80b40fc23aeb5646333440717af4f900ef6c78fa7fca2ff6c2870aa88ec0247c7 2,155835,156013 178 2,153161,153339

解析:与命令6格式及执行流程一致,只是调用的差分算法为imgdiff算法。

stash

8.   stash 087da7ef6fac4bd7f31f19b428fead8067e4ac218,287287,287288,287417,287420,287444,287445,287457,287458

格式:"cmdname"+ "stash hash id" + "source range"

解析:将读取到的源块数据(从287287至287288,287417至287420,287444至287445,287457至287458),并确认其hash值为087da7ef6fac4bd7f31f19b428fead8067e4ac21后,存储于cache目录下文件名为087da7ef6fac4bd7f31f19b428fead8067e4ac21的stash文件。

new

9.   new 2,633274,634729

解析:system.new.dat文件中的数据被system.transfer.list文件中的new命令按顺序读取(634729-633274)*4096个字节,offset为前(n-1)new命令读取的字节总数;读取到的数据存储于block 633274至block 634729中。

erase

10. erase 6,32770,32929,32931,33439,65535,65536

解析:删除block 32770至32929,block32931至33439,block 65535至65536的数据。

zero 

11 .zero 4,1114498,1114620,1133015,1133016

解析:zero [rangeset]将目标分区的range使用0填充,需要填充0的block块范围总数:总共4个范围,【0-1114498】【1114498-1114620】,【1114620-1133015】,【1133015-1133016】

打印每个差分文件大小

以xxx的target zip

full_xxxx_hxxxx-target_files-1533195243/IMAGES/system.map其中内容是有diff相关的文件路径及块区域

.............

/system/xbin/tcpdump 1047561-1047824

/system/vendor/ueventd.rc 1015803-1015804

/system/vendor/thh/soter.raw 1049084-1051079

/system/vendor/res/sound/testpattern1.wav 1015762-1015801

/system/vendor/res/sound/ringtone.wav 1047092-1047560

/system/vendor/res/images/lcd_test_02.png 1046641-1047091 

.............

会与XXX-HXXXE-O-IN-180531V42-180802V69_20180802173249

差分包中system.transfer.list patch

bsdiff 0 8110984 8ca22be96f18677d4d0867f68fa477c77b0bdde2 b15c4c039087cd9288998a0b5b7f995c78f50140 2,124693,128782 4712 2,124693,129405

 //通过这两个值属于system.map 文件中diff文件目录的块区间来匹配上,更改文件的patch大小

bsdiff 8110984 256 5a057126ef0cb595ab425bf618a3eb5520b8f1e9 1c1dbf59201cd2c9e0462ad3a3850c4eacd34f1c 2,967806,967808 2 2,706180,706182

bsdiff 8111240 266 fe97fd30a0fc42d0534d064b5c1c367d270cf2f0 82b3ed14ff51ed22324c6d55f362b21965e0fe2e 2,968647,968649 2 2,707021,707023

目前在blockimgdiff.py中在生成system.transfer.list文件之前添加相关输出

        print("%s %10d %10d %7s %s (from %s) %d " % (

                  xf.style, xf.patch_start, xf.patch_len,

                  xf.tgt_name if xf.tgt_name == xf.src_name else (

                      xf.tgt_name + " (from " + xf.src_name + ")"),

                  str(xf.tgt_ranges), str(xf.src_ranges),xf.src_ranges.size()*4 * 1024))

xf.style差分类型

xf.patch_start patch起始位置

xf.patch_len patch文件大小

xf.src_ranges.size()*4 * 1024)  被写入字节

bsdiff      0       8110984  /system/app/Chrome/Chrome.apk-2 124693-128781 (from 124693-129404) 19300352

bsdiff    8110984    256    /system/vendor/operator/app/TouchPal_ThaiPack/oat/arm64/TouchPal_ThaiPack.odex-cropped 967806-967807 (from 706180-706181) 8192

bsdiff    8111240   266     /system/vendor/operator/app/TouchPal_UrduPack/oat/arm64/TouchPal_UrduPack.odex-cropped 968647-968648 (from 707021-707022) 8192

bsdiff    8111506   2702    /system/vendor/operator/app/instagram/oat/arm/instagram.vdex 1043130-1046640 (from 877522-881032) 14381056

bsdiff    8114208  21259875 /system/vendor/operator/app/facebook_messenger/oat/arm/facebook_messenger.vdex-0 1016316-1030144 (from 740774-753204) 50917376

.......

这样就可以查看是比较详细的查看每个差分文件的改动大小,比较方便的排查差分更新之后哪些文件增长多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章