1. HAL 概述:Android 系统架构与硬件抽象层

各位同学,咱们今天正式开讲。第一章,我打算先带你把 Android 系统的骨架摸清楚。说白了,就是搞清楚 HAL 这玩意儿到底在系统里扮演什么角色。

我刚开始接触 Android 时,也觉得很懵。上层是 Java 写的 App,底层是 C 写的驱动,中间还夹着一堆看不懂的 so 库。后来做了几个项目才明白,HAL 就是连接 Android 框架和硬件驱动的桥梁。没有它,上层应用根本不知道你的摄像头、传感器、音频芯片长什么样。

1.1 Android 系统架构概览

先看整体。Android 系统从上到下,大致分五层:

层级 主要组件 开发语言
应用层 系统 App、第三方 App Java / Kotlin
应用框架层 ActivityManager、WindowManager、ContentProvider 等 Java
系统运行层 ART、Native 库 (libc, libutils 等) C++ / C
硬件抽象层 (HAL) Camera HAL、Audio HAL、Sensor HAL 等 C / C++
Linux 内核层 设备驱动、电源管理、内存管理 C

你想想看,一个 App 要拍照,流程是这样的:App 调用 Camera API → 框架层通过 Binder 调用 CameraService → CameraService 加载 Camera HAL → HAL 调用内核驱动 → 驱动操作摄像头硬件。每一步都环环相扣。

这里有个关键点:HAL 是唯一直接和内核驱动打交道的用户空间层。框架层完全不知道你的摄像头是 OV 还是 Sony 的,它只认 HAL 定义的接口。

核心认知:HAL 的存在,让 Android 框架和硬件厂商解耦。厂商只需要实现 HAL 接口,Google 不需要关心你的硬件怎么工作的。

1.2 HAL 在 Android 中的位置与作用

HAL 的位置很特殊。它不在内核里,也不在框架里,而是作为一个独立的 Native 库存在。我习惯把它比作「硬件驱动的用户空间代理」。

它的作用,我总结为三点:

  • 接口标准化:定义统一的硬件操作接口,比如 open、close、ioctl、start、stop。不管底层驱动怎么变,上层调用的函数签名不变。
  • 逻辑封装:把复杂的硬件初始化、参数配置、中断处理封装成简单的 API。我在项目中遇到过,某个传感器需要先上电、再校准、再设置采样率,这些逻辑全写在 HAL 里,上层根本不用管。
  • 权限隔离:HAL 运行在独立的进程或线程中,通过 HIDL / AIDL 与框架通信。这样即使 HAL 崩溃,也不会直接拖垮 SystemServer。

个人经验:调试 HAL 时,我建议你先把 log 打全。特别是 open 和 ioctl 的返回值,很多坑都出在这里。我曾经因为一个 GPIO 没拉高,查了整整两天,最后发现是 HAL 里少写了一行 usleep。

1.3 HAL 的发展历程:从传统 HAL 到 Treble 架构

这部分是重点。HAL 不是一成不变的,它经历了两次大的演进。

1.3.1 传统 HAL (Android 1.0 ~ 7.x)

早期的 HAL 很简单。每个硬件模块对应一个 .so 动态库,框架层通过 dlopen 加载它。接口定义在 hardware/libhardware 头文件里。

典型的调用方式:

// 传统 HAL 加载方式
hw_module_t* module = NULL;
hw_device_t* device = NULL;

hw_get_module(TARGET_HARDWARE_MODULE_ID, (const hw_module_t**)&module);
module->methods->open(module, TARGET_HARDWARE_DEVICE_ID, &device);

// 然后通过 device 操作硬件
device->set_value(device, 42);

这个模式有什么问题?我举个例子。你买了个新手机,厂商把 HAL 编译进系统镜像。如果 Google 升级了 Android 版本,改了 HAL 接口,厂商就得重新适配、重新发布整个系统镜像。用户想升级?等厂商心情吧。

说白了,传统 HAL 把框架和硬件实现绑死在同一个镜像里。这是 Android 碎片化的根源之一。

1.3.2 Treble 架构 (Android 8.0 起)

Google 也受不了了。从 Android 8.0 开始,他们推出了 Project Treble。核心思路就一句话:把框架和 HAL 彻底分开。

怎么分的?引入 HIDL (HAL Interface Definition Language)。HAL 不再是一个简单的 .so,而是变成一个独立的服务进程,通过 Binder 与框架通信。

对比一下:

特性 传统 HAL Treble HAL
通信方式 直接函数调用 (dlopen) Binder / HIDL 接口
进程模型 与调用方同进程 独立进程 (passthrough 或 binderized)
接口稳定性 随系统版本变化 冻结接口,向后兼容
升级影响 框架升级必须重新编译 HAL 框架升级不影响 HAL 二进制
厂商适配 每次 Android 大版本都要重做 一次适配,多版本兼容

嗯,这里要注意。Treble 不是一刀切。它保留了两种模式:

  • Binderized HAL:HAL 作为独立服务运行,通过 HIDL 接口通信。这是推荐模式。
  • Passthrough HAL:兼容旧模式,HAL 仍然以 .so 形式存在,但通过 HIDL 封装一层。用于性能敏感的场景,比如音频、图形。

避坑指南:我曾经在移植一个老项目到 Android 9 时,发现 Sensor HAL 怎么都加载不上。查了半天,原来是 Treble 要求所有 HAL 必须声明在 manifest.xml 里,而旧代码根本没这个文件。记住:Treble 下,HAL 的注册和发现机制完全变了。

1.4 知识结构图:HAL 在 Android 中的位置

下面这张图,我画了 Android 系统各层的关系,以及 HAL 的两种形态。你仔细看,能看出传统 HAL 和 Treble HAL 在架构上的区别。

Android 系统架构与 HAL 位置 应用层 (App) 应用框架层 (Framework / SystemServer) 系统运行层 (ART / Native Libraries) 传统 HAL (Pre-Treble) dlopen + 直接函数调用 Treble HAL (Android 8.0+) HIDL 接口 + Binder 通信 Linux 内核层 (Kernel Drivers) 硬件设备 (Camera / Audio / Sensor / GPS ...) HAL 是框架与内核之间的桥梁,Treble 架构通过 HIDL 实现了框架与 HAL 的解耦

1.5 小结:你该记住什么

这一章的内容,我不希望你死记硬背。你只需要记住三件事:

  1. HAL 的位置:在框架层之下,内核层之上。它是硬件操作的统一入口。
  2. HAL 的作用:标准化接口、封装硬件逻辑、隔离权限。让厂商和 Google 各司其职。
  3. Treble 的意义:把 HAL 从 .so 变成独立服务,用 HIDL 固定接口。从此 Android 大版本升级不再依赖厂商重新适配 HAL。

我的建议:如果你现在开始做新项目,直接上 Treble 架构。别再用传统 HAL 了。虽然初期配置麻烦点,但后续维护和升级会省很多事。我见过太多团队因为贪图省事用传统 HAL,结果 Android 大版本一升,整个 HAL 层重写,欲哭无泪。