Android 9 對引導程式引導程式引導原因規格進行了以下變更。
開機原因
引導程式使用唯一可用的硬體和記憶體資源來確定裝置重新啟動的原因,然後透過將androidboot.bootreason=<reason>
新增至 Android 核心命令列以進行啟動來傳達該確定。然後init
轉換此命令列以傳播到 Android 屬性bootloader_boot_reason_prop
( ro.boot.bootreason
)。對於使用 Android 12 或更高版本啟動、使用核心版本 5.10 或更高版本的設備, androidboot.bootreason=<reason>
將新增至 bootconfig 而不是核心命令列。
啟動原因規範
Android 的早期版本指定了啟動原因格式,不使用空格,全部小寫,包含很少的要求(例如報告kernel_panic
、 watchdog
、 cold
/ warm
/ hard
),並考慮到其他獨特原因。這種鬆散的規範導致了數百個自訂(有時是無意義的)啟動原因字串的激增,這反過來又導致了難以管理的情況。截至目前的 Android 版本,引導程式提交的幾乎無法解析或無意義的內容的巨大勢頭已經給bootloader_boot_reason_prop
帶來了合規性問題。
隨著 Android 9 版本的發布,Android 團隊認識到舊版bootloader_boot_reason_prop
具有強大的動力,並且無法在運行時重寫。因此,對引導原因規範的任何改進都必須來自與引導程式開發人員的互動以及現有系統的調整。為此,Android 團隊:
- 與引導程式開發人員合作,鼓勵他們:
- 為
bootloader_boot_reason_prop
提供規範的、可解析的和可識別的原因。 - 參與
system/core/bootstat/bootstat.cpp
kBootReasonMap
清單。
- 為
- 新增
system_boot_reason_prop
(sys.boot.reason
) 的受控且執行時間可重寫來源。有限的一組系統應用程式(例如bootstat
和init
)可以重寫此屬性,但所有應用程式都可以被授予讀取它的 sepolicy 權限。 - 通知使用者啟動原因,等待掛載 userdata 之後,再信任系統啟動原因屬性
system_boot_reason_prop
中的內容。
為什麼這麼晚?雖然bootloader_boot_reason_prop
在啟動初期可用,但它會根據需要被 Android 安全性策略阻止,因為它代表不準確、無法解析且不規範的資訊。在大多數情況下,只有對引導系統有深入了解的開發人員才需要存取此資訊。只有在掛載使用者資料後,才能可靠且準確地透過system_boot_reason_prop
取得用於啟動原因的精緻、可解析且規範的 API。具體來說:
- 在掛載 userdata之前,
system_boot_reason_prop
會包含bootloader_boot_reason_prop
中的值。 - 安裝使用者資料後,
system_boot_reason_prop
可能會更新以符合要求或報告更準確的資訊。
為此,Android 9 延長了正式獲取啟動原因的時間,將其從啟動時立即準確(使用bootloader_boot_reason_prop
)更改為僅在用戶資料掛載後才可用(使用system_boot_reason_prop
)。
Bootstat 邏輯依賴資訊更豐富、更相容的bootloader_boot_reason_prop
。當該屬性使用可預測的格式時,它可以提高所有受控重新啟動和關閉場景的準確性,從而細化和擴展system_boot_reason_prop
的準確性和意義。
規範的啟動原因格式
Android 9 中bootloader_boot_reason_prop
的規格啟動原因格式使用下列語法:
<reason>,<subreason>,<detail>…
格式規則:
- 小寫
- 沒有空格(使用底線)
- 所有可列印字符
- 以逗號分隔的
reason
、subreason
和一個或多個detail
。- 必需
reason
,表示設備必須重新啟動或關閉的最高優先原因。 - 可選
subreason
,表示設備必須重新啟動或關閉的原因(或誰重新啟動或關閉設備)的簡短摘要。 - 一個或多個可選
detail
值。detail
可能指向子系統,以幫助確定是哪個特定係統導致了subreason
。您可以指定多個detail
值,這些值通常應遵循重要性層次結構。然而,報告多個同等重要的detail
值也是可以接受的。
- 必需
bootloader_boot_reason_prop
的空值被認為是非法的(因為這允許其他代理在事後注入啟動原因)。
理由要求
為reason
給出的值(第一個跨度,在終止或逗號之前)必須屬於以下集合,分為核心原因、強原因和鈍原因:
- 內核集:
- “
watchdog"
-
"kernel_panic"
- “
- 強組:
-
"recovery"
-
"bootloader"
-
- 鈍設定:
-
"cold"
。通常表示所有設備(包括記憶體)的完全重置。 -
"hard"
。通常表示硬體已重置狀態且ramoops
應保留持久內容。 -
"warm"
。通常表示記憶體和裝置保留某些狀態,並且ramoops
(請參閱核心中的pstore
驅動程式)後備儲存包含持久內容。 -
"shutdown"
-
"reboot"
。一般表示ramoops
狀態未知,硬體狀態未知。該值是一個包羅萬象的值,因為cold
、hard
和warm
值提供了有關設備重置深度的線索。
-
引導程式必須提供內核集或直接設定reason
,並且強烈鼓勵提供subreason
(如果可以確定)。例如,長按電源鍵(可能有或沒有ramoops
備份)將具有啟動原因"reboot,longkey"
。
第一跨度reason
不能成為任何子subreason
或detail
的一部分。但是,由於核心設定原因無法由用戶空間生成,因此在生硬的設定原因後可以重複使用"watchdog"
以及來源的詳細資訊(例如"reboot,watchdog,service_manager_unresponsive"
或"reboot,software,watchdog"
)。
啟動原因不應需要專家的內部知識來破解和/或應透過直觀的報告供人類閱讀。例: "shutdown,vbxd"
(不好)、 "shutdown,uv"
(更好)、 "shutdown,undervoltage"
(首選)。
原因-子原因組合
Android 保留了一組reason
- subreason
,這些組合在正常使用中不應超載,但如果組合準確反映了相關條件,則可以根據具體情況使用。保留組合的範例包括:
-
"reboot,userrequested"
-
"shutdown,userrequested"
-
"shutdown,thermal"
(取自thermald
) -
"shutdown,battery"
-
"shutdown,battery,thermal"
(來自BatteryStatsService
) -
"reboot,adb"
-
"reboot,shell"
-
"reboot,bootloader"
-
"reboot,recovery"
有關更多詳細信息,請參閱system/core/bootstat/bootstat.cpp
中的kBootReasonMap
以及 Android 源代碼存儲庫中相關的 git 更改日誌歷史記錄。
報告啟動原因
所有引導原因,無論是來自引導程式或記錄在規範引導原因中,都必須記錄在system/core/bootstat/bootstat.cpp
的kBootReasonMap
部分中。 kBootReasonMap
清單混合了合規原因和舊的不合規原因。引導程式開發人員應僅在此處註冊新的合規原因(且不應註冊不合規原因,除非產品已出貨且無法變更)。
我們強烈建議使用system/core/bootstat/bootstat.cpp
中現有的合規條目,並在使用不合規字串之前保持克制。作為指導方針,它是:
- 可以從引導程式報告
"kernel_panic"
,因為bootstat
可能能夠檢查ramoops
的kernel_panic signatures
,以將子原因細化為規範的system_boot_reason_prop
。 - 從引導程式報告
kBootReasonMap
中的不合規字串(例如"panic")
是不行的,因為這最終會破壞細化reason
能力。
例如,如果kBootReasonMap
包含"wdog_bark"
,引導程式開發人員應該:
- 更改為
"watchdog,bark"
並添加到kBootReasonMap
中的列表中。 - 考慮
"bark"
對於那些不熟悉該技術的人意味著什麼,並確定是否有更有意義的subreason
。
驗證啟動原因合規性
目前,Android 不提供主動 CTS 測試來準確觸發或檢查引導程式可能提供的所有可能的啟動原因;合作夥伴仍然可以嘗試執行被動測試來確定相容性。
因此,引導程式合規性要求引導程式開發人員自願遵守上述規則和指南的精神。我們敦促此類開發人員為 AOSP 做出貢獻(特別是system/core/bootstat/bootstat.cpp
),並利用這個機會作為討論啟動原因問題的論壇。