本页介绍了 Android 设备 OEM 可以使用的几种机制,以便在产品线中拥有自己的共享系统映像 (SSI)。它还提出了一种基于 AOSP 构建的通用系统映像 (GSI) 构建 OEM 拥有的 SSI 的程序。
背景
借助 Project Treble,单体式 Android 被分为两个部分:硬件特定的部分(供应商实现)和通用操作系统部分(Android 操作系统框架)。每个部分的软件都安装在单独的分区中:供应商分区用于硬件特定的软件,系统分区用于通用操作系统软件。版本化的接口(称为供应商接口 (VINTF))在两个分区之间定义和强制执行。通过使用此分区系统,您可以修改系统分区,而无需修改供应商分区,反之亦然。
动机
AOSP 中发布的基础架构代码一直符合 Treble 架构,并保持了与旧版本供应商实现的向后兼容性。例如,从 Android 10 AOSP 源代码构建的通用系统映像可以运行在任何符合 Treble 规范且运行 Android 8 或更高版本的设备上。消费者设备上搭载的 Android 版本由 SoC 供应商和 OEM 修改。(请参阅 Android 版本发布的生命周期。)对框架进行的这些更改和扩展并非为了保持向后兼容性而编写,这导致了操作系统升级的复杂性和成本增加。设备特定的更改和修改增加了 Android 操作系统版本升级的成本和复杂性。
在 Android 11 之前,没有明确的架构使合作伙伴能够构建 Android 操作系统框架的模块化扩展。本文档介绍了 SoC 供应商和 OEM 可以采取的步骤来创建 SSI。这意味着一个映像,从 Android 操作系统框架源代码构建,可在多个设备上重复使用,以保持与供应商实现的向后兼容性,并显著降低 Android 操作系统升级的复杂性和成本。有关创建 SSI 的具体步骤,请参阅基于 GSI 的 SSI 的建议步骤部分,并注意您不必使用所有四个步骤。您选择哪些步骤(例如,仅步骤 1)取决于您的实现。
SSI 概述
借助 SSI,产品特定的软件组件和 OEM 扩展被放置在一个新的 /product
分区中。/product
分区中的组件使用定义完善的稳定接口与 /system
分区中的组件进行交互。OEM 可以选择构建一个 SSI,也可以选择构建少量 SSI 以供多个设备 SKU 使用。当发布新版本的 Android 操作系统时,OEM 只需一次投入来将其 SSI 更新到最新的 Android 版本。他们可以重用 SSI 来更新多个设备,而无需更新 /product
分区。
请注意,OEM 和 SoC 供应商构建的 SSI 包含 OEM 需要的所有自定义功能和修改。此页面提供的机制和最佳实践旨在供 OEM 使用以实现这些关键目标
- 在多个设备 SKU 之间重用 SSI。
- 使用模块化扩展更新 Android 系统,以简化操作系统升级。
将产品特定组件分离到 product 分区的核心思想类似于将 SoC 特定组件分离到 vendor 分区的 Treble 思想。产品接口(类似于 VINTF)允许 SSI 和 product 分区之间进行通信。请注意,对于 SSI 而言,“组件”一词描述的是安装到映像的所有资源、二进制文件、文本、库等等,这些资源、二进制文件、文本、库等实际上变成了分区。
围绕 SSI 的分区
图 1 显示了围绕 SSI 的分区,以及跨分区的版本化接口和接口策略。本节详细解释了每个分区和接口。
图 1. 围绕 SSI 的分区和接口
映像和分区
本节中的信息区分了术语映像和分区。
- 映像是可独立更新的概念性软件片段。
- 分区是可以独立更新的物理存储位置。
图 1 中的各部分定义如下
SSI: SSI 是 OEM 通用的映像,可以跨多个设备存在。它没有任何硬件特定或产品特定的组件。给定 SSI 中的所有内容,根据定义,在所有使用该 SSI 的设备之间共享。SSI 由单个
/system
映像或/system
和/system_ext
分区组成,如图 1 所示。/system
分区包含基于 AOSP 的组件,而/system_ext
(如果已实现)包含 OEM 和 SoC 供应商扩展以及与 AOSP 组件紧密集成的组件。例如,为 OEM 自己的应用提供自定义 API 的 OEM Java 框架库更适合放在/system_ext
而不是/system
分区中。/system
和/system_ext
分区的内容都来自 OEM 修改的 Android 源代码构建。/system_ext
分区是可选的,但将其用于任何与基于 AOSP 的组件紧密集成的自定义功能和扩展都很有益处。这种区分有助于您识别需要进行的更改,以便随着时间的推移将此类组件从/system_ext
分区移动到/product
分区。
Product: 产品或设备特定组件的集合,代表 OEM 对 Android 操作系统进行的自定义和扩展。将 SoC 特定组件放在
/vendor
分区中。SoC 供应商也可以将/product
分区用于适当的组件,例如独立于 SoC 的组件。例如,如果 SoC 供应商向其 OEM 客户提供独立于 SoC 的组件(该组件是产品附带的可选项),则 SoC 供应商可以将该组件放在产品映像中。组件的位置不是由其所有权决定的,而是由其用途决定的。Vendor: SoC 特定组件的集合。
ODM: 板级特定组件的集合,这些组件不是由 SoC 提供的。通常,SoC 供应商拥有 vendor 映像,而设备制造商拥有 ODM 映像。当没有单独的
/odm
分区时,SoC 供应商和 ODM 映像都合并到/vendor
分区中。
映像之间的接口
围绕 SSI 存在 vendor 和 product 映像的两个主要接口
供应商接口 (VINTF):VINTF 是 vendor 和 ODM 映像中组件的接口。product 和 system 映像中的组件只能通过此接口与 vendor 和 ODM 映像进行交互。例如,vendor 映像不能依赖于 system 映像的私有部分,反之亦然。这最初在 Project Treble 中定义,该项目将映像拆分为 system 和 vendor 分区。该接口使用以下机制进行描述
- HIDL(直通 HAL 仅适用于
system
和system_ext
模块) - 稳定 AIDL
- 配置
- 系统属性 API
- 配置文件模式 API
- VNDK
- Android SDK API
- Java SDK 库
- HIDL(直通 HAL 仅适用于
产品接口: 产品接口是 SSI 和 product 映像之间的接口。定义稳定的接口可以将产品组件与 SSI 中的系统组件解耦。产品接口需要与 VINTF 相同的稳定接口。但是,对于搭载 Android 11(及更高版本)的设备,仅强制执行 VNDK 和 Android SDK API。
在 Android 11 中启用 SSI
本节介绍如何使用新的功能来支持 Android 11 中的 SSI。
/system_ext 分区
/system_ext
分区是在 Android 11 中引入的可选分区。(它是非 AOSP 组件的存放位置,这些组件与 /system
分区中 AOSP 定义的组件紧密耦合。)/system_ext
分区被认为是 /system
分区的 OEM 特定扩展,这两个分区之间没有定义接口。/system_ext
分区中的组件可以私有 API 调用到 /system
分区,而 /system
分区中的组件可以私有 API 调用到 /system_ext
分区。
由于这两个分区紧密耦合,因此当发布新的 Android 版本时,这两个分区会一起升级。为先前版本的 Android 创建的 /system_ext
分区不需要与下一个 Android 版本中的 /system
分区兼容。
要将模块安装到 /system_ext
分区,请将 system_ext_specific: true
添加到 Android.bp
文件中。对于没有 /system_ext
分区的设备,请将此类模块安装到 /system
分区中的 ./system_ext
子目录中。
历史记录
以下是关于 /system_ext
分区的一些历史记录。设计目标是将所有 OEM 特定组件(无论它们是否通用)都放在 /product
分区中。但是,一次性移动所有组件是不可行的,尤其是在某些组件与 /system
分区紧密耦合时。要将紧密耦合的组件移动到 /product
分区,必须扩展产品接口。这通常需要对组件本身进行广泛的重构,这会消耗大量的时间和精力。/system_ext
分区最初是作为临时托管那些尚未准备好移动到 /product
分区的组件的位置而开始的。SSI 的目标是最终消除 /system_ext
分区。
但是,/system_ext
分区对于使 /system
分区尽可能接近 AOSP 非常有用。借助 SSI,大部分升级工作都花在了 /system
和 /system_ext
分区中的组件上。当系统映像是从尽可能类似于 AOSP 中的源代码构建时,您可以将升级工作重点放在 system_ext
映像上。
将组件从 /system 和 /system_ext 分区解绑到 /product 分区中
Android 9 引入了与 /system
分区耦合的 /product
分区。/product
分区中的模块可以不受任何限制地使用系统资源,反之亦然。为了使 Android 10 中能够实现 SSI,产品组件被拆分到 /system_ext
和 /product
分区中。/system_ext
分区不必遵守 Android 9 中 /product
分区对使用系统组件的限制。从 Android 10 开始,/product
分区必须从 /system
分区解绑,并且必须使用来自 /system
和 /system_ext
分区的稳定接口。
/system_ext
分区的主要目的是扩展系统功能,而不是安装捆绑的产品模块,如/system_ext partition
部分所述。为此,请解绑产品特定模块,并将它们移动到 /product
分区中。解绑产品特定模块使 /system_ext
对设备通用。(有关更多详细信息,请参阅使 /system_ext 分区通用。)
为了将 /product
分区从系统组件解绑,/product
分区必须具有与 /vendor
分区相同的强制执行策略,/vendor
分区已通过 Project Treble 解绑。
从 Android 11 开始,/product
分区的原生和 Java 接口将按如下所述强制执行。有关更多信息,请参阅强制执行产品分区接口。
- 原生接口:
/product
分区中的原生模块必须与其他分区解绑。产品模块唯一允许的依赖项是来自/system
分区的一些 VNDK 库(包括 LLNDK)。产品应用所依赖的 JNI 库必须是 NDK 库。 - Java 接口:
/product
分区中的 Java(应用)模块不能使用隐藏的 API,因为它们不稳定。这些模块必须仅使用来自/system
分区的公共 API 和系统 API,以及/system
或/system_ext
分区中的 Java SDK 库。您可以为自定义 API 定义 Java SDK 库。
基于 GSI 的 SSI 的建议步骤
图 2. 基于 GSI 的 SSI 的建议分区
通用系统映像 (GSI) 是直接从 AOSP 构建的系统映像。它用于 Treble 合规性测试(例如,CTS-on-GSI)和作为参考平台,应用开发者可以使用该平台在没有运行所需 Android 版本的真实设备的情况下测试其应用的兼容性。
OEM 也可以使用 GSI 来制作其 SSI。如映像和分区中所述,SSI 由用于 AOSP 定义的组件的系统映像和用于 OEM 定义的组件的 system_ext
映像组成。当 GSI 用作 system
映像时,OEM 可以专注于 system_ext
映像的升级。
本节为希望将其自定义项模块化到 /system_ext
和 /product
分区中,同时使用 AOSP 或接近 AOSP 的系统映像的 OEM 提供指南。如果 OEM 从 AOSP 源代码构建系统映像,则他们可以用 AOSP 提供的 GSI 替换他们构建的系统映像。但是,OEM 不需要一次性完成最后一步(按原样使用 GSI)。
步骤 1. 为 OEM 的系统映像继承 generic_system.mk (OEM GSI)
通过继承 generic_system.mk
(在 Android 11 中名为 mainline_system.mk
,在 AOSP 中重命名为 generic_system.mk
),系统映像 (OEM GSI) 包含 AOSP GSI 拥有的所有文件。OEM 可以修改这些文件,以便 OEM GSI 除了 AOSP GSI 文件外,还可以包含 OEM 专有文件。但是,不允许 OEM 修改 generic_system.mk
文件本身。
图 3. 为 OEM 的系统映像继承 generic_system.mk
步骤 2. 使 OEM GSI 具有与 AOSP GSI 相同的文件列表
在此阶段,OEM GSI 不能有其他文件。OEM 的专有文件必须移出到 system_ext
或 product
分区。
图 4. 将添加的文件移出 OEM GSI
步骤 3. 定义允许列表以限制 OEM GSI 中修改的文件
要检查修改的文件,OEM 可以使用 compare_images
工具,并将 AOSP GSI 与 OEM GSI 进行比较。从 AOSP lunch 目标 generic_system_*
获取 AOSP GSI。
通过使用 allowlist
参数定期运行 compare_images
工具,您可以监视允许列表之外的差异。这可以防止需要对 OEM GSI 进行其他修改。
图 5. 定义允许列表以减少 OEM GSI 中修改的文件列表
步骤 4. 使 OEM GSI 具有与 AOSP GSI 相同的二进制文件
清理允许列表使 OEM 可以使用 AOSP GSI 作为其自身产品的系统映像。要清理允许列表,OEM 可以放弃他们在 OEM GSI 中的更改,或者将其更改向上游到 AOSP,以便 AOSP GSI 包含他们的更改。
图 6. 使 OEM GSI 具有与 AOSP GSI 相同的二进制文件
为 OEM 定义 SSI
在构建时保护 /system 分区
为了避免 /system
分区中的任何产品特定更改并定义 OEM GSI,OEM 可以使用名为 require-artifacts-in-path
的 makefile 宏,以防止在调用宏后声明任何系统模块。请参阅创建 makefile 并启用工件路径检查示例。
OEM 可以定义一个列表,以允许产品特定的模块临时安装在 /system
分区中。但是,该列表必须为空,才能使 OEM GSI 对 OEM 的所有产品通用。此过程用于定义 OEM GSI,并且可以独立于 AOSP GSI 的步骤。
强制执行产品接口
为了保证 /product
分区已解绑,OEM 可以通过为原生模块设置 PRODUCT_PRODUCT_VNDK_VERSION:= current
,并为 Java 模块设置 PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE:= true
,来确保他们的设备强制执行产品接口。如果设备的 PRODUCT_SHIPPING_API_LEVEL
大于或等于 30
,则会自动设置这些变量。有关详细信息,请参阅强制执行产品分区接口。
使 /system_ext 分区通用
/system_ext
分区在不同设备之间可能有所不同,因为它可能具有设备特定的、系统捆绑的模块。由于 SSI 由 /system
和 /system_ext
分区组成,因此 /system_ext
分区的差异会阻碍 OEM 定义 SSI。OEM 可以拥有自己的 SSI,并通过消除任何差异并使 /system_ext
分区通用,在多个设备之间共享该 SSI。
本节提供有关使 /system_ext
分区通用的建议。
在系统分区中公开隐藏的 API
许多产品特定的应用无法安装在 product 分区中,因为它们使用了在 product 分区中被禁止的隐藏 API。要将设备特定的应用移动到 product 分区,请移除隐藏 API 的使用。
从应用中移除隐藏 API 的首选方法是找到替代的公共或系统 API 来替换它们。如果没有 API 可以替换隐藏 API,OEM 可以为 AOSP 贡献来为其设备定义新的系统 API。
或者,OEM 可以通过在 /system_ext
分区中创建自己的 Java SDK 库 来定义自定义 API。它可以使用系统分区中的隐藏 API,并可以为 product 或 vendor 分区中的应用提供 API。OEM 必须冻结面向产品的 API以实现向后兼容性。
包含所有 APK 的超集,并跳过每个设备的某些软件包安装
某些与系统捆绑在一起的软件包在设备之间并不通用。将这些 APK 模块解绑以将它们移动到 product 或 vendor 分区可能很困难。作为临时解决方案,OEM 可以使 SSI 包含所有模块,然后使用 SKU 属性 (ro.boot.hardware.sku
) 过滤掉不需要的模块。要使用过滤器,OEM 可以覆盖框架资源 config_disableApkUnlessMatchedSku_skus_list
和 config_disableApksUnlessMatchedSku_apk_list
。
对于更精确的设置,声明一个广播接收器,该接收器禁用不必要的软件包。广播接收器调用 setApplicationEnabledSetting
以在接收到 ACTION_BOOT_COMPLETED
消息时禁用软件包。
定义 RRO 而不是使用静态资源叠加
静态资源叠加会操纵叠加的软件包。但是,它可能会阻碍定义 SSI,因此请确保 RRO 的属性已打开并正确设置。通过如下设置属性,OEM 可以将所有自动生成的叠加层都作为 RRO。
PRODUCT_ENFORCE_RRO_TARGETS := *
PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS := # leave it empty
如果需要详细的配置,请手动定义 RRO,而不是依赖于自动生成的 RRO。有关详细信息,请参阅运行时资源叠加 (RRO)。OEM 还可以定义依赖于系统属性的条件 RRO,方法是使用 android:requiredSystemPropertyName
和 android:requiredSystemPropertyValue
属性。
常见问题解答 (FAQ)
我可以定义多个 SSI 吗?
这取决于设备(或设备组)的通用性和特性。OEM 可以尝试使 system_ext
分区通用,如使 system_ext 分区通用中所述。如果设备组有很多差异,那么最好定义多个 SSI。
我可以为 OEM GSI 修改 generic_system.mk (mainline_system.mk) 吗?
不可以。但是 OEM 可以为 OEM GSI 定义一个新的 makefile,该 makefile 继承 generic_system.mk
文件并使用新的 makefile 代替。有关示例,请参阅强制执行产品分区接口。
我可以从 generic_system.mk 中删除与我的实现冲突的模块吗?
不可以。GSI 具有可启动和可测试模块的最小集合。如果您认为某个模块不是必不可少的,请提交错误报告以更新 AOSP 中的 generic_system.mk
文件。