第4章:HAL启动流程与初始化
好,咱们今天聊聊Camera HAL的启动流程。说实话,这块内容我当年刚接触时也觉得挺绕的,但搞懂了之后,你会发现它其实就是一个「层层递进」的过程。从Provider启动,到模块加载,再到Device创建,每一步都有它的设计逻辑。
4.1 CameraProvider的启动
先说说Provider。它是什么?说白了,它是HAL层和Framework之间的「中间人」。在Android系统中,CameraProvider是一个独立的服务进程,由cameraserver启动。
我记得第一次调试时,发现Provider死活起不来。查了半天,原来是manifest文件里少配了一个权限。嗯,这里要提醒大家:Provider的启动依赖于正确的SElinux策略和权限配置。
启动流程大致是这样的:
cameraserver进程启动,读取manifest.xml- 根据manifest找到对应的HAL实现库(比如
android.hardware.camera.provider@2.4-impl.so) - 通过HIDL机制加载Provider服务
- Provider注册到ServiceManager中
- Framework通过ServiceManager获取Provider服务
关键点:Provider的启动是「按需加载」的。Framework不会一上来就加载所有Provider,而是等到真正需要时才去获取。这样做的好处是节省内存,但坏处是第一次打开相机时会有点延迟。
4.2 HAL模块加载
Provider启动后,接下来就是加载HAL模块了。这里我习惯把模块加载分为两个阶段:
4.2.1 静态加载 vs 动态加载
| 加载方式 | 特点 | 适用场景 |
|---|---|---|
| 静态加载 | 编译时链接,启动快 | 单摄像头方案 |
| 动态加载 | 运行时加载,灵活 | 多摄像头、热插拔 |
高通平台现在基本都是动态加载了。你想想看,现在手机动辄三四个摄像头,如果全部静态加载,那内存占用得多大?
4.2.2 模块加载的具体流程
模块加载的核心函数是HAL_module_load()。我给大家简化一下关键代码:
// 伪代码,实际实现更复杂
status_t HAL_module_load(const char* module_name) {
// 1. 打开动态库
void* handle = dlopen(module_name, RTLD_NOW);
if (!handle) {
ALOGE("Failed to load module: %s", dlerror());
return -ENODEV;
}
// 2. 获取HMI(HAL Module Interface)
hw_module_t* hmi = (hw_module_t*)dlsym(handle, "HAL_MODULE_INFO_SYM");
if (!hmi) {
ALOGE("Failed to find HMI in module");
dlclose(handle);
return -ENOENT;
}
// 3. 检查模块版本
if (hmi->tag != HARDWARE_MODULE_TAG) {
ALOGE("Module tag mismatch");
return -EINVAL;
}
// 4. 初始化模块
return hmi->init(hmi);
}
个人经验:我在项目中遇到过dlopen失败的情况,多半是因为库的依赖没打全。建议用ldd命令检查一下so文件的依赖关系,省得走弯路。
4.3 CameraDevice的创建
模块加载完成后,就该创建CameraDevice了。这一步是真正和硬件打交道的开始。
4.3.1 创建流程
创建CameraDevice的过程,说白了就是「从抽象到具体」:
- Framework调用
ICameraProvider::getCameraDeviceInterface() - Provider根据cameraId找到对应的模块
- 调用
hw_module_t->methods->open()打开设备 - 返回
camera_device_t接口 - 封装成HIDL接口返回给Framework
4.3.2 初始化细节
设备打开后,还需要做一系列初始化工作。我整理了一个清单:
- Sensor初始化:配置MIPI接口、时钟、电源
- ISP初始化:分配buffer、设置处理管线
- EEPROM读取:获取校准数据(这个经常出问题)
- AF初始化:马达归零、设置初始位置
- 闪光灯初始化:检测LED是否正常
避坑指南:我曾经在EEPROM读取上栽过跟头。有个项目每次开机第一次打开相机都黑屏,查了两天才发现是EEPROM读取超时导致的。后来加了重试机制才解决。所以建议大家在初始化时,对EEPROM这类容易出问题的外设做超时保护。
4.4 初始化中的状态机
CameraDevice的初始化其实是一个状态机。我画了个简化的状态图:
UNINITIALIZED -> OPENED -> INITIALIZED -> CONFIGURED -> STARTED
| | | |
v v v v
ERROR ERROR ERROR ERROR
每个状态转换都有对应的检查点。比如从OPENED到INITIALIZED,必须确保所有硬件都初始化成功。如果某个步骤失败,状态机会回退到ERROR状态。
我个人习惯在状态转换时加日志,这样调试时能清楚看到卡在哪一步。比如:
// 状态转换日志示例
ALOGD("CameraDevice: state transition %s -> %s",
stateToString(old_state), stateToString(new_state));
4.5 常见问题与调试技巧
最后分享几个我实际工作中遇到的问题:
- Provider启动失败:检查SELinux权限,用
audit2allow生成策略 - 模块加载crash:用
addr2line定位crash位置 - Device创建超时:检查硬件初始化流程,特别是I2C通信
- 状态机卡住:加详细日志,打印每个步骤的执行时间
嗯,关于HAL启动流程和初始化,今天就讲这么多。这块内容虽然基础,但搞清楚了,后面调试各种问题就会顺手很多。下一章咱们聊聊数据流,那才是真正考验功底的地方。