常見問題

Google 是否曾在任何裝置上使用 A/B OTA?

可以。A/B 更新的行銷名稱是「無縫更新」。2016 年 10 月推出的 Pixel 和 Pixel XL 手機隨附 A/B 分區,所有 Chromebook 均使用相同的 A/B 分區實作方式。update_engineAndroid 7.1 以上版本已公開必要的平台程式碼實作項目。

為什麼 A/B OTA 更好?

A/B OTA 更新可提供更優質的使用者體驗。從每月安全性更新的測量結果來看,這項功能已證明相當成功:截至 2017 年 5 月,一個月後,95% 的 Pixel 使用者都已安裝最新安全性更新,Nexus 使用者則為 87%,而且 Pixel 使用者更新的速度比 Nexus 使用者更快。在 OTA 期間更新區塊失敗,不會再導致裝置無法啟動;在成功啟動新系統映像檔之前,Android 會保留回復至先前正常運作系統映像檔的能力。

什麼是 system_other?

應用程式會儲存在 .apk 檔案中,而這類檔案實際上是 ZIP 封存檔。每個 .apk 檔案都包含一或多個 .dex 檔案,內含可攜式 Dalvik 位元組代碼。.odex 檔案 (最佳化 .dex) 與 .apk 檔案分開,且可能包含裝置專用的機器碼。如果提供 .odex 檔案,Android 就能以預先編譯的速度執行應用程式,不必在每次啟動應用程式時等待程式碼編譯。.odex 檔案並非必要:Android 實際上可透過解譯或即時 (JIT) 編譯直接執行 .dex 程式碼,但如果空間足夠,.odex 檔案可提供最佳的啟動速度和執行階段速度組合。

示例:以 Nexus 6P (搭載 Android 7.1) 的 installed-files.txt 為例,系統映像檔總大小為 2628 MiB (2755792836 位元組),按檔案類型細分,對系統映像檔總大小影響最大的項目如下:

.odex 1391770312 個位元組 50.5%
.apk 846878259 個位元組 30.7%
.so (原生 C/C++ 程式碼) 202162479 個位元組 7.3%
.oat 檔案/.art 圖片 163892188 個位元組 5.9%
字型 38952361 個位元組 1.4%
icu 語言代碼資料 27468687 個位元組 0.9%

其他裝置也差不多,因此在 Nexus/Pixel 裝置上,.odex 檔案約佔系統分割區的一半。這表示我們可以繼續使用 ext4,但將 .odex 檔案寫入工廠的 B 分區,然後在首次啟動時複製到 /data。使用 ext4 A/B 時,實際使用的儲存空間與 SquashFS A/B 相同,因為如果我們使用 SquashFS,就會在 system_a 上出貨預先最佳化的 .odex 檔案,而不是 system_b。

將 .odex 檔案複製到 /data 後,/system 節省的空間不就等於 /data 失去的空間嗎?

不對喔。在 Pixel 上,.odex 檔案佔用的空間大多是應用程式所用,這些檔案通常位於 /data。這些應用程式會接收 Google Play 更新,因此在裝置的大部分生命週期中,系統映像檔上的 .apk 和 .odex 檔案都不會使用。這類檔案可以完全排除,並在使用者實際使用每個應用程式時,由小型、以設定檔為導向的 .odex 檔案取代 (因此使用者不使用的應用程式不需要空間)。詳情請參閱 Google I/O 2016 的演講「The Evolution of Art」。

由於幾個主要原因,這項比較相當困難:

  • Google Play 更新的應用程式一律會在收到第一次更新後,立即將 .odex 檔案放在 /data 中。
  • 使用者未執行的應用程式完全不需要 .odex 檔案。
  • 與預先編譯相比,設定檔導向編譯產生的 .odex 檔案較小 (因為前者只會最佳化效能關鍵程式碼)。

如要進一步瞭解 OEM 可用的調整選項,請參閱「設定 ART」。

/data 上不是有兩個 .odex 檔案副本嗎?

這有點複雜...寫入新的系統映像檔後,系統會針對新的 .dex 檔案執行新版 dex2oat,產生新的 .odex 檔案。舊系統仍在執行時,就會發生這種情況,因此新舊 .odex 檔案會同時存在。/data

OtaDexoptService 中的程式碼 (frameworks/base/+/android16-release/services/core/java/com/android/server/pm/OtaDexoptService.java) 會在最佳化每個套件之前呼叫 getAvailableSpace,避免過度填滿 /data。請注意,這裡的「可用」仍是保守估計:這是指之前剩餘的空間量達到一般系統空間不足的門檻 (以百分比和位元組數計算)。因此,如果 /data 已滿,就不會有每個 .odex 檔案的兩個副本。同樣的程式碼也有 BULK_DELETE_THRESHOLD:如果裝置即將用盡可用空間 (如上所述),系統會移除未使用應用程式的 .odex 檔案。這是另一個沒有每個 .odex 檔案兩份副本的情況。

