2、CameraService 启动流程:init进程启动、CameraService注册到ServiceManager、CameraProvider加载与初始化、Camera HAL的发现与绑定

各位同学,今天我们来聊聊 CameraService 的启动流程。说实话,这部分内容在 Android 相机框架里属于“地基”级别的存在。你想想看,上层 App 调用 Camera.open() 之前,底层到底发生了什么?嗯,今天我们就把它彻底讲透。

我个人习惯把 CameraService 的启动拆成四个阶段:init 进程启动 → 注册到 ServiceManager → CameraProvider 加载 → HAL 发现与绑定。下面我们一个一个来看。

2.1 init 进程启动 CameraService

Android 系统启动后,init 进程会解析 init.rc 脚本。CameraService 对应的 rc 文件通常叫 cameraserver.rc。我记得在 Android 10 之后,Google 把它移到了 /system/etc/init/ 目录下。

我们来看一段典型的 rc 配置:

service cameraserver /system/bin/cameraserver
    class main
    user cameraserver
    group audio camera input drmrpc
    ioprio rt 4
    task_profiles CameraServiceCapacity
    shutdown_priority shutdown_200

这里有几个关键点:

  • class main:表示它属于 main 类服务,会在系统启动早期被启动。
  • user cameraserver:以独立的 cameraserver 用户运行,权限隔离。
  • group audio camera input drmrpc:加入 camera 组才能访问 /dev/video* 节点。

重要:我曾经在项目中遇到过一个问题——第三方 ROM 把 cameraserver 的 group 里漏掉了 camera,结果 HAL 层打开设备时一直返回权限错误。排查了半天才发现是 group 配置不全。所以大家写 rc 文件时一定要仔细核对 group 列表。

init 进程启动 cameraserver 后,会执行 /system/bin/cameraserver 这个可执行文件。它的入口在 frameworks/av/camera/cameraserver/ 目录下,main 函数很简单:

int main(int argc, char** argv) {
    // 1. 获取 CameraService 实例
    sp<CameraService> cameraService = new CameraService();
    
    // 2. 将服务注册到 ServiceManager
    if (cameraService->init() != OK) {
        ALOGE("Failed to initialize CameraService");
        return -1;
    }
    
    // 3. 进入 Binder 循环
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    
    return 0;
}

说白了,这个 main 函数就做了三件事:创建服务、初始化并注册、等待 Binder 请求。嗯,这里要注意,init() 函数内部会完成很多重要工作,我们接着往下看。

2.2 CameraService 注册到 ServiceManager

CameraService 继承自 BnCameraService,这是一个 Binder 服务。在 CameraService::init() 中,会调用 defaultServiceManager()->addService() 把自己注册到 ServiceManager。

代码大致如下:

status_t CameraService::init() {
    // ... 其他初始化 ...
    
    // 注册到 ServiceManager
    sp<IServiceManager> sm = defaultServiceManager();
    status_t ret = sm->addService(
        String16("media.camera"), 
        this,
        false /* allowIsolated */,
        DUMP_FLAG_PRIORITY_CRITICAL);
    
    if (ret != OK) {
        ALOGE("Failed to add camera service to ServiceManager");
        return ret;
    }
    
    // 注册监听进程死亡通知
    mDeathNotifier = new DeathNotifier();
    sm->linkToDeath(mDeathNotifier, nullptr, 0);
    
    // ... 加载 CameraProvider ...
    return OK;
}

提示:服务名是 "media.camera",不是 "camera"。我见过有新手在调试时用 service check camera 查不到,其实应该用 service check media.camera。这个坑我踩过一次,印象特别深。

注册成功后,任何进程都可以通过 ServiceManager 获取 CameraService 的 Binder 代理。这里有个细节:DUMP_FLAG_PRIORITY_CRITICAL 标志表示这个服务的 dump 优先级很高,方便系统工程师调试。

2.3 CameraProvider 加载与初始化

CameraService 启动后,下一步就是加载 CameraProvider。Provider 是 Android 8.0 之后引入的概念,目的是把 HAL 实现从 cameraserver 进程中解耦出来,放到独立的 HAL 进程中。

加载过程在 CameraService::init() 中继续:

// 在 init() 中调用
status_t CameraService::initializeForProvider() {
    // 1. 获取 CameraProvider 管理器
    sp<ICameraProviderManager> manager = new CameraProviderManager();
    
    // 2. 注册回调,监听 Provider 的加入和移除
    manager->initialize(this);
    
    // 3. 扫描所有已注册的 Provider
    // 这一步会触发 HAL 的发现
    manager->setUpProviderListener();
    
    return OK;
}

