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 权限来读取它。 - 告知用户启动原因,在用户数据分区挂载完成后再信任系统启动原因属性
system_boot_reason_prop
中的内容。
为何如此晚? 虽然 bootloader_boot_reason_prop
在启动早期就可用,但由于它表示不准确、无法解析和非规范的信息,因此 Android 安全策略会根据需要阻止它。 在大多数情况下,只有对启动系统有深入了解的开发人员才需要访问此信息。 只有在用户数据分区挂载后,才能可靠且准确地获取具有 system_boot_reason_prop
的经过改进、可解析和规范的启动原因 API。 具体来说
- 在用户数据分区挂载之前,
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
)做出贡献,并将此机会用作讨论启动原因问题的论坛。