本頁面說明新增至 AOSP 的變更,可減少建構版本之間不必要的檔案變更。 如果裝置實作者維護自己的建構系統,可以參考這項資訊,縮減無線 (OTA) 更新的大小。
Android OTA 更新有時會包含與程式碼變更不符的變更檔案。 這些其實是建構系統構件。如果從不同目錄或機器,在不同時間建構相同程式碼,產生大量變更的檔案,就可能發生這種情況。這類多餘檔案會增加 OTA 修補程式的大小,且難以判斷哪些程式碼已變更。
為提高 OTA 內容的透明度,AOSP 納入的建構系統變更旨在縮減 OTA 修補程式的大小。已消除建構作業之間不必要的檔案變更,OTA 更新只包含修補程式相關檔案。AOSP 也包含建構差異工具,可篩除常見的建構相關檔案變更,提供更乾淨的建構檔案差異,以及區塊對應工具,可協助您保持區塊分配一致性。
建構系統可能會以幾種方式建立過大的修補程式。為解決這個問題,Android 8.0 以上版本導入了新功能,可減少每個檔案差異的修補程式大小。減少 OTA 更新套件大小的改善項目包括:
-
在非 A/B 裝置更新中,使用 ZSTD (適用於完整圖片的通用無損壓縮演算法)。ZSTD 可透過提高壓縮層級,自訂更高的壓縮率。壓縮層級是在 OTA 生成時間設定,可以傳遞
--vabc_compression_param=zstd,$COMPRESSION_LEVEL
旗標來設定。 -
增加 OTA 期間使用的壓縮視窗大小。如要設定壓縮視窗大小上限,請在裝置的
.mk
檔案中自訂建構參數。這個變數會設為PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
。 - 使用 Puffin 重新壓縮,這項工具可針對 deflate 串流執行確定性修補,並處理 A/B OTA 更新產生作業的壓縮和差異函式。
-
變更差異產生工具的使用方式,例如如何使用
bsdiff
程式庫壓縮修補程式。在 Android 9 以上版本中,bsdiff
工具會選取可為修補程式提供最佳壓縮結果的壓縮演算法。 -
update_engine
的改善措施可減少記憶體用量,適用於 A/B 裝置更新的修補程式。
以下各節將討論影響 OTA 更新大小的各種問題、解決方案,以及 AOSP 中的實作範例。
檔案順序
問題:要求目錄中的檔案清單時,檔案系統無法保證檔案順序,但通常在相同結帳程序中會保持一致。工具 (例如 ls
) 預設會排序結果,但 find
和 make
等指令使用的萬用字元函式不會排序。使用這些工具前,請務必先排序輸出內容。
解決方法:使用 find
和 make
等工具搭配萬用字元函式時,請先排序這些指令的輸出內容,再使用這些指令。在 Android.mk
檔案中使用 $(wildcard)
或 $(shell find)
時,也請一併排序。部分工具 (例如 Java) 會排序輸入內容,因此請先確認使用的工具是否已排序檔案,再進行排序。
範例:許多執行個體已在核心建構系統中修正,方法是使用內建的 all-*-files-under
巨集,其中包含 all-cpp-files-under
(因為多個定義分散在其他 Makefile 中)。詳情請參閱下列文章:
- https://android.googlesource.com/platform/build/+/4d66adfd0e6d599d8502007e4ea9aaf82e95569f
- https://android.googlesource.com/platform/build/+/379f9f9cec4fe1c66b6d60a6c19fecb81b9eb410
- https://android.googlesource.com/platform/build/+/7c3e3f8314eec2c053012dd97d2ae649ebeb5653
- https://android.googlesource.com/platform/build/+/5c64b4e81c1331cab56d8a8c201f26bb263b630c
建構目錄
問題:變更建構項目的目錄可能會導致二進位檔不同。Android 建構中的大多數路徑都是相對路徑,因此在 C/C++ 中__FILE__
不會有問題。不過,偵錯符號預設會編碼完整路徑名稱,而 .note.gnu.build-id
是透過雜湊處理預先剝除的二進位檔所產生,因此如果偵錯符號有所變更,.note.gnu.build-id
也會隨之變更。
解決方法:AOSP 現在會將偵錯路徑設為相對路徑。詳情請參閱 CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02。
時間戳記
問題:建構輸出中的時間戳記會導致不必要的檔案變更。 以下地區可能發生這種情況:
- C 或 C++ 程式碼中的
__DATE__/__TIME__/__TIMESTAMP__
巨集。 - ZIP 壓縮檔內嵌的時間戳記。
解決方案/範例:如要從建構輸出內容中移除時間戳記,請按照下列說明操作: C/C++ 中的 __DATE__/__TIME__/__TIMESTAMP__ 和 封存檔中嵌入的時間戳記。
C/C++ 中的 __DATE__/__TIME__/__TIMESTAMP__
這些巨集一律會為不同建構作業產生不同輸出內容,因此請勿使用。以下提供幾種方法,可消除這些巨集:
- 移除這些裝置。如需範例,請參閱 https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f。
- 如要識別執行的二進位檔,請從 ELF 標頭讀取建構 ID。
-
如要瞭解 OS 的建構時間,請讀取
ro.build.date
(這適用於所有項目,但漸進式建構作業除外,因為這類作業可能不會更新這個日期)。如需範例,請參閱 https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84。
封存檔 (zip、jar) 中的內嵌時間戳記
Android 7.0 在所有 zip
指令的使用情形中新增 -X
,修正了 ZIP 封存檔中內嵌時間戳記的問題。這會從 ZIP 檔案中移除建構工具的 UID/GID 和擴充的 Unix 時間戳記。
這項新工具 (位於 /platform/build/+/android16-release/tools/ziptime/
) 會重設 ZIP 標頭中的正常時間戳記。ziptime
詳情請參閱 README 檔案。
signapk
工具會為 APK 檔案設定時間戳記,但時間戳記可能會因伺服器時區而異。詳情請參閱 CL
https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028。
signapk
工具會為 APK 檔案設定時間戳記,但時間戳記可能會因伺服器時區而異。詳情請參閱 CL
https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028。
版本字串
問題:APK 版本字串通常會附加 BUILD_NUMBER
至硬式編碼版本。即使 APK 中沒有其他變更,APK 仍會因此不同。
解決方法:從 APK 版本字串中移除建構編號。
這些檢查包括:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
啟用裝置端驗證計算
如果裝置已啟用 dm-verity,OTA 工具會自動擷取驗證設定,並啟用裝置端驗證運算。這樣一來,系統就能在 Android 裝置上計算驗證區塊,而不是將驗證區塊以原始位元組的形式儲存在 OTA 封裝中。2 GB 分割區的驗證區塊約可使用 16 MB。
不過,在裝置上計算驗證資訊可能需要很長時間。具體來說,前向錯誤更正碼可能需要很長時間。在 Pixel 裝置上,這項程序通常最多需要 10 分鐘。在低階裝置上,可能需要較長的時間。如要停用裝置端完整性計算,但仍啟用 dm-verity,可以在產生 OTA 更新時,將 --disable_fec_computation
傳遞至 ota_from_target_files
工具。這項標記會在 OTA 更新期間停用裝置上的完整性計算。
這會縮短 OTA 安裝時間,但會增加 OTA 套件大小。如果裝置未啟用 dm-verity,傳遞這個標記不會有任何作用。
一致的建構工具
問題:產生已安裝檔案的工具必須保持一致 (特定輸入內容應一律產生相同輸出內容)。
解決方案/範例:您必須在下列建構工具中進行變更:
- 通知檔案建立者。NOTICE 檔案建立者已變更為建立可重現的 NOTICE 集合。請參閱 CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64。
- Java Android 編譯器套件 (Jack)。Jack 工具鍊需要更新,才能處理產生的建構函式排序偶爾發生的變化。工具鍊中新增了建構函式的決定性存取子: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b。
- ART AOT 編譯器 (dex2oat)。ART 編譯器二進位檔已更新,新增了建立決定性映像檔的選項: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9。
-
libpac.so 檔案 (V8)。由於每個建構版本的 V8 快照都會變更,因此每個建構版本都會建立不同的
/system/lib/libpac.so
檔案。解決方法是移除快照: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29。 - 應用程式預先 dexopt (.odex) 檔案。預先 dexopt (.odex) 檔案在 64 位元系統上包含未初始化的填補。這項問題已修正: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029。
使用建構差異工具
如果無法避免建構相關檔案變更,AOSP 包含建構差異工具 target_files_diff.py
,可用於比較兩個檔案套件。這項工具會對兩個建構作業執行遞迴差異,並排除常見的建構相關檔案變更,例如
- 建構輸出內容的預期變更 (例如因建構編號變更而異)。
- 這是因為目前建構系統的已知問題所致。
如要使用建構差異工具,請執行下列指令:
target_files_diff.py dir1 dir2
dir1
和 dir2
是基本目錄,包含每個建構作業的已解壓縮目標檔案。
保持區塊分配一致
以特定檔案來說,雖然兩個建構版本之間的內容相同,但實際保存資料的區塊可能已變更。因此,更新程式必須執行不必要的 I/O,才能移動區塊以進行 OTA 更新。
在虛擬 A/B OTA 更新中,不必要的 I/O 會大幅增加儲存空間需求,以儲存寫入時複製的快照。在非 A/B OTA 更新中,為 OTA 更新移動區塊會增加更新時間,因為區塊移動會導致更多 I/O。
為解決這個問題,Google 在 Android 7.0 中擴充了 make_ext4fs
工具,確保各個建構版本之間的區塊分配一致。make_ext4fs
工具接受選用的 -d base_fs
旗標,在產生 ext4
映像檔時,嘗試將檔案分配到相同區塊。您可以從先前建構作業的目標檔案 ZIP 檔案中,擷取區塊對應檔案 (例如 base_fs
對應檔案)。每個 ext4
分區在 IMAGES
目錄中都有一個 .map
檔案 (例如,IMAGES/system.map
對應於 system
分區)。接著,您可以簽入這些 base_fs
檔案,並透過 PRODUCT_<partition>_BASE_FS_PATH
指定,如以下範例所示:
PRODUCT_SYSTEM_BASE_FS_PATH := path/to/base_fs_files/base_system.map PRODUCT_SYSTEM_EXT_BASE_FS_PATH := path/to/base_fs_files/base_system_ext.map PRODUCT_VENDOR_BASE_FS_PATH := path/to/base_fs_files/base_vendor.map PRODUCT_PRODUCT_BASE_FS_PATH := path/to/base_fs_files/base_product.map PRODUCT_ODM_BASE_FS_PATH := path/to/base_fs_files/base_odm.map
雖然這無法縮減整體 OTA 套件大小,但可減少 I/O 數量,進而提升 OTA 更新效能。對於虛擬 A/B 更新,這項功能可大幅減少套用 OTA 時所需的儲存空間。
避免更新應用程式
除了盡量減少建構差異,您也可以排除透過應用程式商店更新的應用程式,藉此縮減 OTA 更新大小。APK 通常會佔用裝置上各個分割區的大部分空間。如果 OTA 更新包含應用程式商店更新的最新版應用程式,可能會大幅影響 OTA 封裝大小,但對使用者來說效益不大。使用者收到 OTA 套件時,可能已從應用程式商店直接取得更新版應用程式,甚至是更新版本。