A/B(無(wú)縫)系統(tǒng)更新
A/B 系統(tǒng)更新(也稱為無(wú)縫更新)的目標(biāo)是確保在無(wú)線下載 (OTA) 更新期間在磁盤上保留一個(gè)可正常啟動(dòng)和使用的系統(tǒng)。采用這種方式可以降低更新之后設(shè)備無(wú)法啟動(dòng)的可能性购裙,這意味著用戶需要將設(shè)備送到維修和保修中心進(jìn)行更換和刷機(jī)的情況將會(huì)減少施流。其他某些商業(yè)級(jí)操作系統(tǒng)(例如 ChromeOS)也成功運(yùn)用了 A/B 更新機(jī)制踪宠。
要詳細(xì)了解 A/B 系統(tǒng)更新冲泥,請(qǐng)參見(jiàn)分區(qū)選擇(槽位)一節(jié)。
A/B 系統(tǒng)更新可帶來(lái)以下好處:
- OTA 更新可以在系統(tǒng)運(yùn)行期間進(jìn)行怀伦,而不會(huì)打斷用戶脆烟。用戶可以在 OTA 期間繼續(xù)使用其設(shè)備。在更新期間房待,唯一的一次宕機(jī)發(fā)生在設(shè)備重新啟動(dòng)到更新后的磁盤分區(qū)時(shí)邢羔。
- 更新后,重新啟動(dòng)所用的時(shí)間不會(huì)超過(guò)常規(guī)重新啟動(dòng)所用的時(shí)間桑孩。
- 如果 OTA 無(wú)法應(yīng)用(例如拜鹤,因?yàn)樗C(jī)失敗)流椒,用戶將不會(huì)受到影響署惯。用戶將繼續(xù)運(yùn)行舊的操作系統(tǒng),并且客戶端可以重新嘗試進(jìn)行更新镣隶。
- 如果 OTA 更新已應(yīng)用但無(wú)法啟動(dòng)极谊,設(shè)備將重新啟動(dòng)回舊分區(qū),并且仍然可以使用安岂∏岵客戶端可以重新嘗試進(jìn)行更新。
- 任何錯(cuò)誤(例如 I/O 錯(cuò)誤)都只會(huì)影響未使用的分區(qū)組域那,并且用戶可以進(jìn)行重試咙边。由于 I/O 負(fù)載被特意控制在較低水平,以免影響用戶體驗(yàn)次员,因此發(fā)生此類錯(cuò)誤的可能性也會(huì)降低败许。
- 更新包可以流式傳輸?shù)?A/B 設(shè)備,因此在安裝之前不需要先下載更新包淑蔚。流式更新意味著用戶沒(méi)有必要在
/data
或/cache
上留出足夠的可用空間來(lái)存儲(chǔ)更新包市殷。 - 緩存分區(qū)不再用于存儲(chǔ) OTA 更新包,因此無(wú)需確保緩存分區(qū)的大小要足以應(yīng)對(duì)日后的更新刹衫。
- dm-verity 可保證設(shè)備將使用未損壞的啟動(dòng)映像醋寝。如果設(shè)備因 OTA 錯(cuò)誤或 dm-verity 問(wèn)題而無(wú)法啟動(dòng),則可以重新啟動(dòng)到舊映像带迟。(Android 驗(yàn)證啟動(dòng)不需要 A/B 更新音羞。)
關(guān)于 A/B 系統(tǒng)更新
進(jìn)行 A/B 更新時(shí),客戶端和系統(tǒng)都需要進(jìn)行更改仓犬。不過(guò)嗅绰,OTA 更新包服務(wù)器應(yīng)該不需要進(jìn)行更改:更新包仍通過(guò) HTTPS 提供。對(duì)于使用 Google OTA 基礎(chǔ)架構(gòu)的設(shè)備搀继,系統(tǒng)更改全部是在 AOSP 中進(jìn)行窘面,并且客戶端代碼由 Google Play 服務(wù)提供。不使用 Google OTA 基礎(chǔ)架構(gòu)的原始設(shè)備制造商 (OEM) 將能夠重復(fù)使用 AOSP 系統(tǒng)代碼律歼,但需要自行提供客戶端民镜。
如果 OEM 自行提供客戶端,客戶端需要:
- 確定何時(shí)進(jìn)行更新险毁。由于 A/B 更新是在后臺(tái)進(jìn)行制圈,因此不再需要由用戶啟動(dòng)。為了避免干擾用戶畔况,建議將更新安排在設(shè)備處于閑時(shí)維護(hù)模式(如夜間)并已連接到 WLAN 網(wǎng)絡(luò)時(shí)進(jìn)行鲸鹦。不過(guò),客戶端可以使用您希望使用的任何啟發(fā)法跷跪。
- 向 OTA 更新包服務(wù)器進(jìn)行核查馋嗜,確定是否有可用的更新。這應(yīng)與您現(xiàn)有的客戶端代碼大體相同吵瞻,不過(guò)您需要表明相應(yīng)設(shè)備支持 A/B 更新葛菇。(Google 的客戶端還包含立即檢查按鈕甘磨,以便用戶檢查是否有最新更新。)
- 調(diào)用
update_engine
(使用 HTTPS 網(wǎng)址)眯停,以獲取更新包(假設(shè)有可用的更新包)济舆。update_engine
將在流式傳輸更新包的同時(shí),在當(dāng)前未使用的分區(qū)上更新原始數(shù)據(jù)塊莺债。 - 根據(jù)
update_engine
結(jié)果代碼向您的服務(wù)器報(bào)告安裝是成功了還是失敗了滋觉。如果更新已成功應(yīng)用,update_engine
將會(huì)告知引導(dǎo)加載程序在下次重新啟動(dòng)時(shí)啟動(dòng)到新的操作系統(tǒng)齐邦。如果新的操作系統(tǒng)無(wú)法啟動(dòng)椎侠,引導(dǎo)加載程序?qū)?huì)回退到舊的操作系統(tǒng),因此無(wú)需在客戶端執(zhí)行任何操作措拇。如果更新失敗我纪,客戶端將需要根據(jù)詳細(xì)的錯(cuò)誤代碼確定何時(shí)(以及是否)重試。例如儡羔,優(yōu)秀的客戶端能夠識(shí)別出是一部分(“diff”)OTA 更新包失敗宣羊,并改為嘗試完整的 OTA 更新包。
客戶端可能會(huì):
- 顯示通知汰蜘,以提醒用戶重新啟動(dòng)系統(tǒng)仇冯。如果您想要實(shí)施鼓勵(lì)用戶定期更新的政策,則可以將該通知添加到客戶端族操。如果客戶端不提示用戶苛坚,用戶將會(huì)在下次重新啟動(dòng)系統(tǒng)時(shí)收到更新。(Google 的客戶端會(huì)有延遲色难,該延遲可按每次更新進(jìn)行配置泼舱。)
- 顯示通知,以告知用戶他們是啟動(dòng)到了新的操作系統(tǒng)版本枷莉,還是應(yīng)啟動(dòng)到新的操作系統(tǒng)版本娇昙,但卻回退到了舊的操作系統(tǒng)版本。(Google 的客戶端通常不會(huì)顯示此類通知笤妙。)
在系統(tǒng)方面冒掌,A/B 系統(tǒng)更新會(huì)影響以下各項(xiàng):
- 分區(qū)選擇(槽位)、
update_engine
守護(hù)進(jìn)程蹲盘,以及引導(dǎo)加載程序交互(如下所述) - 編譯過(guò)程和 OTA 更新包生成(如實(shí)現(xiàn) A/B 更新中所述)
注意:只有對(duì)于新設(shè)備股毫,才建議通過(guò) OTA 實(shí)現(xiàn) A/B 系統(tǒng)更新。
分區(qū)選擇(槽位)
A/B 系統(tǒng)更新使用兩組稱為槽位(通常是槽位 A 和槽位 B)的分區(qū)召衔。系統(tǒng)從“當(dāng)前”槽位運(yùn)行铃诬,但在正常操作期間,運(yùn)行中的系統(tǒng)不會(huì)訪問(wèn)未使用的槽位中的分區(qū)。這種方法通過(guò)將未使用的槽位保留為后備槽位趣席,來(lái)防范更新出現(xiàn)問(wèn)題:如果在更新期間或更新剛剛完成后出現(xiàn)錯(cuò)誤兵志,系統(tǒng)可以回滾到原來(lái)的槽位并繼續(xù)正常運(yùn)行。為了實(shí)現(xiàn)這一目標(biāo)吩坝,當(dāng)前槽位使用的任何分區(qū)(包括只有一個(gè)副本的分區(qū))都不應(yīng)在 OTA 更新期間進(jìn)行更新毒姨。
每個(gè)槽位都有一個(gè)“可啟動(dòng)”屬性,該屬性用于表明相應(yīng)槽位存儲(chǔ)的系統(tǒng)正確無(wú)誤钉寝,設(shè)備可從相應(yīng)槽位啟動(dòng)。系統(tǒng)運(yùn)行時(shí)闸迷,當(dāng)前槽位處于可啟動(dòng)狀態(tài)嵌纲,但另一個(gè)槽位則可能包含舊版本(仍然正確)的系統(tǒng)、包含更新版本的系統(tǒng)腥沽,或包含無(wú)效的數(shù)據(jù)逮走。無(wú)論當(dāng)前槽位是哪一個(gè),都有一個(gè)槽位是活動(dòng)槽位(引導(dǎo)加載程序在下次啟動(dòng)時(shí)將使用的槽位今阳,也稱為首選槽位)师溅。
此外,每個(gè)槽位還都有一個(gè)由用戶空間設(shè)置的“成功”屬性盾舌,僅當(dāng)相應(yīng)槽位處于可啟動(dòng)狀態(tài)時(shí)墓臭,該屬性才具有相關(guān)性。被標(biāo)記為成功的槽位應(yīng)該能夠自行啟動(dòng)妖谴、運(yùn)行和更新窿锉。未被標(biāo)記為成功的可啟動(dòng)槽位(多次嘗試使用它啟動(dòng)之后)應(yīng)由引導(dǎo)加載程序標(biāo)記為不可啟動(dòng),其中包括將活動(dòng)槽位更改為另一個(gè)可啟動(dòng)的槽位(通常是更改為在嘗試啟動(dòng)到新的活動(dòng)槽位之前正在運(yùn)行的槽位)膝舅。關(guān)于相應(yīng)接口的具體詳細(xì)信息在[ boot_control.h](https://android.googlesource.com/platform/hardware/libhardware/+/master/include/hardware/boot_control.h)
中進(jìn)行了定義嗡载。
更新引擎守護(hù)進(jìn)程
A/B 系統(tǒng)更新過(guò)程會(huì)使用名為 update_engine
的后臺(tái)守護(hù)進(jìn)程來(lái)使系統(tǒng)做好準(zhǔn)備,以啟動(dòng)到更新后的新版本仍稀。該守護(hù)進(jìn)程可以執(zhí)行以下操作:
- 按照 OTA 更新包的指示洼滚,從當(dāng)前槽位 A/B 分區(qū)讀取數(shù)據(jù),然后將所有數(shù)據(jù)寫(xiě)入到未使用槽位 A/B 分區(qū)技潘。
- 在預(yù)定義的工作流程中調(diào)用
boot_control
接口遥巴。 - 按照 OTA 更新包的指示,在將數(shù)據(jù)寫(xiě)入到所有未使用槽位分區(qū)之后崭篡,從新分區(qū)運(yùn)行安裝后程序挪哄。(有關(guān)詳細(xì)信息,請(qǐng)參閱安裝后)琉闪。
由于 update_engine
守護(hù)進(jìn)程本身不會(huì)參與到啟動(dòng)流程中迹炼,因此該守護(hù)進(jìn)程在更新期間可執(zhí)行的操作受限于當(dāng)前槽位中的 SELinux 政策和功能(在系統(tǒng)啟動(dòng)到新版本之前,此類政策和功能無(wú)法更新)。為了維持一個(gè)穩(wěn)定可靠的系統(tǒng)斯入,更新流程不應(yīng)修改分區(qū)表砂碉、當(dāng)前槽位中各個(gè)分區(qū)的內(nèi)容,以及無(wú)法通過(guò)恢復(fù)出廠設(shè)置擦除的非 A/B 分區(qū)的內(nèi)容刻两。
更新引擎源代碼
update_engine
源代碼位于 [system/update_engine](https://android.googlesource.com/platform/system/update_engine/)
中增蹭。A/B OTA dexopt 文件分開(kāi)放到了 installd
和一個(gè)程序包管理器中:
-
[frameworks/native/cmds/installd/](https://android.googlesource.com/platform/frameworks/native/+/master/cmds/installd/)
ota* 包括安裝后腳本、用于 chroot 的二進(jìn)制文件磅摹、負(fù)責(zé)調(diào)用 dex2oat 的已安裝克隆滋迈、OTA 后 move-artifacts 腳本,以及 move 腳本的 rc 文件户誓。 -
[frameworks/base/services/core/java/com/android/server/pm/OtaDexoptService.java](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/OtaDexoptService.java)
(加上[OtaDexoptShellCommand](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java)
)是負(fù)責(zé)為應(yīng)用準(zhǔn)備 dex2oat 命令的程序包管理器饼灿。
如需實(shí)際示例,請(qǐng)參閱 [/device/google/marlin/device-common.mk](https://android.googlesource.com/device/google/marlin/+/nougat-dr1-release/device-common.mk)
帝美。
更新引擎日志
對(duì)于 Android 8.x 及更低版本碍彭,可在 logcat
及錯(cuò)誤報(bào)告中找到 update_engine
日志。要使 update_engine
日志可在文件系統(tǒng)中使用悼潭,請(qǐng)將以下更改添加到您的細(xì)分版本中:
這些更改會(huì)將最新的 update_engine
日志的副本保存到 /data/misc/update_engine_log/update_engine.<var style="box-sizing: inherit; color: rgb(236, 64, 122); -webkit-font-smoothing: auto; font-weight: 700;">YEAR</var>-<var style="box-sizing: inherit; color: rgb(236, 64, 122); -webkit-font-smoothing: auto; font-weight: 700;">TIME</var>
庇忌。除當(dāng)前日志以外,最近的五個(gè)日志也會(huì)保存在 /data/misc/update_engine_log/
下方舰褪。擁有日志組 ID 的用戶將能夠訪問(wèn)相應(yīng)的文件系統(tǒng)日志皆疹。
引導(dǎo)加載程序交互
boot_control
HAL 供 update_engine
(可能還有其他守護(hù)進(jìn)程)用于指示引導(dǎo)加載程序從何處啟動(dòng)。常見(jiàn)的示例情況及其相關(guān)狀態(tài)包括:
- 正常情況:系統(tǒng)正在從其當(dāng)前槽位(槽位 A 或槽位 B)運(yùn)行抵知。到目前為止尚未應(yīng)用任何更新墙基。系統(tǒng)的當(dāng)前槽位是可啟動(dòng)且被標(biāo)記為成功的活動(dòng)槽位。
- 正在更新:系統(tǒng)正在從槽位 B 運(yùn)行刷喜,因此残制,槽位 B 是可啟動(dòng)且被標(biāo)記為成功的活動(dòng)槽位。由于槽位 A 中的內(nèi)容正在更新掖疮,但是尚未完成初茶,因此槽位 A 被標(biāo)記為不可啟動(dòng)。在此狀態(tài)下浊闪,應(yīng)繼續(xù)從槽位 B 重新啟動(dòng)恼布。
- 已應(yīng)用更新,正在等待重新啟動(dòng):系統(tǒng)正在從槽位 B 運(yùn)行搁宾,槽位 B 可啟動(dòng)且被標(biāo)記為成功折汞,但槽位 A 之前被標(biāo)記為活動(dòng)槽位(因此現(xiàn)在被標(biāo)記為可啟動(dòng))。槽位 A 尚未被標(biāo)記為成功盖腿,引導(dǎo)加載程序應(yīng)嘗試從槽位 A 啟動(dòng)若干次爽待。
-
系統(tǒng)已重新啟動(dòng)到新的更新:系統(tǒng)正在首次從槽位 A 運(yùn)行损同,槽位 B 仍可啟動(dòng)且被標(biāo)記為成功,而槽位 A 僅可啟動(dòng)鸟款,且仍是活動(dòng)槽位膏燃,但未被標(biāo)記為成功。在進(jìn)行一些檢查之后何什,用戶空間守護(hù)進(jìn)程
update_verifier
應(yīng)將槽位 A 標(biāo)記為成功组哩。
流式更新支持
用戶設(shè)備并非在 /data
上總是有足夠的空間來(lái)下載更新包。由于 OEM 和用戶都不想浪費(fèi) /cache
分區(qū)上的空間处渣,因此有些用戶會(huì)因?yàn)樵O(shè)備上沒(méi)有空間來(lái)存儲(chǔ)更新包而不進(jìn)行更新伶贰。為了解決這個(gè)問(wèn)題,Android 8.0 中添加了對(duì)流式 A/B 更新(下載數(shù)據(jù)塊后直接將數(shù)據(jù)塊寫(xiě)入 B 分區(qū)罐栈,而無(wú)需將數(shù)據(jù)塊存儲(chǔ)在 /data
上)的支持幕袱。流式 A/B 更新幾乎不需要臨時(shí)存儲(chǔ)空間,并且只需要能夠存儲(chǔ)大約 100KiB 元數(shù)據(jù)的存儲(chǔ)空間即可悠瞬。
要在 Android 7.1 中實(shí)現(xiàn)流式更新,請(qǐng)選擇以下補(bǔ)丁程序:
- 允許取消代理解析請(qǐng)求
- 解決在解析代理時(shí)會(huì)終止傳輸?shù)膯?wèn)題
- 針對(duì)范圍之間的 TerminateTransfer 添加單元測(cè)試
- 清理 RetryTimeoutCallback()
無(wú)論是使用 Google 移動(dòng)服務(wù) (GMS)涯捻,還是使用任何其他更新客戶端浅妆,都需要安裝這些補(bǔ)丁程序,才能在 Android 7.1 中支持流式傳輸 A/B 更新包障癌。
A/B 更新過(guò)程
當(dāng)有 OTA 更新包(在代碼中稱為有效負(fù)載)可供下載時(shí)凌外,更新流程便開(kāi)始了。設(shè)備中的政策可以根據(jù)電池電量涛浙、用戶活動(dòng)康辑、充電狀態(tài)或其他政策來(lái)延遲下載和應(yīng)用有效負(fù)載。此外轿亮,由于更新是在后臺(tái)運(yùn)行疮薇,因此用戶可能并不知道正在進(jìn)行更新。所有這些都意味著我注,更新流程可能隨時(shí)會(huì)由于政策按咒、意外重新啟動(dòng)或用戶操作而中斷。
OTA 更新包本身所含的元數(shù)據(jù)可能會(huì)指示可進(jìn)行流式更新但骨,在這種情況下励七,相應(yīng)更新包也可采用非流式安裝方式。服務(wù)器可以利用這些元數(shù)據(jù)告訴客戶端正在進(jìn)行流式更新奔缠,以便客戶端正確地將 OTA 移交給 update_engine
掠抬。如果設(shè)備制造商具有自己的服務(wù)器和客戶端,便可以通過(guò)確保以下兩項(xiàng)來(lái)實(shí)現(xiàn)流式更新:確保服務(wù)器能夠識(shí)別出更新是流式更新(或假定所有更新都是流式更新)校哎,并確绷讲ǎ客戶端能夠正確調(diào)用 update_engine
來(lái)進(jìn)行流式更新。制造商可以根據(jù)更新包是流式更新變體這一事實(shí)向客戶端發(fā)送一個(gè)標(biāo)記,以便在進(jìn)行流式更新時(shí)觸發(fā)向框架端的移交工作雨女。
有可用的有效負(fù)載后谚攒,更新流程將遵循如下步驟:
| 步驟 | 操作 |
| 1 | 通過(guò) markBootSuccessful()
將當(dāng)前槽位(或“源槽位”)標(biāo)記為成功(如果尚未標(biāo)記)。 |
| 2 | 調(diào)用函數(shù) setSlotAsUnbootable()
氛堕,將未使用的槽位(或“目標(biāo)槽位”)標(biāo)記為不可啟動(dòng)馏臭。當(dāng)前槽位始終會(huì)在更新開(kāi)始時(shí)被標(biāo)記為成功,以防止引導(dǎo)加載程序回退到未使用的槽位(該槽位中很快將會(huì)有無(wú)效數(shù)據(jù))讼稚。如果系統(tǒng)已做好準(zhǔn)備括儒,可以開(kāi)始應(yīng)用更新,那么即使其他主要組件出現(xiàn)損壞(例如界面陷入崩潰循環(huán))锐想,當(dāng)前槽位也會(huì)被標(biāo)記為成功帮寻,因?yàn)榭梢酝ㄟ^(guò)推送新軟件來(lái)解決這些問(wèn)題。
更新有效負(fù)載是不透明的 Blob赠摇,其中包含更新到新版本的指示固逗。更新有效負(fù)載由以下部分組成:
- 元數(shù)據(jù)。元數(shù)據(jù)在更新有效負(fù)載中所占的比重相對(duì)較小藕帜,其中包含一系列用于在目標(biāo)槽位上生成和驗(yàn)證新版本的操作烫罩。例如,某項(xiàng)操作可能會(huì)解壓縮特定 Blob 并將其寫(xiě)入到目標(biāo)分區(qū)中的特定塊洽故,或者從源分區(qū)讀取數(shù)據(jù)贝攒、應(yīng)用二進(jìn)制補(bǔ)丁程序,然后寫(xiě)入到目標(biāo)分區(qū)中的特定塊时甚。
- 額外數(shù)據(jù)隘弊。與操作相關(guān)的額外數(shù)據(jù)在更新有效負(fù)載中占據(jù)了大部分比重,其中包含這些示例中的已壓縮 Blob 或二進(jìn)制補(bǔ)丁程序荒适。
|
| 3 | 下載有效負(fù)載元數(shù)據(jù)梨熙。 |
| 4 | 對(duì)于元數(shù)據(jù)中定義的每項(xiàng)操作,都將按順序發(fā)生以下行為:將相關(guān)數(shù)據(jù)(如果有)下載到內(nèi)存中吻贿、應(yīng)用操作串结,然后釋放關(guān)聯(lián)的內(nèi)存。 |
| 5 | 對(duì)照預(yù)期的哈希重新讀取并驗(yàn)證所有分區(qū)舅列。 |
| 6 | 運(yùn)行安裝后步驟(如果有)肌割。如果在執(zhí)行任何步驟期間出現(xiàn)錯(cuò)誤,則更新失敗帐要,系統(tǒng)可能會(huì)通過(guò)其他有效負(fù)載重新嘗試更新把敞。如果上述所有步驟均已成功完成,則更新成功榨惠,系統(tǒng)會(huì)執(zhí)行最后一個(gè)步驟奋早。 |
| 7 | 調(diào)用 setActiveBootSlot()
盛霎,將未使用的槽位標(biāo)記為活動(dòng)槽位。將未使用的槽位標(biāo)記為活動(dòng)槽位并不意味著它將完成啟動(dòng)耽装。如果引導(dǎo)加載程序(或系統(tǒng)本身)未讀取到“成功”狀態(tài)愤炸,則可以將活動(dòng)槽位切換回來(lái)。 |
| 8 | 安裝后步驟(如下所述)包括從“新更新”版本中運(yùn)行仍在舊版本中運(yùn)行的程序掉奄。如果此步驟已在 OTA 更新包中定義规个,則為強(qiáng)制性步驟,且程序必須返回并顯示退出代碼 0
姓建,否則更新失敗诞仓。 |
| 9 | 在系統(tǒng)足夠深入地成功啟動(dòng)到新槽位并完成重新啟動(dòng)后檢查之后,系統(tǒng)會(huì)調(diào)用 markBootSuccessful()
速兔,將現(xiàn)在的當(dāng)前槽位(原“目標(biāo)槽位”)標(biāo)記為成功墅拭。 |
注意:第 3 步和第 4 步占用了大部分更新時(shí)間,因?yàn)檫@兩個(gè)步驟涉及寫(xiě)入和下載大量數(shù)據(jù)涣狗,并且可能會(huì)因政策或重新啟動(dòng)等原因而中斷谍婉。
安裝后
對(duì)于定義了安裝后步驟的每個(gè)分區(qū),update_engine
都會(huì)將新分區(qū)裝載到特定位置镀钓,并執(zhí)行與裝載的分區(qū)相關(guān)的 OTA 中指定的程序屡萤。例如,如果安裝后程序被定義為相應(yīng)系統(tǒng)分區(qū)中的 usr/bin/postinstall
掸宛,則系統(tǒng)會(huì)將未使用槽位中的這個(gè)分區(qū)裝載到一個(gè)固定位置(例如 /postinstall_mount
),然后執(zhí)行 /postinstall_mount/usr/bin/postinstall
命令招拙。
為確保成功執(zhí)行安裝后步驟唧瘾,舊內(nèi)核必須能夠:
- 裝載新的文件系統(tǒng)格式。文件系統(tǒng)類型不能更改(除非舊內(nèi)核中支持這么做)别凤,包括使用的壓縮算法(如果使用 SquashFS 等經(jīng)過(guò)壓縮的文件系統(tǒng))等詳細(xì)信息饰序。
-
理解新分區(qū)的安裝后程序格式。如果使用可執(zhí)行且可鏈接格式 (ELF) 的二進(jìn)制文件规哪,則該文件應(yīng)該與舊內(nèi)核兼容(例如求豫,如果架構(gòu)從 32 位細(xì)分版本改為使用 64 位細(xì)分版本,則 64 位的新程序應(yīng)該可以在舊的 32 位內(nèi)核上運(yùn)行)诉稍。除非加載程序 (
ld
) 收到使用其他路徑或編譯靜態(tài)二進(jìn)制文件的指令蝠嘉,否則將會(huì)從舊系統(tǒng)映像而非新系統(tǒng)映像加載各種庫(kù)。
例如杯巨,您可以使用 shell 腳本作為安裝后程序(由舊系統(tǒng)中頂部包含 #!
標(biāo)記的 shell 二進(jìn)制文件解析)蚤告,然后從新環(huán)境設(shè)置庫(kù)路徑,以便執(zhí)行更復(fù)雜的二進(jìn)制安裝后程序服爷《徘。或者获诈,您可以從專用的較小分區(qū)執(zhí)行安裝后步驟,以便主系統(tǒng)分區(qū)中的文件系統(tǒng)格式可以得到更新心褐,同時(shí)不會(huì)產(chǎn)生向后兼容問(wèn)題或引發(fā) stepping-stone 更新舔涎;這樣一來(lái),用戶便可以從出廠映像直接更新到最新版本逗爹。
新的安裝后程序?qū)⑹芘f系統(tǒng)中定義的 SELinux 政策限制亡嫌。因此,安裝后步驟適用于在指定設(shè)備上執(zhí)行設(shè)計(jì)所要求的任務(wù)或其他需要盡可能完成的任務(wù)(例如桶至,更新支持 A/B 更新的固件或引導(dǎo)加載程序昼伴、為新版本準(zhǔn)備數(shù)據(jù)庫(kù)副本,等等)镣屹。安裝后步驟不適用于重新啟動(dòng)之前的一次性錯(cuò)誤修復(fù)(此類修復(fù)需要無(wú)法預(yù)見(jiàn)的權(quán)限)圃郊。
所選的安裝后程序在 postinstall
SELinux 環(huán)境中運(yùn)行。新裝載的分區(qū)中的所有文件都將帶有 postinstall_file
標(biāo)記女蜈,無(wú)論在重新啟動(dòng)到新系統(tǒng)后它們的屬性如何持舆,都是如此。在新系統(tǒng)中對(duì) SELinux 屬性進(jìn)行的更改不會(huì)影響安裝后步驟伪窖。如果安裝后程序需要額外的權(quán)限逸寓,則必須將這些權(quán)限添加到安裝后環(huán)境中。
重新啟動(dòng)后
重新啟動(dòng)后覆山,update_verifier
會(huì)觸發(fā)利用 dm-verity 進(jìn)行完整性檢查竹伸。系統(tǒng)會(huì)先啟動(dòng)該檢查,然后再啟動(dòng) zygote簇宽,以避免 Java 服務(wù)進(jìn)行任何無(wú)法撤消且會(huì)導(dǎo)致無(wú)法進(jìn)行安全回滾的更改勋篓。在此過(guò)程中,如果驗(yàn)證啟動(dòng)功能或 dm-verity 檢測(cè)到任何損壞魏割,引導(dǎo)加載程序和內(nèi)核還可能會(huì)觸發(fā)重新啟動(dòng)譬嚣。檢查完成后,update_verifier
會(huì)將啟動(dòng)標(biāo)記為成功钞它。
update_verifier
只會(huì)讀取 /data/ota_package/care_map.txt
(在使用 AOSP 代碼時(shí)拜银,該文件會(huì)包含在 A/B OTA 更新包中)中列出的數(shù)據(jù)塊。Java 系統(tǒng)更新客戶端(例如 GmsCore)會(huì)在重新啟動(dòng)設(shè)備前提取 care_map.txt
并設(shè)置訪問(wèn)權(quán)限遭垛,在系統(tǒng)成功啟動(dòng)到新版本后會(huì)刪除所提取的文件尼桶。
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the Apache 2.0 License. For details, see our Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
上次更新日期:四月 3, 2018