2、HIDL基础回顾:HIDL接口定义语言(IDL)语法、包名规则、数据类型映射、服务端与客户端模型

在深入Camera HAL层初始化工程之前,必须牢固掌握HIDL(HAL Interface Definition Language)的核心基础。HIDL是Android Treble架构的基石,它定义了Framework与HAL之间的契约。本节将系统回顾HIDL的接口定义语言语法、包名规则、数据类型映射以及服务端与客户端模型,为后续分析Camera HAL初始化流程奠定理论基础。

2.1 HIDL接口定义语言(IDL)语法

HIDL使用类似于C++和Java的语法来描述接口。一个典型的HIDL接口文件(.hal)包含包声明、导入语句、接口定义和方法声明。

核心语法要素:
  • 包声明:使用 package 关键字定义接口所属的包路径。
  • 导入:使用 import 关键字引入其他HIDL类型或接口。
  • 接口定义:使用 interface 关键字声明接口名称,可继承自其他接口。
  • 方法声明:定义接口方法,包含返回值类型、方法名和参数列表。
  • 类型定义:支持 enum、struct、union 等复合类型。

以下是一个Camera HAL中常见的HIDL接口定义示例:

// ICameraDevice.hal
package android.hardware.camera.device@3.4;

import android.hardware.camera.common@1.0::Status;
import android.hardware.camera.common@1.0::CameraMetadata;

interface ICameraDevice extends @3.2::ICameraDevice {
    /**
     * 初始化相机设备并返回状态。
     * @param cameraId 相机ID
     * @return Status 初始化结果
     */
    open(string cameraId) generates (Status status);

    /**
     * 配置相机数据流。
     * @param streams 数据流配置列表
     * @return Status 配置结果
     */
    configureStreams(vec<Stream> streams) generates (Status status);

    /**
     * 捕获请求。
     * @param request 捕获请求参数
     * @return Status 捕获状态
     */
    processCaptureRequest(CaptureRequest request) generates (Status status);
};
语法要点:HIDL方法使用 generates 关键字分隔输入参数和返回值,这与AIDL的 out 参数不同。所有HIDL方法调用都是同步的,但可以通过回调接口实现异步通知。

2.2 包名规则

HIDL的包名规则严格遵循Android的命名空间体系,确保接口的唯一性和版本管理。包名由三部分组成:包前缀、包路径和版本号。

组成部分 格式 示例 说明
包前缀 android.hardware android.hardware 固定前缀,表示硬件抽象层接口
包路径 模块名.子模块 camera.device 使用点号分隔的模块层级路径
版本号 @主版本.次版本 @3.4 主版本号不兼容时需升级,次版本号向后兼容

完整的包名示例:android.hardware.camera.device@3.4

版本管理规则:
  • 主版本号(Major)变更:接口不向后兼容,所有实现必须更新。
  • 次版本号(Minor)变更:只能添加新方法或类型,不能修改或删除已有接口。
  • 同一主版本下的不同次版本可以共存,客户端可以选择支持的最高次版本。

2.3 数据类型映射

HIDL定义了一套独立于语言的数据类型系统,并提供了到C++和Java的映射规则。理解这些映射对于编写正确的HAL实现至关重要。

HIDL类型 C++映射 Java映射 说明
int8_t int8_t byte 8位有符号整数
uint8_t uint8_t short 8位无符号整数
int32_t int32_t int 32位有符号整数
uint32_t uint32_t long 32位无符号整数
int64_t int64_t long 64位有符号整数
float float float 32位浮点数
double double double 64位浮点数
string std::string String UTF-8编码字符串
vec<T> std::vector<T> ArrayList<T> 可变长度数组
hidl_array<T, N> std::array<T, N> T[] 固定长度数组
hidl_handle native_handle_t* NativeHandle 文件描述符等原生句柄
MemoryBlock hidl_memory MemoryBlock 共享内存块
Camera HAL中常用类型:
  • CameraMetadata:相机元数据,使用 hidl_vec<uint8_t> 序列化传输。
  • Stream:数据流配置结构体,包含分辨率、格式、使用场景等字段。
  • CaptureRequest:捕获请求,包含输出缓冲区、元数据、物理相机ID等。
  • BufferStatus:缓冲区状态枚举,如 OK、ERROR、DROPPED。

2.4 服务端与客户端模型

