第三章 HAL 接口定义:HIDL 与 AIDL 的抉择

好,咱们进入正题。这一章聊的是 HAL 接口定义,说白了就是 Camera HAL 跟 Framework 之间怎么「说话」。你想想看,一个系统里上层要调用底层硬件,总得有个约定吧?这个约定就是接口。

在 Android 里,这个约定经历了两次大变革。从最早的 HIDL 到现在的 AIDL,背后其实是 Google 对系统架构的重新思考。我个人习惯把 HIDL 看作是「过渡方案」,而 AIDL 才是「终极形态」。为什么这么说?咱们慢慢聊。

3.1 HIDL 与 AIDL 的核心区别

先看一张对比表,心里有个底:

对比项 HIDL AIDL
全称 HAL Interface Definition Language Android Interface Definition Language
诞生时间 Android 8.0 (Treble 项目) Android 10 开始支持,Android 14 成为主流
设计目标 解耦 Framework 与 HAL 统一系统服务与 HAL 的通信方式
稳定性 使用 .hal 文件,版本号严格 使用 .aidl 文件,向后兼容更好
性能 Binder 优化,但序列化开销较大 原生 Binder,更轻量
代码生成 hidl-gen 生成 C++/Java 桩代码 aidl 工具生成,支持 NDK 后端
当前地位 逐渐被淘汰,Android 14 后不再推荐 官方推荐,新项目首选

嗯,这里要注意。HIDL 当年是为了 Treble 项目而生的,目的是让厂商能独立升级 HAL,不用等 Google 发新版本。这个想法很好,但实际用起来有点「重」。我在项目中遇到过,光是写一个 .hal 文件,就要定义一堆版本号、类型、回调接口,代码量翻倍。

而 AIDL 呢?它本来就是 Android 系统里最老的 IPC 方式,从 Android 1.0 就有了。Google 后来把它扩展了一下,加上了 NDK 后端支持,让 HAL 层也能用。说白了,就是「一个工具干所有事」,不再搞两套标准了。

我的建议:如果你现在要开发新项目,直接上 AIDL。别犹豫。HIDL 已经是过去式了,Google 在 Android 14 里已经明确表示不再维护 HIDL 接口。

3.2 ICameraProvider 接口

好,咱们来看第一个核心接口——ICameraProvider。这个接口是 Camera HAL 的「入口」,相当于一个服务注册中心。Framework 通过它来发现系统中到底有多少个 Camera 设备。

它的核心职责就两个:

  • 枚举设备:告诉上层「我有几个摄像头,分别是什么类型」
  • 提供设备:当上层要打开某个摄像头时,返回对应的 ICameraDevice 实例

在 HIDL 时代,它的定义长这样:

// ICameraProvider.hal
interface ICameraProvider {
    getCameraIdList() generates (Status status, vec<string> cameraIds);
    isSetTorchModeSupported() generates (Status status, bool supported);
    getCameraDeviceInterface(string cameraId) generates (Status status, ICameraDevice device);
};

到了 AIDL 时代,接口定义变得简洁多了:

// ICameraProvider.aidl
interface ICameraProvider {
    String[] getCameraIdList();
    ICameraDevice getCameraDeviceInterface(String cameraId);
    boolean isSetTorchModeSupported();
}

你发现没?AIDL 版本去掉了 generates 关键字,返回值直接写在方法签名里。这其实就是 AIDL 的「原生」风格——跟 Java 接口一模一样。

避坑指南:我曾经在移植一个老项目时,发现 HIDL 的 getCameraIdList() 返回的是 vec<string>,而 AIDL 返回的是 String[]。别小看这个区别,序列化方式完全不同。HIDL 的 vec 会多一层内存拷贝,在摄像头数量多的时候(比如 10 个以上),性能差距很明显。

3.3 ICameraDevice 接口

接下来是 ICameraDevice。这个接口代表一个具体的摄像头设备。你可以把它理解成「摄像头对象的抽象」。上层拿到这个接口后,就可以开始配置参数、开启预览、拍照了。

它的核心方法包括:

  • open():打开设备,返回一个 ICameraDeviceSession
  • getCameraCharacteristics():获取设备的静态信息(比如支持的分辨率、对焦模式等)
  • setTorchMode():控制闪光灯

在 AIDL 里,它的定义大概是这样的:

