4. Camera HAL 适配流程:HAL 库加载、open/close 与 initialize 实现

Camera HAL(硬件抽象层)是 Android 相机框架中连接上层 Camera Service 与底层硬件驱动的关键桥梁。在 CTS 测试中,HAL 层的正确实现直接决定了测试用例的通过率。本章将深入剖析 HAL 适配的核心流程,包括 HAL 库的加载机制、open()/close() 接口的实现规范以及 initialize() 接口的初始化逻辑。

核心要点:HAL 适配的三大基石——库加载决定模块发现、open/close 管理设备生命周期、initialize 完成相机管线初始化。任何一环的缺失或错误实现都会导致 CTS 测试用例大面积失败。

4.1 HAL 库加载流程

Android 相机 HAL 采用动态库加载机制,Camera Service 通过 HAL_MODULE_INFO_SYM 符号找到 HAL 模块入口。加载流程分为三个阶段:库文件定位、符号解析、模块注册。

4.1.1 加载路径与优先级

系统按照以下优先级顺序搜索 HAL 库:

优先级 路径 说明
1(最高) /vendor/lib/hw/camera.<variant>.so Vendor 分区,Treble 架构推荐路径
2 /system/lib/hw/camera.<variant>.so System 分区,传统路径
3 /odm/lib/hw/camera.<variant>.so ODM 分区,用于设备定制

其中 <variant> 由 ro.hardware 属性或 ro.product.board 决定,例如 camera.msm8998.so。

4.1.2 模块结构体定义

每个 HAL 库必须导出一个名为 HAL_MODULE_INFO_SYM 的 hw_module_t 结构体实例:

static struct hw_module_methods_t camera_module_methods = {
    .open = camera_device_open,
};

struct camera_module HAL_MODULE_INFO_SYM = {
    .common = {
        .tag           = HARDWARE_MODULE_TAG,
        .module_api_version = CAMERA_MODULE_API_VERSION_2_4,
        .hal_api_version    = HARDWARE_HAL_API_VERSION,
        .id            = CAMERA_HARDWARE_MODULE_ID,
        .name          = "Camera HAL Module",
        .author        = "Vendor Name",
        .methods       = &camera_module_methods,
        .dso           = NULL,
        .reserved      = {0},
    },
    .get_number_of_cameras      = get_number_of_cameras,
    .get_camera_info            = get_camera_info,
    .set_callbacks              = set_callbacks,
    .get_vendor_tag_ops         = get_vendor_tag_ops,
    .open_legacy                = NULL,
    .set_torch_mode             = set_torch_mode,
    .init                       = NULL,
    .reserved                   = {0},
};
CTS 检查点:module_api_version 必须与 Android 版本匹配。Android 13+ 要求 CAMERA_MODULE_API_VERSION_2_4,低于此版本会导致 android.hardware.camera2.cts.CameraManagerTest#testCameraManagerGetDeviceIdList 失败。

4.1.3 加载流程 SVG 图

HAL 库加载流程 Camera Service hw_get_module("camera", &module) dlopen("camera.<variant>.so") dlsym("HAL_MODULE_INFO_SYM") 模块注册到 CameraService 关键:HAL_MODULE_INFO_SYM 必须正确导出,否则 CameraService 无法发现 HAL 模块

4.2 open() / close() 接口实现

open() 和 close() 是 HAL 层最基本的设备生命周期管理接口。CTS 测试会反复调用这两个接口来验证设备的打开与关闭行为是否符合预期。

4.2.1 open() 接口规范

open() 函数原型定义在 hardware/libhardware/include/hardware/hardware.h 中:

int camera_device_open(const struct hw_module_t* module,
                       const char* name,
                       struct hw_device_t** device);

实现要点:

  • 参数校验:检查 module、name、device 是否为 NULL
  • 相机 ID 解析:name 为相机 ID 字符串(如 "0"、"1"),需转换为整数
  • 内存分配:分配 camera2_device_t 结构体,使用 calloc 确保零初始化
  • 函数指针赋值:填充 ops 结构体中的所有回调函数
  • 硬件初始化:打开底层驱动设备节点(如 /dev/video*)
