Android核心主题分享 - 架构

主要内容来自Source Android的主题内容架构篇https://source.android.com/docs/core/architecture?hl=zh-cn
关键字:AOSP、Source、核心主题、架构、HAL(HIDL)、配置、设备叠加层(DTO)、VNDK、AIDL、Binder、引导加载程序(BootLoader)、分区、16KB

00. 总览

架构篇主要罗列了内核与AOSP、AOSP与供应商(vendor)、VNDK与分区之间的耦合关系与架构设计原理,为开发者学习了解AOSP提供一个整体的方向性指南。

Android8:

  • 区分新旧HAL
  • Android 8.x 强制将 SoC 专属组件从 system 分区分隔到了 vendor 分区,不会为从 Android 构建系统构建的原始设备制造商 (OEM) 专属组件提供专用空间

Android9:

  • 在开发 Android 9 版本中,Android 团队发现旧的 bootloader_boot_reason_prop 中内容会急剧增加,并且无法在系统运行时重写
  • Android 9 开始支持构建 product 分区,让您可为不同 product.img 映像提供的多个软件 SKU 使用同一个系统映像。product 分区适用于软件 SKU,而 odm 分区适用于硬件 SKU

Android10:

  • 开始支持稳定的AIDL逐步替换HIDL
  • 之前使用ConfigStore HAL存储系统属性,开始存储在 build.prop,支持将系统属性作为API实现
  • 针对XML的标准化配置,Config File Schema API
  • VINF使用模块化(碎片化)声明+构建期汇总的方式
  • 在 Android 10 及更低版本中,vendor 分区可以关联到 system 分区中的 VNDK 库,但无法链接到 system 分区中的其他库。product 分区中的原生模块可以关联到 system 分区中的任何库
  • 在 Android 10 中,根文件系统已不再包含在 ramdisk.img 中,而是合并到了 system.img(即在创建 system.img 时始终将 BOARD_BUILD_SYSTEM_ROOT_IMAGE 视为已设置)
  • 从 Android 10 起主要以 APEX 形式提供VNDK/LLNDK,系统对外公布的一组“稳定 ABI”库,例如 /apex/com.android.vndk.v{本}/lib[64]、/apex/com.android.runtime/lib[64]、vendor 和(Android 11 起)product 只允许依赖这些集合
  • Android 10 支持使用 Android 构建系统来构建 odm 分区

Android11:

  • 引入了在 Android 中使用 AIDL 实现 HAL 的功能
  • 搭载 Android 11 及更高版本的设备可以支持动态分区。使用此分区系统,您可以在无线下载 (OTA) 更新期间创建、销毁分区或者调整分区大小。借助动态分区,供应商无需担心各个分区(例如 system、vendor 和 product)的大小。取而代之的是,设备会分配一个 super 分区,其中的子分区可动态调整大小
  • 在 Android 11 及更高版本中,product 和 vendor 分区可以关联到 system 分区中的 VNDK 库,但无法链接到 system 分区中的其他库
  • Android 11 取消了 product 分区捆绑,使其独立于 system 和 vendor 分区

Android12:

  • 开始引入ACK(Android通用内核)通用内核保持独立更新与稳定,与SOC等三方供应商交互的内容独立一个GKI(通过ACK编译的内核映像)
  • AOSP正式把 Rust 作为平台语言之一,引入了完整的构建链和运行库
  • 强制开启Virtual A/B系统,OTA时Virtual A/B 写到快照(COW),不破坏当前已运行的系统视图
  • 从 Android 12 开始,bootconfig 功能取代了 Android 11 及更低版本使用的 androidboot.* 内核 cmdline 选项。对于需要传递给 Android 用户空间的 androidboot.* 参数,我们可以使用 bootconfig 而非内核命令行
  • 在 Android 12 中,通用 boot 映像(称为通用内核映像 (GKI))包含通用的 ramdisk 和 GKI 内核

Android13:

  • 内核编译产物:init_boot 第一阶段 init 的独立 ramdisk
  • Android 13 的设备,通用 ramdisk 将从 boot 映像中移除,并放置在单独的 init_boot 映像中。此更改会使 boot 映像仅保留 GKI 内核
  • Android 13 及更高版本的设备都必须包含 system_dlkm 分区

Android15:

  • 不再发展和依赖VNDK
  • Android 15 及更高版本支持使用 16 KB ELF 对齐方式构建 Android,该对齐方式自 android14-6.1 开始适用于 4 KB 和 16 KB 内核

01. 内核

11. 架构图

image.png

总体思路是拆分Android通用内核,从工程化与OTA方面进行解耦。从Android12开始引入ACK(Android通用内核)通用内核保持独立更新与稳定,与SOC等三方供应商交互的内容独立一个GKI(通过ACK编译的内核映像)。内核编译为system_dlkm与vendor_dlkm等多个分区,其中system_dlkm分区配合GKI可以实现与厂商模块(vendor_dlkm)的解耦与独立OTA。

12. 内核编译产物

system_dlkm:系统侧提供的可加载内核模块(GKI 扩展等)。
vendor_dlkm:厂商/SoC 侧提供的可加载内核模块。
可选:odm_dlkm、product_dlkm(是否存在取决于设备配置,动态分区里可创建,用于把模块按 ODM 或产品层归属拆分)。
与内核启动相关的其他分区(非模块分区,不存.ko文件):
boot:内核镜像 + (部分设备上)通用 ramdisk。
vendor_boot:厂商 ramdisk(早期启动所需的厂商组件)。
init_boot(Android 13+):第一阶段 init 的独立 ramdisk。
dtbo:设备树叠加(由引导链应用)。
recovery:恢复模式的内核与 ramdisk。
这些非模块分区在文件系统里面通常表现为块设备,如

boot:/dev/block/by-name/boot[_a|_b]
内含内核镜像和(按机型/版本)ramdisk、dtb 等,供 bootloader 直接加载到内存。
vendor_boot:/dev/block/by-name/vendor_boot[_a|_b]
厂商 ramdisk(早期启动用)。
init_boot(Android 13+):/dev/block/by-name/init_boot[_a|_b]
第一阶段 init 的通用 ramdisk。
dtbo:/dev/block/by-name/dtbo[_a|_b]
设备树叠加镜像,bootloader 在加载内核/DTB 时应用。
vbmeta、vbmeta_system、vbmeta_vendor:/dev/block/by-name/vbmeta*[_a|_b]
AVB 验证元数据,不是文件系统。
recovery(仅部分机型/非 A/B):/dev/block/by-name/recovery
恢复模式用的 boot 镜像。
说明:部分机型还有 dtb 分区;也有机型把 dtb 放在 boot 镜像内部的 dtb 区域。