如果 /data 完全滿載,更新作業會等到裝置重新啟動進入新系統,不再需要舊系統的 .odex 檔案時才會進行。PackageManager 會處理這項作業:(frameworks/base/+/android16-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215)。新系統成功啟動後,installd (frameworks/native/+/android16-release/cmds/installd/dexopt.cpp#2422) 即可移除舊系統使用的 .odex 檔案,讓裝置恢復穩定狀態,只保留一份副本。

因此,雖然 /data 可能包含所有 .odex 檔案的兩個副本,但 (a) 這是暫時性的,且 (b) 只有在 /data 本身有大量可用空間時才會發生。除了更新期間,系統只會有一份副本。此外,ART 的一般穩定性功能不會填入 /data 的 .odex 檔案 (因為這在非 A/B 系統上也會造成問題)。

這麼頻繁地寫入/複製資料,會不會加速快閃記憶體耗損?

只有一小部分快閃記憶體經過重寫:完整 Pixel 系統更新會寫入約 2.3 GiB。 (應用程式也會重新編譯,但這也適用於非 A/B 裝置)。傳統上,以區塊為基礎的完整 OTA 會寫入類似的資料量,因此快閃記憶體耗損率應該相近。

刷寫兩個系統分區會增加工廠刷寫時間嗎?

不會。Pixel 並未增加系統映像檔大小,只是將空間劃分為兩個分區。

如果 B 仍保留 .odex 檔案,恢復原廠設定後重新啟動裝置時,速度是否會變慢?

可以。如果您實際使用裝置、進行 OTA 更新,並執行原廠資料重設,第一次重新啟動時會比平常慢 (Pixel XL 上為 1 分 40 秒,而非 40 秒),因為第一次 OTA 更新後,.odex 檔案會從 B 遺失,因此無法複製到 /data。這就是取捨。

與一般啟動相比,恢復原廠設定是較少執行的操作,因此所花時間較不重要。(如果使用者或評論者是從工廠取得裝置,則不受影響,因為這類裝置會提供 B 分割區。)使用 JIT 編譯器表示我們不需要重新編譯所有內容,因此情況可能不如您想像的糟糕。您也可以使用資訊清單中的 coreApp="true" 將應用程式標示為需要預先編譯:(frameworks/base/+/android16-release/packages/SystemUI/AndroidManifest.xml#23)。system_server 目前使用這項功能,因為基於安全考量,不允許 JIT。

將 .odex 檔案保留在 /data 而非 /system 中,是否會導致無線更新後重新啟動的速度變慢?

不會。如上所述,新的 dex2oat 會在舊系統映像檔仍在執行時執行,以產生新系統所需的檔案。完成上述作業後,系統才會將更新視為可用。

Can (should) we ship a 32GiB A/B device? 16GiB?8GiB?

32 GiB 的效果良好 (已在 Pixel 上驗證),而 16 GiB 中的 320 MiB 代表減少 2%。 同樣地,8 GiB 中的 320 MiB 減少了 4%。顯然在 4 GiB 的裝置上,A/B 並非建議的選擇,因為 320 MiB 的額外負荷幾乎占總可用空間的 10%。

AVB2.0 是否需要 A/B OTA?

否。Android 已驗證啟動一律需要以區塊為基礎的更新,但不一定需要 A/B 更新。

A/B OTA 是否需要 AVB2.0?

編號

A/B OTA 會破壞 AVB2.0 的回溯保護機制嗎?

不會。這裡有些混淆之處,因為如果 A/B 系統無法啟動新的系統映像檔,系統會 (在啟動載入程式決定重試次數後) 自動還原為「先前的」系統映像檔。但這裡的重點是,就 A/B 而言,「先前」實際上仍是「目前」的系統映像檔。裝置成功啟動新映像檔後,系統會立即啟動回溯保護機制,確保您無法返回舊版。但您必須實際成功啟動新映像檔,回溯保護機制才會將其視為目前的系統映像檔。

如果系統正在執行時安裝更新,速度會不會很慢?

非 A/B 更新的目標是盡快安裝更新,因為使用者正在等待,且更新期間無法使用裝置。但 A/B 更新則不然,因為使用者仍在裝置上操作,因此目標是盡量減少影響,所以更新速度會刻意放慢。透過 Java 系統更新用戶端中的邏輯 (對 Google 而言,這是 GmsCore,也就是 GMS 提供的核心套件),Android 也會嘗試選擇使用者完全未使用裝置的時間。平台支援暫停/繼續更新,如果使用者開始使用裝置,用戶端可以暫停更新,並在裝置閒置時繼續更新。

OTA 更新分為兩個階段,使用者介面會清楚顯示進度列下方的「步驟 1 (1/2)」和「步驟 2 (2/2)」。步驟 1 對應於寫入資料區塊,步驟 2 則是預先編譯 .dex 檔案。這兩個階段對效能的影響大不相同。第一階段是簡單的 I/O。這項作業只需要少量資源 (RAM、CPU、I/O),因為只是緩慢地複製區塊。

第二階段會執行 dex2oat,預先編譯新的系統映像檔。由於編譯的是實際應用程式,因此這項要求顯然較不明確。顯然,編譯大型複雜應用程式的工作量,遠遠大於編譯小型簡單應用程式;而在第 1 階段中,沒有任何磁碟區塊比其他區塊更大或更複雜。

這項程序與 Google Play 在背景安裝應用程式更新,然後顯示「已更新 5 個應用程式」通知的程序類似,多年來都是如此。

如果使用者確實在等待更新,該怎麼辦?

GmsCore 目前的實作方式不會區分背景更新和使用者啟動的更新,但未來可能會區分。如果使用者明確要求安裝更新,或是正在觀看更新進度畫面,我們會優先處理更新作業,因為我們假設他們正在等待更新完成。

如果更新套用失敗會怎麼樣?

如果非 A/B 更新無法套用,使用者通常會無法使用裝置。但如果失敗發生在應用程式啟動前 (例如套件驗證失敗),則不在此限。採用 A/B 更新機制時,如果更新作業失敗,目前執行的系統不會受到影響。稍後再試一次即可。