前面几篇文章从service manager的启动,到往service manager里面添加一个组件,做了一个详细的分析。这篇文章来分析一下一个进程如果要通过service manager中获取这个组件,整个流程是怎么样的。
在android中,我们如果开发2个不同的app,需要通信的话,一般会采用aidl的方式,之前在第三篇文章中,我们介绍过aidl的内容。其实不光是我们自己开发的程序,比如AMS这种系统服务也是通过aidl的方式来通信的,比如下面这段代码:
```c
inal IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
```
你看这种形式是不是和我们前面讲的获取service manager很像,也是先通过ServiceManager的getService获取一个IBinder类,然后asInterface转成一个代理类或者本地类,这样就可以发请求了。
当然由于这里我们主要讨论的是系统服务,所以都会通过addService注册到ServiceManager中,而应用层的服务一般会通过调用AMS才使用,这里AMS的机制在AMS的文章中来描述,binder这样主要讨论的是直接和ServiceManager来交互。
## getService开始
好了,下面我们就从获取一个系统服务的入口函数getService说起,在讲addService的时候注册时会提供一个name,用来描述这个service,好像上面AMS的Context.ACTIVITY_SERVICE这个字符串一样,需要把这个字符串传给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(); // 这里获取的binder就是一个BinderProxy或者Fregservice
reply.recycle();
data.recycle();
return binder;
}
```
## 进入service manager
由于这些函数之前binder的时候也都说过,就不细说了,可以翻看前面几篇讲binder的文章。这里往缓冲区写入请求头和组件名后,就调用transact方法会经过一些通信过程,最终走到ServiceManager进程里面的解析方法svcmgr_handler。由于这是第五篇文章了,中间的过程和addService完全一样,所以就都不多说,相信理解了前面addService流程的同学应该可以不用解释。我们看下svcmgr_handler方法的GET_SERVICE_TRANSACTION这个case:
```c
int svcmgr_handler(struct binder_state *bs,
struct binder_txn *txn,
struct binder_io *msg,
struct binder_io *reply)
{
..............
case SVC_MGR_GET_SERVICE: // 这个分支是请求获取一个已经注册的service结构体
case SVC_MGR_CHECK_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;
..................
}
```
注意这里的SVC_MGR_GET_SERVICE对应的枚举值就是GET_SERVICE_TRANSACTION,所以进入这个分支。这个case首先取出组件的名字,然后通过do_find_service方法看看是否已经有注册过这个组件了,我们看下do_find_service方法:
```c
void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len) // 寻找已注册service引用对象句柄
{
struct svcinfo *si;
si = find_svc(s, len);
if (si && si->ptr) { // 找到了,并且不为空,则返回
return si->ptr;
} else {
return 0;
}
}
```
这个方法里面在通过find_svc方法来寻找,如果找到了就返回这个组件的引用对象句柄,这里ptr是引用对象句柄,这是这个组件在注册时候传给ServiceManager的。我们看下
find_svc方法:
```c
struct svcinfo *find_svc(uint16_t *s16, unsigned len) // 寻找是否名字为s16,长度len的service组件已经被注册了
{
struct svcinfo *si;
for (si = svclist; si; si = si->next) { // 遍历全局svcinfo表,比较长度和名字有没有一样的
if ((len == si->len) &&
!memcmp(s16, si->name, len * sizeof(uint16_t))) {
return si;
}
}
return 0;
}
```
这个方法遍历了已经注册过的所有组件,如果名字一样的话就返回这个组件的结构体svcinfo。好了,我们返回到最前面的svcmgr_handler方法中,如果找到了,就执行bio_put_ref(reply, ptr)把这个组件地址设置给回复的缓冲区。我们看下bio_put_ref代码:
```c
void bio_put_ref(struct binder_io *bio, void *ptr) // 从缓冲区分配一个binder_object对象
{
struct binder_object *obj;
if (ptr) // 如果有本地对象值,即一个binder对象,查看缓存区时候有空间写入一个binder_object,如果有返回写入位置给obj,进入设置分支需要设置偏移数组,因为要写入一个binder对象
obj = bio_alloc_obj(bio);
else // 进入这个分支不需要写入binder对象,所以不要设置偏移数组,只要分配一个binder_io就好
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->type = BINDER_TYPE_HANDLE;
obj->pointer = ptr; // 引用对象句柄值
obj->cookie = 0;
}
```
这里参数bio是后面要向客户端返回的缓冲区,ptr这里是之前注册服务的时候注册到service manager中的服务的引用对象句柄,通过引用对象可以找到实体对象,通过实体对象可以找到服务,这样整个路径就连起来了。这个方法中,首先声明了一个变量binder_object,这个结构体可以理解成对应于之前添加服务时候的flat_binder_object结构体,然后我们会把这个结构体保存在缓冲区中,最后给这个结构体type赋值BINDER_TYPE_HANDLE,pointer赋值ptr。这里把binder_object结构体写入缓冲区的方法是bio_alloc_obj,我们看一下:
```c
static struct binder_object *bio_alloc_obj(struct binder_io *bio) // 把缓冲区空出一个binder_object的空间,让obj指向开始写入的位置
{
struct binder_object *obj;
obj = bio_alloc(bio, sizeof(*obj)); // 缓冲区bio看看有没有空间,有的话,返回写入指针
if (obj && bio->offs_avail) { // 下面是设置偏移数组
bio->offs_avail--; // 偏移数组剩余位置减1
*bio->offs++ = ((char*) obj) - ((char*) bio->data0); // 设置这个将要写入binder对象的偏移数组位置
return obj; // 现在这个obj就指向了要写入的位置了,renturn后就可以往obj写数据了,这个数据也就是写入缓冲区bio的
}
bio->flags |= BIO_F_OVERFLOW; // 缓冲区没空间了
return 0;
}
```
这个方法中先通过bio_alloc在缓冲区中更新下这个对象的位置信息,然后返回一个指向binder_object这个结构体的指针。我们看下bio_alloc这个方法:
```c
static void *bio_alloc(struct binder_io *bio, uint32_t size) // 看看缓冲区bio是否还有空间,有的话,返回指针,更新缓冲区使用标识等
{
size = (size + 3) & (~3); // 4字节对齐
if (size > bio->data_avail) { // 没有空间了
bio->flags |= BIO_F_OVERFLOW;
return 0;
} else { // 有空间
void *ptr = bio->data; // 获取当前可写入指针
bio->data += size; // 使用空间加
bio->data_avail -= size; //剩余空间减
return ptr;
}
}
```
这个方法会先判断缓冲区是否还有空间,如果没空间了就返回0。有的话会更新已使用空间和剩余空间,最后返回指针。我们回到bio_put_ref方法,当该方法执行完毕后,service manager就找到了一个客户端需要的服务的引用对象,然后通过binder_send_reply方法再返回binder驱动中。这个方法前面也讲过了,会向binder驱动发送两条命令。一条是释放service manager这边的缓冲区,因为到这里service manager已经完成任务了可以释放了。另一个命令是要发回客户端。释放缓冲区的在之前的文章中已经分析过了,这里就不多赘述。
## 返回binder驱动
我们看下关于向客户端返回数据的代码,基本和之前注册服务是类似的,我们主要看下区别部分:
```c
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
..............
case BINDER_TYPE_HANDLE: // 当寻找一个是否已经注册过的service的时候,service manager向binder驱动请求的时候会走到这里
case BINDER_TYPE_WEAK_HANDLE: {
// 此时proc是service manager进程, target_proc是从发起请求进程的事务堆栈中获得的,所以是client进程,ref是指向本地对象的,即service组件
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;
}
if (ref->node->proc == target_proc) { // server == client? 不等于
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;
// 在进程proc(即service manager)中寻找是否有引用了实体对象ref->node的引用对象
new_ref = binder_get_ref_for_node(target_proc, ref->node);
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
// 把引用对象句柄值修改为client这边的,因为后面返回给client需要用这个来创建代理对象
fp->handle = new_ref->desc;
// 增加这个引用对象计数,内核缓冲区释放的时候,会减少这个引用
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;
....................
}
```
这个方法之前的文章也都分析过,这里主要看下BINDER_TYPE_HANDLE这个case的处理。前面分析bio_put_ref方法的时候,已经看到了在写入缓冲区对象的type字段值是BINDER_TYPE_HANDLE,所以现在进入了这个分支。
这个方法首先在service manager中寻找是否有这个引用对象,因为之前注册的时候,一切正常的话,会创建一个引用对象保留在service manager进程中,所以调用binder_get_ref这个方法来寻找:
```c
// 在进程proc中寻找是否有句柄为desc的引用对象
{
static struct binder_ref *binder_get_ref(struct binder_proc *proc, uint32_t desc)
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;
while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (desc < ref->desc)
n = n->rb_left;
else if (desc > ref->desc)
n = n->rb_right;
else
return ref;
}
return NULL;
}
```
这个方法很简单,就是比较句柄值,如果找到相同的返回,否则返回NULL。返回到前面binder_transaction方法。
接着判断如果这个找到的引用对象和目标对象是同一个进程,那么就说明就是目标对象是自己调用自己,所以从实体对象中取出地址值,赋值给flat_binder_object,然后有个关键的处理,我们执行这个分子的case是BINDER_TYPE_HANDLE,而这里如果是同一个进程的话,会把这个type修改为BINDER_TYPE_BINDER,如果不是同一个进程则不会修改,这个的作用后面会说到。如果不是一个进程,那么我们就要通过binder_get_ref_for_node方法看看目标进程中是否已经有了这个引用对象:
```c
// 在target_proc中寻找一个是否引用了实体对象node的引用对象,没有的话就创建一个引用对象来引用node
static struct binder_ref *
binder_get_ref_for_node(struct binder_proc *proc, struct binder_node *node)
{
struct rb_node *n;
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref;
while (*p) { // 遍历进程的引用对象
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_node);
if (node < ref->node)
p = &(*p)->rb_left;
else if (node > ref->node)
p = &(*p)->rb_right;
else
return ref; // 找到和需要的实体对象一样的就返回
}
new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); // 创建一个引用对象结构体
if (new_ref == NULL)
return NULL;
binder_stats.obj_created[BINDER_STAT_REF]++;
new_ref->debug_id = ++binder_last_id;
new_ref->proc = proc; // 设置client
new_ref->node = node; // 设置实体对象
rb_link_node(&new_ref->rb_node_node, parent, p); // 把该引用对象插入到client进程的红黑树
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; // 如果是service manager,句柄值设置为0,否则先设置为1
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { // 遍历进程的引用对象,设置一个最小未使用的值给new_ref
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc)
break;
new_ref->desc = ref->desc + 1;
}
p = &proc->refs_by_desc.rb_node;
while (*p) { // 重新校验一下
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_desc);
if (new_ref->desc < ref->desc)
p = &(*p)->rb_left;
else if (new_ref->desc > ref->desc)
p = &(*p)->rb_right;
else
BUG();
}
rb_link_node(&new_ref->rb_node_desc, parent, p); // 插入进程的引用对象红黑树
rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
if (node) {
hlist_add_head(&new_ref->node_entry, &node->refs); // 把这个新建立的引用对象插入对应实体对象的引用对象列表中
if (binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS)
printk(KERN_INFO "binder: %d new ref %d desc %d for "
"node %d\n", proc->pid, new_ref->debug_id,
new_ref->desc, node->debug_id);
} else {
if (binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS)
printk(KERN_INFO "binder: %d new ref %d desc %d for "
"dead node\n", proc->pid, new_ref->debug_id,
new_ref->desc);
}
return new_ref;
}
```
这个方法会在目标进程中寻找是否有引用对象的实体对象值是service manager中保存的是一样的,如果是的话,就说明目标对象已经持有的这个引用对象了,那么返回即可。否则就会创建一个引用对象,然后插入到目标对象的红黑树中,然后在这个实体对象中也要插入新建的这个引用对象。最后返回这个新建的引用对象。
## 客户端收到数据和解析数据
至此,binder驱动中需要返回去客户端的数据就准备好了,接下去会在客户端的binder线程中读取这些数据最后返回到客户端,这个过程和之前添加服务是一样的,就不多叙述了。我们回到最开始调用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;
}
```
最后会来到reply.readStrongBinder()这句,这里通过Parcel的readStrongBinder方法读取数据,这是个native方法,我们看下他的实现:
```c++
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz)
{
Parcel* parcel = parcelForJavaObject(env, clazz);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
```
这里的关键是parcel->readStrongBinder()这个读取数据的方法,实现如下:
```c++
sp<IBinder> Parcel::readStrongBinder() const // 创建service的代理对象
{
sp<IBinder> val;
// 当前缓冲区里面保存了一个flat_binder_object数据,现在来解析,解析后创建对象保存在val中,返回
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
```
继续调用unflatten_binder方法来解析:
```c++
// 解析in(即flat_binder_object)数据,解析后创建对象保存在out中,返回
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的值来区分是否是同一个进程,这个在上面binder_transaction方法中分析过了。这里如果是同一个进程我们只要取出地址直接返回即可,此时这地址是一个JavaBBinder对象,这个在注册服务的时候分析过,可以参考注册那里文章。如果不是一个进程,首先通过getStrongProxyForHandle方法,这个方法在第二篇里面也有过分析,这里不过多分析,贴一下代码:
```c++
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) // 根据句柄获取代理对象。ProcessState看成是每个进程的全局类,里面保存了和整个进程有关的信息
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle); // 从全局类中的代理对象列表中查询是否有该句柄对应的代理对象,返回之。
if (e != NULL) {
IBinder* b = e->binder;
// 这里如果b是null,那么说明这个代理对象之前是不存在的,是刚才创建的。所以下面创建BpBinder
// 如果b不是null,就要检查是否还活着,增加代理的对象的弱引用指针,如果能增加成功,说明活着,就跳到else去执行,否则就和b为null一样
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle); // 创建一个代理对象,这里把句柄保存在了BpBinder中
e->binder = b; // 把刚才创建的代理对象结构体中的binder字段指向b
if (b) e->refs = b->getWeakRefs(); // 同样代理对象弱引用计数指向b的弱引用对象
result = b;
} else { // 进入这里肯定是上面上面b不是null,而且attemptIncWeak增加弱引用成功后,说明代理对象存在,所以这里把b设置给代理对象
result.force_set(b); // 设置b是强引用
e->refs->decWeak(this); // 上面attemptIncWeak由于增加了一个弱引用,这个要减少
}
}
return result;
}
```
这个方法在第二篇binder的文章里有过分析,这里也不多做分析了。简单说就是在该进程中寻找一个代理对象,如果找到的话就返回,否则就创建一个代理类BpBinder,把引用对象的句柄值保存在里面,再返回。好了,回到上面的android_os_Parcel_readStrongBinder方法
此时方法会调用javaObjectForIBinder(env, parcel->readStrongBinder())方法,此时第二个参数,如果是同一个进程会是一个JavaBBinder对象,否则是一个BinderProxy对象。javaObjectForIBinder方法在第二篇文章也讲过,那时候会获取一个BinderProxy象,我们现在在看下如果是一个JavaBBinder对象是怎么处理的:
```c++
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) // val要么是BPBinder,跨进程。 要么JavaBBbinder,同进程。这个在addservice的时候调用writeStrongBinder这个方法时候写进去的
{
if (val == NULL) return NULL;
// 如果是代理对象会返回false,JavaBBbinder的话会覆写checkSubclass方法,如果是和gBinderOffsets一样的类,就会取出object
if (val->checkSubclass(&gBinderOffsets)) {
jobject object = static_cast<JavaBBinder*>(val.get())->object(); // 取出javaBBinder的mObject字段,即一个组件的地址
return object;
}
............
}
```
这里可以看到先调用checkSubclass判断这个类是不是一个JavaBBbinder,如果是的话调用它的object方法,这个返回是一个mObject对象,之前文章有讲过如果是一个JavaBBinder那么他的mObject就是一个服务的地址,所以这里就直接返回服务的地址了。
这样getService最后就返回了服务的地址了,我们回到文章的最开头,获取了这个对象后,还会调用asInterface(b)这个函数,我们可以在看下service manager的这个方法做了什么:
```java
static public IServiceManager asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
```
首先调用了IServiceManager的queryLocalInterface方法:
```c
static final String descriptor = "android.os.IServiceManager";
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
```
之前第三篇文章我们说过,一般android中进行进程间通信会使用AIDL,而一个在服务端运行的service其本质是一个stub类,这个继承了aidl生成的java接口,也就是这里的IServiceManager接口,所以如果是在同一个进程中的调用queryLocalInterface的就是一个IServiceManager的实现类,所以上面这里会返回一个mOwner。否则就是一个BpBinder类似,返回是null。
这里mOwner也就是stub类自己,是构造函数中传入的,所以同一个进程中返回的就是服务自己。不同进程返回的是一个BinderProxy。至此,一个普通的服务调用也就和之前讲service manager的一样了,全部的逻辑也就都通了。
通过这几篇文章的分析,我自己也对binder的流程有了更深入的理解,binder是android中非常重要的一个机制,几乎所有的进程交互都是基于binder来实现的,理解了binder,也就对android底层的机制有了比较好的理解了。最后还是把getService的流程图画一下,加深一下理解。

Android进程间通信Binder(六)