HIDL采用经典的C/S(客户端/服务端)架构。服务端(HAL实现)注册服务并等待客户端(Framework)连接;客户端通过服务名获取代理,然后调用接口方法。整个通信过程由HIDL的Binder化机制(hwbinder)驱动。

HIDL 服务端与客户端通信模型 客户端 (Client) Camera Framework CameraService 应用进程 服务端 (Server) Camera HAL 实现 HAL Daemon 进程 硬件驱动层 hwbinder 通信层 Binder 驱动 / 共享内存 / 文件描述符传递 服务注册与发现 hwservicemanager 通过服务名查找/注册 接口调用流程 获取代理 → 序列化参数 → Binder传输 → 反序列化 → 执行
服务端与客户端交互步骤:
  1. 服务注册:HAL实现进程启动后,通过 hwservicemanager 注册服务,例如 ICameraDevice 注册为 "android.hardware.camera.device@3.4::ICameraDevice/default"。
  2. 服务发现:客户端(如CameraService)通过 ICameraDevice::getService() 获取服务代理。
  3. 接口调用:客户端通过代理调用接口方法,参数通过 hwbinder 序列化传输到服务端。
  4. 回调机制:服务端可以通过回调接口(如 ICameraDeviceCallback)异步通知客户端事件。

2.5 服务端与客户端代码骨架

以下展示Camera HAL中服务端和客户端的基本代码结构,帮助理解HIDL接口的实际使用方式。

服务端实现(HAL侧):

// CameraDevice.cpp
#include <android/hardware/camera/device/3.4/ICameraDevice.h>

using namespace android::hardware::camera::device::V3_4;

class CameraDevice : public ICameraDevice {
public:
    // 实现接口方法
    Return<Status> open(const hidl_string& cameraId) override {
        // 实际硬件初始化逻辑
        ALOGI("Opening camera: %s", cameraId.c_str());
        return Status::OK;
    }

    Return<Status> configureStreams(const hidl_vec<Stream>& streams) override {
        // 配置数据流
        return Status::OK;
    }

    Return<Status> processCaptureRequest(const CaptureRequest& request) override {
        // 处理捕获请求
        return Status::OK;
    }
};

// 注册服务
int main() {
    android::sp<ICameraDevice> service = new CameraDevice();
    android::status_t ret = service->registerAsService("default");
    if (ret != android::OK) {
        ALOGE("Failed to register camera device service");
        return -1;
    }
    // 进入服务循环
    android::hardware::joinRpcThreadpool();
    return 0;
}

客户端调用(Framework侧):

// CameraService.cpp
#include <android/hardware/camera/device/3.4/ICameraDevice.h>

using namespace android::hardware::camera::device::V3_4;

// 获取服务代理
android::sp<ICameraDevice> cameraDevice = ICameraDevice::getService("default");
if (cameraDevice == nullptr) {
    ALOGE("Failed to get camera device service");
    return;
}

// 调用接口方法
Status status;
Return<Status> ret = cameraDevice->open("0", [&](Status s) {
    status = s;
});
if (!ret.isOk()) {
    ALOGE("Failed to call open: %s", ret.description().c_str());
}

// 配置数据流
hidl_vec<Stream> streams;
// ... 填充streams ...
ret = cameraDevice->configureStreams(streams, [&](Status s) {
    status = s;
});
关键注意事项:
  • 服务端必须调用 joinRpcThreadpool() 进入线程池循环,否则无法处理客户端请求。
  • 客户端调用 getService() 时,如果服务尚未注册,会阻塞等待直到超时(默认5秒)。
  • 所有HIDL方法调用都是同步的,但可以通过回调接口实现异步通知,避免阻塞客户端。
  • 在Camera HAL中,processCaptureRequest 等高频方法需要特别注意性能,避免在Binder传输中产生过多拷贝。

2.6 小结

本节回顾了HIDL的核心基础:

  • IDL语法:使用 package、import、interface 关键字定义接口,方法使用 generates 分隔参数和返回值。
  • 包名规则:遵循 android.hardware.模块名@版本号 格式,版本管理严格区分主次版本。
  • 数据类型映射:HIDL类型到C++/Java有明确的映射关系,Camera HAL中常用 hidl_vec、CameraMetadata、Stream 等类型。
  • C/S模型:服务端注册服务,客户端通过 hwservicemanager 发现并获取代理,通过 hwbinder 进行跨进程通信。