3. ICameraProvider 接口定义:核心方法解析

ICameraProvider 是 Android Camera HAL 中连接 Framework 与底层硬件服务的核心接口。它定义在 hardware/interfaces/camera/provider/2.4/ICameraProvider.hal(及更高版本)中,是 CameraProvider 进程必须实现的主干接口。本章将深入解析其最关键的三个方法:getCameraIdList、getCameraDeviceInterface 和 setCallback,并分析它们在初始化流程中的调用时序与实现要点。

核心观点:ICameraProvider 是 CameraService 与 HAL 之间的“门面”接口。它负责枚举设备、创建设备接口、以及向 Framework 上报底层状态变化。正确实现这三个方法是 CameraProvider 初始化的基石。

3.1 接口总览与调用关系

在 CameraProvider 初始化完成后,CameraService 会通过 HwBinder 获取到 ICameraProvider 的代理对象,并依次调用以下核心方法:

方法名 调用时机 返回值 核心职责
getCameraIdList Provider 注册后立即调用 Status + vec<string> 返回当前 Provider 管理的所有 Camera ID 列表
getCameraDeviceInterface 对每个 Camera ID 依次调用 Status + ICameraDevice 为指定 Camera ID 创建设备接口对象
setCallback Provider 注册后、getCameraIdList 之前 Status 注册 ICameraProviderCallback,用于上报热插拔等事件
isSetTorchModeSupported 可选,在初始化后期 Status + bool 查询是否支持闪光灯手电筒模式

下图展示了 CameraService 与 ICameraProvider 之间的典型调用时序:

ICameraProvider 核心方法调用时序 CameraService ICameraProvider CameraDevice HAL 1. setCallback(cb) 2. getCameraIdList() 返回 [id0, id1, ...] 3. getDeviceInterface(id0) 创建 CameraDevice 返回 ICameraDevice 4. getDeviceInterface(id1) ... 说明:setCallback 必须在 getCameraIdList 之前调用,否则 CameraService 无法接收热插拔事件

3.2 getCameraIdList 方法详解

getCameraIdList 是 CameraService 获取可用 Camera 设备列表的入口。其方法签名如下:

// ICameraProvider.hal (2.4)
getCameraIdList() generates (Status status, vec<string> cameraIdList);

实现要点:

  • 返回稳定的 ID 列表:每次调用应返回相同的 ID 集合(除非发生热插拔)。ID 通常为 "0"、"1" 等字符串,或物理 Camera 的 unique identifier。
  • ID 与设备类型对应:每个 ID 必须能唯一映射到一个 CameraDevice 实例。对于逻辑多摄像头(Logical Multi-Camera),ID 对应逻辑 Camera,物理 Camera 通过 getPhysicalCameraIds 另行查询。
  • 空列表处理:如果 Provider 当前没有可用 Camera(例如 USB Camera 未插入),应返回空列表而非错误。CameraService 会据此判断设备状态。
  • 线程安全:该方法可能被多个线程同时调用,实现中需加锁保护内部设备列表。
最佳实践:在 Provider 初始化阶段,建议从底层 HAL 驱动或配置文件中读取设备列表并缓存。getCameraIdList 直接返回缓存副本,避免每次调用都访问硬件。

典型实现示例(C++ 伪代码):

Return<void> CameraProvider::getCameraIdList(
    getCameraIdList_cb _hidl_cb) {
    std::vector<std::string> idList;
    {
        std::lock_guard<std::mutex> lock(mLock);
        for (const auto& [id, device] : mCameraDevices) {
            idList.push_back(id);
        }
    }
    _hidl_cb(Status::OK, idList);
    return Void();
}

3.3 getCameraDeviceInterface 方法详解

该方法为指定的 Camera ID 创建并返回一个 ICameraDevice 接口对象。CameraService 会为每个 ID 调用一次此方法,以获取设备操作句柄。

// ICameraProvider.hal (2.4)
getCameraDeviceInterface(string cameraId)
    generates (Status status, ICameraDevice device);

实现要点:

场景 返回值 说明
cameraId 有效且设备可用 Status::OK + ICameraDevice 创建新的 ICameraDevice 实例并返回
cameraId 不存在 Status::ILLEGAL_ARGUMENT 返回错误,CameraService 会记录日志并跳过
设备已被占用或初始化失败 Status::INTERNAL_ERROR 返回错误,CameraService 会尝试重新初始化
设备已断开(热插拔后) Status::CAMERA_STATUS_NOT_PRESENT 通知 CameraService 该设备已不存在

