Google 是否曾在任何裝置上使用 A/B OTA?
可以。A/B 更新的行銷名稱為「無縫更新」。2016 年 10 月推出的 Pixel 和 Pixel XL 手機已搭載 A/B,所有 Chromebook 都使用相同的 update_engine
A/B 實作。在 Android 7.1 以上版本中,必要的平台程式碼實作項目是公開的。
為什麼 A/B OTA 更優?
A/B OTA 可在更新時提供更好的使用者體驗。從每月安全性更新的測量結果來看,這項功能已證明成功:截至 2017 年 5 月,95% 的 Pixel 使用者在一個月後會執行最新的安全性更新,而 Nexus 使用者則為 87%;此外,Pixel 使用者比 Nexus 使用者更快更新。在 OTA 期間未能更新區塊,不再導致裝置無法啟動;在新的系統映像檔成功啟動前,Android 仍可回復先前可用的系統映像檔。
什麼是 system_other?
應用程式會儲存在 .apk 檔案中,而這些檔案實際上是 ZIP 封存檔。每個 .apk 檔案都包含一或多個內含可攜式 Dalvik 位元碼的 .dex 檔案。.odex 檔案 (經過最佳化的 .dex) 與 .apk 檔案分開,可包含裝置專屬的機器碼。如果有 .odex 檔案,Android 就能以提前編譯的速度執行應用程式,不必在每次啟動應用程式時等待程式碼編譯。.odex 檔案並非絕對必要:Android 實際上可以透過解讀或即時 (JIT) 編譯直接執行 .dex 程式碼,但如果有空間,.odex 檔案可提供最佳的啟動速度和執行階段速度組合。
範例:如果 Nexus 6P 執行 Android 7.1,且系統映像檔總大小為 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 更新的應用程式,只要收到第一個更新,就會在
/data
上擁有 .odex 檔案。 - 使用者未執行的應用程式完全不需要 .odex 檔案。
- 設定檔導向編譯會產生比預先編譯更小的 .odex 檔案 (因為前者只會最佳化效能至關重要的程式碼)。
如要進一步瞭解原始設備製造商 (OEM) 可用的調整選項,請參閱「設定 ART」。
/data 上不是有兩個 .odex 檔案副本嗎?
這項作業稍微複雜一些,因為在新的系統映像檔寫入後,系統會針對新的 .dex 檔案執行新的 dex2oat 版本,以產生新的 .odex 檔案。這會在舊系統仍在執行時發生,因此舊版和新版 .odex 檔案會同時出現在 /data
中。
OtaDexoptService (frameworks/base/+/main/services/core/java/com/android/server/pm/OtaDexoptService.java
) 中的程式碼會在最佳化每個套件之前呼叫 getAvailableSpace
,以免過度填滿 /data
。請注意,這裡的「可用」仍是保守的值,代表系統在達到低儲存空間門檻前所剩的空間量 (以百分比和位元組計數量來衡量)。因此,如果 /data
已滿,就不會有兩個 .odex 檔案副本。同樣的程式碼也有 BULK_DELETE_THRESHOLD:如果裝置的空間即將用盡 (如先前所述),系統就會移除不常使用的應用程式所屬的 .odex 檔案。這是另一種沒有每個 .odex 檔案都有兩個副本的情況。
在 /data
完全已滿的極端情況下,更新會等待裝置重新啟動至新系統,並不再需要舊系統的 .odex 檔案。PackageManager 會處理這個問題:(frameworks/base/+/main/services/core/java/com/android/server/pm/PackageManagerService.java#7215
)。新系統成功啟動後,installd
(frameworks/native/+/main/cmds/installd/dexopt.cpp#2422
) 可以移除舊系統使用的 .odex 檔案,讓裝置恢復到只有一個副本的穩定狀態。
因此,雖然 /data
可能包含所有 .odex 檔案的兩個副本,但 (a) 這只是暫時性的,且 (b) 只有在 /data
有大量可用空間時才會發生。除了更新期間,系統只會保留一份副本。而且,ART 的一般穩健功能會確保不會以 .odex 檔案填入 /data
(因為這也會在非 A/B 系統上造成問題)。
這些寫入/複製作業會不會增加快閃記憶體的磨損?
系統只會重新編寫 Flash 的一小部分,完整的 Pixel 系統更新會寫入約 2.3 GB 的資料。(應用程式也會重新編譯,但非 A/B 版本也會這樣做)。傳統上,區塊式完整 OTA 會寫入相似數量的資料,因此快閃記憶體的磨損率應相近。
刷新兩個系統分區會增加工廠刷新時間嗎?
否。Pixel 並未增加系統映像檔大小 (只是將空間分割到兩個分區)。
將 .odex 檔案保留在 B 上,是否會導致恢復原廠設定後的重新啟動速度變慢?
可以。如果您實際使用裝置、取得 OTA 並執行恢復原廠設定,第一次重新啟動會比平常慢 (Pixel XL 為 1 分 40 秒,而非 40 秒),因為在第一次 OTA 後,B 會遺失 .odex 檔案,因此無法複製到 /data
。這就是取捨之處。
與一般啟動作業相比,工廠資料重設應是較少見的操作,因此所需時間不太重要。(這不會影響從工廠取得裝置的使用者或評論者,因為在這種情況下,B 分區可供使用)。使用 JIT 編譯器表示我們不需要重新編譯「所有」,因此這並非您想像的那麼糟糕。您也可以在資訊清單中使用 coreApp="true"
(frameworks/base/+/main/packages/SystemUI/AndroidManifest.xml#23
),將應用程式標示為需要使用提前編譯。system_server
目前使用此方法,因為該應用程式基於安全考量,不允許使用 JIT。
將 .odex 檔案放在 /data 而非 /system 中,會不會導致 OTA 更新後重新啟動速度變慢?
否。如上所述,新的 dex2oat 會在舊系統映像檔仍在執行時執行,以產生新系統所需的檔案。只有在完成這項工作後,更新才會視為可用。
我們可以 (或應該) 出貨 32GiB A/B 裝置嗎?16GiB?8GiB?
32 GiB 在 Pixel 上運作良好,且 16 GiB 中的 320 MiB 代表減少 2%。 同樣地,8 GiB 中 320 MiB 的減少幅度為 4%。顯然,在 4 GiB 的裝置上,A/B 並非建議的選擇,因為 320 MiB 的額外負擔幾乎佔總可用空間的 10%。
AVB 2.0 是否需要 A/B OTA?
否。Android 已驗證啟動一向需要區塊式更新,但不一定需要 A/B 更新。
A/B OTA 是否需要 AVB2.0?
編號
A/B OTA 會破壞 AVB 2.0 的回溯保護功能嗎?
否。這裡有點令人困惑,因為如果 A/B 系統無法啟動至新的系統映像檔,系統會在系統啟動載入程式決定重試次數後,自動還原至「先前」的系統映像檔。不過,關鍵點在於,A/B 測試中「舊版」的意思其實是「目前」的系統映像檔。裝置成功啟動新映像檔後,系統就會啟動回溯保護機制,確保您無法回溯。但在您實際成功啟動新映像檔之前,回溯保護機制不會將其視為目前的系統映像檔。
如果在系統執行期間安裝更新,速度會不會很慢?
對於非 A/B 更新,目標是盡快安裝更新,因為使用者在等待期間無法使用裝置,在 A/B 更新中,情況恰好相反:由於使用者仍在使用裝置,因此目標是盡量減少影響,因此更新速度會刻意放慢。透過 Java 系統更新用戶端 (Google 的 GmsCore,即 GMS 提供的核心套件) 中的邏輯,Android 也會嘗試選擇使用者完全不使用裝置的時間。平台支援暫停/繼續更新,如果使用者開始使用裝置,用戶端可以使用該功能暫停更新,並在裝置再次閒置時繼續更新。
OTA 有兩個階段,在 UI 中清楚顯示為進度列下方的「Step 1 of 2」和「Step 2 of 2」。步驟 1 對應至寫入資料區塊,而步驟 2 則是預先編譯 .dex 檔案。這兩個階段在效能影響方面截然不同。第一階段是簡單的 I/O,這項作業只會緩慢地複製區塊,因此所需資源 (RAM、CPU、I/O) 很少。
第二階段會執行 dex2oat,以便預先編譯新的系統映像檔。這項工具會編譯實際的應用程式,因此對其需求的限制較少。而且,相較於小型簡單應用程式,編譯大型複雜應用程式所需的工作量顯然更大。在第 1 階段,並沒有比其他磁碟區塊更大或更複雜的磁碟區塊。
這個程序類似於 Google Play 在背景安裝應用程式更新,然後顯示「5 個應用程式更新」通知的情況,這項作業已持續多年。
如果使用者實際上正在等待更新,該怎麼辦?
GmsCore 目前的實作方式不會區分背景更新和使用者啟動的更新,但日後可能會有所改變。如果使用者明確要求安裝更新,或是正在查看更新進度畫面,我們會將更新工作列為優先,因為我們假設使用者正在積極等待更新完成。
如果套用更新失敗,會發生什麼事?
在非 A/B 更新中,如果更新套用失敗,使用者通常會無法使用裝置。唯一的例外狀況是,如果失敗發生在應用程式尚未啟動之前 (例如套件驗證失敗)。使用 A/B 更新時,如果無法套用更新,也不會影響目前執行中的系統。您可以稍後再嘗試更新。