static int camera_device_open(const struct hw_module_t* module,
                               const char* name,
                               struct hw_device_t** device) {
    if (module == NULL || name == NULL || device == NULL) {
        ALOGE("Invalid parameters");
        return -EINVAL;
    }

    int camera_id = atoi(name);
    if (camera_id < 0 || camera_id >= MAX_CAMERAS) {
        ALOGE("Invalid camera id: %d", camera_id);
        return -ENODEV;
    }

    camera2_device_t* camera_dev = (camera2_device_t*)calloc(1, sizeof(*camera_dev));
    if (camera_dev == NULL) {
        ALOGE("Failed to allocate camera device");
        return -ENOMEM;
    }

    camera_dev->common.tag     = HARDWARE_DEVICE_TAG;
    camera_dev->common.version = CAMERA_DEVICE_API_VERSION_3_5;
    camera_dev->common.module  = (struct hw_module_t*)module;
    camera_dev->common.close   = camera_device_close;

    // 填充 ops 结构体
    camera_dev->ops = &camera_device_ops;

    // 打开底层驱动
    int fd = open("/dev/video" camera_id, O_RDWR);
    if (fd < 0) {
        ALOGE("Failed to open video device: %s", strerror(errno));
        free(camera_dev);
        return -ENODEV;
    }

    *device = &camera_dev->common;
    return 0;
}

4.2.2 close() 接口规范

close() 函数负责释放所有资源:

static int camera_device_close(struct hw_device_t* device) {
    if (device == NULL) {
        return -EINVAL;
    }

    camera2_device_t* camera_dev = (camera2_device_t*)device;

    // 停止所有正在进行的操作
    camera_dev->ops->flush(camera_dev);

    // 关闭驱动设备
    close(camera_dev->fd);

    // 释放内部资源
    free(camera_dev->static_metadata);
    free(camera_dev->dynamic_metadata);

    // 释放设备结构体
    free(camera_dev);
    return 0;
}
CTS 常见失败场景:
  • testCameraOpenAndClose 失败:close() 后未将 device 指针置 NULL,导致悬空指针
  • testConcurrentOpen 失败:未实现并发打开限制,或未返回 -EBUSY
  • testOpenInvalidId 失败:传入非法相机 ID 时未返回 -ENODEV

4.2.3 open/close 状态机 SVG 图

Camera 设备状态机(open/close 生命周期) CLOSED OPENING OPENED CLOSING open() 调用 初始化完成 close() 调用 资源释放完成 注意:CTS 要求 open() 在 500ms 内完成,close() 在 200ms 内完成,超时会导致测试失败

4.3 initialize() 接口实现

initialize() 是 camera3_device_ops 中的核心接口,在 open() 之后由 Camera Service 调用,用于完成相机管线的初始化配置。

4.3.1 接口定义与参数

int (*initialize)(const struct camera3_device *device,
                  const camera3_callback_ops_t *callback_ops);

参数说明:

参数 类型 说明
device const struct camera3_device * 由 open() 返回的设备句柄
callback_ops const camera3_callback_ops_t * 上层回调函数指针,用于通知结果、错误等

4.3.2 初始化流程

  1. 参数校验:检查 device 和 callback_ops 非空
  2. 状态检查:确保设备处于 OPENED 状态,未初始化
  3. 回调注册:保存 callback_ops 指针,用于后续通知
  4. 传感器初始化:配置传感器输出格式、帧率、分辨率
  5. ISP 初始化:加载 ISP 固件,配置图像处理管线
  6. 元数据初始化:生成静态元数据(static_metadata)
  7. 流配置准备:初始化流配置表,准备接受 configure_streams() 调用