关键实现细节:

  • 延迟创建:不要在此方法中执行耗时的硬件初始化(如打开 Sensor)。真正的初始化应推迟到 ICameraDevice::open() 调用时进行。
  • 唯一实例:每个 Camera ID 通常只创建一个 ICameraDevice 实例。如果 CameraService 重复调用,应返回相同的对象或创建新的独立实例(取决于设计)。
  • 生命周期管理:返回的 ICameraDevice 对象由 CameraService 持有引用。Provider 需确保在设备断开时能正确释放资源。
常见陷阱:不要在 getCameraDeviceInterface 中直接打开 Camera 硬件。如果 HAL 实现在此阶段执行了 open(),会导致 CameraService 在初始化阶段就占用设备,后续 open() 调用可能失败或产生竞态条件。

下图展示了 getCameraDeviceInterface 的内部处理流程:

getCameraDeviceInterface 内部处理流程 收到 cameraId 请求 ID 有效? 是 查找/创建 ICameraDevice 返回 Status::OK + device 否 返回 ILLEGAL_ARGUMENT 注意:创建 ICameraDevice 时不要打开硬件,仅建立接口代理,真正的 open() 在后续调用中完成

3.4 setCallback 方法详解

setCallback 用于向 Provider 注册一个 ICameraProviderCallback 回调接口。CameraService 通过此回调接收 Provider 上报的异步事件,如 Camera 热插拔、Torch 状态变化等。

// ICameraProvider.hal (2.4)
setCallback(ICameraProviderCallback callback)
    generates (Status status);

实现要点:

  • 单回调设计:通常只允许注册一个回调。如果重复调用,应替换旧回调或返回错误。
  • 保存回调引用:Provider 必须持有回调的强引用(sp<ICameraProviderCallback>),以便在事件发生时调用。
  • 热插拔事件:当 Camera 设备插入或拔出时,通过 onCameraDeviceStatusChange 方法通知 CameraService。
  • 初始化完成通知:某些实现中,Provider 初始化完成后会通过回调通知 CameraService 可以开始查询设备列表。
关键约束:setCallback 必须在 getCameraIdList 之前调用。如果 CameraService 先调用 getCameraIdList 再注册回调,Provider 在回调注册前发生的设备变化将无法上报,导致状态不一致。

回调接口定义(简化):

// ICameraProviderCallback.hal
interface ICameraProviderCallback {
    // 设备状态变化:CAMERA_STATUS_PRESENT / NOT_PRESENT / ENUMERATING
    onCameraDeviceStatusChange(
        string cameraId,
        CameraDeviceStatus newStatus);

    // Torch 模式状态变化
    onTorchModeStatusChange(
        string cameraId,
        TorchModeStatus newStatus);
};

3.5 方法间的依赖关系与初始化顺序

三个核心方法之间存在严格的调用顺序依赖,违反顺序会导致初始化失败或状态不一致:

  1. setCallback → 必须先调用,建立事件上报通道。
  2. getCameraIdList → 获取当前设备列表。
  3. getCameraDeviceInterface → 为每个 ID 创建设备接口。

下表总结了违反顺序的后果:

违反场景 后果 严重程度
getCameraIdList 在 setCallback 之前 热插拔事件丢失,设备状态不一致 高
getCameraDeviceInterface 在 getCameraIdList 之前 CameraService 无法验证 ID 有效性,可能崩溃 高
重复调用 setCallback 旧回调被覆盖,之前注册的监听者失效 中
getCameraIdList 返回空列表后调用 getDeviceInterface CameraService 不会调用,无影响 低
调试建议:在 Provider 实现中,建议为每个方法添加日志输出,记录调用时间戳、参数和返回值。这有助于在初始化阶段快速定位时序问题。例如:
ALOGD("[Provider] getCameraIdList: returning %zu cameras", idList.size());

3.6 扩展方法:isSetTorchModeSupported 与 notifyDeviceStateChange

除了上述三个核心方法,ICameraProvider 还定义了以下辅助方法:

  • isSetTorchModeSupported:查询 Provider 是否支持通过 setTorchMode 控制闪光灯。返回 true 表示支持,CameraService 会允许应用调用 Torch API。
  • notifyDeviceStateChange:通知 Provider 系统状态变化(如折叠状态、显示状态),Provider 可据此调整 Camera 行为(如禁用某些物理 Camera)。

这些方法在初始化阶段不是必须调用的,但实现时建议提供合理的默认行为。例如,如果不支持 Torch,isSetTorchModeSupported 应返回 false。