Android HAL与底层驱动集成工程 - 第4节
4. HAL接口定义语言(HIDL)入门
HIDL(HAL Interface Definition Language)是 Android 8.0(Treble 项目)引入的接口定义语言,用于在 Android 框架(Framework) 与 HAL 实现(Vendor HAL) 之间建立稳定、可独立更新的通信契约。其核心作用可归纳为三点:
- 解耦框架与供应商实现:Framework 不再直接链接 HAL 共享库,而是通过 HIDL 定义的接口进行进程间通信(IPC),使得系统更新(OTA)无需重新编译 HAL 代码。
- 标准化硬件抽象层:为摄像头、音频、传感器、图形等硬件模块提供统一的接口规范,确保不同供应商的 HAL 实现可以无缝替换。
- 支持多进程与跨边界通信:HIDL 自动生成客户端(Client)与服务端(Server)的 Binder 通信代码,开发者只需关注业务逻辑。
关键概念: HIDL 接口运行在
hwbinder 域中,与传统的 binder 域隔离,确保系统服务与供应商服务互不干扰。
4.2 HIDL 与 AIDL 的区别:两大接口定义语言对比
Android 系统存在两种接口定义语言:AIDL(Android Interface Definition Language) 用于应用层与系统服务之间的 Binder 通信;HIDL 专用于 Framework 与 HAL 之间的 hwbinder 通信。下表从多个维度进行对比:
| 对比维度 | AIDL | HIDL |
|---|---|---|
| 通信域 | binder 域(应用层) |
hwbinder 域(供应商层) |
| 主要用途 | 应用与服务、服务与服务之间 | Framework 与 HAL 实现之间 |
| 稳定性要求 | 可随系统版本变化 | 必须保持向后兼容(稳定接口) |
| 版本管理 | 无强制版本机制 | 通过 @version 注解管理接口版本 |
| 数据类型 | 支持 Java 基本类型、Parcelable | 支持 HIDL 原生类型、struct、enum、union |
| 生成代码 | 生成 Java/C++ 桩代码 | 生成 C++ 客户端/服务端代码 |
| 接口文件后缀 | .aidl |
.hal |
实践建议: 如果你正在开发一个新的硬件 HAL 模块(如指纹、传感器),请使用 HIDL;如果你在修改系统服务接口(如 ActivityManager),请使用 AIDL。两者不可混用。
4.3 HIDL 文件基本语法:从结构到关键字
一个标准的 .hal 文件由 包声明(package)、导入(import)、类型定义 和 接口定义(interface) 四部分组成。以下是一个典型的 HIDL 文件结构:
// 1. 包声明:定义接口的命名空间和版本
package vendor.example.hardware.led@1.0;
// 2. 导入其他 HIDL 类型
import android.hidl.base@1.0::IBase;
// 3. 枚举类型定义
enum LedColor : uint32_t {
RED = 0,
GREEN = 1,
BLUE = 2,
RGB = 3
};
// 4. 结构体定义
struct LedConfig {
uint32_t brightness;
LedColor color;
bool enable;
};
// 5. 接口定义:包含方法声明
interface ILed extends IBase {
// 同步方法:设置 LED 状态
setConfig(LedConfig config) generates (Result result);
// 同步方法:获取当前配置
getConfig() generates (Result result, LedConfig config);
// 单向方法(fire-and-forget):触发闪烁
oneway blink(uint32_t intervalMs);
};
语法要点详解
- 包声明:格式为
package <vendor>.<subsystem>.<module>@<major>.<minor>,版本号必须与接口文件所在目录一致。 - 导入语句:使用
import关键字引入其他.hal文件中定义的类型或接口。 - 枚举与结构体:枚举必须指定底层整数类型(如
uint32_t);结构体成员支持基本类型、其他结构体、枚举和vec<T>等容器。 - 方法声明:同步方法使用
generates关键字指定返回值;单向方法使用oneway修饰,不阻塞调用方。 - 接口继承:所有 HIDL 接口必须显式继承
IBase(或间接继承),IBase提供了生命周期管理基础方法。
常见错误: 忘记在接口声明中
extends IBase 会导致编译失败;方法参数中不能使用 C++ 指针或引用,必须使用 HIDL 原生类型或 vec<T>。
4.4 .hal 文件结构:目录布局与编译规则
HIDL 接口文件不仅包含语法内容,还遵循严格的 目录结构 和 编译配置。一个完整的 HIDL 模块在源码树中的布局如下:
hardware/interfaces/
└── vendor/
└── example/
└── led/
├── 1.0/
│ ├── default/
│ │ ├── Android.bp # HAL 实现编译配置
│ │ ├── Led.cpp # HAL 实现代码
│ │ ├── Led.h
│ │ └── service.cpp # 服务注册入口
│ ├── ILed.hal # HIDL 接口定义
│ └── types.hal # 类型定义(可选)
└── Android.bp # 接口编译配置
关键文件说明
| 文件/目录 | 作用 | 是否必须 |
|---|---|---|
ILed.hal |
接口定义文件,包含 interface ILed 声明 |
是 |
types.hal |
集中定义枚举、结构体等共享类型 | 推荐 |
Android.bp(接口层) |
使用 hidl_interface 模块编译 .hal 文件,生成 C++ 桩代码 |
是 |
default/ 目录 |
存放 HAL 实现代码,包含 hidl_passthrough 或 hidl_service 编译模块 |
是 |
service.cpp |
将 HAL 服务注册到 hwbinder 域,供 Framework 发现和调用 | 是 |
接口层 Android.bp 示例
// hardware/interfaces/vendor/example/led/Android.bp
hidl_interface {
name: "vendor.example.hardware.led@1.0",
root: "vendor.example.hardware",
srcs: [
"ILed.hal",
"types.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: false,
gen_hal: true,
};
实现层 Android.bp 示例
// hardware/interfaces/vendor/example/led/1.0/default/Android.bp
cc_binary {
name: "vendor.example.hardware.led@1.0-service",
relative_install_path: "hw",
srcs: [
"Led.cpp",
"service.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.example.hardware.led@1.0",
],
init_rc: ["vendor.example.hardware.led@1.0-service.rc"],
};
开发流程提示: 先编写
.hal 文件并编译通过,生成 C++ 桩代码;然后实现桩代码中的 ILed 接口方法;最后通过 service.cpp 注册服务,并使用 lshal 命令验证服务是否成功注册。