4. Camera HAL 适配流程:HAL 库加载、open/close 与 initialize 实现
Camera HAL(硬件抽象层)是 Android 相机框架中连接上层 Camera Service 与底层硬件驱动的关键桥梁。在 CTS 测试中,HAL 层的正确实现直接决定了测试用例的通过率。本章将深入剖析 HAL 适配的核心流程,包括 HAL 库的加载机制、open()/close() 接口的实现规范以及 initialize() 接口的初始化逻辑。
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},
};
module_api_version 必须与 Android 版本匹配。Android 13+ 要求 CAMERA_MODULE_API_VERSION_2_4,低于此版本会导致 android.hardware.camera2.cts.CameraManagerTest#testCameraManagerGetDeviceIdList 失败。
4.1.3 加载流程 SVG 图
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;
}
testCameraOpenAndClose失败:close()后未将device指针置 NULL,导致悬空指针testConcurrentOpen失败:未实现并发打开限制,或未返回-EBUSYtestOpenInvalidId失败:传入非法相机 ID 时未返回-ENODEV
4.2.3 open/close 状态机 SVG 图
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 初始化流程
- 参数校验:检查
device和callback_ops非空 - 状态检查:确保设备处于
OPENED状态,未初始化 - 回调注册:保存
callback_ops指针,用于后续通知 - 传感器初始化:配置传感器输出格式、帧率、分辨率
- ISP 初始化:加载 ISP 固件,配置图像处理管线
- 元数据初始化:生成静态元数据(
static_metadata) - 流配置准备:初始化流配置表,准备接受
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 错误 | 驱动通信异常、寄存器读写失败 |
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 图
4.4 常见适配问题与排查方法
| 接口 | 常见问题 | 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 时,建议遵循以下排查步骤:
- 日志先行:在
open()、close()、initialize()入口和出口添加ALOGI日志,记录参数和返回值 - 状态机验证:确保设备状态转换符合预期,使用
assert或日志检查非法状态转换 - CTS 专项测试:单独运行
android.hardware.camera2.cts.CameraDeviceTest验证 open/close 行为 - 元数据验证:使用
camera_metadata_t工具检查静态元数据的完整性和正确性
initialize() 的调用时机发生了变化——Camera Service 会在 open() 后立即调用 initialize(),不再等待 configure_streams()。如果 HAL 实现在 initialize() 中依赖流配置信息,将导致初始化失败。请确保 initialize() 只做与流无关的初始化工作。