乐于分享
好东西不私藏

从 App 到 CameraService:一文搞懂 Camera Framework 的 AIDL / Binder 通信

从 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
    ICameraDeviceUserICameraDeviceCallbacks 到底有什么区别?
  • 既然 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

它们分别解决三个问题:

Binder 接口
调用方向
解决的问题
ICameraService
App / Framework -> CameraService
找服务、查信息、打开 Camera
ICameraDeviceUser
CameraDeviceImpl -> CameraDeviceClient
打开后控制这台 Camera
ICameraDeviceCallbacks
CameraDeviceClient -> CameraDeviceImpl
服务端把 result / error 回调给 App

你可以先记住一句话:

ICameraService

 负责“打开门”,ICameraDeviceUser 负责“进门后操作设备”,ICameraDeviceCallbacks 负责“服务端反向通知 App”。

后面所有源码,基本都围绕这三根线展开。


二、先看整体架构

先不要急着看代码。先看 App 到 CameraService 的位置关系。

这张图里有两个关键点。

第一个关键点:App 进程里没有真正的 CameraService 对象。App 拿到的是 Binder proxy,看起来像本地接口,实际调用会跨进程进入 cameraserver

第二个关键点:控制链路和图像链路是两条线。

控制命令走:

AIDL / Binder

图像数据走:

Surface / BufferQueue / GraphicBuffer

这两个东西一定不要混在一起。


三、先把 AIDL / Binder 几个词说清楚

如果只看 Camera 源码,很容易被 StubProxyParcelServiceManager 这些词绕晕。

先用最短的话解释一下。

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,传给 CameraServiceCameraService 打开设备成功后,再返回一个 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_USEMAX_CAMERAS_IN_USECAMERA_DISABLEDCAMERA_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,最终触发 HAL configureStreams()

注意,这里传的是 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 这种异步通知。

读源码时要分清两类回调:

App 回调
主要触发来源
含义
CameraDevice.StateCallback.onOpened() setRemoteDevice()

 后 Java 层派发
设备连接建立
CaptureCallback.onCaptureCompleted() ICameraDeviceCallbacks.onResultReceived()

 后 Java 层派发
某个 request 的结果返回

前者是设备状态,后者是 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_USEMAX_CAMERAS_IN_USECAMERA_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 复杂的类名绕晕。