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內核紅黑樹算法的實現

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