Android笔记九(ServiceManager浅析)

该怎么获取和系统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的运作机制

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驱动打交道。

1.binder_open()

这个方法先是open(“/dev/binder”),然后ioctl(bs->fd)读写驱动信息,接着mmap(bs->fd)把binder驱动文件的128K字节映射到内存空间。详细代码暂不分析。

2.binder_become_context_manger()

这个方法里面就一句代码

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),是不是能加深一点儿理解。这段话出自这里

3.binder_loop()

进入循环之前,先向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;
        }
    }
}
  • BC_ENTER_LOOPER
    发出这个命令的目的是通知binder驱动“本线程要进入循环状态”。在binder驱动中,凡是涉及到跨进程通信机制的线程,都会对应一个binder_thread节点。
  • binder_parse()
    解析从binder驱动读取到的数据。前面的ioctl()会将Binder驱动中的数据读取出来,并保存在bwr.read_buffer中,也就是readbuf[]数组中。binder_parse()会在合适的时候,回调func方法,也就是svcmgr_handler()方法,获取服务句柄以及干些其他事情,大概代码如下:
    这里写图片描述
ServiceManager Service解析接收到的命令

每个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的操作

解析addService

我们先研究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;
}
解析getService

和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的合法句柄。
(毕)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值