02. HAL

硬件抽象层 (HAL) 是一种抽象层,其中包含硬件供应商要实现的标准接口。借助 HAL,硬件供应商可以实现设备专属的较低级别功能,而不会影响或修改较高级别层中的代码。Android8开始新旧HAL之间区分。

21. HIDL

HIDL 旨在用于进程间通信 (IPC)。使用 HDL 创建的 HAL 称为绑定式 HAL,因为它们可以使用 Binder 进程间通信 (IPC) 调用与其他架构层进行通信。绑定式 HAL 在独立于使用它们的客户端的进程中运行。对于必须与进程相关联的代码库,还可以使用透传模式(在 Java 中不受支持)。从Android10开始,HIDL已经废弃,取而代之的是AIDL。

  • HAL会生成hidl的跨进程服务,libhw开头的就是hidl跨进程通信框架
  • hwservicemanager是独立的原生进程,不在system_server里运行
  • HIDL专用的“服务管理器”,管理的是/ dev/hwbinder 这个Binder上下文
  • Android上有多套“服务管理器”,各自独立进程、管理不同Binder域:
    • servicemanager:系统域(/dev/binder),系统服务和App用。进程通常是/system/bin/servicemanager。
    • vndservicemanager:厂商/供应商域(/dev/vndbinder),主要给AIDL版HAL用。进程通常是/vendor/bin/vndservicemanager。(给HAL使用的AIDL也在里面??)
    • hwservicemanager:HIDL域(/dev/hwbinder),给HIDL HAL用。进程是/system/bin/hwservicemanager

22. Audio(音频)等HAL实例

Binderized

HIDL/AIDL “binderized” 音频 HAL 服务。某些设备会提供 android.hardware.audio@x.y-serviceandroid.hardware.audio.effect@x.y-service 这类独立进程,将音频 HAL 以 Binder 服务暴露。若采用这种形态,audioserver 通过 hwbinder/aidl 与该服务通信,但在大量商用设备上,核心音频仍常见为“同进程”加载,binderized 音频 HAL并不普遍。比如蓝牙音频往往是独立的HIDL服务,进程名一般类似 /vendor/bin/hw/android.hardware.bluetooth.audio-service,audioserver/蓝牙栈通过 Binder 与其通信,用于蓝牙传输侧数据/控制。

Passthrough(dlopen)

binderized(推荐/新 HAL):HAL 在独立进程,通过 Binder 与 audioserver/系统服务通信。
passthrough(历史/个别场景为延迟考虑):HAL 以动态库被服务进程 dlopen,服务到 HAL 不需要 IPC,但 App 到服务仍是 IPC。Treble 之后新 HAL 基本要求 binderized。其中audio就主要是dlopen的方式针对HAL进行管理:
audioserver(核心音频服务进程)承载 AudioFlinger、AudioPolicyService、SoundTriggerHwService、AAudioService 等。加载并调用“核心音频 HAL”以及“音效 HAL”动态库,库文件位于 /vendor/lib(64)/hw/,如:
audio.primary.<board>.so(主设备)
audio.usb.<board>.so、audio.a2dp.<board>.so、audio.r_submix.<board>.so 等
audio_effects/.so 或 effect/.so(音效 HAL)
这些库是传统 C 接口(hardware/libhardware/include/hardware/audio*.h、effect.h),通过 dlopen/dlsym 进 audioserver,运行在 audioserver 进程空间中。

HIDL独立进程

system          182      1 10777116  2624 binder_thread_read  0 S android.hardware.keymaster@4.0-service.optee
system          215      1 10830880  3164 binder_thread_read  0 S android.hardware.graphics.allocator@4.0-service
system          225      1 10981616  4540 binder_thread_read  0 S android.hardware.graphics.composer@2.1-service
audioserver     264      1   29404    600 binder_thread_read  0 S android.hardware.audio.service
bluetooth       265      1 11141732  3208 binder_thread_read  0 S android.hardware.bluetooth@1.0-service
media           266      1   17148    708 binder_thread_read  0 S android.hardware.cas@1.2-service
media           267      1 10795608  2116 binder_thread_read  0 S android.hardware.drm@1.3-service.clearkey
media           268      1   20356    608 binder_thread_read  0 S android.hardware.drm@1.3-service.widevine
system          269      1 10775916  2196 binder_thread_read  0 S android.hardware.gatekeeper@1.0-service.optee
system          270      1 10775400  2404 do_epoll_wait       0 S android.hardware.health@2.1-service
system          271      1 10792576  2292 binder_thread_read  0 S android.hardware.sensors@1.0-service
system          272      1 10776492  2380 binder_thread_read  0 S android.hardware.weaver@1.0-service
wifi            273      1 10796732  3412 binder_thread_read  0 S android.hardware.wifi@1.0-service
system          274      1 10774876  2660 binder_thread_read  0 S android.hardware.lights-service.rockchip
root            280      1 10774828  2724 binder_thread_read  0 S android.hardware.power-service.rockchip

HIDL与VND服务

system          141      1 10774964  2776 do_epoll_wait       0 S servicemanager
system          142      1 10776204  2872 do_epoll_wait       0 S hwservicemanager
system          143      1 10774868  1856 do_epoll_wait       0 S vndservicemanager

03. 配置

31. 系统属性

系统属性是存储在 build.prop 全局字典中的字符串键值对。系统属性是易于使用且性能开销较低的系统级资源。使用系统属性时,即使系统属性在多个进程之间共享,您也无需使用进程间通信 (IPC)。不过,系统属性与全局变量类似,若被滥用便会造成损害。滥用系统属性可能会导致出现安全漏洞以及应用无法被用户访问等问题。在使用系统属性存储配置信息之前,请考虑其他配置选项。Android10AOSP 使用 ConfigStore HAL 存储系统属性,ConfigStore HAL 已废弃,请勿继续使用。

将系统属性作为API实现

Android10版本开始,跨分区访问的系统属性已架构化为 Sysprop 说明文件,并且用于访问属性的 API 会生成为 C++ 和 Rust 的具体函数及 Java 类。这些 API 使用起来更方便,因为访问时不需要魔法字符串(如 ro.build.date),而且它们可以是静态类型的。在构建时也会检查 ABI 稳定性;如果发生不兼容的更改,build 会中断。此检查充当各个分区之间明确定义的接口。这些 API 还可以在 Rust(Android12开始AOSP支持rust编译)、Java 和 C ++ 之间实现一致性。
现在可以使用 Sysprop 说明文件定义 sysprop_library 模块。 sysprop_library 用作 C++、Java 和 Rust 的 API。对于 sysprop_library 的每个实例,构建系统会在内部生成一个 rust_library、一个 java_library 和一个 cc_library,详情参考链接

