从 App 到 CameraService:一文搞懂 Camera Framework 的 AIDL / Binder 通信
前言
如果你做过 Android Camera App 开发,大概率写过这样一行代码:
cameraManager.openCamera(cameraId, executor, stateCallback);
这行代码看起来是一个普通 Java API 调用。
但它背后真正发生的事情并不普通:App 进程里的 Java Framework,会通过 AIDL / Binder 跨进程进入 cameraserver,再由 C++ 的 CameraService 创建 CameraDeviceClient,继续往下连接 Camera3Device 和 Camera HAL。
很多人第一次读 Camera Framework 源码时,会卡在几个问题上:
-
AIDL 不是 Java App 之间通信用的吗,为什么能调用 C++ CameraService? -
App 调用 openCamera()后,为什么最后会进入CameraService::connectDevice()? ICameraService
、 ICameraDeviceUser、ICameraDeviceCallbacks到底有什么区别?-
既然 App 和 CameraService 通过 AIDL 通信,图像数据是不是也通过 AIDL 传输?
这篇文章就从这些问题出发,把 Camera Framework 中 App 到 CameraService 的 AIDL / Binder 通信讲清楚。
目标不是把所有源码都贴一遍,而是让你读完后能建立一张清晰的地图:
App Camera2 API -> Java Framework -> AIDL / Binder -> CameraService -> CameraDeviceClient -> Camera3Device -> Camera HAL
说明:本文源码以google开源仓库为准。仓库中的 base/...对应完整 AOSP 的frameworks/base/...,av/...对应frameworks/av/...,interfaces/...对应hardware/interfaces/...。
一、先给结论
Camera Framework 中,App 和 CameraService 的通信主要靠 AIDL / Binder。
但要注意:
AIDL / Binder 负责的是控制命令、状态回调、Binder 对象引用和少量结构化数据。真正的图像像素数据,不会通过 Binder byte[]一帧一帧传给 App。
在 Camera2 主链路里,最重要的是三根 Binder 线:
ICameraServiceICameraDeviceUserICameraDeviceCallbacks
它们分别解决三个问题:
|
|
|
|
|---|---|---|
ICameraService |
|
|
ICameraDeviceUser |
|
|
ICameraDeviceCallbacks |
|
|
你可以先记住一句话:
ICameraService负责“打开门”, ICameraDeviceUser负责“进门后操作设备”,ICameraDeviceCallbacks负责“服务端反向通知 App”。
后面所有源码,基本都围绕这三根线展开。
二、先看整体架构
先不要急着看代码。先看 App 到 CameraService 的位置关系。

这张图里有两个关键点。
第一个关键点:App 进程里没有真正的 CameraService 对象。App 拿到的是 Binder proxy,看起来像本地接口,实际调用会跨进程进入 cameraserver。
第二个关键点:控制链路和图像链路是两条线。
控制命令走:
AIDL / Binder
图像数据走:
Surface / BufferQueue / GraphicBuffer
这两个东西一定不要混在一起。
三、先把 AIDL / Binder 几个词说清楚
如果只看 Camera 源码,很容易被 Stub、Proxy、Parcel、ServiceManager 这些词绕晕。
先用最短的话解释一下。
1. AIDL:远程接口说明书
AIDL 全称是 Android Interface Definition Language。
它不是业务语言,而是一种接口描述语言。它用 .aidl 文件描述:
远端对象能提供哪些方法?参数是什么?返回值是什么?
比如 Camera 中的:
av/camera/aidl/android/hardware/ICameraService.aidl
里面定义了 connectDevice(...),意思是 CameraService 对外提供一个“打开 Camera2 设备”的远程接口。
2. Binder:跨进程通信通道
Binder 是 Android 的 IPC 机制。
App 进程不能直接调用 cameraserver 进程里的 C++ 对象,所以需要 Binder 把一次方法调用送到另一个进程。
可以把 Binder 理解成系统级快递通道:

3. Proxy:客户端手里的远程代理
Proxy 在调用方这一侧。
App 调用 cameraService.connectDevice(...),看起来像普通 Java 方法,实际是 Proxy 把参数打包进 Parcel,然后发起 Binder transact()。
4. Stub / BnXXX:服务端接请求的人
Stub 在服务端这一侧。
CameraService 是 C++ 实现,当前仓库里可以看到它继承了:
class CameraService : public BinderService<CameraService>, public virtual ::android::hardware::BnCameraService, ...
这里的 BnCameraService 就是 native 侧的 Binder Stub。Java 侧发来的 Binder 调用,最终会被它接住,然后分发到 CameraService::connectDevice() 这类真正的实现函数。
5. Parcel:跨进程参数包
不同进程不能直接共享普通对象地址。
所以 AIDL 生成的代码会把参数写进 Parcel,服务端再从 Parcel 里读出来。
在 Camera 中,Binder 适合传这些东西:
-
camera id、target SDK、rotation override 这类轻量参数 ICameraDeviceCallbacks
这类 Binder 回调对象 ICameraDeviceUser
这类 Binder 返回对象 Surface
背后的 Binder / native handle 引用 -
metadata 描述或 FMQ 描述符
但它不适合一帧一帧传图像像素数据。
6. ServiceManager:系统服务通讯录
App 要先找到 CameraService,才能调用它。
CameraService 注册到系统里的服务名是:
media.camera
Java Framework 通过 ServiceManager.getService("media.camera") 拿到它的 Binder。
四、Camera Framework 里的三根 Binder 线
理解 Camera AIDL,最重要的不是背 AIDL 语法,而是记住对象关系。

1. ICameraService:打开 Camera 前找总服务台
ICameraService 的方向是:
Java Framework -> C++ CameraService
它负责服务级操作,比如:
-
获取 camera 数量 -
获取 camera info / characteristics -
监听 camera availability -
打开 Camera2 设备 -
查询并发相机、session 支持能力
最关键的方法是:
ICameraDeviceUser connectDevice( ICameraDeviceCallbacks callbacks, String cameraId, int oomScoreOffset, int targetSdkVersion, int rotationOverride, AttributionSourceState clientAttribution, int devicePolicy, boolean sharedMode);
这段接口解决的问题是:
App 把自己的回调 Binder,也就是 ICameraDeviceCallbacks,传给CameraService;CameraService打开设备成功后,再返回一个ICameraDeviceUser给 App。
这就是 Camera2 打开设备时最核心的一次 Binder 交换。
2. ICameraDeviceUser:打开后控制设备的遥控器
ICameraDeviceUser 的方向是:
CameraDeviceImpl -> CameraDeviceClient
当 connectDevice() 成功后,Java 层会拿到一个 ICameraDeviceUser。
后续很多 Camera2 操作都通过它继续跨进程:
beginConfigure()createStream()endConfigure()submitRequestList()flush()disconnect()
换句话说:
openCamera()只是拿到遥控器。真正配置输出流、提交预览请求、停止请求、关闭设备,后面都要靠 ICameraDeviceUser。
3. ICameraDeviceCallbacks:服务端回调 App 的电话线
ICameraDeviceCallbacks 的方向刚好反过来:
CameraDeviceClient -> CameraDeviceImpl
它里面的方法包括:
onDeviceError()onDeviceIdle()onCaptureStarted()onResultReceived()onRepeatingRequestError()onRequestQueueEmpty()
这些方法是 oneway 调用,适合服务端异步通知 App。
例如 HAL 返回 capture result 后,Framework native 层最终会通过 onResultReceived(...) 通知 Java 层,Java 层再派发到 App 的 CaptureCallback.onCaptureCompleted()。
读源码时只要能分清这三根线,Camera Framework 的 AIDL 关系就不会乱。
五、源码链路:App 是怎么找到 CameraService 的?
先看服务在哪里。
当前仓库中,cameraserver 的入口在:
av/camera/cameraserver/main_cameraserver.cpp
启动后会调用:
CameraService::instantiate();
CameraService 自己声明的服务名在:
av/services/camera/libcameraservice/CameraService.h
关键点是:
static char const* getServiceName() { return "media.camera"; }
这一步的意义是:
CameraService把自己注册到 ServiceManager,名字叫 media.camera。以后其他进程要找相机服务,就用这个名字查。
Java Framework 侧查找服务的位置在:
base/core/java/android/hardware/camera2/CameraManager.java
CameraManagerGlobal 里有这样一段逻辑:
IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
这里的 CAMERA_SERVICE_BINDER_NAME 就是:
media.camera
这段代码解决的问题是:
Java Framework 先从 ServiceManager 拿到一个原始 IBinder,再通过ICameraService.Stub.asInterface(...)包装成可以调用的ICameraService接口。
如果远端服务在另一个进程,这个 ICameraService 实际就是一个 Proxy。
所以 App 后面看起来是在调用 Java 接口,实际已经具备了跨进程进入 cameraserver 的能力。
六、源码链路:openCamera() 如何进入 connectDevice()?
App 调用:
cameraManager.openCamera(cameraId, executor, stateCallback);
内部会走到:
CameraManager.openCameraDeviceUserAsync(...)
这个方法里有三个关键动作。
第一步:创建 App 进程内的 CameraDeviceImpl
CameraDeviceImpl deviceImpl = new CameraDeviceImpl( cameraId, callback, executor, characteristics, this, targetSdkVersion, mContext, cameraDeviceSetup, sharedMode);
这段代码解决的问题是:
先在 App 进程内创建一个 CameraDevice的本地实现对象。它不是硬件设备本体,而是 App 后续操作 Camera 的 Java 门面。
它保存了 App 传进来的 StateCallback 和 Executor。
后续 onOpened()、onDisconnected()、onError() 这些回调,也会通过这个 Executor 派发给 App。
第二步:拿到 App 侧的回调 Binder
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
这段代码解决的问题是:
App 进程准备一个 ICameraDeviceCallbacks,稍后传给CameraService。服务端以后要通知 result、error、capture started,就会反向调用这个 Binder。
注意,这里传的是“回调通道”,不是图像数据通道。
第三步:通过 ICameraService.connectDevice() 跨进程打开设备
cameraUser = cameraService.connectDevice( callbacks, cameraId, oomScoreOffset, targetSdkVersion, rotationOverride, clientAttribution, devicePolicy, sharedMode);
这段代码解决的问题是:
Java Framework 正式请求 CameraService打开某个 camera id,并把 App 侧 callback 一起交给服务端。
这一步返回的 cameraUser 就是 ICameraDeviceUser。
也就是说,connectDevice() 不是只返回一个成功失败状态,而是返回了一个后续控制设备的远程 Binder 接口。
七、CameraService 收到请求后做什么?
ICameraService.connectDevice() 跨进程后,会进入 native 层:
av/services/camera/libcameraservice/CameraService.cpp
当前源码中的入口是:
Status CameraService::connectDevice( const sp<ICameraDeviceCallbacks>& cameraCb, const std::string& unresolvedCameraId, ..., sp<ICameraDeviceUser>* device) { return connectDeviceImpl(...);}
这段代码解决的问题是:
Binder Stub 已经把 Java 侧参数解包成 native 参数, CameraService::connectDevice()开始处理真正的 Camera 打开逻辑。
后面会继续进入:
connectDeviceImpl(...)connectHelper(...)makeClient(...)client->initialize(...)
这几步不是简单 new 一个对象,而是要处理很多系统级问题:
-
camera id 是否存在 -
调用方 uid / pid / attribution 是否可信 -
App 是否有权限 -
device policy 是否禁用 Camera -
当前 Camera 是否已经被其他高优先级 client 占用 -
是否超过最大同时打开数量 -
是否需要踢掉低优先级 client -
HAL session 是否能成功打开
这也是为什么 App 层 openCamera() 可能抛出 CAMERA_IN_USE、MAX_CAMERAS_IN_USE、CAMERA_DISABLED、CAMERA_DISCONNECTED 等异常。
从 App 视角看,这些不是简单的 Java 参数错误,而是 cameraserver 在做系统资源仲裁。
八、为什么 onOpened() 不是 HAL 直接回调 App?
这是一个很容易误解的点。
很多人会以为:
HAL open 成功 -> HAL 回调 App 的 onOpened()
实际不是这样。
更准确的链路是:

Java 层关键代码在:
base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
setRemoteDevice(...) 里有这样几步:
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);IBinder remoteDeviceBinder = remoteDevice.asBinder();remoteDeviceBinder.linkToDeath(this, 0);mDeviceExecutor.execute(mCallOnOpened);mDeviceExecutor.execute(mCallOnUnconfigured);
这段代码解决的问题是:
Java 层拿到 CameraService返回的ICameraDeviceUser后,把它保存为后续控制通道,并在 App 提供的Executor上派发StateCallback.onOpened()。
所以 onOpened() 的直接触发点是:
CameraDeviceImpl.setRemoteDevice()
而不是 HAL 直接调用 App。
这也解释了一个 App 开发中的常见现象:
onOpened()只代表设备连接已经建立,不代表已经有预览帧。
预览还需要继续创建 session、配置 Surface、提交 repeating request。
九、打开之后,ICameraDeviceUser 继续负责什么?
拿到 ICameraDeviceUser 后,App 才真正具备“控制这台 Camera”的能力。
后续两个最常见动作是:
createCaptureSession()setRepeatingRequest()
它们仍然会通过 Binder 进入 CameraDeviceClient。
1. createCaptureSession():配置输出流
现代 Camera2 推荐从 SessionConfiguration / OutputConfiguration 理解 session 创建:
OutputConfiguration previewOutput = new OutputConfiguration(previewSurface);SessionConfiguration sessionConfig = new SessionConfiguration( SessionConfiguration.SESSION_REGULAR, Collections.singletonList(previewOutput), executor, stateCallback);cameraDevice.createCaptureSession(sessionConfig);
Framework 内部会通过 ICameraDeviceUser 做类似这样的事情:
beginConfigure()createStream(OutputConfiguration)endConfigure(...)
Java 层可以在 CameraDeviceImpl 中看到:
mRemoteDevice.beginConfigure();int streamId = mRemoteDevice.createStream(outConfig);offlineStreamIds = mRemoteDevice.endConfigure(...);
这段代码解决的问题是:
App 传入的 Surface会被包装在OutputConfiguration中,通过 Binder 送到CameraDeviceClient,再由 native 层创建 Camera stream,最终触发 HALconfigureStreams()。
注意,这里传的是 Surface 相关的引用和配置,不是把图像帧传过去。
2. setRepeatingRequest():提交持续预览请求
预览请求最终会走:
requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);
对应 AIDL:
SubmitInfo submitRequestList( in CaptureRequest[] requestList, boolean streaming);
这段代码解决的问题是:
Java 层把 CaptureRequest通过 Binder 提交给CameraDeviceClient。如果streaming=true,它就是持续预览或持续录像这类 repeating request。
在 native 层,CameraDeviceClient::submitRequestList(...) 会继续校验 request、Surface target、stream id 映射关系,然后把请求送进 Camera3Device 的 request 管线。
十、Result / Error 如何从服务端回到 App?
打开 Camera 时,App 已经把 ICameraDeviceCallbacks 传给了 CameraService。
因此服务端后续可以反向通知 App。