// ICameraDevice.aidl
interface ICameraDevice {
    ICameraDeviceSession open();
    CameraCharacteristics getCameraCharacteristics();
    void setTorchMode(boolean enabled);
}

嗯,这里有个细节。HIDL 版本的 open() 需要传递一个回调接口 ICameraDeviceCallback,用来接收异步事件(比如错误通知、快门回调)。而 AIDL 版本把这个回调移到了 ICameraDeviceSession 里。为什么?

我个人觉得,这是为了「职责单一」。设备打开前的回调(比如权限检查)和设备打开后的回调(比如拍照完成)混在一起,容易让人糊涂。AIDL 把它们拆开了,逻辑更清晰。

注意:在 HIDL 中,ICameraDeviceopen() 是同步的。也就是说,Framework 调用 open() 后,必须等 HAL 返回一个 ICameraDeviceSession 才能继续。如果 HAL 实现里有耗时操作(比如初始化传感器),就会卡住上层。我建议你在 HAL 层把初始化放到 ICameraDeviceSession 的构造函数里异步处理,别堵在 open() 里。

3.4 ICameraDeviceSession 接口

最后,也是最核心的接口——ICameraDeviceSession。这个接口负责真正的数据流处理。你想想看,拍照、录像、预览,所有跟图像数据打交道的操作,都在这里完成。

它的核心方法有:

  • constructDefaultRequestSettings():获取默认的拍照参数模板
  • configureStreams():配置数据流(比如指定输出分辨率、格式)
  • processCaptureRequest():提交一次拍照请求
  • flush():清空所有待处理的请求
  • close():关闭会话

在 AIDL 里,它的定义长这样:

// ICameraDeviceSession.aidl
interface ICameraDeviceSession {
    CameraMetadata constructDefaultRequestSettings(int templateId);
    Status configureStreams(StreamConfiguration config);
    int processCaptureRequest(CaptureRequest request, ICameraDeviceCallback callback);
    void flush();
    void close();
}

这里有个关键点——processCaptureRequest() 的返回值。在 HIDL 里,它返回的是 Status 枚举,表示请求是否提交成功。但在 AIDL 里,它返回一个 int,代表「请求 ID」。这个 ID 有什么用?

嗯,你想想看。当你提交一个拍照请求后,HAL 处理完会通过回调把结果返回。如果同时提交了多个请求(比如连拍),你怎么知道哪个结果对应哪个请求?靠的就是这个 ID。所以,AIDL 的设计更合理——把「提交」和「结果」通过 ID 关联起来。

我的经验:在实现 processCaptureRequest() 时,一定要处理好请求队列。我曾经遇到过一个 bug:上层连续提交了 100 个请求,HAL 处理不过来,导致内存暴涨。后来我在 HAL 里加了一个「最大待处理请求数」的限制,超过 32 个就直接返回错误。这个数字怎么来的?嗯,Android 官方 CTS 测试里就是这么规定的。

3.5 从 HIDL 迁移到 AIDL 的实战要点

如果你手头有老项目要迁移,我给你几个关键点:

  1. 接口命名变化:HIDL 的 @1.0::ICameraProvider 变成 AIDL 的 ICameraProvider,去掉了版本号
  2. 回调机制不同:HIDL 用 generates 关键字定义回调,AIDL 用 oneway 关键字
  3. 类型映射:HIDL 的 vec<uint8_t> 对应 AIDL 的 byte[]hidl_handle 对应 NativeHandle
  4. 服务注册:HIDL 用 hwservicemanager,AIDL 用 servicemanager,注册方式完全不同

我记得有一次帮客户做迁移,最头疼的就是回调接口的适配。HIDL 里回调是同步的,AIDL 里是异步的。如果不小心把同步逻辑写进回调里,就会死锁。嗯,这个坑我踩过,所以特别提醒你。

小技巧:迁移时,可以先在 AIDL 接口里保留 HIDL 的「影子方法」,比如 openLegacy(),用来兼容旧代码。等所有模块都迁移完了,再一次性清理掉。这样风险最小。

好了,这一章的内容就到这里。HIDL 和 AIDL 的区别,说白了就是「历史包袱」和「未来方向」的区别。ICameraProvider、ICameraDevice、ICameraDeviceSession 这三个接口,构成了 Camera HAL 的骨架。下一章,咱们会深入 configureStreams()processCaptureRequest() 的实现细节,那才是真正「硬核」的地方。