添加系统属性

使用以下格式,并采用 snake_case 蛇形命名法

[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]

对元素 prefix 使用 “”(省略)、ro(用于仅设置一次的属性)或 persist(用于在重新启动后保持不变的属性)。
在构建时使用 makefile 变量设置属性。从技术上讲,这些值将被内置到 {partition}/build.prop。然后,init 会读取 {partition}/build.prop 以设置属性。有两组此类变量:PRODUCT_{PARTITION}PROPERTIES 和 TARGET{PARTITION}PROP。
PRODUCT
{PARTITION}_PROPERTIES 包含属性值的列表。语法为 {prop}={value} 或 {prop}?={value}。
{prop}={value} 是常规赋值,可确保将 {prop} 设置为 {value};每种属性只能有一个此类赋值。
{prop}?={value} 是可选赋值;仅在没有任何 {prop}={value} 赋值时,{prop} 才会设为 {value}。如果存在多个可选赋值,则第一赋值为优先选项。

同一个键不应在多个分区的 build.prop 里重复定义;属性一旦被首次加载设置,后续重复加载会被忽略。
从 Android 10 起属性“分区化”更严格:ro.product.* 放到 /product,ro.vendor.* 放到 /vendor,ro.odm.* 放到 /odm,ro.system_ext.* 放到 /system_ext,通用的 ro.build.* 多由 /system 提供

Selinux规则

在添加系统属性时,确定属性的名称,并将其与 SELinux 属性上下文相关联。如果没有合适的现有上下文,就创建新的上下文。该名称在访问属性时使用;属性上下文用于根据 SELinux 来控制访问权限。名称可为任何字符串,但 AOSP 建议您采用结构化格式,以使它们更清晰。系统属性是否允许写入(添加)是SELinux限制的,详情查看aosp system/sepolicy/private/property_contexts。

32. 实现 Config File Schema API

Android 平台包含许多用于存储配置数据(如音频配置)的 XML 文件。很多 XML 文件都位于 vendor 分区中,但读取它们的操作在 system 分区中进行。在这种情况下,XML 文件的架构充当这两个分区之间的接口,因此您必须明确指定架构,并且必须以向后兼容的方式改进该架构。
Android 10 之前的版本中,Android 平台没有提供需要指定和使用 XML 架构的机制,也没有提供防止架构中出现不兼容更改的机制。Android 10 提供了这种机制,称为 Config File Schema API。该机制由一个名为 xsdc 的工具和一个名为 xsd_config 的 build 规则组成。

主要流程简化归纳为:

  1. 平台/模块作者编写 .xsd,定义配置文件的结构与约束。
  2. 在 Android.bp 编写 xsd_config,配置 xsdc 如何生成解析代码及它的 API 属性。
  3. 构建时 Soong 调用 xsdc,生成 Java 源并编译为库。
  4. 系统服务/组件依赖该库,在开机或运行时按既定路径读取来自 system/vendor 等分区的 XML,使用生成的解析器进行严格校验与解析,再执行合并/覆盖策略。
  5. 版本演进时,通过更新 .xsd 并走 API 审查/兼容测试,确保对旧配置保持可解析或有清晰的升级规则。

04. 设备树叠加层(DTBO)

主要面向BSP和内核开发者。

  • 通过DTBO可以复用一套“通用内核+通用 SoC 设备树”,用不同的 DTBO 来适配不同的板卡/SKU/运营商版本,减少维护多份大DTB的重复。
  • 便于独立更新:Android 把 DTBO 打在单独的 dtbo 分区里,AVB 验证后,OTA 时可以只更新 DTBO 而不改内核或 boot 分区。
  • 启动灵活:bootloader 可根据硬件ID、SKU、插拔的配件等,动态选择一个或多个叠加层。

启动流程简化版:

  • 基础 DTB:放在 boot 分区的内核镜像里(或单独的 dtb 分区),描述 SoC 通用部分。
  • 叠加层 DTBO:打包在 dtbo 分区(原始镜像,非文件系统)。一个 dtbo.img 里可包含多个条目(不同板型/版本)。
  • bootloader:
    • 读取并验证 dtbo(AVB)。
    • 根据设备识别信息选对条目。
    • 调用 overlay 例程把 DTBO 应用到基础 DTB,得到最终 FDT。
    • 把最终设备树连同内核一起加载到内存,交给内核启动。

05. VNDK(供应商NDK)

供应商原生开发套件 (VNDK) 是一组库,可供供应商分区或产品分区中的其他库或二进制文件在 dlopen 运行时期间使用。从Android 15开始不再“发展/依赖”VNDK,方向是用稳定的跨分区接口(AIDL/Binder)替代“跨分区共享库链接”。今后 vendor 代码应尽量通过稳定 AIDL 服务/HAL与系统交互,仅链接一小撮长期稳定的 LLNDK/NDK基础库(如 bionic libc/libm/libdl 等)。很多设备仍会带着旧的 VNDK APEX作兼容,但请把工程迁移到不依赖 VNDK 的模式。

51. VNDK概念

在理想的 Android 8.0 及更高版本环境中,框架进程不加载供应商共享库,所有供应商进程仅加载供应商共享库(和一部分框架共享库),而框架进程与供应商进程之间的通信由 HIDL 和硬件 binder 控制。

这样的环境存在以下可能:来自框架共享库的稳定、公共 API 可能不足以满足供应商模块开发者的需求(尽管 API 在不同的 Android 版本之间会有所变化),要防止出现这种情况,供应商进程需要能够访问一部分框架共享库。此外,由于性能要求可能会导致折中方案,因此必须区别对待某些对响应时间要求严格的 HAL。

SP-HAL

以下部分详细介绍了 VNDK 如何处理适用于供应商的框架共享库以及 Same-Process HAL (SP-HAL)。
LL-NDK 库是已知稳定的框架共享库。它们的开发者致力于保持其 API/ABI 稳定性。
LL-NDK 包含以下库:libEGL.so、libGLESv1_CM.so、libGLESv2.so、libGLESv3.so、libandroid_net.so、libc.so、libdl.so、liblog.so、libm.so、libnativewindow.so、libneuralnetworks.so、libsync.so、libvndksupport.so 和 libvulkan.so。

Same-Process HAL (SP-HAL) 是一组预先确定的 HAL,作为供应商共享库进行实现,并被加载到框架进程中。SP-HAL 由链接器命名空间(控制共享库可见的库和符号)进行隔离。SP-HAL 必须仅依赖于 LL-NDK 和 VNDK-SP。

