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 发现。
核心调用链如下:
CameraProviderManager::initialize()会遍历所有已注册的 Provider。- 对每个 Provider,调用
getCameraIdList()获取它支持的 Camera ID 列表。 - 对每个 Camera ID,调用
getCameraDeviceInterface_V3_X()获取设备接口。 - 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 图
为了让大家更直观地理解,我画了一张流程图:
图中①是注册到 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 进程的通信。嗯,这类问题通常需要结合 dmesg 和 logcat 一起看。
好了,CameraService 的启动流程就讲到这里。这部分内容虽然偏底层,但理解了它,你就能明白整个相机框架的“骨架”是怎么搭起来的。下一章我们会深入 CameraDevice 的创建流程,看看上层 App 打开相机时,底层到底发生了什么。