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

.......

這樣就可以查看是比較詳細的查看每個差分文件的改動大小,比較方便的排查差分更新之後哪些文件增長多。

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