VNDK-SP 是一部分预定义的符合条件的 VNDK 库。我们会仔细审查 VNDK-SP 库,以确保将 VNDK-SP 库双重加载到框架进程中不会导致问题。SP-HAL 和 VNDK-SP 均由 Google 定义。
以下库是经过批准的 SP-HAL:

libGLESv1_CM_${driver}.so
libGLESv2_${driver}.so
libGLESv3_${driver}.so
libEGL_${driver}.so
vulkan.${driver}.so
android.hardware.renderscript@1.0-impl.so
android.hardware.graphics.mapper@2.0-impl.so

52. VINF(供应商接口)

  • 作用:定义并验证 framework/system 与 vendor/odm 之间的稳定接口契约,保证它们可以独立升级而不互相破坏。
  • 载体文件(位于镜像中,启动和OTA时用于匹配):设备清单 manifest(通常在 /vendor/etc/vintf/manifest.xml 和/或 /odm/etc/vintf/manifest.xml):厂商声明本机实现了哪些 HAL(HIDL/AIDL)、版本、实例名、是否是binderized等。框架兼容矩阵 framework compatibility matrix(通常在 /system/etc/vintf/compatibility_matrix.xml,系统镜像提供):系统声明它需要哪些HAL能力、SEPolicy兼容级别、内核/特性等。有时还会有“vendor compatibility matrix”和“framework manifest”用于反向约束,最终由工具合并校验。
  • 校验工具/时机:checkvintf/libvintf 在OTA阶段和开机早期执行匹配:把“系统侧矩阵”与“设备侧manifest(及可选vendor矩阵/框架manifest)”合并比对,版本不兼容就拒绝升级/拒绝启动。
  • 覆盖范围:
    HAL契约:HIDL版本、AIDL稳定性与哈希、实例名等。
    VNDK版本要求:矩阵里有<vndk>条目,规定系统必须提供哪些VNDK版本;与ro.vndk.version等属性相匹配。
    SEPolicy兼容:规定平台/厂商策略的兼容版本,确保分区独立演进不破坏策略加载。
    内核/特性:可声明最低内核版本或所需特性(在GKI时代多由GKI+模块保证,但VINTF仍可承载约束)。

VINTF有几类文件:

  1. 设备(vendor/odm)manifest:声明设备实现的HAL实例。它是设备相关的,通常位于 device/<vendor>/<device>/(公开设备,如goldfish/cuttlefish/某些Pixel设备)或厂商闭源vendor树中;AOSP主线没有你真实手机的那份。
  2. 框架兼容矩阵(framework compatibility matrix,FCM):系统侧提供,编译进/system/etc/vintf/。源码里不是以一份固定XML存在,而是分散在多个仓库(HIDL/AIDL定义处)的“矩阵片段”,构建时汇总生成。
  3. 框架 manifest(framework manifest,可选):只有当系统分区自己提供HIDL/AIDL HAL实例时才需要,很多目标没有这份文件。
    自Android 10起,AOSP普遍改为“碎片化声明 + 构建期汇总”的方式:
  4. 各个HAL/模块在各自目录下放 vintf_fragments/ 或 vintf/ 的小片段XML;
    构建系统用 assemble_vintf(libvintf里的工具)把这些片段合并,生成最终的 manifest/matrix 并安装到相应分区。
    所以你在源码根目录找不到“完整 manifest.xml”,但编译后会在 out/target/product/<target>/{system,vendor}/etc/vintf/ 下看到生成物。

53. APEX

APEX是一种系统级模块打包与交付形式,主要用于把“核心系统组件”拆分成独立、可更新、可回滚的只读模块。它在Android 10引入,是“Android Mainline”(系统模块可更新)的关键支撑之一。
APEX的目标是让影响早期启动、系统稳定性的组件(如ART运行时、时区数据、媒体框架、conscrypt等)能够在不重刷整机系统镜像的情况下安全更新,同时保证这些模块在运行时仍然以只读、受完整性保护的方式呈现。

APEX的生命周期与运行机制

存放位置(预装)
典型路径:/system/apex、/system_ext/apex、/product/apex。高版本也支持/vendor/apex(用于厂商侧模块,不能通过Play更新,只能OTA)。

激活与挂载
开机早期,apexd扫描并验证APEX包,选择同名模块的最高有效版本,loop-mount它的payload镜像到/apex/<module_name>@<version>,并设置bind mount到/apex/<module_name>。
模块内容以只读方式呈现,里面的bin、lib、etc等被系统各组件使用,就像它们位于系统镜像的一部分。

更新与回滚
更新的APEX通过PackageManager以“分阶段安装”(staged install)写入/data/apex/sessions,并在下次重启由apexd激活。
支持原子性和回滚:多个APEX可以作为一个会话一致性更新;引导失败可自动回滚到旧版本(结合checkpoint/rollback机制)。

早期引导的“引导型APEX”
某些模块(如com.android.runtime/ART)被标记为bootstrap apex,必须最早激活,以便系统能运行Java部分。这类APEX通常只在/system/apex预装。

APEX状态

[apexd.status]: [ready]
[init.svc.apexd]: [stopped]
[init.svc.apexd-bootstrap]: [stopped]
[init.svc.apexd-snapshotde]: [stopped]
[init.svc_debug_pid.apexd]: []
[init.svc_debug_pid.apexd-bootstrap]: []
[init.svc_debug_pid.apexd-snapshotde]: []
[ro.boottime.apexd]: [3639749315]
[ro.boottime.apexd-bootstrap]: [1674370493]
[ro.boottime.apexd-snapshotde]: [3839844044]

整体图景(一次正常开机)

init 在早期启动 apexd-bootstrap → 挂载/激活所有 APEX 模块 → 置位 apexd.status=ready → init 开始从每个 /apex/<name>/etc/init/ 导入 rc,并继续后续启动。
随后按需要短暂启动 apexd、apexd-snapshotde 执行善后或与 OTA/快照相关的任务,完成后即退出。
因为这些服务是“一次性/短跑型”,所以开机完成后它们通常都是 stopped,这是正常现象。
各属性含义

