4. CameraProvider 初始化流程:从 ServiceManager 注册到 HAL 服务启动

CameraProvider 的初始化是 Android Camera 架构中承上启下的关键环节。它负责将底层的 HAL 实现封装为可被 CameraService 发现和调用的 Binder 服务。整个流程从进程启动开始,经过 ServiceManager 注册、HAL 接口加载、设备枚举,最终进入消息循环等待客户端请求。本章将拆解这一完整链路,并重点分析 HIDL / AIDL 接口的实现差异。

4.1 初始化全流程概览

CameraProvider 的初始化可划分为 5 个主要阶段,每个阶段都有明确的职责和关键检查点。下表总结了各阶段的核心任务:

阶段 核心任务 关键类 / 接口 典型耗时
1. 进程启动与配置解析 解析 SELinux 策略、加载系统属性、初始化日志系统 main()、property_get < 5ms
2. ServiceManager 注册 将 ICameraProvider 服务注册到 ServiceManager,设置服务名称 defaultServiceManager()、addService() 10-30ms
3. HAL 接口加载 通过 HIDL / AIDL 方式加载 HAL 实现库,获取 ICameraDevice 工厂 ICameraProvider::getCameraDeviceInterface() 50-200ms
4. 设备枚举与能力上报 枚举所有物理 Camera 设备,上报设备 ID、朝向、支持的分辨率等 getCameraIdList()、getCameraCharacteristics() 100-500ms
5. 消息循环与请求分发 进入 Binder 线程池,等待 CameraService 的绑定和调用 ProcessState::startThreadPool()、joinThreadPool() 持续运行
关键指标: 从进程启动到 ServiceManager 注册完成,理想情况下应控制在 50ms 以内。若超过 200ms,系统服务启动时序可能出现异常,导致 CameraService 首次连接超时。

4.2 核心初始化流程 SVG 架构图

下图展示了 CameraProvider 从进程入口到 HAL 服务就绪的完整调用链。注意 HIDL 与 AIDL 两种接口在加载阶段的路径差异:

CameraProvider 初始化流程架构图 CameraProvider 初始化流程架构图 1. 进程启动 main() → 配置解析 2. ServiceManager 注册 addService("media.camera.provider") 3. HAL 接口加载 HIDL / AIDL 实现库 4. 设备枚举 getCameraIdList() 5. 能力上报 getCameraCharacteristics() 6. 消息循环 Binder 线程池等待 HIDL vs AIDL 加载路径差异 • HIDL 路径:ICameraProvider@3.x → hw_get_module() → camera_module_t • AIDL 路径:ICameraProvider.aidl → 直接 dlopen() 实现库 → 工厂方法创建

4.3 ServiceManager 注册细节

CameraProvider 在注册到 ServiceManager 时,服务名称的命名规则直接影响 CameraService 的发现策略。当前主流实现采用以下命名格式:

// HIDL 实现(Android 10 及以下)
android.hardware.camera.provider@3.4::ICameraProvider/legacy/0

// AIDL 实现(Android 11 及以上)
media.camera.provider.legacy_0

// 多 provider 场景(例如外接 USB 摄像头)
media.camera.provider.external_0
media.camera.provider.internal_0
注册时序要点:
  • CameraProvider 进程启动后,首先通过 defaultServiceManager() 获取 ServiceManager 代理。
  • 调用 addService() 时,第二个参数为服务名称,第三个参数为 allowIsolated 标志(通常设为 false)。
  • 注册成功后,ServiceManager 会维护一个服务名称到 Binder 引用的映射表。
  • 若注册失败(如 SELinux 权限不足),进程应主动退出,避免僵尸服务。

4.4 HAL 接口加载:HIDL 与 AIDL 实现对比

HAL 接口的加载方式在 Android 11 前后发生了显著变化。下表从多个维度对比两种实现方案:

对比维度 HIDL 实现(@3.x / @2.x) AIDL 实现(Android 11+)
接口定义语言 .hal 文件 → hidl-gen 生成 C++ 桩代码 .aidl 文件 → aidl-cpp 生成 C++ 桩代码
HAL 库加载方式 hw_get_module() + camera_module_t 回调 dlopen() + 工厂函数(如 createCameraProvider())
服务注册方式 HwServiceManager::addService() ServiceManager::addService()
设备枚举接口 getCameraInfo() / getNumberOfCameras() getCameraIdList() / getCameraCharacteristics()
进程模型 独立进程,通过 HWBinder 通信 独立进程,通过 Binder 通信
典型实现文件 CameraProvider@3.4.cpp CameraProvider.cpp (AIDL)

4.5 设备枚举与能力上报流程

设备枚举是初始化阶段最耗时的操作,直接影响系统启动速度。以下代码片段展示了 AIDL 实现中设备枚举的核心逻辑:

// CameraProvider.cpp (AIDL 实现)
ndk::ScopedAStatus CameraProvider::getCameraIdList(
    std::vector<std::string>* _aidl_return) {
    std::lock_guard<std::mutex> lock(mLock);
    
    // 1. 检查 HAL 是否已初始化
    if (mHalDevice == nullptr) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }
    
    // 2. 调用 HAL 层获取设备数量
    int numCameras = mHalDevice->getNumberOfCameras();
    if (numCameras <= 0) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    }
    
    // 3. 遍历所有设备,收集 ID
    _aidl_return->clear();
    for (int i = 0; i < numCameras; ++i) {
        char id[PROPERTY_VALUE_MAX];
        mHalDevice->getCameraInfo(i, &mCameraInfo[i]);
        snprintf(id, sizeof(id), "%d", i);
        _aidl_return->push_back(id);
    }
    
    return ndk::ScopedAStatus::ok();
}
常见陷阱: 在设备枚举过程中,HAL 层可能因为硬件初始化失败(如 CSI 接口未就绪、电源未上电)而返回错误。CameraProvider 应实现重试机制或降级策略,避免因单个设备故障导致整个 Provider 无法注册。

4.6 初始化阶段的异常处理策略

CameraProvider 初始化过程中可能遇到多种异常情况,合理的处理策略能提升系统稳定性:

  • HAL 库加载失败: 检查 dlopen() 返回值,若为 nullptr 则记录 dlerror() 日志,并返回错误码。不应直接 abort()。
  • 设备枚举超时: 为 getNumberOfCameras() 设置 500ms 超时,超时后仅注册已枚举成功的设备。
  • ServiceManager 注册冲突: 若服务名称已被占用,尝试使用备用名称(如添加后缀 _1)。
  • SELinux 权限拒绝: 在 init.rc 中配置正确的 context,避免在运行时才暴露权限问题。

4.7 初始化完成后的状态验证

CameraProvider 完成初始化后,应通过以下检查点验证服务是否就绪:

  1. Binder 服务可达性: 通过 IServiceManager::checkService() 验证服务名称已注册。
  2. 设备列表非空: 至少返回一个有效的 Camera ID,否则视为初始化失败。
  3. 能力数据完整性: 对每个设备调用 getCameraCharacteristics(),检查关键 Key(如 SCALER_AVAILABLE_STREAM_CONFIGURATIONS)是否存在。
  4. 线程池状态: 确认 Binder 线程池已启动,且线程数至少为 4(默认配置)。
调试技巧: 在初始化阶段增加 ATRACE_CALL() 宏,配合 systrace 可以精确分析每个阶段的耗时分布。重点关注 HAL 接口加载和设备枚举两个阶段,它们通常占初始化总耗时的 80% 以上。