Camera HAL开发入门:从HIDL接口到驱动交互 - 第3节
3.1 HIDL 的定义与设计目标
HIDL(HAL Interface Definition Language)是 Android Treble 架构中用于定义 HAL 接口的跨进程通信语言。它的核心目标是将 Android 框架(Framework)与硬件供应商实现(Vendor HAL)之间的编译时依赖,转变为运行时稳定的二进制接口(ABI)。
关键定义: HIDL 是一种接口定义语言,用于描述 HAL 服务端与客户端之间的契约。它生成的语言绑定(C++/Java)确保接口的二进制兼容性,使得 Framework 和 Vendor 分区可以独立更新。
3.1.1 HIDL 的核心设计原则
- 二进制稳定性: 接口一旦发布,其 ABI 在后续版本中必须保持向后兼容。
- 强类型化: 所有参数、返回值、回调函数都必须显式声明类型,消除运行时类型歧义。
- 单向/双向通信: 支持同步调用(blocking)和异步回调(oneway)。
- 版本化管理: 每个接口通过
@version注解标识,支持接口演进。
3.2 HIDL 与 AIDL 的区别
在 Android 系统中,AIDL(Android Interface Definition Language)主要用于应用层(App <-> Service)的 Binder 通信,而 HIDL 专为 HAL 层设计。两者虽然都基于 Binder 驱动,但在设计哲学和使用场景上有本质区别。
| 对比维度 | HIDL | AIDL |
|---|---|---|
| 目标层级 | Framework <-> Vendor HAL | App <-> System Service |
| 稳定性要求 | 严格 ABI 稳定,跨分区独立更新 | 无强制 ABI 要求,依赖 SDK 版本 |
| 接口定义文件 | .hal 文件 |
.aidl 文件 |
| 支持的数据类型 | HIDL 原生类型 + struct/union/enum | Java 原生类型 + Parcelable |
| 服务注册方式 | hwservicemanager |
servicemanager |
| 传输层 | Binder(直通模式)/ HwBinder | Binder |
| 版本管理 | 显式 @version,支持 minor/major |
隐式,依赖包名 |
开发提示: 在 Camera HAL 开发中,你几乎只与 HIDL 打交道。AIDL 主要用于 CameraService 与上层 App 的交互,而 Camera HAL 接口(如
ICameraDevice、ICameraProvider)全部使用 HIDL 定义。
3.3 HIDL 在 Treble 架构中的角色
Treble 架构是 Android 8.0 引入的重大重构,其核心目标是将 Framework 与 Vendor 实现解耦。HIDL 在这一架构中扮演着“契约层”的角色,位于 Framework 和 HAL 实现之间。
架构要点: 在 Treble 架构中,Framework 分区(system.img)和 Vendor 分区(vendor.img)通过 HIDL 接口进行通信。CameraService 通过 HIDL 调用 Camera HAL 实现,而无需关心底层驱动细节。这种解耦使得 Google 可以独立推送 Framework 更新,而 OEM 只需维护 Vendor 分区的 HAL 实现。
3.4 .hal 文件的基本语法结构
.hal 文件是 HIDL 接口的载体,采用类似 C++ 的语法风格。一个完整的 .hal 文件包含包声明、导入语句、接口定义、数据类型定义等部分。
3.4.1 文件基本骨架
// 包声明:定义接口的命名空间和版本
package android.hardware.camera.provider@3.5;
// 导入其他 HIDL 接口
import android.hardware.camera.common@3.5::types;
import android.hardware.camera.device@3.5::ICameraDevice;
// 接口定义
interface ICameraProvider {
// 方法声明
getCameraIdList() generates (Status status, vec<string> cameraIds);
// 带回调的方法
getCameraDeviceInterface(
string cameraId
) generates (Status status, ICameraDevice device);
// oneway 异步回调
oneway notifyDeviceStateChange(
int64_t deviceState
);
};
3.4.2 核心语法元素
| 语法元素 | 说明 | 示例 |
|---|---|---|
| package | 声明接口所属的包名和版本号 | package android.hardware.camera@3.5; |
| import | 导入其他 .hal 文件中的类型或接口 | import android.hardware.camera.common@3.5::types; |
| interface | 定义 HAL 接口,包含方法声明 | interface ICameraProvider { ... }; |
| generates | 指定方法的返回类型(可返回多个值) | generates (Status status, vec<string> list); |
| oneway | 标记异步调用,不阻塞客户端 | oneway notify(); |
| struct | 定义复合数据类型 | struct CameraInfo { ... }; |
| enum | 定义枚举类型 | enum Status : int32_t { OK, ERROR }; |
| @version | 注解,标记接口或类型的版本 | @version 3.5 |
3.4.3 数据类型定义示例
package android.hardware.camera.common@3.5;
// 枚举定义
enum Status : int32_t {
OK = 0,
ERROR_ILLEGAL_ARGUMENT = -1,
ERROR_INVALID_OPERATION = -2,
ERROR_DEVICE_UNAVAILABLE = -3,
};
// 结构体定义
struct CameraMetadata {
int32_t tag;
int32_t dataType;
vec<uint8_t> data;
};
// 联合体定义(使用 safe_union)
safe_union CameraMetadataValue {
int32_t int32Value;
int64_t int64Value;
float floatValue;
vec<uint8_t> bytesValue;
};
3.4.4 接口继承与回调
HIDL 支持接口继承,允许子接口扩展父接口的功能。Camera HAL 中大量使用这种模式:
package android.hardware.camera.device@3.5;
// 基础接口
interface ICameraDevice {
// 打开相机设备,返回会话接口
open() generates (Status status, ICameraDeviceSession session);
// 获取相机特性元数据
getCameraCharacteristics() generates (Status status, CameraMetadata chars);
};
// 子接口继承
interface ICameraDeviceSession extends ICameraDevice {
// 配置输出流
configureStreams(
StreamConfiguration config
) generates (Status status, HalStreamConfiguration halConfig);
// 处理捕获请求
processCaptureRequest(
vec<CaptureRequest> requests,
vec<BufferCache> cachesToRemove
) generates (Status status, uint32_t numRequestProcessed);
// 回调接口:通知框架捕获结果
oneway notify(
vec<NotifyMsg> msgs
);
};
常见陷阱:
- HIDL 不支持方法重载(overload),每个方法名必须唯一。
- 所有方法必须显式声明返回类型,即使没有返回值也要使用
generates ()。 - 接口版本号必须与包声明中的版本一致,否则编译会报错。
oneway方法不能有返回值,且不能保证送达顺序。
3.5 HIDL 接口的版本管理
HIDL 的版本管理采用 major.minor 格式。major 版本变更表示不兼容的 ABI 改动,minor 版本变更表示向后兼容的扩展。
实践建议: 在 Camera HAL 开发中,通常使用
@3.x 版本系列。如果你需要扩展接口,优先考虑 minor 版本升级(如从 3.4 到 3.5),而不是创建新的 major 版本。这样可以保持与旧版本 Framework 的兼容性。
3.6 HIDL 接口的编译与生成
.hal 文件不能直接使用,需要通过 hidl-gen 工具编译生成 C++ 或 Java 的源代码。生成的代码包括客户端代理(Proxy)和服务端桩(Stub)。
3.6.1 编译流程
# 1. 生成 C++ 头文件和实现
hidl-gen -o output_dir -L c++-impl \
-r android.hardware:hardware/interfaces \
android.hardware.camera.provider@3.5
# 2. 生成 Android.bp 构建文件
hidl-gen -o output_dir -L androidbp \
-r android.hardware:hardware/interfaces \
android.hardware.camera.provider@3.5
# 3. 生成 Java 文件(用于 Framework 端)
hidl-gen -o output_dir -L java \
-r android.hardware:hardware/interfaces \
android.hardware.camera.provider@3.5
3.6.2 生成的文件结构
| 生成文件 | 用途 |
|---|---|
ICameraProvider.h |
接口定义头文件,包含纯虚函数 |
ICameraProvider.cpp |
接口实现基类,包含 Binder 序列化代码 |
BnCameraProvider.h |
服务端桩类,处理 Binder 请求分发 |
BpCameraProvider.h |
客户端代理类,封装 Binder 调用 |
types.h |
所有自定义类型的 C++ 定义 |
Android.bp |
构建配置,包含依赖和编译选项 |
关键理解: 作为 Camera HAL 开发者,你主要关注的是 服务端实现。你需要继承
BnCameraProvider 或 ICameraProvider 基类,并实现其中的纯虚函数。客户端代理(Bp)由 Framework 的 CameraService 使用,你通常不需要关心。
3.7 本章小结
- HIDL 是 Treble 架构中 Framework 与 Vendor 之间的契约语言,保证二进制接口稳定性。
- 与 AIDL 的区别 主要体现在目标层级、稳定性要求和版本管理策略上。
- 在 Treble 架构中,HIDL 位于 Framework 和 HAL 实现之间,是解耦的关键层。
- .hal 文件 使用
package、import、interface、generates、oneway等关键字定义接口契约。 - 版本管理 采用 major.minor 格式,minor 升级必须保持向后兼容。
- 编译工具
hidl-gen将 .hal 文件转换为 C++/Java 代码,生成客户端代理和服务端桩。