縮減 OTA 大小

本頁面說明 AOSP 新增的變更,可減少建構之間不必要的檔案變更。自行維護建構系統的裝置導入者,可以參考這項資訊,縮減無線 (OTA) 更新的大小。

Android OTA 更新偶爾會包含與程式碼變更不相符的變更檔案。其實是建構系統構件。當相同程式碼在不同時間、不同目錄或不同電腦上建構時,就可能產生大量變更檔案。這些多餘的檔案會增加 OTA 修補程式的大小,並使您難以判斷哪些程式碼已變更。

為讓 OTA 內容更透明,AOSP 納入了建構系統變更,旨在縮減 OTA 修補程式的大小。我們已移除建構版本之間不必要的檔案變更,OTA 更新只會包含修補程式相關檔案。AOSP 也包含建構差異工具,可篩除與建構相關的常見檔案變更,提供更清晰的建構檔案差異,以及區塊對應工具,可協助您保持區塊配置的一致性。

建構系統可以透過多種方式建立不必要的大型修補程式。為緩解這個問題,我們在 Android 8.0 以上版本中導入了新功能,以縮小每個檔案差異的修補程式大小。縮減 OTA 更新套件大小的改善項目包括:

  • 使用 ZSTD,這是一種通用的無損壓縮演算法,可用於非 A/B 裝置更新的完整圖片。您可以透過提高壓縮等級,自訂 ZSTD 以獲得更高的壓縮率。壓縮等級會在 OTA 產生期間設定,您可以傳遞標記 --vabc_compression_param=zstd,$COMPRESSION_LEVEL 來設定
  • 增加 OTA 期間使用的壓縮視窗大小。您可以自訂裝置 .mk 檔案中的建構參數,設定最大壓縮視窗大小。這個變數會設為 PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
  • 使用 Puffin 重新壓縮功能,這是用於解壓縮串流的決定性修補工具,可處理 A/B OTA 更新產生的壓縮和差異函式。
  • 變更微調產生工具的用法,例如如何使用 bsdiff 程式庫來壓縮修補程式。在 Android 9 以上版本中,bsdiff 工具會選取可為修補程式提供最佳壓縮結果的壓縮演算法。
  • 改善 update_engine 後,在為 A/B 裝置更新套用修補程式時,所需的記憶體用量會減少。

以下各節將討論影響 OTA 更新大小的各種問題、解決方案,以及在 AOSP 中實作的範例。

檔案順序

問題:系統要求目錄中的檔案清單時,檔案系統不會保證檔案順序,但通常在同一次結帳時,檔案順序會相同。ls 等工具會依預設排序結果,但 findmake 等指令使用的萬用字元函式不會排序。使用這些工具前,您必須先排序輸出內容。

解決方案:當您使用 findmake 等工具搭配萬用字元函式時,請先排序這些指令的輸出內容,再使用這些指令。在 Android.mk 檔案中使用 $(wildcard)$(shell find) 時,請一併對其進行排序。有些工具 (例如 Java) 會對輸入內容進行排序,因此在排序檔案前,請確認您使用的工具是否已完成排序。

範例:許多例項已使用內建的 all-*-files-under 巨集 (包括 all-cpp-files-under,因為許多定義已分散至其他 Makefile) 在核心建構系統中修正。詳情請參閱以下文章:

建構目錄

問題:變更建構項目的目錄可能會導致二進位檔不同。Android 版本中的大多數路徑都是相對路徑,因此 C/C++ 中的 __FILE__ 不會造成問題。不過,偵錯符號預設會對完整路徑名稱進行編碼,而 .note.gnu.build-id 則是透過對預先去除的二進位檔進行雜湊運算而產生,因此如果偵錯符號有所變更,.note.gnu.build-id 也會隨之變更。

解決方法:AOSP 現在會將偵錯路徑設為相對路徑。詳情請參閱 CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02

時間戳記

問題:建構輸出內容中的時間戳記會導致不必要的檔案變更。這可能會發生在以下位置:

  • __DATE__/__TIME__/__TIMESTAMP__ C 或 C++ 程式碼中的巨集。
  • 在以 ZIP 為基礎的封存檔中嵌入時間戳記。