apexd 的状态属性。ready 表示本次开机需要激活的 APEX 都已经挂载就绪,init 可以安全地导入 APEX 内的 rc 并继续启动阶段。
其他阶段可能见到的值(不同版本略有差异):starting、activated、restarting 等,但常见稳态就是 ready。
init.svc.<service> 是 init 对服务当前状态的标准属性。
apexd 是主守护进程,负责 APEX 包的验证、会话管理、更新与回滚协调、暴露 IApexService(若设备支持可更新 APEX)。
大多数设备上,apexd 在开机早期做完需要的工作后就退出,因而这里是 stopped。日后有更新事务时才会被再拉起。
在“flattened APEX”的机型上,apexd 甚至可能基本不常驻(你当前的设备就是这种情况)。
apexd 的“引导阶段”助手,负责最早期挂载 APEX(保证 /apex/<name> 可用),完成后立刻退出。所以稳定状态为 stopped。
正是它把 APEX 激活好后设置了 apexd.status=ready。
与 A/B 无缝更新(特别是 Virtual A/B/快照机制)配合的辅助进程,处理与 APEX 相关的快照/回滚清理或准备工作。
多数情况下只在开机阶段短暂运行,完成任务后退出,因而是 stopped。没有启用相关特性时基本是“跑一下就结束”。
这是 init 在“调试包装”模式下暴露的子进程 pid 属性。为空通常表示该服务当前不在运行,或没有以调试包装方式启动。对日常判断无关紧要。
ro.boottime.<service> 是只读属性,记录“该服务第一次被 init 启动”的时间戳,单位为纳秒,基于系统开机时钟(大致可理解为‘自开机起过了多少秒启动的’)。
你这组数值约等于:
apexd-bootstrap ≈ 1.674 s
apexd ≈ 3.640 s
apexd-snapshotde ≈ 3.840 s
它们都存在,说明这些服务在本次开机确实启动过,只是任务完成后已退出。

06. AIDL

61. 结构化AIDL

  • 传统 AIDL 的做法
    • 在 .aidl 文件里只“声明有这么个类型”,比如写一行“parcelable Foo;”。
    • 这叫“前向声明”:编译器只知道有个名叫 Foo 的 Parcelable,但完全不知道它里面有哪些字段、怎么序列化。
    • 真正的读写由你自己在 Java/C++ 类里实现 writeToParcel/readFromParcel。你想改字段、改写法都可以,AIDL 看不见、也管不了。
    • 结果就是“非结构化”:没有一份被 AIDL 语言层面约束的“数据结构定义”。不同版本、不同语言端可能各自用了不同的序列化细节,升级很容易破兼容。
  • 稳定(也叫结构化)AIDL 的做法
    • 要在 .aidl 里“把这个 Parcelable 的字段一项项写清楚”,比如“parcelable Foo { int id; String name; @nullable String note; }”。
    • 这叫“显式定义”:AIDL 语言本身就承载了“这个类型的结构是什么”,工具会按这个结构生成一致的读写代码(跨 Java/C++/NDK 后端都一样)。
    • 因为有了清晰的“结构图”,系统就能制定并检查“稳定性”规则(比如只能新增字段、不能删/改类型/改含义、不能随便改序列化顺序等),从而保证不同版本之间的线上的兼容。
  • “不允许非结构化 Parcelable”是什么意思
    • 指的是:在“稳定 AIDL”接口里,不能再用那种只写一行“parcelable Foo;”然后靠自己手写 writeToParcel/readFromParcel 的自定义类。
    • 必须改为“结构化 Parcelable”,即在 .aidl 里定义字段,让 AIDL 生成序列化代码,这样才可被认定为“稳定”。
  • 为什么要这样做
    • 非结构化做法没有统一的“线上的协议”,升级时很容易因为某次改动(新增/删除/重命名字段、换写法)造成老客户端/老服务端读不懂新数据。
    • 结构化 AIDL 把“协议”固化在 .aidl 里,编译器能帮你守规则,后端生成的代码也统一,跨版本、跨语言更稳。

62. AIDL后端

AIDL 的后端如下所示:
Android 12(S)开始,AOSP正式把 Rust 作为平台语言之一,引入了完整的构建链和运行库;AIDL 也新增了 Rust 后端,能为 AIDL 接口生成 Rust 客户端/服务端桩代码。Android 13/14/15 又进一步扩大了 Rust 的使用范围和成熟度。
不需要“Rust 运行时”这种额外环境。Rust 编译产物是原生 ELF,可直接在 Android 的 Linux 内核 + Bionic libc 上运行,像 C/C++ 一样部署。所谓“支持 Rust”主要是指平台自带了编译工具链、标准库、Binder 绑定等基础设施。)

后端 语言 API Surface 构建系统
Java Java SDK/SystemApi(稳定) 全部
NDK C++ libbinder_ndk(稳定
) aidl_interface
CPP C++ libbinder(不稳定) 全部
Rust Rust libbinder_rs(稳定*) aidl_interface

63. 适用于HAL的AIDL

https://source.android.com/docs/core/architecture/aidl/aidl-hals?hl=zh-cn
Android11 中引入了在 Android 中使用 AIDL 实现 HAL 的功能,从而可以在不使用 HIDL 的情况下实现 Android 的部分代码。在可能的情况下,应将 HAL 转换为仅使用 AIDL(当上行 HAL 使用 HIDL 时,必须使用 HIDL)。
如果 HAL 使用 AIDL 在框架组件(例如 system.img 中的组件)和硬件组件(例如 vendor.img 中的组件)之间进行通信,必须使用稳定的 AIDL。不过,如需在分区内进行通信(例如从一个 HAL 到另一个 HAL),则对需要使用的 IPC 机制没有任何限制。

设计初衷

AIDL 出现在 HIDL 之前,而且在 Android 框架组件之间或应用内等其他很多地方都有使用。现在,由于 AIDL 具备了稳定性支持,所以能够仅使用一个 IPC 运行时环境来实现整个堆栈。此外,AIDL 的版本控制系统也优于 HIDL。 AIDL 的一些优势如下:

  1. 因为仅使用一种 IPC 语言,所以意味着只需了解、调试、优化和保护一个运行时环境。
  2. AIDL 可为接口所有者提供内建的版本控制机制。
  3. 所有者可以将方法添加到 Parcelable 的接口或字段的末尾。这意味着可以在持续多年的开发过程中简化对代码的版本控制,并逐年降低产生的开销(可以就地修改类型,而且更新接口版本不需要新增额外的库)。
  4. 扩展接口可以在运行时附加,而不是在类型系统中附加,因此无需将下游扩展 rebase 到新版接口上。
  5. 如果现有 AIDL 接口的所有者选择使其稳定化,此类接口可以直接沿用。而在以前,这种情况下必须用 HIDL 创建接口的完整副本。

扩展接口

