前面两章讲了怎么用 Camera2 API 写 App。但你有没有想过——你的 openCamera() 调用发出去之后,到底是谁在处理?
答案是 CameraService——一个运行在 system_server 进程里的 Android 系统服务。它是整条 Camera 调用链的中转站,负责把 App 的请求转发给 HAL,再把 HAL 的结果转发回 App。
理解 CameraService 有三个实际作用:
dumpsys media.camera 的输出来自 CameraService,不懂它的结构就读不懂输出CameraService 是 Android 的系统服务(System Service)——和 ActivityManager、WindowManager、PackageManager 性质相同。它由 SystemServer 在系统启动时注册到 ServiceManager 中,所有 App 通过 Binder 访问它。

Android 的进程布局:
┌─────────┐ Binder ┌─────────────────┐ HIDL ┌──────────────────┐
│ App │ ←──────────→ │ system_server │ ←────────→ │ CameraProvider │
│ (相机/ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ 微信/ │ │ │CameraService│ │ │ │ camera.qcom │ │
│ 抖音) │ │ │ │ │ │ │ .so │ │
└─────────┘ │ │DeviceClient │ │ │ │ (CamX/CHI) │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ CameraService │ │ CameraProvider │
│ 运行在这里 │ │ 独立进程 │
└─────────────────┘ └──────────────────┘
三个关键进程:
| 进程 | 里面有什么 | 崩溃了会怎样 |
|---|---|---|
| App 进程 | 你的 App 代码 | 仅 App 退出 |
| system_server | CameraService(Java/C++) | 手机重启! system_server 是 Android 的核心进程 |
| CameraProvider | CamX + CHI(HAL 实现) | Provider 重启,手机不受影响 |
💡 为什么 CameraService 在 system_server 里?
因为 CameraService 需要做权限检查(调用 PackageManager)、需要和 SurfaceFlinger 配合(Buffer 管理)、需要 Binder 支持(跨进程通信)。这些设施都在 system_server 里。
HAL 不在 system_server 里——因为 HAL 是厂商代码,可能不稳定。把它放到独立的 CameraProvider 进程中,就是 Treble(Android 8)最重要的改动。这章的 7.4 节会详细讲。
当手机开机时,SystemServer 会按顺序启动所有系统服务。CameraService 的启动是比较晚的——因为它的依赖(HAL Provider)需要等底层初始化完才能用。
手机开机
↓
init 进程启动
↓
Zygote 启动 → SystemServer 启动
↓
SystemServer.startOtherServices()
↓
① System.loadLibrary("camera_client") — 加载 native 库
↓
② new CameraService() — 创建服务实例
↓
③ ServiceManager.addService("media.camera") — 注册到 ServiceManager
↓ (这样 App 才能通过 Binder 找到它)
④ CameraService.onFirstRef() — 首次被引用时
↓ 扫描 HIDL/AIDL 注册表
↓ 找到所有 "android.hardware.camera.provider" 服务
↓ 每个 Provider 调用 get_number_of_cameras()
↓ 收集所有 Camera ID
↓
⑤ 相机就绪,等待 App 连接
验证 CameraService 是否正常运行:
adb shell dumpsys media.camera
# 有输出 → CameraService 正常,Provider 正常
adb shell service check media.camera
# Service media.camera: found → 服务已注册
# Service media.camera: not found → 没注册,启动失败
adb shell ps -A | grep camera
# 可以看到 CameraProvider 进程
CameraService 不是一个巨大的单体。它内部有几个关键组件:
CameraService
│
├── CameraProviderManager
│ └── 管理所有 CameraProvider(内置/USB/虚拟)
│ └── 每个 Provider 通过 HIDL/AIDL 连接
│
├── CameraDeviceClient (每个 App 连接一个)
│ └── App 1, 连接 camera 0 → CameraDeviceClient[0]
│ └── App 2, 连接 camera 0 → 创建失败!(已被占用)
│
├── Camera3Device (每个 camera 对应一个)
│ └── 封装 camera3_device_t 的操作
│ └── 管理 HAL 的 request queue
│
└── CameraService::BasicClient (基类)
└── 管理每个客户端连接的权限和优先级
CameraProviderManager 的职责:
- 扫描系统中有哪些 CameraProvider
- 向每个 Provider 查询它有多少个 Camera
- 汇总成"所有可用的 Camera ID"列表
Provider 类型:
"internal/0" → 内置后摄 + 前摄(高通平台)
Provider 名: android.hardware.camera.provider@2.4
"external/0" → USB 摄像头(如果插入了外接摄像头)
"virtual/0" → 虚拟摄像头(某些调试工具会注册)
当 App 调用 openCamera(cameraId) 时,CameraService 创建 CameraDeviceClient:
openCamera("0") 触发的内部流程:
① 权限检查
检查 App 是否声明了 android.permission.CAMERA
② 可用性检查
cameraId "0" 是否存在?
是否被其他 App 占用?
③ 创建 CameraDeviceClient
CameraDeviceClient[0] = new CameraDeviceClient(cameraId, appUid)
④ 调用 Provider
通过 HIDL/AIDL 调用 CameraProvider,打开 HAL 的 camera3_device_t
⑤ 返回 Binder 代理
把 CameraDeviceClient 的 Binder 代理返回给 App
App 拿到的是 Binder 代理,不是真正的对象引用
一个物理相机同一时间只能被一个 App 占用。
如果 App 2 在 App 1 已经占着后摄时尝试
openCamera("0"):App 2 收到异常: CameraAccessException(CAMERA_ERROR_IN_USE) App 1 不受影响
Camera3Device 是 CameraService 内部对 HAL camera3_device_t 的封装:
Camera3Device 的核心工作:
① 请求排队
App 可能连续发多个 capture request
Camera3Device 把它们排成队列,逐个发给 HAL
② 请求状态跟踪
记住每个 request 是哪个 App 发的、发的什么参数
当 HAL 返回 result 时,匹配回对应的 App
③ 错误处理
如果 HAL process_capture_request() 返回错误
Camera3Device 把错误转成 App 能理解的 onCaptureFailed()
④ 资源管理
跟踪每个 Stream 的 Buffer 使用
在 Session 关闭时确保 Buffer 被释放
如果你只会一个 Camera 架构的概念,记住这个:Treble 让 HAL 崩溃不再导致手机重启。
mediaserver 进程(native)
├── CameraService(native 服务)
└── Camera HAL(C++,厂商提供)
└── camera.qcom.so(dlopen 加载)
└── CamX / mm-camera
Camera HAL 运行在 mediaserver 进程里(和 CameraService 同属一个 native 进程,并不是 Java 的 system_server)。
如果 HAL 中有一个空指针:
camera.qcom.so → SIGSEGV → mediaserver 进程崩溃
→ 相机服务不可用(手机不必重启,重启相机 App 即可)
对于 OEM 来说,每次改 HAL 代码都心惊胆战——一个内存越界能让手机变成砖。
system_server 进程 CameraProvider 独立进程
┌──────────────────────┐ ┌─────────────────────────┐
│ CameraService │ HIDL │ CameraProvider@2.4 │
│ (Java/C++) │ ←───→ │ ├── camera_module_t │
│ │ │ ├── camera3_device_t │
│ CameraProviderMgr │ │ └── camera.qcom.so │
│ CameraDeviceClient │ │ (CamX + CHI) │
└──────────────────────┘ └─────────────────────────┘
HAL 被移到了独立进程中。
现在 HAL 崩溃时发生什么:
① camera.qcom.so 崩溃 → CameraProvider 进程死亡
② 内核通知 system_server:"HAL 进程挂了"
③ CameraService 收到 Binder death notification
④ CameraService 标记该 Provider 为"不可用"
⑤ 如果 App 正在用相机 → App 收到 onError(ERROR_CAMERA_SERVICE)
⑥ CameraService 尝试重启 Provider 进程
⑦ 如果重启成功 → App 可以重新 openCamera()
⑧ **system_server 不受影响!手机不会重启!**
💡 这对开发意味着什么
Treble 之前你 debug HAL 时如果搞崩了,手机会重启——调试效率极低。
Treble 之后你可以大胆地在 HAL 代码里加日志、改参数——崩了最多 Provider 重启,不影响系统。
Treble (Android 8) → 引入 HIDL,HAL 进程隔离
Camera HAL 接口: camera.provider@2.4
Android 13 → 弃用 HIDL,统一到 AIDL
Camera HAL 接口: android.hardware.camera.provider
为什么换?
HIDL 需要专门的 hidl-gen 工具链,额外的语言和构建规则
AIDL 复用了 Android 已有的 Binder IPC 基础设施
维护一套工具链比维护两套更容易
openCamera → capture 的完整路由把前面的知识串起来,走一遍完整流程:
App 调用 CameraManager.openCamera("0", callback, handler)
│
▼ Binder IPC(跨进程:App → system_server)
CameraService.openCamera("0", appUid)
│
├─ 权限检查:App 有 CAMERA 权限吗?
├─ 可用性检查:Camera "0" 空闲吗?
├─ CameraDeviceClient 创建
│
▼ HIDL/AIDL(跨进程:system_server → Provider)
CameraProvider.open("0")
│
▼ 加载 HAL(同进程内:dlopen)
camera_module_t.open("0") → camera3_device_t
│
▼ CamX 内部
ChiContext::Create() → HALDevice 初始化
│
▼ 回调链
Provider → CameraService → App
│
App 收到 onOpened(cameraDevice) ← CameraDevice 的 Binder 代理
App 调用 cameraDevice.createCaptureSession(surfaces, callback)
│
▼ Binder
CameraService.configureStreams()
│
▼ HIDL
CameraProvider.configure_streams(streamConfig)
│
▼ CamX 内部
UsecaseSelector::Select() → 选 Usecase
Usecase::Create() → 创建 Pipeline
Pipeline::AcquireResources() → 分配 Buffer
│
▼ 完成
App 收到 onConfigured(session)
App 调用 session.setRepeatingRequest(request, callback)
│
▼ Binder
CameraService.submitRequestList()
│
▼ HIDL
CameraProvider.process_capture_request()
│
▼ CamX 内部
Usecase::SubmitChiRequest()
→ Session::ProcessCaptureRequest()
→ Pipeline::ProcessRequest()
→ Node::ProcessRequest()
→ CSLSubmit() → ioctl → Kernel → ISP HW
│
┌──────────────────────────────────────┘
▼ ISP 硬件处理完成 → Fence Signal
CSLFenceSignaled() → Node::ProcessResult()
→ Pipeline::ProcessPipelineResult()
→ Session::ProcessPipelineResult()
→ Usecase::ProcessCaptureResult()
│
▼ HIDL
CameraProvider.process_capture_result()
│
▼ Binder
CameraService → App
│
App 收到 onCaptureCompleted(result) ← 每帧一次回调
上一节画了函数调用链,但没有解释数据本身是怎么从 ISP 到 App 的 Surface 上的。这一节专门讲这个。
整个过程分三步:HAL 生产 → BufferQueue 传递 → App 消费。
ISP 硬件处理完一帧(IFE → IPE,预览主路径;快照才经 BPS 做完整 demosaic)
│
▼ ISP DMA 引擎
把 YUV 数据写入一块 Gralloc Buffer(物理连续内存)
这块内存在 configure_streams 时就分配好了
│
▼ HAL 拿到 Buffer 后
Node::ProcessRequestResult() 中调用:
1. CacheOps(invalidate) — 确保 CPU 能看到最新的硬件写入
2. 把 buffer handle 放入 result 结构中
│
▼ HAL → Provider
camera3_capture_result_t result = {
.frame_number = 42,
.result = metadata, // AE/AF/AWB 等参数回读
.output_buffers = &{ // ← 图像数据在这里
.stream = previewStream,
.buffer = &bufferHandle, // Gralloc buffer 的句柄
.status = BUFFER_STATUS_OK,
.acquire_fence = -1,
.release_fence = -1,
},
.num_output_buffers = 1,
};
关键点:
process_capture_result()传递的不是图像数据本身,而是 Gralloc buffer 的句柄(handle)。真正的像素数据已经在 ISP 写入的物理内存里了。传递 handle 避免了拷贝整帧数据。
HAL 调了 process_capture_result() 之后,CameraService 做了什么?
CameraService 收到 result
│
▼ 取出 output_buffers[0].buffer — 这是个 buffer_handle_t
CameraService 找到这个 buffer 对应的 Stream
│
▼ 调用 IGraphicBufferProducer.queueBuffer()
把 buffer handle 交还给 BufferQueue
│
▼ BufferQueue 内部
- 把 buffer 标记为 QUEUED 状态
- 通知 Consumer 端:"有新数据了"
│
▼ Consumer 端(App 的 Surface)
收到通知 → 可以 consume 了
BufferQueue 的核心作用:它是 HAL(Producer)和 App(Consumer)之间的共享内存管理器。HAL 生产和归还 Buffer,App 获取和释放 Buffer。整帧图像数据从来没有被拷贝过——始终在原地,只传 handle。
物理内存中的一帧 Buffer 的生命周期:
ISP DMA 写入(HAL Producer)
→ BufferQueue.queueBuffer()(handle 交给 BufferQueue)
→ Consumer acquireBuffer()(App 拿到 handle)
→ SurfaceFlinger 合成到屏幕(读同一块物理内存)
→ 或 ImageReader 通过 lockCanvas/lockImage 映射到 CPU 虚拟地址
→ Consumer releaseBuffer()(handle 回收)
→ HAL dequeueBuffer()(再次拿去用)
路径 A:显示到屏幕(SurfaceView / TextureView)
BufferQueue Consumer: SurfaceFlinger
流程:
HAL queueBuffer() → BufferQueue → SurfaceFlinger acquireBuffer()
→ SurfaceFlinger 用 GPU 把 buffer 合成到屏幕 framebuffer
→ SurfaceFlinger releaseBuffer() → buffer 回空闲池
这条路径上 App 代码碰不到像素数据。
PRIVATE 格式的 buffer 直接被 GPU 消费,效率最高。
路径 B:App 代码读取像素(ImageReader)
BufferQueue Consumer: ImageReader
流程:
HAL queueBuffer() → BufferQueue → ImageReader acquireBuffer()
→ Image 对象可用 → onImageAvailable() 回调触发
→ App 代码中:
Image image = reader.acquireLatestImage();
Image.Plane[] planes = image.getPlanes();
ByteBuffer yBuffer = planes[0].getBuffer(); // CPU 现在可以读了
// 处理像素...
image.close(); // 必须!releaseBuffer() 回到空闲池
这条路径涉及一次 CPU 映射:Gralloc buffer → CPU 虚拟地址空间。
YUV_420_888 和 JPEG 格式的 buffer 走这条路径。
现在回头看第 6 章讲的"YUV 数据拷贝时要按 RowStride 逐行操作",原因就很清楚了:
Gralloc buffer 在物理内存中:
排列由硬件 DMA 的对齐要求决定
RowStride 可能大于 width(多出的叫 padding)
如果你直接用 width 来遍历 → 读到了 padding → 花屏
所以正确的做法是:
按 RowStride 跳到每一行的起始位置
只拷贝前 width 个有效字节
跳过行尾的 padding
logcat 中的每一层标识:
App → CameraService: (Java 层日志,不经过 CamX)
CameraService → Provider: [CameraService] (logcat)
Provider → HAL: [CamX] [HAL ] camxhal3entry.cpp
HAL → CamX: [CHI] chxusecase.cpp
CamX → Session: [CamX] [CORE] camxsession.cpp
Session → Pipeline: [CamX] [CORE] camxpipeline.cpp
Pipeline → Node: [CamX] [CORE] camxnode.cpp
HAL → Kernel: [CSL]
Kernel → ISP: dmesg
CameraService 内部维护了每个相机的简单状态。它比 CamX Pipeline 的 8 状态状态机粗粒度得多:
CameraService 的状态(每个 Camera 一个):
CLOSED ──→ OPEN ──→ ACTIVE ──→ CLOSED
│ │
相机已打开 │ 正在处理请求
Stream 未配置 │ (capture/repeating)
状态转换:
CLOSED → OPEN: openCamera() 成功
OPEN → ACTIVE: configureStreams() 成功
ACTIVE → OPEN: Stream 被 flush 或关闭
OPEN → CLOSED: disconnect() 或 close()
ACTIVE → CLOSED: 直接 close()(跳过 flush)
和 CamX Pipeline 状态机的区别
CameraService 的状态机是"业务级别"的——关心相机是否打开、是否在拍照。它不需要关心 IFE 的 buffer 数量、BPS 的依赖是否满足。
CamX Pipeline 的 8 状态状态机(第 13 章)是"硬件控制级别"的——需要精确管理 ISP 硬件资源。两者粒度完全不同。
这是实际开发中最常见的场景之一:你改了 HAL 代码,push 到设备上,一测试崩了。然后呢?
① HAL 崩溃 — SIGSEGV / null pointer / assert fail
camera.qcom.so → crash → CameraProvider 进程被 kill
② 内核通知:SIGCHLD → init → CameraProvider 进程死亡
③ CameraService 感知
方式:Binder Death Notification
CameraService 和 CameraProvider 之间有一个 Binder 连接
Provider 进程死亡 → Binder 连接断开 → CameraService 收到通知
④ CameraService 清理
notifyAllClients(ERROR_CAMERA_SERVICE)
→ 所有正在使用相机的 App 收到 onError() 回调
→ CameraDeviceClient 被清理
→ CameraProviderManager 标记 Provider 为"不可用"
⑤ Provider 重启
Android init 进程检测到 CameraProvider 死亡
→ 根据 init.rc 中的配置重启它
→ 新的 Provider 进程启动 → 重新注册 HIDL/AIDL 服务
⑥ CameraService 重新发现
CameraProviderManager 检测到新的 Provider 注册
→ 重新连接
→ 相机恢复可用
⑦ App 重试
App 收到 onError() → 显示提示 → 用户重试
→ App 再次调用 openCamera() → 成功
💡 开发时的实用技巧
# 手动模拟 HAL 崩溃 adb shell killall -9 android.hardware.camera.provider@2.4-service # 观察恢复过程 adb logcat | grep -E "CameraService|Provider|death"
问题不在 HAL 层,在 CameraService 层。
可能的原因:
- CameraService 没有注册到 ServiceManager(检查 service check media.camera)
- CameraProviderManager 没有发现 Provider
- Binder 通信超时
排查:
adb shell dumpsys media.camera → 看有没有输出
adb shell service check media.camera → 看服务是否注册
几乎可以确定是该 App 的问题:
- 权限没声明(AndroidManifest.xml 中缺 CAMERA 权限)
- 运行时权限被拒绝
- App 在后台被限制了相机访问(Android 9+ 后台相机限制)
- App 代码异常(没正确处理 onDisconnected)
看 logcat:
adb logcat | grep -E "CameraProvider|death|restart"
可能原因:
- HAL 代码中有偶发的崩溃(通常和特定场景/特定 sensor 相关)
- 内存不足导致 Provider 被 OOM killer 杀掉
- 多个 App 竞争打开相机导致的竞态条件
解决:先抓 tombstone,再分析崩溃栈。tombstone 在 /data/tombstones/
### Q4: dumpsys media.camera 输出为空
说明 CameraService 没有正常连接到 Provider。
排查:
① adb shell ps -A | grep camera → Provider 进程在吗?
② 不在 → Provider 启动失败,看 logcat 中 HAL 加载日志
③ 在但 dumpsys 为空 → CameraService 和 Provider 之间的 HIDL/AIDL 连接断了
---
## 7.9 本章总结
CameraService 的核心要点
──────────────────────────────────────
① 位置
运行在 system_server 进程
是 App 和 HAL 之间的中转站
② 内部结构
CameraProviderManager — 管理 Provider
CameraDeviceClient — 每个 App 连接一个
Camera3Device — 封装 HAL 的 camera3_device_t
③ Treble 架构(最核心的变化)
之前:HAL 在 mediaserver 中 → HAL 崩溃 = 相机服务挂掉(手机不重启)
之后:HAL 在独立 Provider 进程 → HAL 崩溃 = Provider 重启,手机不受影响
④ 跨进程通信
App → CameraService: Binder
CameraService → Provider: HIDL(8.0~12)→ AIDL(13+)
Provider → HAL: 函数调用(同进程,dlopen)
HAL → Kernel: ioctl
⑤ 调试
dumpsys media.camera — 查看 CameraService 状态
service check media.camera — 确认服务是否注册
ps -A | grep camera — 确认 Provider 进程是否在跑
---
## 动手验证
1. 执行 `adb shell service list | grep camera`,看 CameraService 的服务名。
2. 执行 `adb shell ps -A | grep camera`,记录 Provider 的 PID。然后:
```bash
adb shell killall -9 android.hardware.camera.provider@2.4-service
再执行 ps -A | grep camera——PID 变了吗?说明 Provider 被重启了。
killall,观察 App 的行为——是崩溃了还是收到了 onError?