本文首先参考Android Binder IPC分析一文分析了Android Binder IPC通信机制过程及涉及到的各个子元素相关概念,从代码细节脱离出来,因而整体上把握Android binder IPC通信机制,是能够理解文章最后Camera Framework进程间通信实现的基础。参考Android 4.4版本源码。
本文首先参考Android Binder IPC分析一文分析了Android Binder IPC通信机制过程及涉及到的各个子元素相关概念,从代码细节脱离出来,因而整体上把握Android binder IPC通信机制,是能够理解文章最后Camera Framework进程间通信实现的基础。参考Android 4.4版本源码。
Binder通信概述
Android进程间通信(Inter-Process Communication, IPC)采用binder通信机制,是一种client/server结构。一个典型的binder单工通信过程如下述示意图。
其基本点为:
1. 表面上看client通过获得一个server的代理接口,对server进行调用。
2. 代理接口中定义的方法与server中定义的方法时一一对应的。
3. client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成Parcel对象。
4. 代理接口将Parcel发送给内核中的binder driver。
5. server会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回。
6. 整个的调用过程是一个同步过程,在server处理的时候,client会block住。
ServiceManager
ServiceManager(SM)是service的管理器,任何service在被使用之前,均要向SM注册,客户端需要访问某个service时,应该首先向SM查询是否存在该服务。如果SM存在这个service,那么会将该service的handle返回给client,handle是每个service的唯一标识符。
SM的入口函数int main(int argc, char **argv) 在service_manager.c中,进程主要工作为:
1. 初始化binder,打开/dev/binder设备,在内存中为binder映射128k字节空间;
2. 指定SM对应的代理binder的handle为0,当client尝试与SM通信时,需要创建一个handle为0的代理binder;
3. 通知binder driver(BD)使SM成为BD的context manager;
4. 维护一个死循环,在这个死循环中,不停地去读内核中binder driver,查看是否有可读的内容,即是否有对service的操作需求,如果有,则调用svcmgr_handler回调来处理请求的操作。
5. SM维护了一个svclist列表来存储service的信息。
当service在向SM注册时,该service就是一个client,而SM则作为了server。而某个进程需要与service通信时,此时这个进程为client,service才作为server。
应用和service之间的通信会涉及到2次binder通信:
1. 应用向SM查询service是否存在,如果存在获得该service的代理binder,为第一次binder通信;
2. 应用通过binder调用service的方法,为第二次binder通信。
ProcessState
ProcessState是以单例模式设计的。每个进程在使用binder机制通信时,均需要维护一个ProcessState实例来描述当前进程在binder通信时的binder状态。
ProcessState有2个主要功能:
1. 创建一个thread,该线程负责与内核中的binder模块进行通信,该线程称为Poolthread;
2. 为指定的handle创建一个BpBinder对象,并管理该进程中所有的BpBinder对象。
在binder IPC中,所有进程均会启动一个thread来负责与BD来直接通信,也就是不听的读写BD。Poolthread的启动方式为,
ProcessState::self()->startThreadPool();
BpBinder主要功能是负责client向BD发送调用请求的数据,它是client端binder通信的核心对象,通过调用transact函数向BD发送调用请求的数据。BpBinder构造函数为,
BpBinder(int32_t handle);
通过该构造函数可见,BpBinder会将当前通信中server的handle记录下来,当有数据发送时,会通知BD数据的发送目标。
ProcessState通过下述方式来获取BpBinder对象,
ProcessState::self()->getContextObject(handle);
ProcessState创建的BpBinder实例,一盘情况下回作为参数创建一个client端的service代理接口,形如BpXXXX,例如与SM通信时,client会创建一个代理接口BpServiceManager。
IPCThreadState
IPCThreadState是以单例模式设计的。因每个进程只维护了一个ProcessState实例,同时ProcessState只启动一个Poolthread,因此每个进程只需要一个IPCThreadState即可。
Poolthread实际内容为:
IPCThreadState::self()->joinThreadPool();
ProcessState中有两个Parcel成员,mIn和mOut。Poolthread会不停查询BD中是否有数据可读,如果有,将其读出并保存到mIn;同时不停检查mOut是否有数据需要向BD发送,如果有,则将其内容写入到BD中。总而言之,从BD中读出的数据保存到mIn,待写入到BD中的数据保存在mOut中。
ProcessState中生成的BpBinder实例通过调用IPCThreadState的transact函数来向mOut中写入数据,这样binder IPC过程的client端的调用请求的发送过程就明了了。
IPCThreadState 有两个重要的函数, talkWithDriver 函数负责从 BD 读写数据, executeCommand 函数负责解析并执行mIn 中的数据。
Binder IPC基本元素分析
基类IInterface
为client/server端提供接口,它的子类分别声明了client/service能够实现的所有的方法。
基类IBinder
定义了binder IPC的通信协议。BBinder和BpBinder均为IBinder的子类,是在IBinder协议框架内进行的收和发操作,构建了基本的binder IPC机制。
基类BpRefBase
client端在查询ServerManager获得所需的BpBinder后,BpRefBase负责管理当前获得的BpBinder实例。
接口类BpInterface
如果client端想要使用binder IPC与Service通信,那么首先会从ServiceManager处查询并获得server端service的BpBinder,在client端,这个对象被认为是server端的远程代理。为了使client能够像本地调用一样调用一个远程server,server端需要向client提供一个接口,client在这个接口的基础上创建一个BpInterface,使用这个对象,client的应用能够像本地调用一样直接调用server端的方法。而不用去关心具体的binder IPC实现。
BpInterface的原型:
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
BpInterface是一个模板类,当server提供了INTERFACE接口(形如IXXXService)后,通常会继承BpInterface模板类实现一个BpXXXService:
class BpXXXService: public BpInterface<IXXXService>
实际上BpXXXService实现了双继承IXXXService和BpRefBase,这样既实现了service中各方法的本地操作,将每个方法的参数以Parcel的形式发给binder driver;同时又将BpBinder作为了自己的成员来管理,将BpBinder存储在mRemote中,通过调用BpRefBase的remote()来获得BpBinder指针。
接口类BnInterface
BnInterface也是一个模板类。在定义native端的service时,基于server提供的INTERFACE接口(IXXXService),通常会继承BnInterface模板类实现一个BnXXXService,而native端的service继承自BnXXXService。BnXXXService定义了一个onTransact函数,这个函数负责解包收到的Parcel并执行client端的请求的方法。
BnInterface的原型是:
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
BnXXXService形如:
class BnXXXService: public BnInterface<IXXXService>
IXXXService为client端的代理接口BpXXXService和server端的BnXXXService的共同接口类,这个共同接口类的目的就是保证service方法在C/S两端的一致性。
每个service都可视为一个binder,而真正的service端的binder的操作及状态的维护就是通过继承自BBinder来实现的。BBinder是service作为binder的本质所在。
那么,BBinder与BpBinder的区别是什么呢?BpBinder是client端创建的用于向server发送消息的代理,而BBinder是server端用于接收消息的通道。他们的代码中虽然均有transact方法,但两者的作用不同,BpBinder的transact方法时想IPCThreadState实例发送消息,通知其有消息要发送给binder driver;而BBinder则是当IPCThreadState实例收到binder driver消息时,通过BBinder的transact方法将其传递给它的子类BnXXXService的onTransact函数执行server端的操作。
Parcel类
Parcel是binder IPC中的最基本的通信单元,它存储C/S间函数调用的参数。Parcel只能存储基本的数据类型,如果是复杂的数据类型的话,在存储时,需要将其拆分为基本的数据类型来存储。
binder通信的双方既可以作为client,也可以作为server,此时的binder通信是一个半双工的通信,操作的过程会比单工的情况复杂,但是基本原理是一样的。
Camera Framework的IPC机制
Camera Framework C/S实现类图如下:
仔细阅读本文前面的内容,能够很容易理解Camera Framework采取的C/S结构设计方式。Camera C/S binder IPC通信采用的是半双工通信的方式。client端提供的接口为ICameraClient,分别通过继承BpInterface与BnInterface模板类的方式实现代理BpCameraClient(server端的client代理)与client端本地BnCameraClient,Camera为本地具体的实现;server端提供的接口为ICameraServer,分别通过继承BpInterface与BnInterface模板类的方式实现代理BpCameraService(client端的server代理)与server端本地BnCameraService。