Android Binder浅析 --- Client获取CameraService

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通信实质;
抛开业务如何调用,理解数据结构才能快速掌握:

  1. 每个进程有唯一的proc的结构体,每个进程内可能有1个或多个binder节点,一个binder节点对应一个binder服务端,如CameraService、SystemService等等
  2. serviceManager作为所有服务的管理者,采用类似键值对方式存储,key是handle句柄(可以理解为int型),value则是对象类型,包含了服务的名称(字符串类型)和指针强引用,如camera.service;注意,servicemanager本身自己也是一个binder服务,他的handle值默认为0
  3. 在内核空间中,两个进程之间通过(wait-notify)方式来触发相互间的调用,如A进程处于wait中,B进程将相关参数封装好后放入A的proc结构体中,然后唤醒A进程,A唤醒后则处理相关参数,至于B怎么拿到A的proc看后面

来几个业务串联下,加深理解:

  1. 如何获取serviceManager的代理类?
    通常framework里面就一句代码;
sp<IServiceManager> sm = defaultServiceManager();
其实质内部转换BpServiceManager(BpBinder(0))类型,0就是handler句柄,这行代码并没有向驱动层调用
  1. 如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进程处理上述工作后,会开启自己内部的死循环,轮询来自客户端的请求

  2. 假设进程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))

  3. 进程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关键字

更多精彩博文,加入我们,一同进步!

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气好男人_Jack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值