解決方案/範例:如要從建構輸出內容中移除時間戳記,請參閱下列 __DATE__/__TIME__/__TIMESTAMP__ 在 C/C++ 中的使用方式封存檔中的嵌入式時間戳記的操作說明。

C/C++ 中的 __DATE__/__TIME__/__TIMESTAMP__

這些巨集一律會為不同的版本產生不同的輸出內容,因此請勿使用。以下提供幾種方法,可用來消除這些巨集:

封存檔案 (zip、jar) 中的內嵌時間戳記

Android 7.0 修正了在 ZIP 封存檔中嵌入時間戳記的問題,方法是在所有 zip 指令的用途中加入 -X。這會從 ZIP 檔案中移除建構工具的 UID/GID 和延長的 Unix 時間戳記。

新的工具 ziptime (位於 /platform/build/+/android16-release/tools/ziptime/ 中) 會重設 ZIP 標頭中的一般時間戳記。詳情請參閱 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 版本字串中移除版本號碼。

這些檢查包括:

啟用裝置端驗證運算

如果裝置已啟用 dm-verity,OTA 工具會自動選取 verity 設定,並啟用裝置端 verity 運算。這樣一來,系統就能在 Android 裝置上計算 Verity 區塊,而非以原始位元組的形式儲存在 OTA 套件中。在 2 GB 分割區中,Verity 區塊可使用約 16 MB 的空間。

不過,在裝置上計算驗證可能需要很長的時間。具體來說,前向錯誤修正碼可能需要很長的時間。在 Pixel 裝置上,這項作業通常會花費最多 10 分鐘。在低階裝置上可能需要更長的時間。如果您想停用裝置端 Verity 運算,但仍啟用 dm-verity,請在產生 OTA 更新時,將 --disable_fec_computation 傳遞至 ota_from_target_files 工具。這個標記會在 OTA 更新期間停用裝置端的驗證運算。這樣做可縮短 OTA 安裝時間,但會增加 OTA 套件大小。如果裝置未啟用 dm-verity,傳遞此標記就不會有任何作用。

一致的建構工具

問題:產生已安裝檔案的工具必須一致 (給定的輸入內容應一律產生相同的輸出內容)。

解決方案/範例:您必須在下列建構工具中進行變更:

使用建構差異工具

如果無法排除與建構相關的檔案變更,AOSP 會提供建構差異工具 target_files_diff.py,用於比較兩個檔案套件。這項工具會在兩個版本之間執行遞迴差異比較,但會排除常見的建構相關檔案變更,例如

  • 預期的建構輸出變更 (例如因版本號碼變更而導致)。
  • 由於目前建構系統存在已知問題,因此做出了變更。

如要使用建構差異工具,請執行下列指令:

target_files_diff.py dir1 dir2

dir1dir2 是基本目錄,其中包含每個版本的已擷取目標檔案。

保持區塊配置的一致性

對於特定檔案,雖然兩個版本之間的內容相同,但實際儲存資料的區塊可能已變更。因此,更新器必須執行不必要的 I/O,才能移動無線更新的區塊。

在虛擬 A/B OTA 更新中,不必要的 I/O 可能會大幅增加儲存寫入時複製快照所需的儲存空間。在非 A/B OTA 更新中,由於區塊移動會導致更多 I/O,因此移動 OTA 更新的區塊會影響更新時間。

為解決這個問題,Google 在 Android 7.0 中擴充了 make_ext4fs 工具,以便在各版本中維持一致的區塊配置。make_ext4fs 工具可接受選用的 -d base_fs 旗標,該旗標會在產生 ext4 映像檔時,嘗試將檔案分配至相同的區塊。您可以從先前版本的目標檔案 ZIP 檔案中,擷取區塊對應檔案 (例如 base_fs 對應檔案)。每個 ext4 分區都有一個 .map 檔案,位於 IMAGES 目錄中 (例如,IMAGES/system.map 對應至 system 分區)。接著,您可以透過 PRODUCT_<partition>_BASE_FS_PATH 檢入這些 base_fs 檔案並指定它們,如以下範例所示:

  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 套件時,可能已經從應用程式商店直接取得更新版應用程式,甚至是更新版本。