Binder连载--如何启动ServiceManager

title: Binder连载–如何启动ServiceManager
date: 2020-06-17 22:54
category: android
tags: binder
url: hashwaney.github.io

基于Android7.0源码进行分析
基于goldfish3.4版本kernel源码分析

framework/native/cmds/servicemanger/
- sevice_manager.c
- binder.c

kernel/drivers/
- staging/android/binder.c
- android/binder.c

kernel/security/security.c

1.概述

ServiceManager是Binder IPC通信过程中的守护者进程,本身也是一个Binder服务;
ServiceManager本身工作相对简单,功能:为Client提供查询服务的接口和注册服务。
对于Binder IPC通信过程中,其实更多的情形是BpBinder(远程)和BBinder(本地)之间进行通信。

1.1 流程图

service_manager创建过程

启动过程阶段:

  1. 打开binder驱动,binder_open();

  2. 注册为binder管理者,binder_become_context_manager();

  3. 进入无限循环,处理client端发来的请求:binder_loop;

2.启动过程

ServiceManager (7.0之前)是由**init** 进程通过解析init.rc文件创建。
但是在7.0之后将init.rc文件进行了拆分,拆分为了servicemanager.rc文件

frameworks/native/cmds/servicemanager/servicemanager.rc

service servicemanager /system/bin/serviceManager
	#class 名
    class core
    #用户名
    user system
    #组名
    group system readproc
    #意外停止后会重新启动
    critical
    #重启时同时重启
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

对应的可执行程序/system/bin/servicemanager,对应的源文件时service_manager.c
进程名/system/bin/servicemanager

启动ServiceManager的入口函数是service_manager.c中的**main()**方法。

2.1 main()


int main(int argc, char**argv){
 struct binder_state *bs;
    //128  1024 B  =1 K
    // 128 *

    // 1B =8bit
    // 1K =1024B
    // 128*1024 =128K
    // 1。 打开一个Binder设备文件(驱动) (注意文件,任何都是一个文件,在linux,binder设备文件)
    // 打开设备文件/dev/binder,返回一个文件描述符
    bs = binder_open(128*1024);
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }

//  2。告诉Binder驱动程序(bs)自己是Binder上下文管理者 前提是要拥有selinux权限 
    //5. binder_become_context_manager
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    // selinux -=== Security-Enhanced Linux  安全增强式Linux
    // 背景知识:为了防止内核、应用以及配置中任意一个环节出现问题而导致整个系统崩溃而提供的一种安全策略手段
    // Selinux提供了一种强制访问控制、强制完整性控制、以角色为基础的访问控制,和类型强制架构安全策略
    // Android 4.3版本增加了
    selinux_enabled = is_selinux_enabled();//selinux权限是否开启
    sehandle = selinux_android_service_context_handle(); // 返回一个selinux权限句柄
    selinux_status_open(true); // 打开selinux权限
    // 不是说你调用了binder_become_context_manager 你就成为一个binder上下文管理者,还需要进行一次安全增强linux权限的校验的
    if (selinux_enabled > 0) {
        if (sehandle == NULL) { // 无法获取sehandler句柄(指针)
            ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
            abort();
        }

        if (getcon(&service_manager_context) != 0) { // 拒绝你的申请成为Binder上下文管理者 无法获取service_manager上下文/
            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
            abort();
        }
    }
    //安全策略的回调
    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    //svcmgr_handler 这个函数正常的返回值应该是0  BINDER_SERVICE_MANAGER
    // 无限循环。
    binder_loop(bs, svcmgr_handler);

    return 0;
}

总结一下:
main()函数是执行了ServiceManager 进程打开binder设备文件,调用binder_become_context_manager()成为binder驱动的上下文管理者,这样就使得所有的进程间通信所使用的Binder实体都可以被这个上下文管理者统一调度,以及开启一个无限循环来等待client的通信请求,至于是为什么无限循环,1.考虑到系统不能退出,2.Binder驱动通道不能断开。即起到了对Binder进行守护的操作,因此 Service Manager 也被称为Binder驱动的守护进程。

2.2 binder_open

函数路径:framework/native/cmds/servicemanger/binder.c

