Android Binder浅析 — Client获取CameraService
继前一章Android Binder浅析 — CameraService往ServiceManager添加服务后,CameraService已经添加到ServiceManager的svinfo服务列表中去,我们现在要从客户端向ServiceManager拿到这个服务并使用,源码分析下客户端是如何拿到这个服务的?
ServiceManger代码架构
为什么要先说ServiceManager架构呢?
因为在Camera相机这个模块,最终会使用到其底层的CamerService服务,这些服务都是被注册到底层ServiceManager(c++)上去,而java层要使用到这些服务,会经过java层的ServiceManager然后跨进程binder去获取服务,调用服务等,而java层这块ServiceManager代码架构理解后有助于对整体代码的认识,并且我们看源码不仅仅是看代码逻辑如何调用,更多的是理解其各个模块的设计架构精髓,这才是最重要的!
首先看看我总结ServiceManager的架构:
架构解释:
上图可以分为三个模块:Binder模块、ServiceManager模块以及CamerManager模块,从逻辑调用的方向从外向内依次解释各个模块的作用:
CameraManager模块
主体包含上图Camera关键字的几个类和IBinder.DeathRecipient类!
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
manager.openCamera(cameraId, callback,handler);
CameraManager是我们做相机开发时会用到的类,通过调用他的方法openCamera可以帮助我们打开相机,这个类主要是提供给外部模块使用。
但是openCamera如何执行,真正工作的则是CameraManagerGlobal,它继承ICameraServiceListener.Stub和实现IBinder.DeathRecipient接口,ICameraServiceListener.Stub使用了aidl方式的的Stub类,说明它是一个binder服务端的类,这里的作用主要是会将CameraManagerGlobal自己这个服务Listener监听注册到CamerService服务端,待CameraService状态变化回调自己,后面代码讲到时具体讲解;同理IBinder.DeathRecipient目的也是一样的,它注册到ServiceManager那端,主要是服务死亡时断开连接。
同时,CameraManagerGlobal可以任意调用ServiceManager类的方法,继续看下面
ServiceManager模块
ServiceManager模块主要是上图包含servicemanager关键字的类,IServiceManager接口定义了该模块工作的能力,可以做哪些事情。
public interface IServiceManager
{
public IBinder getService(String name) throws RemoteException;
public IBinder checkService(String name) throws RemoteException;
public void addService(String name, IBinder service, boolean allowIsolated)
throws RemoteException;
public String[] listServices() throws RemoteException;
}
该模块如上代码所示,可以进场查询、添加和展示binder服务等工作,以上代码只是部分哈,还有一些其他功能!实现这些功能主要有两个类:ServiceManagerNative和ServiceManagerProxy;如前一篇文章Android Binder浅析 — CameraService往ServiceManager添加服务里面讲到的binder服务架构,在java层ServiceManagerNative更像binder服务架构里面的BnServiceManager,隶属于服务端真正实现的类,这里并没有用到它;而ServiceManagerProxy则是在客户端的代理使用类BpServiceManager,通过ServiceManagerProxy向我们底层真正ServiceManager发起getService、addService等等
那ServiceManagerProxy是怎么get/addService的呢?
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
//mRemote类型为IBinder
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
原因在于ServiceManagerProxy有一个IBinder成员mRemote,如getService方法就是把参数和方法封装到Parcel,在调用mRemote的transact方法去跨进程IPC获取我们的service,先透露一下,这个mRemote成员实质是Binder模块的BinderProxy类,具体内部如何IPC跨进程,那就看看后面的Binder模块吧
Binder模块
同上,Binder主体也是上图包含binder关键字的几个类,IBinder也是该模块的定义的能力接口;其子类Binder主要是用于他的继承者,表明这是一个Binder服务的类,暂时用不到;BinderProxy主要重写了transact,向Native层发起调用,与此同时BinderProxy包含许多native方法如transactNative,java向native发起调用工作;一般来说一个BinderProxy对应一个BpBinder(c++),也就是BinderProxy的mObject持有c++对象BpBinder的指针,如在这里ServiceManager架构中,BinderProxy如下:
final class BinderProxy implements IBinder {
mObject就是持有底层ServiceManager服务,handle=0就是他的唯一标识
private long mObject = BpBinder(handle = 0)
}
以上mObject是查看源码得知的,他是如何给mObject赋值的呢?,大致是这样一个过程,从源码这里入口:
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
以上代码是java层ServiceManager中获取底层C++的ServiceManager服务过程,其中关键代码是BinderInternal.getContextObject()这句代码
这是一个native方法
public static final native IBinder getContextObject();
继续看他的Native方法,这个native方法采用动态注册的方法,在jni_onLoad的时候把java的函数和native函数连接在一起,分析之,其native对应方法为:
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
返回的就是BpBinder(0),前文已经分析过
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
将b转换成一个java对象,这个方法是重点
return javaObjectForIBinder(env, b);
}
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
if (val == NULL) return NULL;
BpBinder返回false,不执行
if (val->checkSubclass(&gBinderOffsets)) {
// One of our own!
jobject object = static_cast<JavaBBinder*>(val.get())->object();
LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
AutoMutex _l(mProxyLock);
创建BinderProxy的java对象,gBinderProxyOffsets.mClass是BinderProxy的类和构造方法
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
将BpBinder指针设置到mObject去
env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
为这个sp智能指针引用加1
val->incStrong((void*)javaObjectForIBinder);
返回BinderProxy java层的弱引用,同时为他创建全局引用,native层持有这个所引用
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
为这个弱引用绑定一个代理清理函数proxy_cleanup
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Also remember the death recipients registered on this proxy
sp<DeathRecipientList> drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
这个object也就是BinderProxy,其内部mObject=BpBinder(0)
return object;
}
管理android.os.BinderProxy的结构
static struct binderproxy_offsets_t
{
// Class state.
jclass mClass; android.os.BinderProxy的class
jmethodID mConstructor; 构造方法
jmethodID mSendDeathNotice; senddeathNotice函数
// Object state.
jfieldID mObject;
jfieldID mSelf;
jfieldID mOrgue;
} gBinderProxyOffsets;
const char* const kBinderProxyPathName = "android/os/BinderProxy";
static int int_register_android_os_BinderProxy(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, kBinderProxyPathName); 获取class
因为clazz是一个局部引用,不能全局使用,所以要为它创建全局引用
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
获取BinderProxy的构造方法
gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;)V");
获取BinderProxy的long类型,名字为mObject成员,以下同理
gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
"Ljava/lang/ref/WeakReference;");
gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
return RegisterMethodsOrDie(
env, kBinderProxyPathName,
gBinderProxyMethods, NELEM(gBinderProxyMethods));
}
至此,BinderInternal.getContextObject分析完成!得到一个BinderProxy,其内部mObject=BpBinder(0);可以理解为native层的BpBinder在java层的代理就是BinderProxy,而BinderProxy的mObject持有了native层BpBinder指针,最终调用BinderProxy可以操作底层的业务访问!
插入一个java-native之间的互通模型:
主要解决两者层之间快速的互通数据模型,连接初始化这些结构体的值参考上面的代码:
小结这个架构设计,上面这种架构设计应该很常见,IXX接口定义该模块的能力,XX实现该接口能力,XXManger持有IXX成员,并且提供一些访问方法暴露给外部接口调用,这样做的好处就是内外分离,职责明确,可替代性好,当模块内部逻辑了,我们重现实现一个XXX类,在把他设置到XXManager中去就行,不需要改变其他代码
Binder客户端获取CameraService的调用逻辑
以上只是谈谈了ServiceManager架构,再次我们进入本文主题,获取CameraService;先看看我总结的调用图:
图片查看解释:
灰色字体表示某个类,如CameraManagerGlobal是一个java的类,类下面竖直虚线表示该类的方法,方法之间的箭头表明了他们的调用逻辑;红色字体表示该大虚线左右两侧分属不同的场景,如java到native两个场景
从最外层CameraManager的openCamera调用开始,会调用其真正CameraManagerGlobal类发起连接相机设备服务,但是前提要先获取服务端CameraService才行,上图就是从CameraManagerGlobal获取CameraService的调用逻辑,部分源码可以按照我上图展示的逻辑去查看即可,这里只阐述其中关键的一些代码:
从IPC binder返回后的逻辑
binder跨进程IPC访问,一般都是把结果写到Parcel对象reply里面去,这里也不例外,从上面逻辑图可知,当在IPCThreadState::transact的IPC调用执行完成后,退回到ServiceMangerProxy类时,会对reply结果进行处理,主要是对结果readStrongBinder,然后逻辑会走到Native层的Parcel对象中去,我们就从这里开始:
sp<IBinder> Parcel::readStrongBinder() const
{
sp<IBinder> val;
// Note that a lot of code in Android reads binders by hand with this
// method, and that code has historically been ok with getting nullptr
// back (while ignoring error codes).
readNullableStrongBinder(&val);
这里虽然返回的是sp局部变量,但是泛型IBinder在unflatten_binder是new出来的,不会被栈回收
return返回会对sp拷贝,所以没得问题
return val;
}
status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
return unflatten_binder(ProcessState::self(), *this, val);
}
binder实体都是写到flat_binder_object对象的,所以要反序列化把他读出来
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
此类型说明返回的是binder实体,通常用于和服务端同属一个进程
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
返回的是handle句柄,跨进程会使用到,执行的是这里
case BINDER_TYPE_HANDLE:
flat.handle就是服务端的唯一标识句柄,getStrong方法就是返回了BpBinder
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
不明白看代码注释,readStrongBinder返回后还会在android_os_Parcel类文件中调用javaObjectForBinder方法,主要就是将c的BpBinder指针转换为java层的BinderProxy对象,这个转换过程本文上面已经讲过,就不在阐述了;最后在CameraManagerGlobal中对返回的BinderProxy对象进行了一次转换,调用ICameraService.Stub.asInterface方法,主要就是对BinderProxy在进行一次包裹,然后使用包裹后的CameraService调用方法时,会先将方法名、参数封装到Parcel对象,然后在调用BinderProxy的transact进行跨进程方法,可以理解包裹类就是BpCameraService;
对我后面这层包裹不理解的,可以看看我这篇文章的aidl浅谈,或者直接写个aidl,查看他生成的类,里面就有描述;
驱动层如何进程数据交换
从上面IPCThreadState的transact方法开始,和前文addService一样,依次是客户端进程把数据封装为binder_transaction_data结构,在封装binder_write_read结构,内和空间会进行解封装参数拷贝,最后封装为binder_transaction结构,并将其赋值到目标进程binder_proc结构体上,然后唤醒目标进程;
目标进程被唤醒后,从binder_proc的todo里面取出待处理事项,解析binder_transaction结构体,依次对请求参数封装为binder_transaction_data和binder_write_read,最后返回到目标进程serviceManger的用户空间binder_loop循环,对参数进程解析binder_parse;
是不是和前文addService一样?
唯一不同的是方法code变成了GET_SERVICE,还有驱动层的数据交换函数binder_transaction执行逻辑稍许不同;
我们从binder_loop解析客户端进程请求参数开始分析:
void binder_loop(struct binder_state *bs, binder_handler func)
{
struct binder_write_read bwr;
uint32_t readbuf[32];
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
readbuf就是从内核空间返回的binder_write_read的读取参数部分
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = 1;
确定ptr的指针范围
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
读取ptr前面4字节的数据,在内核空间给他赋值为BR_TRANSACTION
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
case BR_TRANSACTION: {
指针强转为binder_transaction_data
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: txn too small!\n");
return -1;
}
判断回调函数func是否为空
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
为replay结构体分配内存,内存就是rdata
bio_init(&reply, rdata, sizeof(rdata), 4);
使用txn初始化msg
bio_init_from_txn(&msg, txn);
回调func函数
res = func(bs, txn, &msg, &reply);
if (txn->flags & TF_ONE_WAY) {
binder_free_buffer(bs, txn->data.ptr.buffer);
} else {
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
}
ptr += sizeof(*txn);
break;
}
}
}
以上就是对内和空间返回的binder_transaction_data转换为binder_io结构体并初始化;继续看看回调函数func
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
GetService就是执行这里
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
取出来的s等于meida.camera
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
do_find_service会遍历查找服务列表,找到名字相同且长度一样的服务,返回handle
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
把handle值写入到reply结构体重
bio_put_ref(reply, handle);
return 0;
}
}
void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
struct flat_binder_object *obj;
if (handle)
会从bio里面去分配内存给obj
obj = bio_alloc_obj(bio);
else
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
注意这个类型为handle
obj->type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
}
以上查找到服务后就将服务的handle值写入到reply变量中,最后返回到binder_parse中去,执行binder_send_reply方法,看看:
void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
binder_uintptr_t buffer_to_free,
int status)
{
data结构体依次为cmd-buffer-cmd-buffer
struct {
uint32_t cmd_free;
binder_uintptr_t buffer;
uint32_t cmd_reply;
struct binder_transaction_data txn;
} __attribute__((packed)) data;
以下是赋值,BC_FREE_BUFFER命令,在驱动层会对buffer_to_free释放掉buffer_to_free内存
data.cmd_free = BC_FREE_BUFFER;
data.buffer = buffer_to_free;
BC_REPLY命令则会对txn数据发送出去
data.cmd_reply = BC_REPLY;
data.txn.target.ptr = 0;
data.txn.cookie = 0;
data.txn.code = 0;
status为0
if (status) {
data.txn.flags = TF_STATUS_CODE;
data.txn.data_size = sizeof(int);
data.txn.offsets_size = 0;
data.txn.data.ptr.buffer = (uintptr_t)&status;
data.txn.data.ptr.offsets = 0;
} else {
执行else
data.txn.flags = 0;
数据长度
data.txn.data_size = reply->data - reply->data0;
数据偏移长度
data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
存放的数据起始地址,这是一个flat_binder_object结构体类型
data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
偏移起始地址
data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
}
进入内核写数据
binder_write(bs, &data, sizeof(data));
}
内核写数据开始:
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
struct binder_context *context = proc->context;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
会从数据中取出命令在case条件执行,上面传入了两个命令,一个BC_FREE_BUFFER
命令,一个BC_REPLY;第一个命令就不分析了,主要看第二个命令
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
拷贝参数
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
数据转换为binder_transaction并交换
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
}
}
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t *offp, *off_end, *off_start;
binder_size_t off_min;
u8 *sg_bufp, *sg_buf_end;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
struct binder_context *context = proc->context;
if (reply) {
thread为servicemanger的进程线程,transaction_stack是客户端之前的结构体
in_reply_to = thread->transaction_stack;
thread->transaction_stack = in_reply_to->to_parent;
获取到了我们客户端进程的线程
target_thread = in_reply_to->from;
获取到了我们客户端进程的进程结构体binder_proc
target_proc = target_thread->proc;
} else {
}
if (target_thread) {
获取目标线程,也就是客户端线程的任务列表,中断参数
e->to_thread = target_thread->pid;
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else {
}
直接从内核空间分配t结构体,t是内核空间进程间传递的参数结构
t = kzalloc(sizeof(*t), GFP_KERNEL);
同上,只是tcomplete会被用到当前进程用于处理收尾工作
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
为t赋值
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
从目标进程也就是客户端进程分配binder_buffer节点,binder_buffer管理了内核映射内存节点
和进程空间部分内存一一对应
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
置buffer节点已经被使用,不能给别人使用了
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
增加引用计数
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
计算buffer中存放数据的起始地址
off_start = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
offp = off_start;
将数据从tr中buffer数据拷贝到buffer中去
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
将数据从tr中偏移数据拷贝到buffer中去,也就是tr中的buffer和offsets都存放到
buffer中同一个位置,offsets在前,buffer在后
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
off_end = (void *)off_start + tr->offsets_size;
for (; offp < off_end; offp++) {
struct binder_object_header *hdr;
size_t object_size = binder_validate_object(t->buffer, *offp);
//这个hdr读取的实质是flat_binder_object里面的type
hdr = (struct binder_object_header *)(t->buffer->data + *offp);
off_min = *offp + object_size;
switch (hdr->type) {
我们在bio_put_ref函数中设置了这个type为BINDER_TYPE_HANDLE
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp;
获取fp指针,fp实质也是上面bio_put_ref进去的那些值
fp = to_flat_binder_object(hdr);
根据fp进行对象转换,这个函数很重要,看后面代码
ret = binder_translate_handle(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
goto err_translate_failed;
}
} break;
}
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
将in_reply_to从目标线程中弹出
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
} else {
}
将事务添加到客户端进程上去
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
将事务添加到servicemanager进程上去
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
唤醒客户端进程
if (target_wait)
wake_up_interruptible(target_wait);
return;
}
static int binder_translate_handle(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_ref *ref;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
根据handle值,从当前binder_proc结构体对binder_node引用树去搜寻对应的引用节点;
还记得addService时,为服务创建binder_node节点,并让serviceManager进程的binder_proc持有
这个binder_node引用,并且把他返回的描述desc赋值给handle;所以这里是反向操作,根据handle去
查找节点引用
ref = binder_get_ref(proc, fp->handle,
fp->hdr.type == BINDER_TYPE_HANDLE);
如果这个节点的proc和目标进程相同,说明客户端和服务端同属一个进程
if (ref->node->proc == target_proc) {
if (fp->hdr.type == BINDER_TYPE_HANDLE)
fp->hdr.type = BINDER_TYPE_BINDER;
else
fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
就会存储binder服务的指针地址,同一个进程直接通过指针调用
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER,
0, NULL);
trace_binder_transaction_ref_to_node(t, ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> node %d u%016llx\n",
ref->debug_id, ref->desc, ref->node->debug_id,
(u64)ref->node->ptr);
} else {
不属于同一个进程,就要通过handle句柄来调用
struct binder_ref *new_ref;
客户端创建一个binder_ref引用,连接到服务端也就是CameraService的binder_node节点;
后续我们在客户端请求服务端时,可以根据这个引用关系,在内核空间将参数拷贝赋值到服务端进程
new_ref = binder_get_ref_for_node(target_proc, ref->node);
if (!new_ref)
return -EINVAL;
将引用赋值给fp返回
fp->binder = 0;
fp->handle = new_ref->desc;
fp->cookie = 0;
binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE,
NULL);
}
return 0;
}
以上代码主要是从serviceManager进程查找到服务后,将服务handle句柄封装到binder_io结构,在转化为binder_transaction_data结构体,在binder_translate_handle函数中,使用handle查找到binder_proc的服务binder_node节点,这个节点是真正的CameraService服务在驱动层对应的节点,该节点存放了CameraService的指针以及描述handle,拿到这个节点后,为我们客户端进程创建一个引用binder_ref指向这个节点,后续方法调用就可以利用这个引用找到CameraService驱动层的节点,并为服务进程添加处理方法事项,就可以跨进程访问了;客户端拿到服务端CameraService服务后,如何调用服务端方法也是在这个binder_transaction函数中,如下代码:
此时target.handle属于CameraService的handle句柄,而且不等于0,所以执行这个条件
if (tr->target.handle) {
struct binder_ref *ref;
从本进程proc中找到这个handle的引用,也就是上面binder_translate_handle中先前创建爱你
ref = binder_get_ref(proc, tr->target.handle, true);
if (ref == NULL) {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_invalid_target_handle;
}
ref.node就是CameraService在驱动层的binder_node节点,后续就是往这个target_node上
做事情
target_node = ref->node;
} else {
target_node = context->binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
}
}
上面执行完后,客户端进程被唤醒,在驱动程序中拿到binder_transaction数据,取出里面的值,最后回到IPCThreadState中根据BC_REPLY命令解析参数,然后退出IPCThreadState类,就返回到应用层了,就可以接上本文前明的逻辑了;
总结
Android的binder通信博大精深,博主也是了解了一些粗浅的原理!binder通信依靠底层的内存映射技术,同一块物理内存同时映射两块虚拟空间-内核空间与用户空间,并且内核空间与用户空间存在简单的对应关系,当内核空间内存发生变化,与他对应的用户空间内存也发生相应的变化,进程间数据传递时,减少内存拷贝次数!binder驱动创建了许多结构体来维护这些映射的内存节点,以及维护进程间数据传递的结构体,牢记这些结构体的意义以及用法将帮助我们快速理解binder通信原理!
快速理解总结
binder通信实质;
抛开业务如何调用,理解数据结构才能快速掌握:
- 每个进程有唯一的proc的结构体,每个进程内可能有1个或多个binder节点,一个binder节点对应一个binder服务端,如CameraService、SystemService等等
- serviceManager作为所有服务的管理者,采用类似键值对方式存储,key是handle句柄(可以理解为int型),value则是对象类型,包含了服务的名称(字符串类型)和指针强引用,如camera.service;注意,servicemanager本身自己也是一个binder服务,他的handle值默认为0
- 在内核空间中,两个进程之间通过(wait-notify)方式来触发相互间的调用,如A进程处于wait中,B进程将相关参数封装好后放入A的proc结构体中,然后唤醒A进程,A唤醒后则处理相关参数,至于B怎么拿到A的proc看后面
来几个业务串联下,加深理解:
- 如何获取serviceManager的代理类?
通常framework里面就一句代码;
sp<IServiceManager> sm = defaultServiceManager();
其实质内部转换BpServiceManager(BpBinder(0))类型,0就是handler句柄,这行代码并没有向驱动层调用
-
如CameraService如何将自己添加到ServiceManager的服务集合中,假设CameraService进程为A,则有proc_A进程,ServiceManager进程为B,有proc_B
A进程在内核空间会创建CameraService的binder结构体,并与proc_A有引用关系,而在内核查找目标进程B时,发现serviceManager的handler为0,则会通过上下文(可以理解为有一个全局结构体,各个进程都能找到他)找到目标proc_B,这样就可以把A进程的服务以规定的参数放到B进程,
进而唤醒B进程处理即可;
A进程处理上述工作后,会开启自己内部的死循环,轮询来自客户端的请求 -
假设进程A需要进程B的CameraService服务,ServiceManager在进程C,如何拿到它呢?
首先,肯定第一步也是defaultServiceManager,拿到服务管理者。
其次,A只知道CameraService的名字为"camera.service",同上面问题2,可以通过进程C,拿到CameraService的handler值,比如为2,拿到了以后会通过handler值去建议一个到B进程binder的引用,并将引用的desc成员返回给A
最后,A会将这个返回值handler包裹为BpCameraService(BpBinder(desc)) -
进程A需要CameraService服务进程B,ServiceManager在进程C,那假如CameraService的有个test接口方法,A怎么能调用到B的test方法?
接问题3,A拿到CameraService在C中的handler值,包裹为BpCameraService(BpBinder(desc)),会将test的方法名字经过一定的封装,转换为参数
在内核调用时,通过desc会找到进程B的binder,并将参数拷贝到B的binder中去,将任务添加到进程B的proc去,唤醒进程B
最后,进程B的framework层轮询拿到B的binder相关参数,最后通过binder中的指针去调用test方法
binder是同步还是异步
什么是同步和异步?
同步:client调用服务端方法,无论是否有返回值,都要等服务端执行完后才返回,没执行完之前,客户端线程一直处于阻塞状态
异步:client调用服务端方法,不能有返回值,都不用等服务端执行完成与否,调用后立即返回;
binder访问默认是同步,如果要采用异步方式,需要在方法前面加oneway关键字
更多精彩博文,加入我们,一同进步!