HIDL:把“扩展”体现在类型系统里。每次增加能力都要发布新版本的接口(如 @1.0、@1.1 的 IFoo),客户端要按新类型编译并在运行时协商/cast 到更高版本;厂商侧要实现并注册新版本的 HAL,更新 VINTF manifest。也就是说下游扩展通常需要“rebase”到新版接口。
AIDL:提供“运行时扩展”机制。基础接口保持不变(其稳定哈希也不变),在运行时通过 Binder 的 getExtension 返回一个附加的 Binder 对象,它实现新增的 AIDL 接口;客户端如果发现扩展存在就用,否则继续使用基础接口。扩展不是通过“改基础接口类型”来体现,而是作为可选的附加接口在运行时协商,因此不需要把下游代码 rebase 到新版基础接口。
它是怎么实现的

基础稳定 AIDL 接口一旦跨分区发布,就不轻易改(因为哈希受 VINTF 校验约束)。
新能力用单独的 AIDL 接口定义(可以是平台扩展或厂商扩展),服务端在 asBinder().getExtension(C++/NDK/Java/Rust均有相应API)里返回该扩展的 Binder。
客户端在运行时调用 getExtension,如果拿到扩展就启用新能力;拿不到则优雅降级。扩展对象的生命周期与基础 Binder 绑定。

64. Binder通信

使用 Android 接口设计语言 (AIDL) 定义使用 binder 进行 IPC 的编程接口。
关注Binder的线程池之间引发的死锁问题:
https://source.android.com/docs/core/architecture/ipc/binder-threading?hl=zh-cn

image.png

07. 引导加载程序(BootLoader)

引导加载程序是供应商专有的映像,负责在设备上启动内核。引导加载程序会监护设备状态,负责初始化可信执行环境 (TEE) 以及绑定其信任根。引导加载程序还会在将执行工作移到内核之前先验证 bootrecovery 分区的完整性。

71. 规范化启动原因

启动原因

引导加载程序使用专用的硬件和内存资源来确定设备重新启动的原因,然后将 androidboot.bootreason=<reason> 添加到用于启动设备的 Android 内核命令行中,以传达这一判定。然后,init 会转换此命令行,使其传播到 Android 属性 bootloader_boot_reason_prop (ro.boot.bootreason) 中。对于发布时搭载 Android 12 或更高版本(内核版本为 5.10 或更高版本)的设备中,androidboot.bootreason=<reason> 添加到了 bootconfig,而非内核命令行中。

规范化启动原因格式

在 Android 9 中,bootloader_boot_reason_prop 的规范化启动原因格式使用以下语法:<reason>,<subreason>,<detail>…

  • 向 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 中的内容

72. 实现 bootconfig

内核和 Android 用户空间都必须支持 bootconfig。
首个提供此支持的版本:Android 12
首个提供此支持的内核版本:12-5.4.xx 内核
请为搭载 12-5.10.xx 内核版本的新设备实现 bootconfig 功能。如果是升级设备,就不需要实现此功能

Linux 开启 bootconfig(内核≥5.10,CONFIG_BOOT_CONFIG=y)后,会在用户态暴露一个只读的 /proc/bootconfig,里面是结构化的键值内容。Android 的 init 就是从这里读 androidboot.* 键。
具体流程是:内核在早期从引导镜像中提取的一段配置块(通常附加在 ramdisk/initramfs 末尾的“Bootconfig 区”),解析进内核的数据结构后再通过 /proc/bootconfig 提供给用户态查看与读取。


image.png

73. AVB属性

AVB是“Android Verified Boot”(2.0)的实现,核心作用是对引导镜像和各系统分区的完整性、来源和版本进行加密校验,并在启动时强制执行或告警,防止被篡改与回滚。
它不等同于“Secure Boot”(SoC厂商的硬件级安全启动)。两者是上下游关系:Secure Boot负责“ROM→引导加载程序”的可信根,AVB负责“引导加载程序→内核/各分区”的验证。合起来构成完整的信任链。

verifiedbootstate

ro.boot.verifiedbootstate
取值:green | yellow | orange | red
含义:Verified Boot 状态
green:引导加载程序处于锁定状态,使用受信任 OEM 密钥验证通过
yellow:设备锁定,但用的是非出厂信任集合中的密钥(用户接受了自有密钥)
orange:设备解锁(不强制验证)
red:验证失败(通常会显示红屏警告并拒绝正常引导)

引导加载程序执行 AVB 验证(签名、哈希、回滚索引、锁定状态等),根据结果判定为 green/yellow/orange/red。

dm-verity

全称 device-mapper verity,内核源码在上游Linux的drivers/md(泛用,不仅用于Android)。
作用是对只读块设备建立Merkle哈希树映射,读时验证数据与“根哈希”一致,发现不一致可直接返回I/O错误(enforcing/EIO)或仅记录(logging)。
在Android中,Bootloader通过AVB验证vbmeta并提供各分区的哈希根与模式,内核启动后由fs_mgr/dm-verity把system、vendor等分区以“受校验”的方式挂载。

硬件安全启动→Bootloader验证(AVB)→内核运行时块级校验(dm-verity)

dm-verity是“块级完整性”机制(Linux内核通用)。
AVB是“引导期真实性+反回滚”机制(Android规范,Bootloader实现)。
Secure Boot是“硬件级引导可信”机制(平台通用,先于Android)。

08. 分区

分区是块设备或逻辑卷(如 dynamic partitions 下的 system、vendor、product…),只有“包含文件系统”的分区才会被挂载到某个挂载点(目录)。很多分区根本不会挂载进根文件系统。