struct binder_state *binder_open(size_t mapsize)
{
	 //binder的状态信息 包括文件描述符fd
    //(用于接收打开binder驱动设备文件以及其他文件的文件描述符),
    //mapsize(表示接受传入的外界传入的地址空间大小,
    //以便后续在binder驱动的binder_mmap函数进行大小为4M的地址空间的校验),
    //mmaped(表示通过mmap函数申请的物理空间,接收其返回的内存地址)
    struct binder_state *bs;


    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }
    // 2. open 通过系统调用陷入内核 其实也就是用户态切换到内核态
    // 存在一个用户态和内核态的切换过程,--- cpu切换;
    bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
    //3. ioctl
    // vers.protocol_version kernel的协议版本
    // BINDER_CURRENT_PROTOCOL_VERSION: 用户态的binder版本
    // ioctl 其实就是去读取了内核的信息。
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        // 内核空间所读取的版本与用户空间的binder版本不是同一个版本。
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;
    //binder_state 接受一个mmap内存映射的指针
    //4. mmap
    // 系统调用。
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}


流程如下:

  1. open()系统调用函数打开binder设备文件;

  2. 切换到了内核态,进入到Binder驱动层,调用binder_open(),该方法会在Binder驱动层创建一个binder_proc(其实就是对应的进程的上下文,你也可以理解就是一个进程),将binder_proc对象赋值给file->proviate_data,同时还会加入到全局hash链表中binder_procs,最核心的功能就是将需要进行进程间通信的进程记录到binder_proc结构体中,以便在后续的数据传输过程中不会发生紊乱;

  3. 通过ioctl文件操作系统调用函数校验当前的binder版本(用户)与 Binder驱动层(内核)的版本是否一致?(不一致会如何??🤔)

  4. 调用mmap系统调用函数进行内存映射,对应于Binder驱动层binder_mmap()该方法会在Binder驱动层创建Binder_buffer对象,并放入当前binder_porc(用户进程)的proc_buffers链表。

2.2.1 binder_state

struct binder_state{
	int fd; // /dev/binder文件描述符
	size_t mapsize; //分配的内存大小 默认是128K
	void * mapped;// 指向mmap申请地址空间的内存地址(关于这里,参见Linux内核源代码情景分析)
}

2.3 binder_become_context_manager

函数路径:framework/native/cmds/servicemanger/binder.c

int binder_become_context_manager(){

	// 通过ioctl,传递BINDER_SET_CONTEXT_MGR指令
	return ioctl(bs->fd,BINDER_SET_CONTEXT_MGR,0);
}

2.3.1 binder_ioctl

kernel/drivers/staging/binder.c

