4、Camera HAL层初始化:HAL Module加载流程、CameraProvider启动、CameraDeviceSession创建

好,咱们今天聊聊Camera HAL层初始化的核心流程。这部分内容,说白了就是回答一个问题:系统怎么找到你的Sensor,并且让它开始干活?

我刚开始接触HAL层时,觉得这玩意儿就是个黑盒子。后来踩了不少坑,才慢慢理清楚。今天我把整个流程拆成三个阶段来讲:HAL Module加载CameraProvider启动CameraDeviceSession创建。每个阶段我都会结合自己的项目经验,给你讲讲那些容易翻车的地方。

4.1 HAL Module加载流程

先说说HAL Module是什么。你可以把它理解成一个插件管理器。系统启动时,会去加载这个模块,然后从里面找到所有可用的Camera设备。

在Android系统中,HAL Module的入口函数是HAL_MODULE_INFO_SYM。这个结构体定义在camera_common.h里。我贴一段典型的代码,你感受一下:

// 这是HAL Module的核心结构体
struct hw_module_t HAL_MODULE_INFO_SYM = {
    .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 = "My Camera HAL Module",
    .author = "Your Company",
    .methods = &hal_module_methods,
};

这里有个关键点——hal_module_methods。它里面定义了open函数指针。系统就是通过这个open来打开具体的Camera设备的。

核心要点:HAL Module加载时,系统会调用hw_get_module()函数。这个函数会根据id字段(这里是CAMERA_HARDWARE_MODULE_ID)去查找对应的.so文件。查找路径通常是/vendor/lib/hw//system/lib/hw/

我记得有一次,我编译完HAL后,发现系统死活加载不到我的模块。查了半天,原来是.so文件名没按规范命名。Android要求文件名必须是camera.<variant>.so这种格式。比如camera.myplatform.so。这个坑,我劝你提前注意。

4.2 CameraProvider启动

HAL Module加载完成后,接下来就是CameraProvider的启动。这部分是Android Treble架构引入的,目的是把HAL层和Framework层解耦。

CameraProvider运行在一个独立的进程中,通过HIDL接口与Framework通信。启动流程大致如下:

  1. 注册服务:CameraProvider启动后,会向hwservicemanager注册自己。
  2. 枚举设备:Framework通过HIDL调用getCameraIdList(),获取所有可用的Camera ID。
  3. 获取信息:Framework再调用getCameraCharacteristics(),获取每个Camera的静态信息。

我画个简单的流程图,帮你理解:

Framework (Java层)
    ↓ HIDL调用
CameraProvider (Native进程)
    ↓ 调用HAL Module
HAL Module (你的代码)
    ↓ 返回设备信息
CameraProvider
    ↓ 返回给Framework
Framework

个人经验:在实现getCameraIdList()时,一定要确保返回的ID是连续的。我曾经因为ID不连续,导致上层应用在切换摄像头时崩溃。嗯,这个坑我印象很深。

另外,CameraProvider启动时,会检查HAL Module的版本。如果你的HAL版本和Framework期望的不一致,Provider会直接拒绝启动。所以,module_api_version这个字段一定要填对。

4.3 CameraDeviceSession创建

最后一步,也是最关键的一步——创建CameraDeviceSession。这个Session,说白了就是你和Sensor之间的一个会话通道。所有拍照、预览的操作,都要通过这个Session来完成。

创建Session的流程,我拆成几步来讲:

4.3.1 打开设备

Framework调用open()函数,传入Camera ID。HAL层需要返回一个camera3_device_t结构体。这个结构体里,最重要的就是ops字段,它定义了所有操作的回调函数。

// camera3_device_t 结构体
typedef struct camera3_device {
    hw_device_t common;
    camera3_device_ops_t *ops;
    void *priv;
} camera3_device_t;

这里的ops包含了initialize()configure_streams()process_capture_request()等关键函数。你想想看,这些函数就是你和Sensor交互的接口。

4.3.2 初始化Session

initialize()函数被调用时,HAL层需要做两件事:

  • 分配资源:比如分配内存池、创建工作线程。
  • 注册回调:把camera3_callback_ops_t结构体保存下来。这个回调用于向Framework通知事件,比如拍照完成、错误发生等。

注意:initialize()中,千万不要做耗时操作。比如初始化Sensor、加载固件这些,应该放到后面configure_streams()或第一次process_capture_request()时再做。否则,Framework可能会因为超时而杀掉你的Provider进程。

4.3.3 配置数据流

configure_streams()是Session创建中最复杂的一步。Framework会告诉HAL层,它需要哪些数据流(比如预览流、拍照流)。HAL层需要根据这些要求,配置Sensor的输出格式、分辨率、帧率等。

我举个例子,假设Framework要求两个流:

Stream ID 用途 分辨率 格式
0 预览 1920x1080 NV12
1 拍照 4000x3000 JPEG

HAL层需要判断,Sensor能否同时输出这两个流。如果不能,就需要做Stream SplitDownscale。我个人的习惯是,在configure_streams()中先做一次可行性检查,如果不行,直接返回错误码-EINVAL

4.3.4 开始捕获

Session创建完成后,Framework会通过process_capture_request()发送捕获请求。HAL层收到请求后,需要:

  1. 解析请求:提取出需要的参数,比如曝光时间、增益、对焦位置等。
  2. 配置Sensor:通过I2C/SPI接口,把这些参数写入Sensor寄存器。
  3. 触发捕获:发送软件或硬件触发信号,让Sensor开始曝光。
  4. 返回数据:曝光完成后,把图像数据通过回调函数返回给Framework。

避坑指南:我曾经在process_capture_request()中直接操作Sensor寄存器,结果导致UI卡顿。后来发现,千万不要在回调线程中做耗时操作。正确的做法是,把请求放入队列,由专门的工作线程去处理。这样既保证了响应速度,又避免了阻塞。

4.4 总结与经验分享

好了,整个HAL层初始化的流程,我带你走了一遍。从Module加载,到Provider启动,再到Session创建,每一步都有它的设计意图。

最后,我分享几个实战中的小技巧:

  • 日志要打全:在关键函数入口和出口都加上ALOGD。调试时,看日志就能定位问题。
  • 超时处理:Framework对HAL的调用是有超时限制的。比如configure_streams(),如果超过5秒没返回,Provider会被杀掉。所以,耗时操作一定要异步处理。
  • 版本兼容:如果你的HAL要适配多个Android版本,记得检查module_api_versionhal_api_version。不同版本对HAL的要求不一样。

嗯,今天就先聊到这儿。下一章,我会讲讲Sensor上电与初始化时序,那是另一个容易踩坑的地方。咱们到时候见。