本页介绍了通用内核映像 (GKI) 的版本控制方案。通用内核映像 (GKI) 具有一个唯一标识符,称为内核版本。内核版本包含内核模块接口 (KMI) 版本和子级别。内核版本特定于要发布的映像,而 KMI 版本表示版本构建所依据的接口。一个 KMI 版本可以支持多个内核版本。一个内核版本仅与一个 KMI 版本相关联。在极少数情况下,如果必须更改内核模块接口,则 KMI 代会进行迭代以反映 KMI 版本的更改。
术语摘要
下表总结了本页和 GKI 更新中使用的重要术语。
名称 | 符号 | 示例 | 说明 |
---|---|---|---|
内核版本 | w.x.y-zzz-k-suffix | 5.4.42-android12-0-foo | GKI 版本的唯一标识符。这是 uname 返回的值。 |
KMI 版本 | w.x-zzz-k | 5.4-android12-0 | 描述 GKI 和动态加载内核模块 (DLKM) 之间的内核模块接口 (KMI)。 |
子级别 | y | 42 | 描述同一 KMI 版本内内核版本的发布顺序。 |
下表列出了其他相关术语以供参考。
名称 | 符号 | 示例 | 说明 |
---|---|---|---|
w.x.y | w.x.y | 5.4.42 |
如需了解详情,请参阅 Linux 内核 Makefile(搜索“KERNELRELEASE”)。 w.x.y 在本文档中直接使用。这也通常称为三部分版本号。VINTF 中使用的术语内核版本可能会与其他术语(尤其是 w)混淆。 此变量在 libkver 中称为 kernel_version_tuple。 任何更新(包括 OTA 或主线)都不得降低此元组。 |
内核分支 | zzz-w.x | android12-5.4 | 此术语在通用内核分支类型中使用。 |
版本 | w | 5 | 本文档中未使用此术语。此变量在 libkver 中称为 version。 |
补丁程序级别 | x | 4 | 本文档中未使用此术语。此变量在 libkver 中称为 patch_level。 |
Android 版本 | zzz | android12 |
这是内核关联的 Android (甜点) 版本号。 在比较 Android 版本号不得因任何更新而降低,包括 OTA 或主线更新。 |
KMI 代际 | k | 0 |
这是一个为应对不太可能发生的事件而添加的附加数字。如果安全漏洞修复需要在同一 Android 版本中更改 KMI,则 KMI 代际会增加。 KMI 代际编号从 0 开始。 |
版本控制设计
内核版本
定义
对于随 GKI 出货的设备,内核版本定义如下
KernelRelease :=
Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
w .x .y -zzz -k -something
有关详细信息,请参阅 从设备确定内核版本。
以下是内核版本的示例。
5.4.42-android12-0-00544-ged21d463f856
说明
内核版本是 GKI 版本的唯一 ID。如果两个 GKI 二进制文件具有相同的内核版本,则它们必须是字节级相同的。
内核版本由 KMI 版本、子级别和后缀组成。就本文档而言,KMI 代际之后的后缀将被忽略。
KMI 版本
定义
KMI 版本定义如下
KmiVersion :=
Version.PatchLevel-AndroidRelease-KmiGeneration
w .x -zzz -k
请注意,子级别 y
不是 KMI 版本的一部分。对于内核版本中的示例,KMI 版本是
5.4-android12-0
说明
KMI 版本描述了 GKI 和动态可加载内核模块 (DLKM) 之间的内核模块接口 (KMI)。
如果两个内核版本具有相同的 KMI 版本,则它们实现相同的内核模块接口。与其中一个兼容的 DLKM 也与另一个兼容。
KMI 版本不得因任何 OTA 更新而降低。
子级别
子级别 y
描述了同一 KMI 版本中内核版本的发布顺序。
对于两个具有相同 KMI 版本但子级别分别为 Y1 和 Y2 的内核版本
- 如果 Y1 小于或等于 Y2,则运行 Y1 的设备可以接收到 Y2 的更新。
- 如果 Y1 大于 Y2,则运行 Y1 的设备无法更新到 Y2。
也就是说,如果 KMI 版本没有更改,则子级别不得因任何 OTA 更新而降低。
从设备确定内核版本
可以通过执行 uname -r
或使用以下代码段 uname(2)
来找到完整的内核版本
std::string get_kernel_release() {
struct utsname buf;
return uname(&buf) == 0 ? buf.release : "";
}
示例输出为
5.4.42-android12-0-00544-ged21d463f856
就本文档而言,提取内核信息时,KMI 代际之后的任何内容都将被忽略。更正式地说,uname -r
的输出使用以下 正则表达式 进行解析(假设 zzz 始终以 “android” 开头)
^(?P<w>\d+)[.](?P<x>\d+)[.](?P<y>\d+)-(?P<z>android\d+)-(?P<k>\d+).*$
忽略的信息可以包括诸如 ci.android.com 构建编号、基线内核之上的补丁数量以及 git 提交的 SHA 哈希值等信息。
libkver
库 libkver 提供了一个 C++ 接口来解析内核版本或 KMI 版本字符串。有关 libkver 公开的 API 列表,请参阅 packages/modules/Gki/libkver/include/kver
。
VINTF 检查
对于 Android 11 或更低版本,KMI 版本的 Android 版本部分由设备制造商在设备清单中手动指定。有关详细信息,请参阅 VINTF 内核匹配规则。
从 Android S 开始,KMI 版本的 Android 版本部分可以从内核中提取并在构建时注入到设备清单中。
由于内核配置要求通常不会更改,因此无需在兼容性矩阵中编码 k
。但是,在不太可能需要更改内核配置要求的情况下,请确保以下事项
- 从兼容性矩阵中删除相应的要求。
- 添加额外的 VTS 测试以检查以 KMI 代际为条件的新要求。
OTA 元数据中的启动映像版本
即使启动映像通过 OTA 更新进行了更新,也必须将其包装在 OTA 负载格式 payload.bin
中。OTA 负载为每个分区编码一个 version
字段。当 update_engine
处理 OTA 负载时,它会比较此字段以确保分区未降级。
为避免混淆,OTA 元数据中启动分区的 version
字段称为 boot image version
。
因为 ramdisk 始终是从头开始构建的,所以使用 ramdisk 时间戳 就足以描述整个启动映像。除非将来要将旧启动映像缝合到新的内核二进制文件中,否则无需在启动映像版本中编码内核版本。
在 OTA 更新之前,OTA 客户端以与任何其他分区相同的方式检查启动映像版本。