static long binder_ioctl(struct file* filp,unsigned int cmd
	,unsigned long arg){
	
	binder_lock(__func__);
	int ret= 0;
	struct 
	switch(cmd){
		case BINDER_SET_CONTEXT_MGR:
		// 保证只会创建一次mgr_node对象
		if (binder_context_mgr_node!=NULL){
			printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
			ret = -EBUSY;
			goto err;
		}
		//用来检测当前进程是否有注册Context Manager的SEAndroid权限,
		//也就是说需要检测当前进程mgr是否具有类型为SECCLASS_BINDER的安全权限BINDER_SET_CONTEXT_MGR,如果没有就会报错。
		ret = security_binder_set_context_mgr(proc->tsk);
		if (ret < 0)
			goto err;
		if (binder_context_mgr_uid != -1) {
			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{
			binder_context_mgr_uid = current->cred->euid;
		}
		binder_context_mgr_node=binder_new_node(proc,NULL,NULL);
		if (binder_context_mgr_node==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;
	}
	binder_unlock(__func__);
}

kernel/security/security.c

int security_binder_set_context_mgr(struct task_struct *mgr)
{	// 通过selinux
	return security_ops->binder_set_context_mgr(mgr);
}

kernel/security/selinux/hooks.c

static int selinux_binder_set_context_mgr(struct task_struct *mgr){
	//当前进程的安全上下文sid=== securityid
	u32 mysid = current_sid();
	u32 mgrsid =task_sid(mgr);
	// 正好BINDER_SET_CONTEXT_MGR对应着上面的case语句。
	return avc_has_perm(mysid,mgrsid,SECCLASS_BINDER,BINDER_SET_CONTEXT_MGR);
}

创建了binder_context_mgr_node是一个全局的binder_node变量,用来描述注册在Binder驱动里面的Context Manager,当binder_context_mgr_node的值不为NULL的时候,就表示已经有进程注册过了Context Manager。
如果为NULL,就会调用binder_new_node创建一个binder_node对象,并且保存在全局变量binder_conetxt_mgr_node;

2.3.2 binder_new_node

static struct binder_node *binder_new_node(struct binder_proc* proc,
						void __user *ptr,
					   void __user *cookie){
	//
struct rb_node **p = &proc->nodes.rb_node;
	struct rb_node *parent = NULL;
	struct binder_node *node;

	//p是二级指针
	while (*p) {
		parent = *p;
		node = rb_entry(parent, struct binder_node, rb_node);

		if (ptr < node->ptr)
			p = &(*p)->rb_left;
		else if (ptr > node->ptr)
			p = &(*p)->rb_right;
		else
			return NULL;
	}

	node = kzalloc(sizeof(*node), GFP_KERNEL);
	if (node == NULL)
		return NULL;
	binder_stats_created(BINDER_STAT_NODE);

	//红黑数的定义:
	// 1. 每个节点为红色或者黑色
	// 2.根必须为黑色
	// 3.每个叶子结点都是黑色
	// 4.如果有一个节点是红色,那么它的两个儿子都是黑色
	// 5.对于每个节点从该节点出发到其子孙节点的所有路径包含相同数目的黑节点
	// 清空rb_node的左右节点置空,通过p指针连接到parent节点的左或者右节点
	rb_link_node(&node->rb_node, parent, p);
	// 调整红黑树
	rb_insert_color(&node->rb_node, &proc->nodes);
	node->debug_id = ++binder_last_id;
	//将binder_proc保存在binder_node的proc中
	//proc记录的是该Binder实例所属的进程
	node->proc = proc;
	// ptr 和 cookie 表示的该Binder实体在用户空间的地址和附加数据。
	node->ptr = ptr;
	node->cookie = cookie;
	node->work.type = BINDER_WORK_NODE;
	//创建了async_todo和binder_work两个队列
	INIT_LIST_HEAD(&node->work.entry);
	INIT_LIST_HEAD(&node->async_todo);
	binder_debug(BINDER_DEBUG_INTERNAL_REFS,
		     "binder: %d:%d node %d u%p c%p created\n",
		     proc->pid, current->pid, node->debug_id,
		     node->ptr, node->cookie);
	return node;

}

在Binder驱动层创建binder_node(其实就是一个Binder实体类,该Binder实体类会记录相关的进程信息,比如记录进程的proc,以及Binder实体被哪些进程引用,以及binder所属的进程已经销毁了,但是该Binder实体还被其他进程引用,会通过dead_node存到一个哈希表中(猜测:如果其他需要使用该binder实体的是否需要去这个哈希表中去获取这个binder实例呢?))结构体对象,并且将binder_proc加入到binder_node的proc中,
并创建了binder_node的async_todo和binder_work两个队列用于接收异步和同步的任务请求;

2.4 binder_loop

frameworks/native/cmds/servicemanager/binder.c


void binder_loop(struct binder_state * bs, binder_handler func){
	int res;
	struct binder_write_read bwr;
	uint32_t readbuf[32];
	bwr.write_size=0;
	bwr.write_consumed=0;
	bwr.write_buffer=0;

	readbuf[0]=BC_ENTER_LOOPER;// 进入looper

	//将BC_ENTER_LOOPER命令发送给binder驱动,让Service Manager进入循环	
	// uint32_t --- 32无符号比特位 --- 无符号整型int 4个字节
	binder_write(bs,readbuf,sizeof(uint32_t));
	for(;;){
		bwr.read_size=sizeof(readbuf);
		bwr.read_consumed=0;
		bwr.read_buffer=(uintptr_t)readbuf;
		// ioctl 是一个IO操作, 不断的进行binder读写过程
		res = ioctl(bs->fd,BINDER_WRITE_READ,&bwr);
		if (res<0)
		{
			break;
		}

		//解析binder信息
		res = binder_parse(bs,0,(uintptr_t)readbuf,bwr.read_consumed,func);
		if (res==0)
		{
			break;
		}
		if (res<0)
		{
			break;
		}


	}
}

进入循环读写操作,由main()方法传递过来的参数func指向svcmgr_handler;
binder_write通过ioctl将BC_ENTER_LOOPER命令发送给Binder驱动,此时bwr只有write_buffer有数据,进入到binder驱动的binder_thread_write()方法,接下来进入到for循环,执行ioctl()进行读操作,
此时bwr只有read_buffer有数据,进入到binder_thread_read()方法;

2.4.1 IPC通信

Binder IPC 通信至少是两个进程的交互:

1、client 进程执行binder_thread_write,根据BC_XXX命令,生成对应的binder_work

2、server进程执行binder_thread_read,根据binder_work.type类型,生成BR_XXX,发送到Server的用户空间处理。

binder通信过程

上述的binder_work.type共有6种:

BINDER_WORK_TRANSACTION 
BINDER_WORK_TRANSACTION_COMPLETE
BINDER_WORK_NODE
BINDER_WORK_DEAD_BINDER
BINDER_WORK_DEAD_BINDER_AND_CLEAR
BINDER_WORK_CLEAR_DEATH_NOTIFICATION

2.4.2 binder_write

int binder_write(struct binder_state *bs,void *data,size_t len){

struct binder_write_read bwr;
int res;

bwr.write_size=len; // sizeof(uint32_t) 4
bwr.write_consumed=0;
bwr.write_buffer=(uintptr_t)data; // data  === BC_ENTER_LOOPER
bwr.read_size=0;
bwr.read_consumed=0;
bwr.read_buffer=0;

res =ioctl(bs->fd,BINDER_WRITE_READ,&bwr);

if (res<0)
{
	fprintf(stderr, "binder_write: ioctl failed (%s)\n",strerror(stderr));
}
return res;

}

根据传递进来的参数,初始化bwr,write_size为4,write_buffer指向缓冲区的其实地址,其内容为BC_ENTER_LOOPER请求协议号,通过ioctl函数将bwr数据发送给binder驱动,最终调用的是binder_ioctl方法。

2.4.3 binder_ioctl

kernel/drivers/android/binder.c


static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
	// 全局的binder_proc
	struct binder_proc *proc = filp->private_data;
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;

	binder_lock(__func__);
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	switch (cmd) {
		case BINDER_WRITE_READ: {
			struct binder_write_read bwr;
			//把用户空间数据ubuf拷贝到bwr中
			if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
				ret = -EFAULT;
				goto err;
			}
			// 此时写缓存有数据
			if (bwr.write_size > 0) {
				ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
			}
			// 此读缓存无数据。判断不成立
			if (bwr.read_size > 0) {
				//...
			}
			// 将内核数据bwr拷贝到用户空间ubuf
			if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
				ret = -EFAULT;
				goto err;
			}
			break;
		}
	}
	ret = 0;
	
