6、HAL 硬件抽象层:HAL 架构设计、Stub 实现、HIDL 与 AIDL 接口定义
说到 Android 系统的 HAL,我脑子里第一个蹦出来的词就是「中间人」。它夹在 Linux 内核驱动和 Android Framework 之间,干的是翻译和隔离的活儿。说白了,HAL 就是让 Google 的工程师不用管你用的是高通还是联发科的芯片,上层代码写一套,底层各家自己适配。
我在 2016 年做过一个项目,要把某款老手机移植到 Android 8.0。那时候最大的坑就是 HAL 层不兼容。嗯,从那以后我对 HAL 的敬畏心就上来了。今天咱们就把 HAL 的架构、Stub 实现、还有 HIDL 和 AIDL 的区别,一次性讲透。
6.1 HAL 架构设计:分层与解耦
HAL 的全称是 Hardware Abstraction Layer。它的核心目标就一个:让 Framework 不直接碰硬件。你想想看,如果上层代码直接调用内核驱动,那换一颗 SoC 就得重写整个系统。这谁受得了?
传统的 HAL 架构(Android 7.0 之前)是「模块 + Stub」模式。每个硬件模块(比如 Camera、GPS)对应一个 .so 动态库。Framework 通过 dlopen 加载这个库,然后调用固定的函数指针。这种方式简单粗暴,但有个致命问题:Framework 进程如果崩溃,硬件状态就丢了。
到了 Android 8.0,Google 推出了 Treble 架构。HAL 被拆成了两部分:HAL 实现端(跑在独立的守护进程里)和 HAL 客户端(Framework 端)。两者通过 HIDL 或 AIDL 通信。这样一来,Framework 再怎么崩,HAL 进程依然健在。我个人习惯把这个变化叫做「HAL 的进程化」。
核心要点:Treble 架构的本质是「接口与实现分离」。HAL 接口定义在 /hardware/interfaces/ 下,各家芯片厂商只需要实现这些接口,Google 负责 Framework 端。从此系统升级不再依赖芯片厂商。
下面这张图展示了 HAL 在 Android 系统中的位置和通信路径:
6.2 Stub 实现:传统 HAL 的写法
Stub 这个词,直译是「存根」。在 HAL 语境下,它指的是一个 硬件模块的具体实现。每个 Stub 对应一个硬件模块,比如 camera.stub.so、gps.stub.so。
传统 HAL 的 Stub 实现遵循固定的结构。每个模块必须暴露一个 hw_module_t 结构体,以及一个或多个 hw_device_t 结构体。我刚开始写 HAL 时,老搞混这两个结构体的关系。后来我总结了一句话:module 代表「谁家的硬件」,device 代表「怎么操作这个硬件」。
来看一个典型的 Stub 实现代码框架:
// hardware/libhardware/include/hardware/camera.h
typedef struct camera_module {
hw_module_t common;
int (*get_number_of_cameras)(void);
int (*get_camera_info)(int camera_id, struct camera_info *info);
} camera_module_t;
// 实现端:camera_stub.cpp
static int camera_device_open(const hw_module_t* module, const char* id,
hw_device_t** device) {
// 分配设备结构体,初始化硬件
return 0;
}
static struct hw_module_methods_t camera_module_methods = {
.open = camera_device_open,
};
camera_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "Camera Stub Module",
.author = "Your Company",
.methods = &camera_module_methods,
},
.get_number_of_cameras = stub_get_number_of_cameras,
.get_camera_info = stub_get_camera_info,
};
避坑指南:我曾经在写 GPS HAL 时,忘记在 HAL_MODULE_INFO_SYM 中正确填充 .id 字段。结果 Framework 死活加载不到模块。调试了两天才发现,id 必须和 /hardware/libhardware/include/hardware/gps.h 中定义的 GPS_HARDWARE_MODULE_ID 完全一致。大小写、下划线,一个都不能错。
6.3 HIDL 接口定义:Treble 时代的标配
HIDL 全称是 HAL Interface Definition Language。它是 Google 为 Treble 架构量身定做的接口语言。HIDL 文件以 .hal 结尾,语法上有点像 C++ 和 Java 的混合体。
为什么要用 HIDL?说白了,就是为了 跨进程通信的标准化。传统 HAL 的 Stub 是直接 dlopen 加载的,Framework 和 HAL 在同一个进程里。HIDL 则强制把 HAL 放到独立进程,通过 Binder 通信。这样一来,稳定性大幅提升。
一个典型的 HIDL 接口定义长这样:
// android.hardware.camera@3.2::ICameraDevice.hal
package android.hardware.camera@3.2;
interface ICameraDevice {
/**
* 打开相机设备
* @param cameraId 相机 ID
* @return 操作状态
*/
open(string cameraId) generates (Status status);
/**
* 配置相机流
* @param config 流配置
* @return 配置结果
*/
configureStreams(StreamConfiguration config) generates (Status status);
/**
* 捕获图像
* @param request 捕获请求
* @return 捕获结果
*/
capture(CaptureRequest request) generates (Status status, CaptureResult result);
};
HIDL 接口定义好之后,用 hidl-gen 工具生成 C++ 或 Java 的客户端和服务端代码。我个人习惯用 C++ 写 HAL 实现,因为性能更好,而且可以直接操作硬件寄存器。
关键区别:HIDL 是 Google 专门为 HAL 设计的,它支持「直通式」(passthrough)和「绑定式」(binderized)两种模式。直通式就是传统 Stub 的变种,绑定式才是真正的跨进程。Android 8.0 之后,Google 推荐全部使用绑定式。
6.4 AIDL 接口定义:从 Framework 到 HAL 的桥梁
AIDL 大家应该不陌生,Android 应用开发里经常用。但在 HAL 层面,AIDL 是 Android 11 之后才被正式引入的。Google 发现,HIDL 虽然好,但毕竟是一门新语言,学习成本高。而 AIDL 是 Android 的老牌 IPC 语言,开发者更熟悉。
所以从 Android 11 开始,Google 允许用 AIDL 来定义 HAL 接口。AIDL 和 HIDL 在功能上是等价的,但语法更接近 Java。来看一个例子:
// IMySensor.aidl
package android.hardware.mysensor;
interface IMySensor {
/**
* 获取传感器数据
* @return 传感器值数组
*/
float[] getSensorData();
/**
* 设置采样率
* @param rateHz 采样率(Hz)
*/
void setSamplingRate(int rateHz);
}
用 AIDL 定义 HAL 接口的好处是:Framework 端的代码可以直接复用。因为 AIDL 生成的 Java 代码和普通的 Service 通信一模一样。我在一个项目中把 HIDL 的 Camera HAL 迁移到 AIDL,Framework 端的改动量减少了大概 40%。
注意:HIDL 和 AIDL 不能混用。一个 HAL 服务要么全部用 HIDL,要么全部用 AIDL。Google 的路线图是逐步用 AIDL 取代 HIDL,但这个过程很漫长。目前(Android 14)两种方式都支持,但新开发的 HAL 建议直接用 AIDL。
6.5 HIDL vs AIDL:怎么选?
很多刚接触 HAL 的同学会问:到底用 HIDL 还是 AIDL?我根据实际项目经验,整理了一个对比表:
| 对比维度 | HIDL | AIDL |
|---|---|---|
| 引入版本 | Android 8.0 | Android 11 |
| 语言风格 | 类似 C++/IDL 混合 | 类似 Java 接口 |
| 代码生成 | hidl-gen 生成 C++/Java | aidl 工具生成 Java/C++ |
| 跨进程性能 | 优秀(原生 Binder 优化) | 优秀(与 Framework 一致) |
| 学习曲线 | 较陡(新语言) | 平缓(Android 开发者熟悉) |
| Google 推荐 | 旧项目维护 | 新项目首选 |
我个人建议:如果你是从零开始一个新项目,直接用 AIDL。如果是维护 Android 8.0~10 的老项目,那就继续用 HIDL,别折腾迁移。我曾经试图把一个运行稳定的 HIDL HAL 强行迁移到 AIDL,结果引入了一堆 Binder 超时的 bug,得不偿失。
6.6 实战:实现一个简单的 HAL 服务
光说不练假把式。咱们来实现一个最简单的 HAL 服务:一个虚拟的 LED 控制 HAL。它只有一个功能:开灯和关灯。
第一步,定义 AIDL 接口:
// IMyLed.aidl
package android.hardware.myled;
interface IMyLed {
void setOn(bool on);
bool isOn();
}
第二步,实现 HAL 服务端(C++):
// MyLed.cpp
#include "android/hardware/myled/IMyLed.h"
#include <utils/Log.h>
class MyLed : public android::hardware::myled::BnMyLed {
public:
android::binder::Status setOn(bool on) override {
mIsOn = on;
// 这里操作 GPIO 或 I2C 控制 LED
ALOGD("LED set to %s", on ? "ON" : "OFF");
return android::binder::Status::ok();
}
android::binder::Status isOn(bool* _aidl_return) override {
*_aidl_return = mIsOn;
return android::binder::Status::ok();
}
private:
bool mIsOn = false;
};
第三步,注册服务:
// main.cpp
int main() {
android::sp<MyLed> service = new MyLed();
android::status_t ret = android::defaultServiceManager()->addService(
android::String16("android.hardware.myled.IMyLed/default"),
service);
if (ret != android::OK) {
ALOGE("Failed to add service");
return -1;
}
ALOGD("MyLed HAL service started");
android::ProcessState::self()->startThreadPool();
android::IPCThreadState::self()->joinThreadPool();
return 0;
}
避坑指南:我曾经在注册服务时,忘了调用 startThreadPool() 和 joinThreadPool()。结果服务注册成功了,但客户端一调用就卡死。因为 Binder 线程池没启动,根本没人处理请求。这个坑我踩了整整一个下午。
第四步,Framework 端调用(Java):
// 在 SystemServer 或 App 中调用
IMyLed ledService = IMyLed.Stub.asInterface(
ServiceManager.getService("android.hardware.myled.IMyLed/default"));
if (ledService != null) {
ledService.setOn(true);
boolean isOn = ledService.isOn();
Log.d("MyLed", "LED is " + (isOn ? "ON" : "OFF"));
}
嗯,到这里,一个完整的 HAL 服务就搭建好了。从接口定义到服务注册,再到客户端调用,整个链路是通的。你可以在自己的开发板上试试,把 GPIO 控制逻辑加进去,就能真正控制硬件了。
最后说一句:HAL 是 Android 系统中最接近硬件的一层,也是最能体现嵌入式功底的地方。把 HAL 吃透了,你就能真正理解「硬件无关」这四个字背后的代价和智慧。
公众号:蓝海资料掘金营,微信deep3321