你可能会问:Provider 是谁注册的?答案是 cameraserver 进程启动后,会通过 HIDL 机制发现系统中所有可用的 CameraProvider。每个 Provider 对应一个 HAL 实现,比如:

  • android.hardware.camera.provider@2.4::ICameraProvider(旧版)
  • android.hardware.camera.provider@2.5::ICameraProvider(新版)
  • 或者 vendor 自定义的版本

Provider 的注册信息通常写在 /vendor/etc/vintf/manifest.xml 中。举个例子:

<hal format="hidl">
    <name>android.hardware.camera.provider</name>
    <transport>hwbinder</transport>
    <version>2.4</version>
    <interface>
        <name>ICameraProvider</name>
        <instance>legacy/0</instance>
    </interface>
</hal>

警告:如果 manifest.xml 中 Provider 的版本号写错了,或者 instance name 不匹配,CameraService 会一直等待 Provider 上线,导致相机无法打开。我曾经在移植新 HAL 时,忘记更新 manifest.xml 中的版本号,结果调试了一整天……嗯,大家引以为戒。

2.4 Camera HAL 的发现与绑定

当 CameraProvider 加载完成后,CameraService 会通过 ICameraProvider 接口查询该 Provider 支持哪些 Camera 设备。这个过程叫做 HAL 发现

核心调用链如下:

  1. CameraProviderManager::initialize() 会遍历所有已注册的 Provider。
  2. 对每个 Provider,调用 getCameraIdList() 获取它支持的 Camera ID 列表。
  3. 对每个 Camera ID,调用 getCameraDeviceInterface_V3_X() 获取设备接口。
  4. CameraService 将设备接口包装成 CameraDevice 对象,供上层使用。

我们来看一段简化后的代码:

// CameraProviderManager.cpp
void CameraProviderManager::initialize(sp<CameraService> service) {
    // 获取所有注册的 Provider
    auto providers = getHidlManager()->getAllProviders();
    
    for (auto& provider : providers) {
        // 查询 Camera ID 列表
        Return<Status> status;
        hidl_vec<CameraIdAndStreamCombination> cameraIds;
        status = provider->getCameraIdList(
            [&](auto& ids) { cameraIds = ids; });
        
        if (status.isOk()) {
            for (auto& id : cameraIds) {
                // 绑定设备接口
                sp<ICameraDevice> device;
                provider->getCameraDeviceInterface_V3_X(
                    id.id, [&](auto& dev) { device = dev; });
                
                // 注册到 CameraService
                service->addCameraDevice(id.id, device);
            }
        }
    }
}

这里有个关键点:HAL 发现是异步的。Provider 可能不会立即返回所有设备,比如 USB 摄像头是热插拔的。所以 CameraProviderManager 会注册一个监听器,当有新的 Camera 设备插入时,Provider 会主动通知 CameraService。

核心逻辑:整个 HAL 发现与绑定的过程,说白了就是 CameraService 通过 HIDL 机制找到所有可用的 CameraProvider,然后向每个 Provider 询问“你有哪些 Camera?”,最后把每个 Camera 的设备接口保存下来,等待上层调用。

2.5 整体流程 SVG 图

为了让大家更直观地理解,我画了一张流程图:

CameraService 启动流程 init 进程启动 cameraserver CameraService 创建 & init() 注册到 ServiceManager 加载 CameraProvider 等待 Binder 请求 HAL 发现与绑定 CameraService 就绪

图中①是注册到 ServiceManager,②是加载 Provider,③是等待上层调用,④是 HAL 发现与绑定。这四个步骤缺一不可。

2.6 避坑总结

最后,我结合自己的经验,给大家列几个常见问题:

问题 原因 解决方法
CameraService 启动失败 rc 文件中 group 缺少 camera 检查 /system/etc/init/cameraserver.rc 的 group 字段
ServiceManager 中找不到 media.camera CameraService 未成功注册 查看 logcat 中 CameraService 的 init 日志
Provider 加载超时 manifest.xml 中版本号不匹配 确认 HAL 版本与 manifest 声明一致
HAL 发现不到设备 Provider 未正确实现 getCameraIdList 检查 HAL 层的 getCameraIdList 返回值

个人经验:我曾经在调试一款 MTK 平台的手机时,发现 CameraService 启动后一直卡在“等待 Provider”状态。后来用 lshal 命令查看,发现 Provider 进程根本没启动。原因是 SELinux 策略阻止了 cameraserver 与 HAL 进程的通信。嗯,这类问题通常需要结合 dmesglogcat 一起看。

好了,CameraService 的启动流程就讲到这里。这部分内容虽然偏底层,但理解了它,你就能明白整个相机框架的“骨架”是怎么搭起来的。下一章我们会深入 CameraDevice 的创建流程,看看上层 App 打开相机时,底层到底发生了什么。

公众号:蓝海资料掘金营,微信deep3321