关键 AIDL 是:
oneway void onResultReceived( in CameraMetadataInfo resultInfo, in CaptureResultExtras resultExtras, in PhysicalCaptureResultInfo[] physicalCaptureResultInfos);
这里的 oneway 很适合 result / error 这种异步通知。
读源码时要分清两类回调:
|
|
|
|
|---|---|---|
CameraDevice.StateCallback.onOpened() |
setRemoteDevice()
|
|
CaptureCallback.onCaptureCompleted() |
ICameraDeviceCallbacks.onResultReceived()
|
|
前者是设备状态,后者是 capture request 的结果。
不要把这两条链路混成一条。
十一、AIDL、JNI、HAL AIDL/HIDL、FMQ、BufferQueue 到底怎么分?
Camera Framework 里术语很多,最容易混的是这几个:
AIDL / BinderJNIHAL AIDL / HIDLFMQBufferQueue / GraphicBuffer
可以按“连接谁”和“传什么”来区分。
1. CameraManager 到 CameraService 不是 JNI
JNI 适合:
同一个进程内 Java 调 C/C++
但 CameraManager 在 App 进程,CameraService 在 cameraserver 进程。
这是跨进程,所以主链路是:
AIDL / Binder
而不是 JNI。
2. CameraService 到 HAL 可能是 HIDL,也可能是 AIDL
不同 Android 版本、不同设备实现会有差异。
从学习角度可以先记:
App / Framework -> CameraService:AIDL / BinderCameraService -> Camera HAL:HAL AIDL 或 HIDL,取决于版本和实现
不要把 Framework 层 AIDL 和 HAL 层 AIDL/HIDL 混成一个东西。
3. FMQ 不是 BufferQueue
FMQ 全称是 Fast Message Queue。
在 Camera 中,它常用于传 request / result metadata 这类结构化数据,减少 Binder 大量拷贝和同步成本。
但它不是用来传预览图像帧的。
4. 图像数据走 BufferQueue / GraphicBuffer
预览、拍照、录像的图像数据通常走:
Surface / BufferQueue / GraphicBuffer / HardwareBuffer
App 传给 Camera 的 Surface,本质上是在告诉 Framework:
你把图像输出到这条 BufferQueue 里,我这边用 Preview、ImageReader 或 MediaRecorder 去消费。
所以千万不要把 Camera 图像数据理解成 Binder byte[]。
十二、完整流程图
把前面的内容合起来,openCamera() 的主流程可以这样看:
图 7:openCamera() 到 onOpened() 完整流程,稍后插入图片
这张图里真正需要记住的是两个 Binder 对象的交换:
App -> CameraService:传入 ICameraDeviceCallbacksCameraService -> App:返回 ICameraDeviceUser
这就是 openCamera() 里最重要的 AIDL / Binder 关系。
十三、容易误解的点
误区一:AIDL 只能 Java 调 Java
不是。
现代 AIDL 可以生成 Java、C++、NDK、Rust 等不同后端代码。
Camera Framework 中的典型场景就是:
Java Framework -> Binder -> C++ CameraService
误区二:Java 调 C++ 就一定是 JNI
不是。
判断标准不是语言,而是不是跨进程。
同进程 Java 调 C++:通常是 JNI跨进程 Java 调 C++:通常是 AIDL / Binder
CameraManager 调 CameraService 是跨进程,所以不是 JNI 主链路。
误区三:onOpened() 是 HAL 直接回调给 App
不准确。
HAL open 成功是前提,但 App 的 onOpened() 通常是 Java 层 CameraDeviceImpl.setRemoteDevice() 成功绑定 ICameraDeviceUser 后,通过 App 提供的 Executor 派发。
误区四:ICameraDeviceUser 和 ICameraDeviceCallbacks 分不清
记方向就行:
ICameraDeviceUser:App -> CameraService,控制设备ICameraDeviceCallbacks:CameraService -> App,回调事件
一个是 App 操作服务端,一个是服务端通知 App。
误区五:图像数据通过 AIDL byte[] 传输
不是。
AIDL / Binder 主要传控制命令、对象引用和回调。
图像像素数据走:
Surface / BufferQueue / GraphicBuffer
误区六:createCaptureSession() 和 HAL openSession() 是一回事
不是。
openSession() 更靠近 CameraService 打开 HAL device session。
createCaptureSession() 是 App 在设备打开之后配置输出流,后面会触发 stream 创建和 HAL configureStreams()。
两者处在不同阶段。
十四、对 App / Camera App 开发者有什么意义?
1. onOpened() 不代表预览已经开始
onOpened() 只代表 App 拿到了 ICameraDeviceUser,可以继续配置 Camera。
真正看到画面,还需要:
createCaptureSession()setRepeatingRequest()HAL 输出 buffer 到 Surface
所以不要在 onOpened() 里期待已经有预览帧。
2. Surface 配错,通常在 session 阶段暴露
openCamera() 成功,不代表所有输出组合都支持。
真正的输出组合检查发生在 createCaptureSession() 之后,也就是 createStream() / endConfigure() / configureStreams() 这条链路。
如果尺寸、格式、Surface 组合不被 HAL 支持,问题通常会在这里暴露。
3. Camera 被占用不是 App 自己能完全决定的
CameraService 会做系统级资源仲裁。
比如其他高优先级 client 正在使用 Camera,或者系统策略禁用了 Camera,App 层就可能收到 CAMERA_IN_USE、MAX_CAMERAS_IN_USE、CAMERA_DISABLED。
这些异常背后不是简单 Java 状态,而是 cameraserver 的权限和资源管理结果。
4. ImageReader 不及时 close() 会影响图像链路
图像不走 Binder,但会走 BufferQueue。
如果 App 用 ImageReader 拿到 Image 后不及时 close(),buffer 可能被 App 长时间占住,导致 HAL / Framework 拿不到空闲 buffer。
表现出来可能就是预览卡住、拍照不回调、帧率下降。
5. 读 Camera 源码时先画 Binder 线
建议你读源码时按这个顺序:
1. ICameraService:怎么找到 CameraService,怎么 connectDevice2. ICameraDeviceUser:打开后怎么 createStream / submitRequest3. ICameraDeviceCallbacks:result / error 怎么回到 App
先把这三根线画出来,再继续看 Camera3Device、HAL、BufferQueue,思路会清楚很多。
十五、总结
到这里,我们可以把 Camera Framework 的 AIDL / Binder 通信总结成三句话。
第一,App 调用 Camera2 API,本质上不是直接操作硬件,而是通过 Java Framework 间接和 cameraserver 通信。
第二,Java Framework 和 C++ CameraService 之间的主通信机制是 AIDL / Binder,不是 JNI。
第三,AIDL / Binder 负责控制命令、对象引用和回调;真正的图像像素数据走 Surface / BufferQueue / GraphicBuffer。
最后再记住这三根线:
ICameraService:打开 Camera / 查询服务级信息ICameraDeviceUser:打开后控制设备ICameraDeviceCallbacks:服务端回调 App
只要这三根线清楚了,再读 openCamera()、createCaptureSession()、setRepeatingRequest()、processCaptureResult(),就不会被 Camera Framework 复杂的类名绕晕。
夜雨聆风