81. 标准分区

  • boot 分区:此分区包含一个内核映像,使用 mkbootimg 创建。您可以使用虚拟分区直接刷写任意映像,而无需刷写新的 boot 分区。 此分区还包含在 Android 13 之前发布的设备中的通用 ramdisk。

    • kernel:kernel 虚拟分区通过将新内核映像写入旧内核映像来覆盖内核(zImagezImage-dtbImage.gz-dtb)。如果提供的开发内核不兼容,则可能需要使用关联的内核模块更新 vendorsystemdtb 分区(如果存在)。

    • ramdisk:ramdisk 虚拟分区通过将新 ramdisk 映像写入旧 ramdisk 映像来覆盖 ramdisk。

    覆盖操作会确定 eMMC 中现有映像的起始位置,并将新映像复制到该位置。新映像(内核或 ramdisk)可能大于现有映像;为了腾出空间,引导加载程序可移动映像之后的数据,或放弃操作并报告错误。

  • init_boot 分区:此分区包含发布时搭载 Android 13 及更高版本的设备的通用 ramdisk。

  • system 分区:此分区包含 Android 框架。

  • odm 分区:此分区包含原始设计制造商 (ODM) 对系统芯片 (SoC) 供应商板级支持包 (BSP) 的自定义设置。利用此类自定义设置,ODM 可以替换或自定义 SoC 组件,并在硬件抽象层 (HAL) 上为板级组件、守护程序和 ODM 特定的功能实现内核模块。此分区是可选的;通常情况下,它用于存储自定义设置,以便设备可以针对多个硬件 SKU 使用单个供应商映像。如需了解详情,请参阅 ODM 分区

  • odm_dlkm 分区:此分区专门用于存储 ODM 内核模块。将 ODM 内核模块存储在 odm_dlkm 分区(而不是 odm 分区)中后,无需更新 odm 分区即可更新 ODM 内核模块。

  • recovery 分区:此分区会存储在 OTA 过程中启动的恢复映像。支持无缝更新的设备可以将恢复映像存储为 bootinit_boot 映像中包含的 ramdisk(而不是单独的映像)。

  • cache 分区:此分区会存储临时数据,如果设备使用无缝更新,则此分区是可选的。cache 分区并非必须可从引导加载程序写入,但必须可清空。此分区大小取决于设备类型和 userdata 上的可用空间。通常,50-100 MB 就足够了。

  • misc 分区:此分区供 recovery 分区使用,大小为 4 KB 或更大。

  • userdata 分区:此分区包含用户安装的应用和数据,包括自定义数据。

  • metadata 分区:此分区用于在设备使用元数据加密时存储元数据加密密钥。大小为 16 MB 或更大。 此分区未经加密,其中的数据不会被拍摄快照,但在设备恢复出厂设置时会被清空。此分区的使用受到严格限制。

  • vendor 分区:此分区包含所有无法分发给 AOSP 的二进制文件。如果设备不包含专有信息,则可以忽略此分区。

  • vendor_dlkm 分区:此分区专门用于存储供应商内核模块。将供应商内核模块存储在 vendor_dlkm 分区(而不是 vendor 分区)中后,无需更新 vendor 分区即可更新内核模块。

  • radio 分区:此分区包含无线装置映像,只有包含无线装置且在专用分区中存储无线装置专用软件的设备才需要此分区。

  • tos 分区:此分区用于存储 Trusty 操作系统的二进制映像文件,仅在设备包含 Trusty 时使用。如需了解详情,请参阅 TOS 分区

  • pvmfw 分区:此分区会存储受保护的虚拟机固件 (pvmfw),即在受保护的虚拟机中运行的第一个代码。如需了解详情,请参阅受保护的虚拟机固件

82. 供应商叠加层

使用供应商叠加层,您可以在设备启动时叠加对 vendor 分区的更改。供应商叠加层是 product 分区中的一组供应商模块,当设备启动时,它们叠加在 vendor 分区上,用于替换和补充现有模块。

当设备启动时,init 进程会完成第一阶段装载并读取默认属性。然后,它会搜索 /product/vendor_overlay/<target_vendor_version>,并且如果满足以下条件,还会在其相应的 vendor 分区目录上装载每个子目录:

/vendor/<overlay_dir> 已存在。
/product/vendor_overlay/<target_vendor_version>/<overlay_dir> 与 /vendor/<overlay_dir> 具有相同的文件上下文。
允许 init 在 /vendor/<overlay_dir> 的文件上下文中进行装载。

如需使用供应商叠加层,内核必须通过设置 CONFIG_OVERLAY_FS=y 来启用 OverlayFS。此外,必须从通用内核 4.4 或更高版本合并内核,或使用 "overlayfs: override_creds=off option bypass creator_cred" 为内核打补丁。

为什么不让组件直接读取 /product 的内容

路径契约和兼容性:大量框架组件、HAL、init 脚本、权限 XML 等约定在 /vendor 下的固定路径(例如 /vendor/etc/...)。改成去 /product 找文件,需要改动广泛的代码与配置,破坏 Treble 以来的清晰分层与稳定接口。
只读与可更新性:Treble 设计希望 vendor 保持稳定、只读,由芯片/设备厂商提供;system/product 由平台侧更新。用 OverlayFS 可以在不改动 /vendor 分区的前提下,通过 /product 叠加“热修复/小改动”,提高系统 OTA 的可维护性。
SELinux 标注与访问控制:很多策略按路径和类型区分(vendor_file 等)。直接把文件放在 /product,其上下文通常是 product_file,很多进程不具备访问权限;通过叠加到 /vendor,要求上下文匹配,最终统一以 vendor_file 规则被访问。
启动时机与一致性:系统在早期按 fstab 挂载并使用 /vendor 的内容。用 OverlayFS把 /product 的覆盖在挂载阶段并入 /vendor,可确保所有读取 /vendor 的组件(含早期 init/rc、HAL)一致地看到同一套内容。
代码透明:对消费者来说路径不变(仍是 /vendor),无需新增“到 /product 兜底”的逻辑,减少复杂性与潜在分歧。

83. Android共享系统映像(SSI)

构建SSI详细步骤参考文档 https://source.android.com/docs/core/architecture/partitions/shared-system-image?hl=zh-cn#enable-ssi
在SSI中提供了一种APK的停用方案:OEM 可以让 SSI 包含所有模块,然后通过 SKU 属性 (ro.boot.hardware.sku) 过滤不需要的模块。为使用过滤器,OEM 可叠加框架资源 config_disableApkUnlessMatchedSku_skus_listconfig_disableApksUnlessMatchedSku_apk_list

如需进行更精确的设置,请声明用于停用不必要的软件包的广播接收器。广播接收器在收到 ACTION_BOOT_COMPLETED 消息时会调用 setApplicationEnabledSetting 以停用该软件包。

84. 强制执行分区接口

从Android11开始,可以控制 product 分区对原生和 Java 接口的访问权限(与 vendor 分区的接口强制工作原理类似)

85. ODM分区

Android 9 开始支持构建 product 分区,让您可为不同 product.img 映像提供的多个软件 SKU 使用同一个系统映像。product 分区适用于软件 SKU,而 odm 分区适用于硬件 SKU。

有了专用的 product 分区和 ODM 分区,您可以使用 system 分区来容纳通用代码(这类代码在许多软件 SKU 之间共享),并使用 vendor 分区来容纳 SoC 专属 BSP 代码(这类代码根据特定的 SoC 在多款设备之间共享)。

维护分区之间的 ABI

odm 分区是 vendor 分区的扩展。在考虑应用二进制接口 (ABI) 稳定性时,请记住以下架构。