static int camera_device_initialize(
        const struct camera3_device *device,
        const camera3_callback_ops_t *callback_ops) {
    if (device == NULL || callback_ops == NULL) {
        ALOGE("initialize: invalid parameters");
        return -EINVAL;
    }

    camera2_device_t* cam_dev = (camera2_device_t*)device;
    if (cam_dev->status != CAMERA_DEVICE_STATUS_OPENED) {
        ALOGE("initialize: device not in OPENED state");
        return -ENODEV;
    }

    // 保存回调函数
    cam_dev->callback_ops = callback_ops;

    // 初始化传感器
    int ret = sensor_init(&cam_dev->sensor_ctx);
    if (ret != 0) {
        ALOGE("initialize: sensor init failed: %d", ret);
        return ret;
    }

    // 初始化 ISP
    ret = isp_init(&cam_dev->isp_ctx);
    if (ret != 0) {
        ALOGE("initialize: ISP init failed: %d", ret);
        sensor_deinit(&cam_dev->sensor_ctx);
        return ret;
    }

    // 生成静态元数据
    ret = generate_static_metadata(cam_dev);
    if (ret != 0) {
        ALOGE("initialize: metadata generation failed");
        isp_deinit(&cam_dev->isp_ctx);
        sensor_deinit(&cam_dev->sensor_ctx);
        return ret;
    }

    cam_dev->status = CAMERA_DEVICE_STATUS_INITIALIZED;
    ALOGI("Camera device %d initialized successfully", cam_dev->camera_id);
    return 0;
}

4.3.3 初始化失败处理

CTS 要求 initialize() 在失败时必须回滚所有已分配的资源,并返回明确的错误码:

错误码 含义 典型场景
-EINVAL 参数无效 device 或 callback_ops 为 NULL
-ENODEV 设备不可用 传感器未连接、ISP 固件加载失败
-ENOMEM 内存不足 元数据缓冲区分配失败
-EIO I/O 错误 驱动通信异常、寄存器读写失败
CTS 测试用例关联:
  • android.hardware.camera2.cts.CameraDeviceTest#testCameraDeviceCreateCaptureSession — 依赖 initialize() 正确完成
  • android.hardware.camera2.cts.CameraMetadataTest#testStaticMetadata — 验证 initialize() 生成的静态元数据完整性
  • android.hardware.camera2.cts.CameraExtensionTest#testExtensionInitialize — 扩展功能初始化依赖基础初始化成功

4.3.4 initialize 调用序列 SVG 图

initialize() 调用序列与内部流程 Camera Service initialize() HAL initialize() 参数校验 回调注册 传感器初始化 ISP 初始化 元数据生成 返回 0 / 错误码 Camera Service 关键:initialize() 失败后必须回滚所有资源,且设备状态回到 OPENED,允许再次调用 initialize()

4.4 常见适配问题与排查方法

实战经验:在 CTS 测试中,HAL 适配问题通常表现为特定测试用例的稳定失败。以下表格总结了三个核心接口的常见问题模式。
接口 常见问题 CTS 测试用例 排查方法
HAL_MODULE_INFO_SYM 符号未导出或版本号错误 CameraManagerTest#testCameraManagerGetDeviceIdList 使用 readelf -s camera.so | grep HAL_MODULE 检查符号
open() 返回错误码不符合规范 CameraDeviceTest#testCameraOpenAndClose 添加日志,检查 errno 与返回码的对应关系
close() 资源泄漏或重复关闭 CameraDeviceTest#testCameraOpenAndClose 使用 valgrind 或 asan 检测内存泄漏
initialize() 回调函数未保存或元数据不完整 CameraMetadataTest#testStaticMetadata 对比 camera_metadata_enum_sizes.txt 检查元数据标签

适配 HAL 时,建议遵循以下排查步骤:

  1. 日志先行:在 open()、close()、initialize() 入口和出口添加 ALOGI 日志,记录参数和返回值
  2. 状态机验证:确保设备状态转换符合预期,使用 assert 或日志检查非法状态转换
  3. CTS 专项测试:单独运行 android.hardware.camera2.cts.CameraDeviceTest 验证 open/close 行为
  4. 元数据验证:使用 camera_metadata_t 工具检查静态元数据的完整性和正确性
重要提醒:在 Android 13+ 中,initialize() 的调用时机发生了变化——Camera Service 会在 open() 后立即调用 initialize(),不再等待 configure_streams()。如果 HAL 实现在 initialize() 中依赖流配置信息,将导致初始化失败。请确保 initialize() 只做与流无关的初始化工作。