3、HAL回调注册流程:CameraDevice.open()流程、回调接口注册、Session创建

好,咱们今天来聊聊HAL回调注册的核心流程。说白了,就是搞清楚Camera应用层到底是怎么跟HAL层“握手”的。我个人习惯把这一整套流程拆成三个关键节点:CameraDevice.open()回调接口注册、以及Session创建。这三个节点串起来,就是一条完整的回调链路。

3.1 CameraDevice.open():一切从这里开始

你想想看,应用层要拍照,第一步肯定是打开相机设备。这个open()调用,在Framework层会一路往下走,最终通过HIDL或AIDL接口,触达到HAL层。

我记得在Android 8.0之后,Google强制要求用Treble架构。那时候我正好在做一个项目,从旧版HAL1迁移到HAL3,open()的流程变化特别大。以前是直接调hw_module_t->open(),现在变成了通过ICameraDevice.hal接口来操作。

具体来说,open()流程里HAL层会做几件事:

  • 初始化硬件资源:比如打开Sensor、ISP、Flash等。
  • 分配内存池:为后续的Buffer流转做准备。
  • 返回一个CameraDevice的代理对象:这个对象就是Framework后续操作相机的“遥控器”。

这里有个坑,我曾经遇到过:如果HAL层在open()里做了太多耗时操作,比如加载固件、校准数据,会导致应用层ANR。所以,我建议把重活放到后面异步去做,open()只做最轻量的初始化。

核心要点open()返回的CameraDevice对象,是后续所有回调注册的“前提”。没有它,后面什么都干不了。

3.2 回调接口注册:Framework与HAL的“通信协议”

好,设备打开了。接下来要干什么?注册回调。说白了,就是Framework告诉HAL:“嘿,我这里有三个回调接口,你以后有数据或者事件,就通过这些接口通知我。”

在HAL3架构下,回调接口主要分三类:

回调接口 作用 我见过的坑
notify() 传递异步事件,比如快门、错误、3A收敛等 事件ID定义不一致,导致Framework解析失败
processCaptureResult() 返回拍照或预览的帧数据(metadata + buffer) Buffer Handle传递错误,导致黑屏或花屏
processRequest()(部分版本) 处理请求的回调确认 回调顺序错乱,导致预览卡顿

注册的过程,其实就是Framework调用HAL的ICameraDeviceSession::setCallback()方法,把这三个接口的指针传进去。HAL层会把这些指针保存下来,以后用它们来“反向调用”Framework。

嗯,这里要注意:回调接口的注册时机,必须在第一次下发Request之前完成。否则,HAL层可能已经产生了数据,但Framework还没准备好接收,数据就丢了。

我的经验:我曾经在一个项目里,因为HAL层在open()之后立即开始内部预览,而Framework的回调注册还没完成,结果导致第一帧数据直接丢失。后来我强制在setCallback()返回之前,HAL层不允许产生任何数据。这个改动虽然小,但解决了大问题。

3.3 Session创建:真正的“工作区”建立

回调注册完了,是不是就能直接拍照了?还不行。你还需要创建一个Session。Session是什么?你可以把它理解成一个“工作区”。

Framework会调用ICameraDevice::createDefaultRequest()拿到默认的Request模板,然后调用ICameraDeviceSession::constructDefaultRequestSettings()来创建Session。这个Session对象,就是后续所有Capture Request的“容器”。

Session创建时,HAL层会做几件关键的事:

  • 配置Stream:根据Framework传过来的Stream配置(比如预览流、拍照流、分析流),HAL层会分配对应的硬件通道和Buffer队列。
  • 初始化Pipeline:把Sensor、ISP、后处理模块串联起来,形成一个完整的处理链路。
  • 返回Session对象:Framework拿到这个Session后,才能下发Request。

这里有一个容易忽略的点:Session创建失败,会导致整个相机无法使用。我遇到过一种情况,HAL层在配置Stream时,发现某个分辨率不支持,直接返回了错误。但Framework没有做充分的容错处理,结果应用直接崩溃了。

避坑指南:我曾经在调试一个双摄项目时,发现主摄和副摄的Stream配置冲突。主摄要求4K预览,副摄只支持1080P。HAL层在创建Session时,因为Stream组合不合法,直接返回了-EINVAL。后来我修改了HAL的Stream校验逻辑,允许降级处理,问题才解决。

3.4 三个节点的时序关系

好,咱们把这三个节点串起来,看看完整的时序:

  1. CameraDevice.open():打开硬件,返回设备对象。
  2. 回调接口注册:Framework把notifyprocessCaptureResult等接口传给HAL。
  3. Session创建:配置Stream,初始化Pipeline,返回Session对象。

这三个步骤,必须严格按顺序执行。你不能先创建Session再注册回调,也不能先注册回调再打开设备。顺序错了,整个相机系统就会乱套。

我个人习惯在代码里加一个状态机,用来跟踪这三个步骤的完成情况。比如:

enum CameraState {
    STATE_CLOSED,
    STATE_OPENED,
    STATE_CALLBACKS_REGISTERED,
    STATE_SESSION_CREATED,
    STATE_STREAMING
};

每次状态切换时,都做一次断言检查。这样能提前发现很多时序问题。

3.5 总结

好了,这一章的内容就到这里。总结一下:

  • CameraDevice.open()是起点,轻量初始化是关键。
  • 回调接口注册是通信协议,必须在Request下发前完成。
  • Session创建是工作区,Stream配置要小心。

下一章,我会详细讲讲Capture Request的构建与下发流程,看看Request是怎么从Framework一路走到HAL的。到时候咱们再聊。