2. Android系统架构概览

Android 并不是一个“从零重写”的操作系统,而是在 Linux 内核之上叠加了一整套面向移动设备的运行时、系统服务与应用框架。理解它的分层结构,是后续做内核裁剪、HAL 适配、设备树移植的前提。很多适配问题之所以“看起来在应用层,根因却在内核或设备树”,正是因为开发者没有建立清晰的分层调用链认知。

一、五层架构的核心原理

从下往上,Android 系统可以划分为五个主要层次,每一层只通过明确定义的接口向上提供服务:

  • Linux 内核层:负责进程调度、内存管理、文件系统、网络协议栈、电源管理,以及最关键的设备驱动。显示、触摸、音频、摄像头、Wi-Fi、传感器等硬件能力,最终都由内核驱动暴露为设备节点或子系统接口。
  • HAL 层(硬件抽象层):以一组标准接口(如 hardware/libhardware 定义的模块)屏蔽不同芯片与板级的差异。Framework 只调用 HAL 接口,不直接操作驱动节点,从而实现“同一套 Android 跑在不同硬件上”。
  • Native 层:由 C/C++ 实现的库与守护进程,包括 Bionic libc、SurfaceFlinger、AudioFlinger、MediaCodec、OpenGL ES/Vulkan 等,是性能敏感与图形音视频处理的主战场。
  • Framework 层(Java API 框架):以 Java/Kotlin 实现,提供 ActivityManager、WindowManager、PackageManager、SensorManager 等系统服务,是 App 直接调用的 API 来源。
  • App 层:系统应用与第三方应用,运行在 ART 虚拟机之上,通过 Binder IPC 与系统服务交互。
分层的关键价值在于“接口稳定、实现可替换”。设备树正是把“硬件描述”从内核代码中解耦出来的机制,它让同一份内核镜像可以适配不同板级硬件,而无需为每块板子重新编译内核。

二、设备树在架构中的位置

设备树(Device Tree,DT)位于Linux 内核层的最底层入口。Bootloader 在启动内核时,把编译好的 .dtb 文件地址传给内核,内核在启动早期解析它,据此创建 platform_device、注册 i2c_client、配置引脚复用与时钟,并完成驱动与设备的匹配(match)。

因此设备树并不“属于”HAL 或 Framework,但它的描述结果会一路向上传导:设备树里写错 I2C 地址,HAL 就读不到传感器;设备树里漏配显示时序,SurfaceFlinger 就无法正常合成画面。这条链路可以用下面的信号与依赖关系图表示。

Android 五层架构与设备树位置关系 App 层 系统应用 / 第三方应用(ART 运行时,Java/Kotlin) Binder IPC Framework 层 ActivityManager · WindowManager · SensorManager · PackageManager Native 层 Bionic libc · SurfaceFlinger · AudioFlinger · MediaCodec · Vulkan HAL 层 hw_get_module 标准接口 · 屏蔽芯片与板级差异 Linux 内核层 进程调度 · 内存管理 · 文件系统 · 网络协议栈 · 设备驱动 I2C / SPI / GPIO / 显示 / 触摸 / 音频 / 摄像头 / 传感器驱动 设备树(.dts / .dtsi / .dtb) Bootloader 传递 → 内核解析 → 创建 platform_device / i2c_client → 驱动匹配 驱动 与设备 匹配点 硬件描述 与内核解耦 一镜像多板

三、一次“传感器读取”的完整调用链

以读取加速度传感器为例,可以清楚看到各层如何协作,以及设备树在何处介入:

  1. App 调用 SensorManager.getDefaultSensor() 与 registerListener()。
  2. Framework 的 SensorService 通过 Binder 接收请求,并转发给 Native 层的 sensors HAL。
  3. HAL 实现(如 android.hardware.sensors@2.1-service)打开 /dev/iio:device0 或对应字符设备。
  4. 内核 IIO 驱动从 I2C 总线读取芯片寄存器,而该 I2C 设备的地址、中断引脚、供电 regulator 全部来自设备树节点。
  5. 数据沿原路返回,最终回调到 App 的 onSensorChanged()。
// 设备树中描述一个 I2C 加速度计(示意)
&i2c3 {
    status = "okay";
    clock-frequency = <400000>;

    accel@68 {
        compatible = "bosch,bmi160";
        reg = <0x68>;
        interrupt-parent = <&gpio1>;
        interrupts = <12 IRQ_TYPE_EDGE_RISING>;
        vdd-supply = <&vdd_1v8>;
    };
};

如果 reg = <0x68> 写错,内核不会报“应用崩溃”,而是驱动 probe 失败,HAL 打开设备失败,最终表现为 App 收不到传感器数据。这就是典型的“上层现象、底层根因”。

四、各层职责与设备树关联度对比

层次 主要语言/形态 核心职责 与设备树的关联
App 层 Java/Kotlin 业务逻辑与界面 无直接关联
Framework 层 Java + JNI 系统服务与 API 无直接关联
Native 层 C/C++ 图形、音视频、运行时 间接(通过 HAL)
HAL 层 C/C++ 模块 屏蔽硬件差异 间接(依赖驱动节点)
Linux 内核层 C + 汇编 驱动、调度、内存 直接解析与使用
设备树 DTS/DTB 描述板级硬件 内核启动入口

五、常见误区与复盘要点

误区一:把设备树当成“配置文件”随意改。设备树节点必须与驱动中的 compatible、reg、中断类型严格匹配,否则 probe 静默失败。
误区二:认为 HAL 可以绕过内核直接访问硬件。除极少数用户态驱动外,HAL 仍通过内核暴露的设备节点工作,设备树错误会直接传导到 HAL。
误区三:只改 Framework 层“打补丁”。若根因在设备树或驱动,上层补丁只会掩盖问题,换一块板子立刻复现。
复盘方法:遇到硬件功能异常时,按“App → Framework → HAL → 内核驱动 → 设备树”自下而上或自上而下逐层验证。常用命令包括 dmesg | grep -i probe 查看驱动匹配、ls /sys/firmware/devicetree/base/ 查看运行时设备树、cat /proc/device-tree/... 核对节点属性。