之前的六篇文章,从binder驱动讲起,然后详细分析了Service manager以及一般进程间通信的过程,通过这些文章,大致把binder的主要内容都讲到了,对于binder中最重要的部分,也都做了分析,至此binder的内容基本就差不多了。这篇文章准备从总体上在总结一下binder的流程,因为之前的文章都是一步一步来分析的,可能看了前面的就忘记后面的了,所以这个文章准备做一个概括的总结,力求在已经了解binder的内容前提了,可以快速了提高挈领的把整体流程把握住,并且对之前有些没讲清楚的地方做一些补充。好了,下面还是从binder驱动讲起,总体的概括一下。
#binder驱动
binder驱动是在linux内核加载完后就初始化的。所有需要用到binder通信的进程,都会在binder驱动中保存有一个文件,这样binder驱动就可以知道通信者的信息了。对应binder驱动,主要要知道以下这个结构体,这里指出了mmap,open等主要函数对应的方法。
```c
static struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
```
# Service manager初始化
binder驱动就知道下几个会调用的函数就好了。下面开始讲service manager,我们知道所有系统的关键的服务都是注册在service manager上,所以service manager是一个根服务,他没有人可以注册,但是binder驱动还是知道他的,所以初始化service manager的时候,首先是执行binder驱动的open方法,在binder驱动里生成一个文件,表示本进程需要使用binder驱动了,并且把本进程的信息封装为结构体binder_proc保存在binder中。然后调用mmap方法,和内核共享一页内存空间,这样就不需要内核和service manager之间进程内存拷贝了。
做完了以上两件事之后,接在会继续调用binder驱动的binder_ioctl方法,进行读写操作,这步是要在binder驱动中建立一个实体对象,这个实体对象保存着service manager的进程信息,以及地址等。由于service manager是个特殊的根对象,所以地址特意设置为0。这步的操作是在binder_ioctl方法的BINDER_SET_CONTEXT_MGR分支中执行的:
```c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
.............
case BINDER_SET_MAX_THREADS: // 设置进程最大线程数。 从ubuf到proc->max_threads
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR:
if (binder_context_mgr_node != NULL) { // service manager的实体对象是否已经有了,如果有了退出
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto err;
}
if (binder_context_mgr_uid != -1) { // 有效用户id非等于-1
// 如果和当前进程有效用户id不一样退出,如果一样,同一个进程还可以重复注册,说不定之前这个进程注册的时候出错了。但是不同进程就不可以了
if (binder_context_mgr_uid != current->cred->euid) {
printk(KERN_ERR "binder: BINDER_SET_"
"CONTEXT_MGR bad uid %d != %d\n",
current->cred->euid,
binder_context_mgr_uid);
ret = -EPERM;
goto err;
}
} else // 到这里说明service manager没有注册过
binder_context_mgr_uid = current->cred->euid; // 复制有效用户id
// 新建service manager实体对象。参数2本地对象多引用地址,参数3本地对象地址。service manager都是0,所以这里都是null。这里的null应该和service_manager.c中的BINDER_SERVICE_MANAGER是一样的意思
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
if (binder_context_mgr_node == NULL) { // null说明已经有这个实体对象了
ret = -ENOMEM;
goto err;
}
binder_context_mgr_node->local_weak_refs++; // 增加实体对象对本地对象的内部弱引用计数
binder_context_mgr_node->local_strong_refs++;// 增加实体对象对本地对象的内部强引用计数
binder_context_mgr_node->has_strong_ref = 1; // 本地对象标识有强引用
binder_context_mgr_node->has_weak_ref = 1; // 本地对象标识有弱引用
break;
.............
}
```
执行完以上这步后,service manager会在binder驱动中设置一个binder线程,每一个使用binder的进程都会使用binder线程来进行数据的读写操作,这里同样是调用binder驱动中的binder_ioctl方法,进而在调用binder_thread_write方法,将当前线程注册为一个binder线程,执行是在binder_thread_write方法的BC_ENTER_LOOPER分支上:
```c
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed)
{
.............
case BC_ENTER_LOOPER: // 这里是注册为一个binder线程
if (binder_debug_mask & BINDER_DEBUG_THREADS)
printk(KERN_INFO "binder: %d:%d BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
// 该线程是一个非binder线程,BINDER_LOOPER_STATE_REGISTERED是当线程不够时
// binder驱动主动请求的线程
if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("binder: %d:%d ERROR:"
" BC_ENTER_LOOPER called after "
"BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
}
thread->looper |= BINDER_LOOPER_STATE_ENTERED; // 注册为binder线程
break;
.............
}
```
至此,service manager的初始化基本就完成了,最后service manager还是进入binder的binder_ioctl方法,进而最终进入到binder_thread_read方法中。如果此时读缓冲区中没有数据的话,service manager就会睡眠在这个方法上,等待新任务唤醒。
```c
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed, int non_block)
{
................
if (wait_for_proc_work) { // 当前没有任务
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
binder_user_error("binder: %d:%d ERROR: Thread waiting "
"for process work before calling BC_REGISTER_"
"LOOPER or BC_ENTER_LOOPER (state %x)\n",
proc->pid, thread->pid, thread->looper);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); // 线程中断休眠
}
binder_set_nice(proc->default_priority); // 设置优先级
if (non_block) { // 非阻塞可以接着执行
if (!binder_has_proc_work(proc, thread)) // 没有任务
ret = -EAGAIN;
} else // 阻塞的话,让进程在等待队列上
ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
}
..............
}
```
service manager的启动过程就大致这样,接下来如果有其他进程需要往service manager中注册或者请求任务,首先需要获得service manager的代理对象。
## service manager代理对象的获得
获取service manager的代理对象是通过以下方法来获取的:
```c
private static IServiceManager getIServiceManager() { // 获取service manager代理对象
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
// BinderInternal.getContextObject() 是jni方法 public static final native IBinder getContextObject();
// 创建一个service manager代理对象,在android_util_Binder中
// 获取BpBInder封装后的BinderProxy,在调用ServiceManagerNative的asInterface方法最终返回service manager的java代理对象
ServiceManagerProxy sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
```
首先通过BinderInternal.getContextObject()来获取一个BinderProxy对象,之后封装为一个ServiceManagerProxy返回。
获取BinderProxy前,先通过ProcessState::self()->getContextObject获取BpBinder:
```c
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller) // 获得一个ibinder,即BpBinder
{
if (supportsProcesses()) { // 检查是否已经打开了binder驱动,即是否文件描述符 > 0
return getStrongProxyForHandle(0); // 获取代理对象,service manager的句柄是0
} else {
return getContextObject(String16("default"), caller);
}
}
```
这里看到这个getContextObject方法传入的参数0,代表service manager的地址是0,这个是service manager注册到binder驱动中特意设置的值。
这里BpBinder返回后,还会封装为BinderProxy,这里具体封装就不多叙述了,具体可以参考之前的文章,但是这里有几个类相互持有,这里也分别说明下
BinderProxy的mObject变量持有BpBinder
BpBinder的ObjectManger类的mObjects变量也持有BinderProxy
JavaBBinder的mObject持有一个BBinder
JavaBBinderHold的mObject持有一个BBinder,mBinder持有一个JavaBBinder
以上的JavaBBinder是一个具体的服务端需要注册到service manager的java对象,这里之所以要加一个JavaBBinderHold对象来分别持有BBinder和JavaBBinder,是因为BBinder是一个C++层的对象,由于JavaBBinder中也持有BBinder,其他不需要JavaBBinderHold也可以在java层获取JavaBBinder,但是我的理解是为了解耦,通过BBinder先找到JavaBBinderHold,然后再让JavaBBinderHold返回给上层JavaBBinder,这里,如果需要对返回上层的JavaBBinder做一些处理的话,可以在JavaBBinderHold中操作,这样就不用影响其他的地方。这些都是服务端的东西,我们会说到这里由于提到了这些类,就顺带说一下。
好了,这里这里获取了BinderProxy后,最后就封装成ServiceManagerProxy返回给调用者了。
## service manager获取注册数据
现在获取了一个service manager的代理对象,下面开始说addService这个方法。
```c
public void addService(String name, IBinder service)
throws RemoteException {
Parcel data = Parcel.obtain(); // 向binder驱动请求的数据
Parcel reply = Parcel.obtain(); // binder返回的数据
data.writeInterfaceToken(IServiceManager.descriptor); // 写入请求头
data.writeString(name); // 写入service名字
data.writeStrongBinder(service); // 写入service,这里service实际上是JavaBBinder或者BpBInder
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0); // 发送请求给binder驱动
reply.recycle();
data.recycle();
}
```
这里封装了需要请求的数据,调用transact方法会通过BpBinder::transact -> IPCThreadState::self()->transact -> IPCThreadState::waitForResponse -> IPCThreadState::talkWithDriver之后进入binder驱动的binder_ioctl方法,进而在进入binder_thread_write,执行BC_TRANSACTION分支的方法:
```c
case BC_TRANSACTION: // client给binder驱动发命令
case BC_REPLY: { // server段处理完了后,发给binder驱动,binder驱动再发给client说明已经梳理完了
struct binder_transaction_data tr;
// 从用户空间读取数据到tr,这里在内核开辟的只有binder_transaction_data结构体,不包括这里的缓冲区
// 缓冲区在binder_transaction里面会开辟,是开辟在目标server的进程空间
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr); // 读取数据缓冲区指针前移
binder_transaction(proc, thread, &tr, cmd == BC_REPLY); // 处理命令
break;
}
```
这里会再进入binder_transaction方法执行。这里的目标是寻找到service manager,然后把请求注册的数据传给他。在这个方法中,首先要找到目标的进程或者线程,然后创建一个事务项binder_transaction,把一些相关的信息写入这个结构体,比如处理这个事务的线程,缓冲区等。由于需要把数据写入目标进程的缓冲区,所以还要在目标进程的内存空间中开辟事务的物理区域,然后把数据写入。这样等service manager来处理的时候,读取数据就可以进行处理了。
除了把数据准备好传给service manager后,注册的进程也就是服务进程,需要在binder驱动中建立实体对象和引用对象。实体对象保存的服务端进程中。引用对象中有指向实体对象的地址,并且会保存在service manager中,这个引用对象的句柄值和服务名字会保存在service manager的一个结构体中,这样其他进程可以通过服务名字在service manager中找到对应的句柄值,而通过这个句柄值有可以找到实体对象的地址,继而就可以调用到服务了。
至此服务端第一次请求就完成了。最后还会有2个动作,一是binder驱动会把事务项写到service manager的进程缓冲区中,让service manager来获取数据以便继续进行下一步的处理,这个在事务项type是BINDER_WORK_TRANSACTION 。另一方面会通知注册进程这次的任务完成了,这个的事务项是BINDER_WORK_TRANSACTION_COMPLETE。
```c
t->work.type = BINDER_WORK_TRANSACTION; // getservice的时候,这里会发给client,通知他给他一个service的引用句柄,让client创建server的代理对象,即回到client的getService中
// 添加事务到进程,线程的todo队列, 或者到实体对象的异步队列中。
// 这里记住&t->work.entry是一个binder_transaction下的binder_work下的list_head,后面在binder_thread_read会根据这个路径来取数据
list_add_tail(&t->work.entry, target_list); // 添加到目标进程尾部
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; // 这个工作项会回到用户空间通知用户已经接收到命令
list_add_tail(&tcomplete->entry, &thread->todo); // 添加一个事务完成工作项到线程队列
if (target_wait) // 等待队列非空,唤醒队列
wake_up_interruptible(target_wait);
```
注册进程这边接收到binder的消息后最终还是会继续睡眠在binder驱动里面等待新的数据,这个也就不多说了。主要看下service manager怎么处理注册的信息。
##service manager处理注册
service manager收到了新的数据,所以会在binder_thread_read方法中取出数据。前面注册进程提供的数据封装成了一个事务,所以这里把这个事务封装为binder_transaction_data,然后写入到service manager的缓冲区中,这样service manager接下去就回到自己的进程空间中继续执行了。
这个值得注意的是,这里service manager的缓冲区,并不是mmap那里的缓冲区。之前注册进程往service manager进程空间中写数据的时候是往mmap的空间中写的数据,因为那时候service manager被看做一个服务端。而这里的缓冲区是service manager在自己进程空间访问binder时候传过来的,可以被看做是一个客户端调用,所以需要弄清楚。
##service manager回到自己进程处理addService
回到service manager自己的进程后,会继续执行binder_loop方法,最终会执行到svcmgr_handler方法,在这个方法中会执行SVC_MGR_ADD_SERVICE分支的内容:
```c
int svcmgr_handler(struct binder_state *bs,
struct binder_txn *txn,
struct binder_io *msg,
struct binder_io *reply)
{
............
case SVC_MGR_ADD_SERVICE: // 这个分支是把一个service组件注册到service manager
s = bio_get_string16(msg, &len); // s指向service名字的字符串
ptr = bio_get_ref(msg); //ptr是待注册的引用对象句柄值,可以理解为一个long数值
if (do_add_service(bs, s, len, ptr, txn->sender_euid)) // 在service manager中增加一个结构体给注册的service组件
return -1;
break;
............
}
```
这里主要是获取带注册组件的名字和引用对象的句柄值,然后调用do_add_service方法把这个注册组件封装为一个svcinfo结构体,然后挂在这个结构体的链表上。
```c
struct svcinfo // 被注册了的service组件结构体
{
struct svcinfo *next; // 下一个svcinfo
void *ptr; // 引用对象的句柄值
struct binder_death death; // 死亡通知
unsigned len; // 长度
uint16_t name[0]; // 组件名字
};
```
结构体如上,可以看到next字段把所有被注册的组件连在一起。
最后调用binder_send_reply -> binder_write方法会再次进入binder驱动中。这里返回binder驱动会请求有两个命令,一个是service manager这边缓冲区的数据都处理完了,后面也不需要再处理了,所以请求binder驱动可以释放这片缓冲区了。另一个命令就是给请求注册的进程回复,说明service manager已经注册好了。
```c
data.cmd_free = BC_FREE_BUFFER; // server处理完事务,用来通知binder驱动清理缓冲区
data.buffer = buffer_to_free;
data.cmd_reply = BC_REPLY;
```
## 再次进入binder驱动
先看释放缓冲器,是在binder_thread_write方法的BC_FREE_BUFFER分支上:
```c
case BC_FREE_BUFFER: { // 通知binder可以释放对应进程的内核缓冲区
void __user *data_ptr;
struct binder_buffer *buffer;
if (get_user(data_ptr, (void * __user *)ptr)) // 指向在用户空间的内核缓冲区
return -EFAULT;
ptr += sizeof(void *);
buffer = binder_buffer_lookup(proc, data_ptr); // 在进程中的已经分配的缓冲区树中查找是否有这个缓冲区
if (buffer == NULL) { // 没找到退出
binder_user_error("binder: %d:%d "
"BC_FREE_BUFFER u%p no match\n",
proc->pid, thread->pid, data_ptr);
break;
}
if (!buffer->allow_user_free) { // 不允许用户进程释放,退出
binder_user_error("binder: %d:%d "
"BC_FREE_BUFFER u%p matched "
"unreturned buffer\n",
proc->pid, thread->pid, data_ptr);
break;
}
if (binder_debug_mask & BINDER_DEBUG_FREE_BUFFER)
printk(KERN_INFO "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n",
proc->pid, thread->pid, data_ptr, buffer->debug_id,
buffer->transaction ? "active" : "finished");
if (buffer->transaction) { // 这个缓冲区是否被分配给一个事务处理了,一般要清缓冲区了,说明事务都完成了,所以下面也都清0
buffer->transaction->buffer = NULL; // 将这个事务的缓冲区清0
buffer->transaction = NULL; // 这个缓冲区指向的事务也置null
}
if (buffer->async_transaction && buffer->target_node) { // 如果这个缓冲区用于异步处理,并且他的实体对象还存在
BUG_ON(!buffer->target_node->has_async_transaction);
if (list_empty(&buffer->target_node->async_todo)) // 实体对象的异步事务队列是否为空
buffer->target_node->has_async_transaction = 0; // 空的话置0
else // 如果实体对象还有异步事务
list_move_tail(buffer->target_node->async_todo.next, &thread->todo); // 把异步事务给当前线程,这个当前线程应该是server
}
binder_transaction_buffer_release(proc, buffer, NULL); // buffer里面有binder对象,减少他们的引用计数
binder_free_buf(proc, buffer); // 释放这个buffer缓冲区,处理释放物理页之外,主要是把缓冲区前后合并之类的。这个buffer是内核的进程共享的映射关系
break;
}
```
这个缓冲区是当初注册的进程请求任务时候,binder驱动在service manager上开辟的,现在用完了把它释放掉。
在看BC_REPLY分支,这个最终又会调用到binder_transaction这个方法里面,由于之前会把相关的事务项压入栈中,所以现在从栈中取出事务项,从事务项中可以获取请求进程等信息,然后会在注册进程上开辟一片缓冲区,然后封装一个事务项到他的todo队列中。最终还会发出2个请求,一个是通知注册进程,他请求的注册的组件已经处理好了。另一个是通知自己任务已经完成。
```c
t->work.type = BINDER_WORK_TRANSACTION
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
wake_up_interruptible(target_wait);
```
BINDER_WORK_TRANSACTION_COMPLETE命令的处理会把事务项从自己的todo队列中删除,最终又会回到binder驱动中继续等待下一次新的任务。
这时候轮到注册的进程来处理任务了,之前这个进程阻塞在binder驱动中的binder_thread_read方法中,现在他接受到命令了,继续往下执行从缓冲区中取出数据,并把一个命令写入到用户空间:
```c
if (t->buffer->target_node) {
} else {
tr.target.ptr = NULL;
tr.cookie = NULL;
cmd = BR_REPLY;
}
```
最终会回到注册进程的waitForResponse方法中,这个方法会把传过来的数据写parccel中:
```c
case BR_REPLY:
{
.............
if ((tr.flags & TF_STATUS_CODE) == 0) { // 0表示请求被处理成功,把返回的缓冲区保存在parcel中
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this); // 析构reply时会调用freeBuffer这个方法,该方法中会调用BC_FREE_BUFFER来释放缓冲区
}
.............
}
```
如果parcel被析构了,就会调用freeBuffer方法来释放缓冲区,而这个方法也会再次调用到binder驱动中释放缓冲区的逻辑。现在这个服务进程如果没有新的数据会再次进入binder驱动中睡眠,直到有新任务把他唤醒,至此一个完整的注册流程就结束了。下面再看下一个客户端进程向一个注册进程发请求的过程,由于其中有很多步骤和以上所讲的是完全一样的,所以我们就主要不有区别的重点说下,其余部分可以参考上面的流程。
## 向一个服务发请求
一般通过service manager来注册的是系统的一些服务,这些服务都比较重要,所以一般在应用层不会直接通过Service Manager来进行相关操作,而且像AMS等也是隐藏类,只对上层开发相关的结果,所以我们平时如果不进行AIDL等相关的开发其实感觉不出有进程间相同的调用,他们都被封装在里面。所以如果你理解aidl的话,那么在去看系统的一些服务请求,那就很容易了,因为他们都是通过AIDL这种方法来调用的,套路都是一样的,比如你看下面这个:
```c
inal IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
```
这个就是在framework中获取一个AMS的调用,其实就两步,第一步获取一个IBinder,第二步把他封装为一个IActivityManager,也就是代理类。所以我们只要看这个两步怎么实现的就可以。
其实这在前面我们讲获取service manager的代理类是一样的,还记得吗,也是先获取一个BinderProxy,这个就是一个IBinder,然后封装成ServiceManagerProxy。另外,如果你熟悉AIDL的开发,一般我们本地都会有个一个stub,其实他就是IBinder,之后通过asInterface封装成一个代理类。综上所述,android中binder间通信其实万变不离其宗,当然我们这里获取的都是那些注册在service manager中的服务,如果是你自定义的AIDL,流程其实和我们这里是不一样的,这个会在分析service的时候分析,我们现在就看如果通过service manager的getService方法,来获取一个已经注册在他里面的服务。
```c
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
```
这个方法首先写入请求头和服务名字,然后调用transact,传过去的参数有GET_SERVICE_TRANSACTION这个值,最终会进入service manager中,其中的过程和之前分析的addService是一样的,这里就不说了,最后会进入service manager的svcmgr_handler方法:
```c
case SVC_MGR_GET_SERVICE:
s = bio_get_string16(msg, &len); // 获取这个service的名字
ptr = do_find_service(bs, s, len); // 寻找和这个名字对象的注册service引用对象句柄
if (!ptr) // 获取service引用对象句柄
break;
bio_put_ref(reply, ptr); // 把找到的引用对象句柄封装成一个binder_object结构体到reply缓冲区中
return 0;
```
可以会通过名字来寻找引用对象的句柄,然后把找到的对象封装为binder_object,然后写在binder_io结构体中,最后这个数据写回缓冲区。然后调用binder_send_reply -> binder_write方法,会给binder驱动发两条命令,BC_FREE_BUFFER和BC_REPLY,即释放缓冲区和回复客户端。
## service manager找到请求的组件返回binder驱动
释放缓冲区就不多说了,和之前的也一样。回复给调用者的流程最终会走到binder_transaction这个方法,而我们写入的binder对象的type是BINDER_TYPE_HANDLE,所以最终会进入到这个分支
```c
case BINDER_TYPE_HANDLE: // 当寻找一个是否已经注册过的service的时候,service manager向binder驱动请求的时候会走到这里
case BINDER_TYPE_WEAK_HANDLE: {
// 此时proc是service manager进程, target_proc是从发起请求进程的事务堆栈中获得的,所以是client进程。ref是引用对象
struct binder_ref *ref = binder_get_ref(proc, fp->handle); // 在进程proc(即service manager)中寻找是否有句柄为fp->handle引用对象
if (ref == NULL) {
binder_user_error("binder: %d:%d got "
"transaction with invalid "
"handle, %ld\n", proc->pid,
thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
// server == client, 同一个进程,即service自己调用自己,需要把BINDER_TYPE_HANDLE修改为BINDER_TYPE_BINDER
// else分支是不同进程type保留BINDER_TYPE_HANDLE,这个在Parcel方法unflatten_binder可以看到作用
if (ref->node->proc == target_proc) {
if (fp->type == BINDER_TYPE_HANDLE)
fp->type = BINDER_TYPE_BINDER;
else
fp->type = BINDER_TYPE_WEAK_BINDER;
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
printk(KERN_INFO " ref %d desc %d -> node %d u%p\n",
ref->debug_id, ref->desc, ref->node->debug_id, ref->node->ptr);
} else {
struct binder_ref *new_ref;
new_ref = binder_get_r ef_for_node(target_proc, ref->node); // 在进程proc(即service manager)中寻找是否有引用了实体对象ref->node的引用对象
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
fp->handle = new_ref->desc; // 把引用对象句柄值修改为client这边的,因为后面返回给client需要用这个来创建代理对象
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); // 增加这个引用对象计数,内核缓冲区释放的时候,会减少这个引用
if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
printk(KERN_INFO " ref %d desc %d -> ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, new_ref->debug_id, new_ref->desc, ref->node->debug_id);
}
} break;
```
这个方法首先通过句柄值在service manager中寻找是否有这么个引用对象,如果有的话再比较和请求的进程是不是一个进程,如果是一个进程,就需要把后面返回数据type的值改为BINDER_TYPE_BINDER,这个在后面客户端中,会根据这个值来判断是不是同一个进程,从而决定到底是创建一个本地对象还是代理对象。如果不是一个进程的话 ,就会在客户端进程中寻找是否已经有了相同实体对象的引用对象,如果有就返回,没有就创建后再返回。
至此,service manager就处理完了,接下去还是和之前的一样,会通知自己任务完成,然后也会把找到的服务数据写入客户端的todo队列。等待客户端读出后,最终会返回到客户端的进程中,一路返回后通过getService方法就返回了一个Ibinder了。
##调用者客户端接收到数据
前面说到客户端会根据返回的数据类型,来判断需要给上层调用的是同一个进程的服务还是不同的。这个方法通过getService中调用parcel的readStrongBinder()方法路径走到unflatten_binder方法中:
```c
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) {
case BINDER_TYPE_BINDER: // 这里是同一个进程
*out = static_cast<IBinder*>(flat->cookie); // 直接把同一个进程的地址赋给out
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE: // 这里是不同进程
*out = proc->getStrongProxyForHandle(flat->handle); // 从本地进程获取代理对象
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
```
这里就通过type的类型来判断是不是同一个进程,分别返回一个本地对象还是代理对象。
## 最终获取一个可调用对象
通过之前分享service manager可以知道如果是返回一个代理对象,会通过把BpBinder封装为BinderProxy,然后在封装为一个代理对象返回给上层。那么我们怎么简单的知道获得的这个对象要不要封装为代理对象呢?这里会通过queryLocalInterface来判断具体这个对象是不是本地对象。
我们知道本地对象是一个Binder类,所以在他初始化的时候会调用attachInterface方法,类似于这样:
```java
public ServiceManagerNative()
{
attachInterface(this, descriptor);
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
```
这个方法会把本地对象的这个名字写入mDescriptor。当调用这个对象的queryLocalInterface方法时,会进行判断:
```java
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
```
这个方法是Binder实现类中覆写的,如果是一样的,就说明这个是本地对象。而如果是一个BinderProxy对象重写了queryLocalInterface方法,会返回null。
```java
final class BinderProxy implements IBinder {
.................
public IInterface queryLocalInterface(String descriptor) {
return null;
}
................
}
```
所以通过queryLocalInterface就可以判断到底目前这个对象是什么类型的。
通过这一系列的操作,最后上层获得的就是一个面向接口的对象,但是具体是本地对象还是代理对象不用关心,如果本地对象就可以直接调用本地的方法,如果是代理对象就会通过binder通信来交互,这样对上层的开发就比较友好了。
说了那么多,这篇文章基于前面的几篇做了一个总结和概括,目前想在已经理解binder的情况下,可以快速的了解binder的大体流程,直到binder的主要内容。好了,binder的文章就暂时告一段落了,后面新的文章见。
Android进程间通信Binder(七)