第4章:HAL启动流程与初始化

好,咱们今天聊聊Camera HAL的启动流程。说实话,这块内容我当年刚接触时也觉得挺绕的,但搞懂了之后,你会发现它其实就是一个「层层递进」的过程。从Provider启动,到模块加载,再到Device创建,每一步都有它的设计逻辑。

4.1 CameraProvider的启动

先说说Provider。它是什么?说白了,它是HAL层和Framework之间的「中间人」。在Android系统中,CameraProvider是一个独立的服务进程,由cameraserver启动。

我记得第一次调试时,发现Provider死活起不来。查了半天,原来是manifest文件里少配了一个权限。嗯,这里要提醒大家:Provider的启动依赖于正确的SElinux策略和权限配置

启动流程大致是这样的:

  1. cameraserver进程启动,读取manifest.xml
  2. 根据manifest找到对应的HAL实现库(比如android.hardware.camera.provider@2.4-impl.so
  3. 通过HIDL机制加载Provider服务
  4. Provider注册到ServiceManager中
  5. 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的过程,说白了就是「从抽象到具体」:

  1. Framework调用ICameraProvider::getCameraDeviceInterface()
  2. Provider根据cameraId找到对应的模块
  3. 调用hw_module_t->methods->open()打开设备
  4. 返回camera_device_t接口
  5. 封装成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

每个状态转换都有对应的检查点。比如从OPENEDINITIALIZED,必须确保所有硬件都初始化成功。如果某个步骤失败,状态机会回退到ERROR状态。

我个人习惯在状态转换时加日志,这样调试时能清楚看到卡在哪一步。比如:

// 状态转换日志示例
ALOGD("CameraDevice: state transition %s -> %s", 
      stateToString(old_state), stateToString(new_state));

4.5 常见问题与调试技巧

最后分享几个我实际工作中遇到的问题:

  • Provider启动失败:检查SELinux权限,用audit2allow生成策略
  • 模块加载crash:用addr2line定位crash位置
  • Device创建超时:检查硬件初始化流程,特别是I2C通信
  • 状态机卡住:加详细日志,打印每个步骤的执行时间

嗯,关于HAL启动流程和初始化,今天就讲这么多。这块内容虽然基础,但搞清楚了,后面调试各种问题就会顺手很多。下一章咱们聊聊数据流,那才是真正考验功底的地方。