return ret;




将用户空间的binder_write_read结构体拷贝到内核空间

2.4.4 binder_thread_write

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;
  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) {
    get_user(cmd, (uint32_t __user *)ptr); //获取命令
    switch (cmd) {
      case BC_ENTER_LOOPER:
          //设置该线程的looper状态
          thread->looper |= BINDER_LOOPER_STATE_ENTERED;
          break;
      case ...;
  }
}

    

从bwr.write_buffer中取出cmd数据,此处的命令为BC_ENTER_LOOPER,将当前的线程状态置为BINDER_LOOPER_STATE_ENTERED。

2.5 binder_parse


int binder_parse(struct binder_state * bs,struct binder_io * bio, uintptr_t ptr, size_t size, binder_handler func){
	int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        switch(cmd) {
        case BR_NOOP:  //无操作,退出循环
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
            ptr += sizeof(struct binder_ptr_cookie);
            break;
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            ...
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg; 
                struct binder_io reply;
                int res;
                //【见小节2.5.1】
                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn); //从txn解析出binder_io信息
                 //【见小节2.6】
                res = func(bs, txn, &msg, &reply);
                //【见小节3.4】
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
        }
        case BR_REPLY: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            ...
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            }
            ptr += sizeof(*txn);
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
            struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;
            ptr += sizeof(binder_uintptr_t);
            // binder死亡消息【见小节3.3】
            death->func(bs, death->ptr);
            break;
        }
        case BR_FAILED_REPLY:
            r = -1;
            break;
        case BR_DEAD_REPLY:
            r = -1;
            break;
        default:
            return -1;
        }
    }
    return r;

}

