该怎么获取和系统service对应的代理接口呢?Android是这样设计的:先启动一个特殊的系统服务,叫作Service Manager Service(简称SMS),它的基本任务就是管理其他系统服务。其他系统服务在系统启动之时,就会向SMS注册自己,于是SMS先记录下与那个service对应的名字和句柄值。有了句柄值就可以用来创建合法的BpBinder了。这段话出自这里
用图表示大概如下:
其中,ServiceManagerService不是真实存在的,而是存在于C++层的一个单独的进程。我们在Java层也只能获取到SMS的代理,获取方式如下:
sServiceManager就是这个代理对象,通过asInterface(IBinder)方式获取到。而这个asInterface()方法很常见,在我们写aidl远程服务时,都会自动生成,用来获取代理服务。这个方法其实也就是用来将IBinder封装成IInterface的。在我们平常远程调用service时,执行bindService()方法,里面有个ServiceConnection参数,定义该参数时就会在回调中执行这个方法,用来获取服务代理。
servicemanager是一种native service,它的启动在init.rc文件中
该服务进程对应的源文件为Service_manger.c,里面有我们熟悉的main函数。SM对所有的系统服务是通过链表来进行管理的,进程里面有一个全局性的单链表结构变量:
struct svcinfo *svclist = null;
链表节点类型为:
struct svcinfo{
struct svcinfo *next;
unit32_t handle;//service 句柄值
struct binder_death death;//看代码好像在服务重添加时用到
int allow_isolated;
size_t len;
unit16_t name[0];//service 名字
}
下面重点分析main函数中的逻辑,可以猜到,类似于zygote进程一样,在执行完最初的任务之后一直处于for(;;)循环中。其中主要的的步骤如下:
init main(){
//1.打开binder驱动
bs = binder_open(128*1024);
//2.让自己成为整个系统中唯一的上下文管理器
binder_become_context_manager(bs);
//3.循环,binder通信在里面完成,其中svcmgr_handler是一个函数指针
binder_loop(bs,svcmgr_handler);
}
上面三个方法中,最终都是执行ioctl()方法,和Binder驱动打交道。
这个方法先是open(“/dev/binder”),然后ioctl(bs->fd)读写驱动信息,接着mmap(bs->fd)把binder驱动文件的128K字节映射到内存空间。详细代码暂不分析。
这个方法里面就一句代码
int binder_become_context_manger(struct binder_state *bs){
return ioctl(bs->fd,BINDER_SET_CONTEXT_MSG,0);
}
把BINDER_SET_CONTEXT_MGR发送到binder驱动。驱动中与ioctl()对应的binder_ioctl()是这样的:
switch(cmd){
case BINDER_SET_CONTEXT_MSG:
....
//对binder_node节点操作
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;
....
}
代码的意思很明确,要为整个系统的上下文管理器专门生成一个binder_node节点,并记入静态变量binder_context_mgr_node。我们在这里多说两句,一般情况下,应用层的每个binder实体都会在binder驱动层对应一个binder_node节点,然而binder_context_mgr_node比较特殊,它没有对应的应用层binder实体。在整个系统里,它是如此特殊,以至于系统规定,任何应用都必须使用句柄0来跨进程地访问它。现在大家可以回想一下前文在获取SMS接口时说到的那句new BpBinder(0),是不是能加深一点儿理解。这段话出自这里
进入循环之前,先向binder驱动发送一个BC_ENTER_LOOPER命令,接着进入for(;;)循环,不断调用ioctl()读取发出的数据,接着解析(parse)这些数据,没有则阻塞在ioctl()
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
unsigned readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
//1
binder_write(bs, readbuf, sizeof(unsigned));
for (;;)
{
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
//2
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
//3
res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
if (res == 0) {
LOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
LOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
每个BR命令都是由一个命令号(uint32)以及若干相关数据组成,不同命令长度不同
命令 | 长度 |
---|---|
BRNOOP | 命令本身长度+0 |
BR_TRANSACTION_COMPLATE | 命令本身长度+0 |
…. | …. |
BR_TRANSACTION | 命令本身长度+sizeof(binder_transaction_data)/sizeof(uint32_t) |
BR_REPLY | 命令本身长度+sizeof(binder_transaction_data)/sizeof(uint32_t) |
再来分析下binder_parse(),其中,第三个参数为*ptr,从上面传输的实参可以看出,这个值就是由binder_looper()传递过来的readbuf,也就是从binder驱动中读取到的信息,最终放在了binder_txn数据结构中。
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
实际上,binder_parse()中用来判断接收到的命令的参数cmd,也是从readbuf中的首地址中读取到的。
cmd = *(uint32_t*)ptr;
我们从驱动读到的binder_transaction_data中并没有保存传输的具体内容,而只是保存了指针data(txn中还保存了code域,表示所传输的语义码,如SVC_MGR_ADD_SERVICE等,在svcmgr_handler()中用到),因此现在的重点放在binder_transaction_data的data指向的那部分binder传输的内容。为了解析具体内容,binder_parse声明了两个binder_io的局部变量msg和reply。binder_io结构如下
struct binder_io{
char *data //从binder读取或者写入到binder中的内容指针
binder_size_t *offs;
char *data0; //内容起始位置,用作基准值
...
}
binder_parse()中用bio_init_from_txn(&msg,txn)将两者联系起来,都指向一块binder传输的内容区域。完成后执行func(bs,txn,&msg,&reply)方法,func是一个函数指针指向svcmgr_handler()函数,里面通过code域执行添加服务,查找服务等操作。并将查找到的handle句柄保存在reply中,通过binder_send_reply()发送出去。
下面的内容重点为查找、添加服务时对handle的操作
我们先研究add service的动作。前文我们已经介绍过,service manager进程里有一个全局性的svclist变量,记录着所有添加进系统的“service代理”信息,这些信息被组织成一条单向链表,即“服务向量表”。现在我们要看service manager是如何向这张表中添加新节点的。
注册Serivce是一个跨进程的操作,涉及到SystemServer和SM进程之间的通信。根据上面的解读,这个注册动作最后会通过判断binder_transaction_data的code域,走到svcmgr_handler()中的SVC_MGR_ADD_SERVICE中:
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg,&len);
if(s == NULL) return NULL;
handle = bio_get_ref(msg);//获取句柄值
allow_isolated = bio_get_unit32(msg)?1:0;
//注册服务
if(do_add_service(bs,s,len,handle,txn->sender_euid,allow_isolated,txn->sender_pid))
return -1;
break;
当binder_transaction_data的code为SVC_MGR_ADD_SERVICE时,*data所指的其实就是句柄。这点我们可以在bio_get_ref()中找到答案,向servicemanager注册服务时,obj->type一定是BINDER_TYPE_HANDLE,所以正常情况下会走到下面的return obj->handle;
下面再看看do_add_service的主要步骤
do_add_service(struct binder_state *b,uint16_t *s,unsigned len ,uint32_t handle,uid_t uid int allow_isolated,pid_t spid){
struct svcinfo *si;
//判断服务是否能注册
if(!svc_can_register()){
return -1;
}
si = find_svc(s,len);
if(si){ //注册过该服务
if(si -> handle){
//如果已经注册,则覆盖
svcinfo_death();
}
si->handle = handle;
}else{//分配内存,添加到链表
}
...
return 0;
}
和addService类似,其他应用通过调用ServiceManager的getService()方法获取到系统服务代理,这过程也是通过Binder调用完成。根据上面的解读,这个获取服务动作最后会通过判断binder_transaction_data的code域,走到svcmgr_handler()中
case SVC_MGR_GET_SERVICE:
s = bio_get_string16(msg,&len);
if(s == NULL) return NULL;
handle = do_find_service(s,len,txn->sender_euid,txn->sender_pid);//从txn中获取handle句柄值
if(!handle) break;
bio_put_ref(reply,handle);//将handle句柄值保存在reply中
return 0;
前面的addservice操作,句柄值是从msg中获取到,也就是从Binder中传递到SM。这里,句柄值从SM中取出来,我们用bio_put_ref()将它传给到Binder驱动,最后让客户端获取到。让客户端获取到句柄值的操作是在binder_parse()中完成的
res = func(bs,txn,&msg,&reply);//svcmgr_handler中完成数据操作,具体就是txn msg reply之间操作,
根据txn中的code域,将txn中的data值和msg reply进行绑
定,data中也就是binder传递的内容。
如果code为ADD_SERVICE或者GET_SERVICE就表示是注册/获
取服务,此时的data为handle句柄值;否则是普通的Binder
传递
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);//这儿会把查找到的信息,发送给发起查找的一方
}
...
实际上binder_send_reply()最后也是将reply中的数据通过ioctl()传递给Binder驱动。这些数据中包括了两个命令BC_FREE_BUFFER BC_REPLY,一并传递给getservice的发起端,于是发起端就获得了service的合法句柄。
(毕)