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 三个节点的时序关系
好,咱们把这三个节点串起来,看看完整的时序:
- CameraDevice.open():打开硬件,返回设备对象。
- 回调接口注册:Framework把
notify、processCaptureResult等接口传给HAL。 - 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的。到时候咱们再聊。