解析binder信息,ptr指向BC_ENTER_LOOPER,func指向的svcmgr_handler,那么有请求到来就会去调用svcmgr_handler

2.6 svcmgr_handler

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;

    //ALOGI("target=%p code=%d pid=%d uid=%d\n",
    //      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);

//pointer ptr target.ptr == service manager 的指针是否等于 BINDER_SERVICE_MANAGER
    if (txn->target.ptr != BINDER_SERVICE_MANAGER)
        return -1;

    if (txn->code == PING_TRANSACTION)

        return BINDER_SERVICE_MANAGER;

    // Equivalent to Parcel::enforceInterface(), reading the RPC
    // header with the strict mode policy mask and the interface name.
    // Note that we ignore the strict_policy and don't propagate it
    // further (since we do no outbound RPCs anyway).
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }


    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
        fprintf(stderr,"invalid id %s\n", str8(s, len));
        return -1;
    }

    if (sehandle && selinux_status_updated() > 0) {
        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
        if (tmp_sehandle) {
            selabel_close(sehandle);
            sehandle = tmp_sehandle;
        }
    }

    switch(txn->code) {
    // 检测这个注册的服务
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }

        //1。 查询服务,返回该服务的handle
        handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);

        if (!handle)
            break;
        // 将服务的handle封装到reply。
        bio_put_ref(reply, handle);
        return BINDER_SERVICE_MANAGER;
    // 添加服务
    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len); // 服务名称
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg); 
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
       // binder_state: 这个结构体很重要,在添加服务的时候要用到,在mmap的时候,
        // do_add_service(bs
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;
    //SVC == Service
    // MGR == Manager
    //列举所有的服务。
    case SVC_MGR_LIST_SERVICES: {
        uint32_t n = bio_get_uint32(msg);

        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                    txn->sender_euid);
            return -1;
        }
        si = svclist;/// svclist == servicelist
        while ((n-- > 0) && si) // n个数 
            si = si->next; // 取出下一条服务
        if (si) {
            bio_put_string16(reply, si->name);
            return BINDER_SERVICE_MANAGER;
        }
        return -1;
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return BINDER_SERVICE_MANAGER;
}

查询服务,注册服务,以及列举所有服务。

strcut svcinfo{
	strcut svcinfo *next; // 服务信息
	uint32_t handle; //服务的句柄值
	struct binder_death death; // 死亡进程
	int allow_isolated; //是否允许进程隔离
	size_t len; //名称长度
	uint16_t name[0]; // 服务名称
}

每一个服务用svcinfo结构体来表示,该handle值是在注册服务过程中,由服务所在进程那一端所确定的。

3.核心

servicemanager 的核心工作就是注册服务和查询服务

3.1 do_find_service

uint32_t do_find_service(struct binder_state *bs,
	const uint16_t *s, size_t len, uid_t uid,pid_t spid){

	// 查询相应的服务
	struct svcinfo* si = find_svc(s,len);

	if (!si || !si->handle)
	{
		return 0;
	}

	if (!si->allow_isolated)
	{
		uid_t appid =uid%AID_USER;
		// 检测该服务是否
	}

	 if (!si->allow_isolated) {
        // If this service doesn't allow access from isolated processes,
        // then check the uid to see if it is isolated.

        //#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
      //  #define AID_ISOLATED_END   99999 /* end of uids for fully isolated sandboxed processes */

       // #define AID_USER        100000  /* offset for uid ranges for each user */

        uid_t appid = uid % AID_USER; // 求偏移量
        if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
        // 这个偏移量的id在 隔离进程的范围内。 说明这个server是一个隔离的进程
            return 0; // 无效的service
        }
    }
    // 服务是否满足查询条件
    if (!svc_can_find(s,len,spid))
    {
    	return 0;
    }
    return si->handle;
}