image.png
  • odm 和 vendor 分区之间不具有 ABI 稳定性。必须同时升级这两个分区。
  • odm 和 vendor 分区可以相互依赖,但是在没有 odm 分区的情况下,vendor 分区必须正常运行。
  • odm 和 system 之间的 ABI 与 vendor 和 system 之间的 ABI 相同。
    product 分区不得与 vendor 或 odm 分区有直接交互。(这一规则将由 SEpolicy 强制执行。)

86. product分区

许多 OEM 会自定义 AOSP 系统映像,以实现自己的功能并满足运营商的要求。不过,如果进行这类自定义,则无法针对多个软件 SKU 使用单个系统映像。映像必须各不相同,才能支持不同的语言区域、运营商等自定义。如果使用单独的 product 分区来包含自定义项,则可以针对多个软件 SKU 使用单个系统映像(system 分区会托管可在众多软件 SKU 之间共享的通用代码)。vendor 分区会继续托管 SoC 专属 BSP 代码,这类代码可以基于指定 SoC 在多台设备之间共享。

使用单独的分区存在一些弊端,例如,难以管理磁盘空间(应该预留一定的空间满足未来增长的空间需求),以及难以在各分区之间维护稳定的应用二进制接口 (ABI)。在决定使用 product 分区之前,请花些时间考虑一下您的 AOSP 实现的具体情况和可行的缓解策略(例如,在无线下载 (OTA) 更新期间对设备进行重新分区;此操作不是由 Google 来完成,而是由某些 OEM 来完成)。 动态分区是一个很好的解决方案。

product 分区和权限

在 Android 9 及更高版本中,权限和列入白名单过程的更改会影响您在 product 分区上授予特权应用权限的方式。permissions.xml 文件必须与特权应用位于同一个分区中。在特权应用所在的 system 分区中放置 permissions.xml 文件不会将这些权限扩展到 product 分区中的特权应用,即使前者是后者的扩展也不例外。如需详细了解相关权限和列入白名单的操作流程,请参阅特许权限许可名单

多sku如何编译和管理分支

用同一套代码与同一个 manifest 分支,通过不同的“产品目标”(TARGET_PRODUCT)来产出各自的 product 分区;system 和 odm如果在各 SKU 上完全一致,则可以复用同一份镜像。

单一 manifest 分支按平台版本管理(比如 android11-release),不要为每个 SKU拉出独立分支。

在 device 树里为每个 SKU定义一个产品目标:
device/<vendor>/<platform>/common:公共的 BoardConfigCommon.mk、device-common.mk 等
device/<vendor>/<platform>/<skuA>/product_<skuA>.mk
device/<vendor>/<platform>/<skuB>/product_<skuB>.mk
每个 product_*.mk 用 inherit-product 继承 common,再把差异(PRODUCT_PACKAGES、PRODUCT_COPY_FILES、属性、RRO 资源覆盖、/product/vendor_overlay 等)放进去

构建时通过 lunch 选择不同产品目标:
lunch <vendor><skuA>-userdebug → 生成一套镜像;其中 system.img 和 odm.img与其他 SKU相同,product.img不同
lunch <vendor>
<skuB>-userdebug → 同理

需要为某些 SKU拉取额外专有仓库时,用 manifest 的 groups 或本地 manifest,仅在 repo sync 时选择相应组,而不必分支:
在 manifest 里给项目标记 groups="skuA"、"skuB"
repo sync -g default,skuA 只拉取 A 所需的额外仓库

Soong/Make 层面的差异化用法:
在 product_*.mk 设置 PRODUCT_PACKAGES、PRODUCT_SOONG_NAMESPACES、PRODUCT_PROPERTY_OVERRIDES
通过 RRO 放到 /product/overlay、/odm/overlay 或 /vendor/overlay 覆盖框架资源
若需对 /vendor 的配置做小修正,用 /product/vendor_overlay/<target_vendor_version> 的 OverlayFS 叠加

87. 实现 GKI 模块分区(system_dlkm)

GKI 和 GKI 模块可独立于分区的其余部分进行更新,因为 GKI 模块位于超级映像中名为 system_dlkm 的单独动态分区中。GKI 模块由 Google 使用内核构建时密钥对进行签名,并且仅与其构建时使用的 GKI 兼容。GKI 和 GKI 模块之间不具有 ABI 稳定性;为使模块在运行时能够正确加载,必须一同构建和更新 GKI 和 GKI 模块。

在 first_stage_init 期间,system_dlkm 分区会作为只读文件系统装载到 /system_dlkm 中。装载成功后,/system/lib/modules 中指向 /system_dlkm/lib/modules 的符号链接将可用。

然后,供应商进程(如 .rc 脚本)可以根据 modules.load 中指定的顺序加载内核模块。供应商进程必须使用符号链接 /system/lib/modules 加载模块。 供应商进程也可以稍后根据需要加载模块。

88. Trusty 操作系统 (TOS) 分区

Trusty 是 Google 的可信执行环境 (TEE) 操作系统实现,与 Android 并行运行。本文列出了使用 Arm Trustzone 技术提供 TEE 的设备需遵循的规范。如果您的 ARM 设备将 Trusty 用作安全操作系统解决方案,则应按照下面几个部分所述的方式实现引导加载程序。

89. 内核编译产物

不完全是“同一个二进制分别编到多个分区”,而是“同一套内核源码会产出多种不同的内核产物,它们分别被打到不同分区”。具体如下:

  • 内核镜像(kernel image)
    • 从同一源码构建出的内核镜像(Image.gz、vmlinux)放到 boot 分区(以及恢复模式的 recovery 分区会有自己的内核镜像,通常同源但可能不同配置)。
    • 在 GKI 设备上,boot 里的核心内核通常是 Google 提供的 GKI 预编译;厂商一般不改内核本体,而是通过模块扩展。
  • 可加载内核模块(.ko)
    • 同一源码树里的不同模块会按“所有权/更新链路”被打到不同模块分区:
      • system_dlkm:系统侧/GKI 提供的模块(Google 构建,随系统升级)。
      • vendor_dlkm:厂商/SoC 侧的模块(由厂商基于 GKI 的 KMI 头文件构建,随厂商映像升级)。
      • 可选:odm_dlkm、product_dlkm(若设备启用,用于进一步按 ODM/产品层拆分)。
    • 注意:同一个模块不应“重复编到多个分区”。每个模块只放在它的归属分区,避免重复、符号冲突和加载混乱。
  • 设备树叠加
    • 同一内核源码(或设备的 DTS/DTBO 工程)会生成 DTBO,放到 dtbo 分区,由引导链加载。
  • ramdisk 相关
    • vendor_boot、init_boot、boot 里的 ramdisk 内容不是“内核代码”本身,而是 early init 等用户态文件;它们与内核启动强相关,但不属于内核源码产物。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容