这个方法做了一系列的判断,比如说查询到的服务为空,或者是服务是处于隔离状态的,是否满足查询条件Selinux 安全机制,不允许查询,满足一系列的条件才能是正常的服务。查询到了目标服务,就返回该服务对应的handle。

3.1 find_svc

struct svcinfo * find_svc(const uint16_t *s16,size_t len){

	struct svcinfo * si;

	for (si=svclist; si; si=si->next)
	{
		    //if ((len == si->len) && // 1。len 等于 si -》len
        // 比较名称  !memcmp(s16, si->name, len * sizeof(uint16_t)) 为true 
        // memcmp 比较的结果为0 也即是两个字符是完全一样的,0 代表false 非0 代表true
           // !memcmp(s16, si->name, len * sizeof(uint16_t))) {
            return si;
		if (len==si->len && !memcmp(s16,si->name,len*sizeof(uint16_t)))
		{
			return si;
		}
	}
}

从svclist服务列表中,根据服务名遍历查找是否已经注册,如果服务已经存在于svclist中,则返回相应的服务名,否则返回NULL。
当找到服务的handle,则调用bio_put_rer(reply,handle)函数将handle封装到binder_io结构体reply中。

3.1.2 bio_put_ref

void bio_put_ref(struct binder_io *bio,uint32_t handle){
	struct flat_binder_object * obj;

	if (handle){	
		obj = bio_alloc_obj(bio);
	}else{
		obj = bio_alloc(bio,sizeof(*obj));
	}

	if (!obj)
	{
		return;
	}

	obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
	obj->type= BINDER_TYPE_HANDLE;
	obj->handle=handle;
	obj->cookie=0;


}

3.2 do_add_service

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;

    //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
    //        allow_isolated ? "allow_isolated" : "!allow_isolated", uid);

    if (!handle || (len == 0) || (len > 127))
        return -1;
    // 权限检查 。selinux
    if (!svc_can_register(s, len, spid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }
   
   //2. 查询服务
    si = find_svc(s, len); //  添加的服务一般是没有被添加进来的,如果查询的服务存在,以及服务的句柄不为0,那么这个服务对于添加动作来说就是
    // 就不会重复添加,因此如果判断handle不为0 的话就会释放该服务的handle
    if (si) {
        if (si->handle) { // 服务已经注册时,释放相应的服务
            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                 str8(s, len), handle, uid);
                 // 释放该服务的handle,
            svcinfo_death(bs, si);
        }
        // 将添加的handle句柄重新给查询到的服务的handle
        si->handle = handle;
    } else {	// 4个字节    
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); // 2个字节
        if (!si) { // 为空 out of memory 内存溢出 无法为服务分配空间。 内存不足,无法分配足够的内存
            ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
                 str8(s, len), handle, uid);
            return -1;
        }
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); // 内存拷贝服务的信息
        si->name[len] = '\0';// 结束符标记
        //3. 释放服务
        si->death.func = (void*) svcinfo_death; // 释放服务,
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist; // svclist 保存所有已注册的服务。
        svclist = si;
    }
    // 以BC_ACQUIRE命令,handle为目标的信息,通过ioctl发送给binder驱动
    binder_acquire(bs, handle);
    //以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通过ioctl发送给binder驱动,主要用于清理内存等工作。
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

小结:

  • svc_can_reigster: 检查权限,检查selinux权限是否满足;
  • find_svc: 服务检索,根据服务名来查询匹配的服务;
  • svcinfo_death: 释放服务,当查询到已存在同名的服务,则先清理该服务的信息,再将当前的服务加入到服务列表svclist;

3.2.1 svc_can_register

static int svc_can_register(const uint16_t *name,
	size_t name_len,pid_t spid){

const char * permision = "add";
// 检查selinux权限是否满足
return check_mac_perms_from_lookup(spid,permision,str8(name,name_len)?1:0)
}

3.2.2 svcinfo_death

void svcinfo_death(struct binder_state *bs ,void * ptr){
	struct svcinfo *si = (strcut svcinfo*)ptr;
	if (si->handle)
	{
		binder_release(bs,bs->handle);
		si->handle=0;
	}
}

致谢

Binder系列3-启动ServiceManager

SEAndroid安全机制对Binder IPC的保护分析

Linux内核源代码情景分析

详解Linux内核红黑树算法的实现

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章