Android10.0 Binder通信原理(三)-ServiceManager篇

摘要:本节主要来讲解Android10.0 Binder中守护进程ServiceManager是如何启动、注册、获取服务

阅读本文大约需要花费35分钟。

文章首发微信公众号:IngresGe

专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路] 系列文章:

 [1] Android系统架构

 [2]Android是怎么启动的

 [3]Android 10.0系统启动之init进程

 [4]Android10.0系统启动之Zygote进程

 [5]Android 10.0 系统启动之SystemServer进程

 [6]Android 10.0 系统服务之ActivityMnagerService

 [7]Android10.0系统启动之Launcher(桌面)启动流程

 [8]Android10.0应用进程创建过程以及Zygote的fork流程

 [9]Android 10.0 PackageManagerService(一)工作原理及启动流程

 [10]Android 10.0 PackageManagerService(二)权限扫描

 [11]Android 10.0 PackageManagerService(三)APK扫描

 [12]Android 10.0 PackageManagerService(四)APK安装流程

 [13]Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性

 [14]Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化

 [15]Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析

 [16]Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

[Binder通信原理] :

Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要

Android10.0 Binder通信原理(二)-Binder入门篇

1.概述

在Android中,系统提供的服务被包装成一个个系统级service,这些service往往会在设备启动之时添加进Android系统。当我们某个应用想要调用系统某个服务的功能时,往往是向系统发出请求,调用该服务的外部接口。在上一节我们也了解到,这种外部接口,我们通常称为代理接口,也就是我们要拿到目标服务对应的代理对象。

但是Android中有很多个服务,如果都是直接向目标服务拿对象也不太现实,开销会很大。因此在Android中,引入了一个ServiceManager的系统级服务--也可以称之为Binder的守护进程来管理这些服务对象。

上一节我们大体的了解了Binder的设计思路,了解了Binder机制的四要素:Client、Server、ServiceManager、Binder驱动。其中ServiceManager主要用来管理Server的名称和对象。

ServiceManager本身的工作很简单:注册服务、查询服务、列出所有服务,启动一个死循环来解析Binder驱动读写动作,进行事务处理。

2.Binder架构

Binder通信流程如下:

1)首先服务端需要向ServiceManager进行服务注册,ServiceManager有一个全局的service列表svcinfo,用来缓存所有服务的handler和name。

2)客户端与服务端通信,需要拿到服务端的对象,由于进程隔离,客户端拿到的其实是服务端的代理,也可以理解为引用。客户端通过ServiceManager从svcinfo中查找服务,ServiceManager返回服务的代理。

3)拿到服务对象后,我们需要向服务发送请求,实现我们需要的功能。通过 BinderProxy 将我们的请求参数发送给 内核,通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态。然后 Binder 驱动向服务端的 todo 队列里面插入一条事务,执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命令,并没有拷贝数据,binder只进行一次拷贝),唤醒等待的客户端并把结果响应回来,这样就完成了一次通讯。

 

3.servicemanager的启动

init进程启动时,加载servicemanager.rc,启动serviecmanager:

servicemanager.rc 文件如下:

/frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
        ...

从上面的rc文件可知道,servicemanager编译后,位于手机的/system/bin中,这是一个Native C/C++的进程,C/C++的进程都会有一个main()的入口,顺藤摸瓜,我们找到了servicemanager的入口:service_manager.c 的main()。

4.service_manager调用栈:

我们在看源码或者调试死机的时候,最喜欢看的就是上下文的调用关系,在此,我列出了service_manager.c 的调用栈信息,暂时先忽略细节部分

5. 源码分析

   servicemanager的整体流程和 service管理的结构我们都有了一个整体认识,那么细节部分就得靠看源码来深入理解了。

 

5.1 主程序启动main()

前面我们知道,servicemanager 是一个Native C/C++的程序,那么会存在一个main()的入口,代码如下:


int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    //0. servicemanager 和vndservicemanager 共享一套代码,vndservicemanager传入参数"/dev/vndbinder"
    if (argc > 1) {
        driver = argv[1];   //vndservicemanager 的driver为 "/dev/vndbinder"
    } else {
        driver = "/dev/binder"; //servicemanager 访问的driver为"/dev/binder"
    }

    //1.通过系统调用 open "/dev/binder", mmap 申请一块128K的内存空间
    bs = binder_open(driver, 128*1024);
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        ALOGW("failed to open binder driver %s\n", driver);
        while (true) {
            sleep(UINT_MAX);    //如果是"/dev/vndbinder" 打开失败,陷入死循环继续等待
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);  //如果是"/dev/binder" 打开失败,直接跳出
#endif
        return -1;
    }

    //3.注册servicemanager为binder 机制守护进程 ,其实就是把0号的handler给servicemanager使用,以后只要访问0号的handler,binder驱动就知道是与servicemanager进行交互
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
#ifdef VENDORSERVICEMANAGER
    cb.func_log = selinux_vendor_log_callback;
#else
    cb.func_log = selinux_log_callback;
#endif
    selinux_set_callback(SELINUX_CB_LOG, cb);   //注册selinux的callback操作

#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);  //启动selinux的检查

    if (sehandle == NULL) {
        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
        abort();
    }

    //如果selinux检查不过,则不允许进行binder的操作
    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }

    //4. 启动loop循环机制,等待"/dev/binder" 发来读写信息,解析这些内容,执行相应的操作,例如:注册服务、获取服务、列出服务信息
    binder_loop(bs, svcmgr_handler);

    return 0;
}

在Android8.0后,谷歌引入Treble机制,binder机制增加了hwbinder和vndbinder,其中vndbinder的守护进程为vndservicemanager。

vndservicemanager和service共用同一份代码,只是传入的参数和宏控制的流程有部分差异。

vndservicemanager会传入参数"/dev/vndbinder", servicemanager使用默认的"/dev/binder".

servicemanager主要做了以下几件事:

1.打开binder驱动,申请了128K的内存空间

2.然后调用binder_become_context_manager()让自己成为整个系统中唯一的上下文管理器,其实也就是service管理器

3.调用binder_loop()进入无限循环,不断监听并解析binder驱动发来的命令。

4.binder_loop()通过binder_parse()进行命令解析,然后调用回调函数svcmgr_handler()进行处理,例如:注册服务、获取服务、列出服务信息

 

下面我们按步来看看各个回合工作的细节部分。

 

5.2 binder_open()

servicemanager启动后,先通过binder_open()来打开"/dev/binder", 代码如下:

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    ...
    //1.打开Binder设备驱动,陷入内核
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    ...

    //2.获取Binder的版本信息,比较协议版本是否相同,不同则跳出
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || 
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION))
        ...
    }

    bs->mapsize = mapsize;
    //3.mmap内存映射128K内存空间,mmap必须是page的整数倍
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    ...

    return bs;
    ...
}

binder_open()的工作也比较简单,分为以下几步

1.通过系统调用open()来打开"/dev/binder",获得一个句柄信息,在Binder驱动中对应的是函数binder_open()

2.通过ioctl 获取binder的版本信息,比较binder协议版本是否相同,不同则跳出,在Binder驱动中对应的是函数binder_ioctl()

3.通过mmap内存映射128K的内存空间,即把binder驱动文件的128K字节映射到了内存空间,这128K内存空间为servicemanager使用。在Binder驱动中对应的是函数binder_mmap()。

其他的binder服务进程会映射 BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) 的内存空间,_SC_PAGE_SIZE表示一个page页的大小,通常情况下为4K,即(1M - 4K*2)=(1M-7K)

这个page的大小,不同厂家有时候也会调整大小,一般有1M,64K,4K,1KB,通常为4K。

ServiceManager 进程mmap的内存大小可以通过adb shell命令得出

其中 0x7457d61000 - 0x7457d41000 = 0x20000, 转成10进制,即为128K

ARM32内存映射:

虚拟空间的低3GB部分从0-0XBFFFFFFF的虚拟线性地址,用户态和内核态都可以寻址,这部分也是每个进程的独立空间。

虚拟空间的高1G部分从0XC0000000到0XFFFFFFFF的虚拟地址,只有内核态的进程才能访问,这种限制由页目录和页表描述符的权限标志位决定,通过MMU自动控制。

虚拟地址空间为4G,0-3G是用户地址空间(用户态和内核态都可以寻址,这也是每个进行的独立空间),3G-4G内核地址空间,每个进程都是拥有4G地址空间,只是用户态下面无法访问高1G空间;内核空间是被所有进程共享的

ARM64内存映射:

默认情况下,32位系统默认只能支持4G的内存,在打开PAE后,最大可以扩展到64G的内存。随着物理硬件的不断升级,现在的内存越来越大,因此基本上都切换到了64位系统。

理论上讲,64位的地址总线可以支持高达16EB(2^64)的内存。

2^64 次方太大了,Linux 内核只采用了 64 bits 的一部分(开启 CONFIG_ARM64_64K_PAGES 时使用 42 bits,页大小是 4K 时使用 39 bits),该文假设使用的页大小是 4K(VA_BITS = 39)

ARM64 有足够的虚拟地址,用户空间和内核空间可以有各自的 2^39 = 512GB 的虚拟地址。

需要注意到,32 位应用仍然拥有 512GB 的内核虚拟地址空间,并且不与内核共享自己的 4GB 空间。但在 ARM32 上,32 位应用只有 3GB 的地址空间。

ARM32和ARM64内存地址比较:

ARM32

ARM64

32位应用虚拟地址空间

3G

4G

64位应用虚拟地址空间

不支持

512G

内核虚拟空间

1G

512G

5.3 binder_become_context_manager()

binder_become_context_manager()的作用是让ServiceManager成为整个系统中唯一的上下文管理器,其实也就是service管理器,这样我们就可以把ServiceManager称之为守护进程。

int binder_become_context_manager(struct binder_state *bs)
{
    struct flat_binder_object obj;
    memset(&obj, 0, sizeof(obj));
    obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;

    //Android10.0 中引入BINDER_SET_CONTEXT_MGR_EXT,用来把ServiecManager设置成为安全的上下文,
    int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);

    // fallback to original method
    if (result != 0) {
        android_errorWriteLog(0x534e4554, "121035042");
        //如果安全上下文设置失败,继续使用原有的BINDER_SET_CONTEXT_MGR来进行控制
        result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
    }
    return result;
}

对应的binder驱动中操作如下:

从用户空间拷贝ioctl的参数,调用binder_ioctl_set_ctx_mgr()进行设置

BINDER_SET_CONTEXT_MGR_EXT带参数,BINDER_SET_CONTEXT_MGR不带参数

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    ...
    switch (cmd) {
        case BINDER_SET_CONTEXT_MGR_EXT: {
            struct flat_binder_object fbo;

            if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
                ret = -EINVAL;
                goto err;
            }
            ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
            if (ret)
                goto err;
            break;
        }
        case BINDER_SET_CONTEXT_MGR:
            ret = binder_ioctl_set_ctx_mgr(filp, NULL);
            if (ret)
                goto err;
            break;
    }
    ...
}

static int binder_ioctl_set_ctx_mgr(struct file *filp,
        struct flat_binder_object *fbo)
{
    int ret = 0;
    //进程的binder_proc, 这里是ServiceManager的 binder_proc,之前通过open("/dev/binder")得来
    struct binder_proc *proc = filp->private_data;
    struct binder_context *context = proc->context;
    struct binder_node *new_node;
    // 线程的uid
    kuid_t curr_euid = current_euid();

    //互斥锁
    mutex_lock(&context->context_mgr_node_lock);
     //正常第一次为null,如果不为null则说明该进程已经设置过context mgr则直接退出
    if (context->binder_context_mgr_node) {
        pr_err("BINDER_SET_CONTEXT_MGR already set\n");
        ret = -EBUSY;
        goto out;
    }
    //检查当前进程是否具有注册Context Manager的SEAndroid安全权限
    ret = security_binder_set_context_mgr(proc->tsk);
    if (ret < 0)
        goto out;
    if (uid_valid(context->binder_context_mgr_uid)) {
          //读取binder_context_mgr_uid和当前的比,如果不一样,报错。
        if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
            pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
                   from_kuid(&init_user_ns, curr_euid),
                   from_kuid(&init_user_ns,
                     context->binder_context_mgr_uid));
            ret = -EPERM;
            goto out;
        }
    } else {
         //将当前进程的uid赋值给context的binder_context_mgr_uid变量以作保存
        context->binder_context_mgr_uid = curr_euid;
    }
    // 创建binder_node对象
    new_node = binder_new_node(proc, fbo);
    if (!new_node) {
        ret = -ENOMEM;
        goto out;
    }
    binder_node_lock(new_node);
    // 增加强弱引用计数
    new_node->local_weak_refs++;
    new_node->local_strong_refs++;
    new_node->has_strong_ref = 1;
    new_node->has_weak_ref = 1;
    context->binder_context_mgr_node = new_node; //把新创建的node对象赋值给context->binder_context_mgr_node,成为serviceManager的binder管理实体
    binder_node_unlock(new_node);
    binder_put_node(new_node);
out:
    //释放锁
    mutex_unlock(&context->context_mgr_node_lock);
    return ret;
}

binder_ioctl_set_ctx_mgr()的流程也比较简单

1.先检查当前进程是否具有注册Context Manager的SEAndroid安全权限

2.如果具有SELinux权限,会为整个系统的上下文管理器专门生成一个binder_node节点,使该节点的强弱应用加1

3.新创建的binder_node 节点,记入context->binder_context_mgr_node,即ServiceManager 进程的context binder节点,使之成为serviceManager的binder管理实体

5.4 binder_loop()

下一步进行守护进程的循环处理,binder_loop()会先向binder驱动发出了BC_ENTER_LOOPER命令,告诉binder驱动"本线程要进入循环状态了",接着进入一个for循环不断调用ioctl()读取发来的数据,接着解析这些数据

[frameworks/native/cmds/servicemanager/service_manager.c]

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;   //binder 读写结构,记录buffer中读和写的数据信息
    uint32_t readbuf[32];   //准备128= 32*4字节的读取buffer

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    // 向binder驱动发送命令协议BC_ENTER_LOOPER,告诉binder驱动"本线程要进入循环状态了".
    //binder_write 中把 binder_write_read的read的信息置空,这样在binder_ioctl中,只会处理write的流程
    binder_write(bs, readbuf, sizeof(uint32_t));

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);   //进入循环,不断地binder读写过程

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
         // 解析binder信息,传入的回调func 函数为 svcmgr_handler()
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

其中最重要的一个结构体是binder_write_read,它用来记录Binder buffer中读和写的数据信息结构体如下:

struct binder_write_read {
    binder_size_t       write_size;     //要写入的字节数,write_buffer的总字节数
    binder_size_t       write_consumed; //驱动程序占用的字节数,write_buffer已消费的字节数
    binder_uintptr_t    write_buffer;   //写缓冲数据的指针
    binder_size_t       read_size;      //要读的字节数,read_buffer的总字节数
    binder_size_t       read_consumed;  //驱动程序占用的字节数,read_buffer已消费的字节数
    binder_uintptr_t    read_buffer;    //读缓存数据的指针
};

5.5 binder_parse()

在binder_loop()进入for循环之后,核心处理流程就是ioctl和binder_parse(), 即不停的从Binder驱动接收读写数据,进行binder解析后,进行处理.

[frameworks/native/cmds/servicemanager/binder.c]
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;

    //每个命令中可能包含多个BR码,因此需要用while循环来解析buffer中所有的BR_XX码
    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr; //从ptr指向的地址读取cmd(协议)命令
        ptr += sizeof(uint32_t);        //cmd占用 readbuf的4个字节
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
#if TRACE
            fprintf(stderr,"  %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *)));
#endif
            ptr += sizeof(struct binder_ptr_cookie);
            break;
        case BR_TRANSACTION_SEC_CTX:
        case BR_TRANSACTION: {
               struct binder_transaction_data_secctx txn;
               ...
               binder_dump_txn(&txn.transaction_data);
               if (func) {
                       ...
                       res = func(bs, &txn, &msg, &reply);
                       ...
               }
        case BR_REPLY: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                /* todo FREE BUFFER */
            }
            ptr += sizeof(*txn);
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
                  //获取binder_death数据
            struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;
            ptr += sizeof(binder_uintptr_t);
            death->func(bs, death->ptr);
            break;
        }
        case BR_FAILED_REPLY:
            r = -1;
            break;
        case BR_DEAD_REPLY:
            r = -1;
            break;
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }

    return r;
}

在binder_loop()中声明了一个128字节的栈内存-readbuf, 用BINDER_WRITE_READ命令从驱动读取一些内容,并传入binder_parse(),binder_parse()根据binder驱动传来的 "BR_XXX"协议码,进行相关的逻辑处理,最主要的有三个"BR_XXX"协议

BR_TRANSACTION  : 事务处理,解析binder_transaction_data的数据,调用回调函数svcmgr_handler() 进行服务的注册、获取等操作

BR_REPLY       : 消息回复

BR_DEAD_BINDER  : 死亡通知

只要binder_parse()解析正常,binder_loop()就会一直执行下去,ServiceManager进程不退出。

binder_parse()解析binder驱动传来的readbuf的内存,readbuf拥有128字节的栈内存,每次可以只处理一个cmd,也可以有多个cmd,所以存在一个while循环,可以同时解析多个cmd,多个cmd的结构如下图所示:

5.5.1 BR_XXX 协议码分析

BR_XXX码,也成为Binder响应码,这里介绍了ServiceManager处理的一些响应码的作用:

5.5.2 BR_TRANSACTION 解析

我们这里单独分析下 BR_TRANSACTION 的流程,这也是我们常用的一个流程,这是Binder驱动向Server端发送请求数据。

从readbuf中解析出binder_transaction_data的数据,最后对接收和发送数据进行了封装,传递给svcmgr_handler()做详细处理


int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    case BR_TRANSACTION_SEC_CTX:
    case BR_TRANSACTION: {
        //binder_transaction_data_secctx 比 binder_transaction_data 多一个 secctx成员,主要用来指向安全上下文
        struct binder_transaction_data_secctx txn;
        if (cmd == BR_TRANSACTION_SEC_CTX) {
            if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) {
                ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n");
                return -1;
            }
            memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx));
            ptr += sizeof(struct binder_transaction_data_secctx);
        } else /* BR_TRANSACTION */ {
            if ((end - ptr) < sizeof(struct binder_transaction_data)) {
                ALOGE("parse: txn too small (binder_transaction_data)!\n");
                return -1;
            }
            //readbuf 偏移4个自己的cmd后,拷贝一个binder_transaction_data 结构的数据给transaction_data
            memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
            ptr += sizeof(struct binder_transaction_data);  //readbuf偏移 binder_transaction_data的内容,这样readbuf可以指向下一个BR_XX结构

            txn.secctx = 0;
        }
        //打印binder_transaction_data数据
        binder_dump_txn(&txn.transaction_data);
        if (func) {
            unsigned rdata[256/4];
            struct binder_io msg;   //收到数据封装
            struct binder_io reply; //发送数据封装
            int res;

            bio_init(&reply, rdata, sizeof(rdata), 4);     //reply初始化
            bio_init_from_txn(&msg, &txn.transaction_data); //从txn.transaction_data 解析出binder_io的信息,存入msg
            res = func(bs, &txn, &msg, &reply); //svcmgr_handler 回调处理
            if (txn.transaction_data.flags & TF_ONE_WAY) {
                 //如果是TF_ONE_WAY处理,则释放txn->data的数据
                binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
            } else {
                 //如果不是TF_ONE_WAY处理,给binder驱动回复数据
                binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
            }
        }
        break;
    }
}

从上面的逻辑看,我们重点关注的是 binder_transaction_data 这个结构,binder_transaction_data说明了transaction到底在传输什么语义,而语义码就记录在其code成员中。不同语义码需要携带的数据也是不同的,这些数据由data指定.

结构体说明如下:


struct binder_transaction_data {
       union {
         __u32        handle;       //binder_ref(即handle)对于发送数据包的一方,该成员指明发送目的地。
                                      //由于目的是在远端,所以这里填入的是对Binder实体的引用,存放在target.handle中。如前述,Binder的引用在代码中也叫句柄(handle)
         binder_uintptr_t ptr;    //Binder_node的内存地址,当数据包到达接收方时,驱动已将该成员修改成Binder实体,即指向Binder对象内存的指针,使用target.ptr来获得
       } target;
       binder_uintptr_t        cookie;   //BBinder指针, 发送方忽略该成员;接收方收到数据包时,该成员存放的是创建Binder实体时由该接收方自定义的任意数值,做为与Binder指针相关的额外信息存放在驱动中
       __u32                code;                   //RPC代码,代表Client与Server双方约定的命令码,例如:ADD_SERVICE_TRANSACTION
       __u32                flags;       //标志位,比如TF_ONE_WAY代表异步,即不等待Server端回复
       pid_t                sender_pid;   //发送端进程的pid
       uid_t                sender_euid;  //发送端进程的uid
       binder_size_t        data_size;    //data数据的总大小
       binder_size_t        offsets_size; //IPC对象的大小

       union {
               struct {
                       binder_uintptr_t        buffer; //数据区起始地址
                       binder_uintptr_t        offsets;//数据区IPC对象偏移量
               } ptr;
               __u8        buf[8]; 
       } data;//RPC数据
}

从上面binder_transaction_data的结构可以看出,data可存入的数据很少,主要采用了数据其实地址和数据偏移量,根据代码上下文可知,调用了bio_init_from_txn(),从txn.transaction_data 解析出binder_io的信息,存入msg

5.5.2.1 bio_init_from_txn()

   bio_init_from_txn()的作用就是把 binder_transaction_data 的“数据起始地址”、“偏移量”、“data数据的总大小”和“偏移数组中可用的条目”:

void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
{
    bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer;
    bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets;
    bio->data_avail = txn->data_size;
    bio->offs_avail = txn->offsets_size / sizeof(size_t);        //计算 偏移数组中可用的条目
    bio->flags = BIO_F_SHARED;                //标志需要释放缓冲区
}

binder_io 这个结构从字面意思我们就可以看出要用它来读取binder传递来的数据,结构如下:
struct binder_io
{
    char *data;            //指向数据 读/写的指针
    binder_size_t *offs;   //数据偏移量
    size_t data_avail;     //数据缓冲区中可用的字节数
    size_t offs_avail;     //偏移数组中可用的条目

    char *data0;           //数据缓冲区的起始地址
    binder_size_t *offs0;  //缓冲区偏移量的起始位置
    uint32_t flags;
    uint32_t unused;
};

binder_transaction_data 和  binder_io的关联

初始化完 binder_io 的replay,并把transaction_data 转换成了binder_io 的msg后,调用回调函数svcmgr_handler()进行最终逻辑处理

5.6 svcmgr_handler()

在BR_TRANSACTION的命令解析后,就把binder_transaction_data_secctx的数据传给回调函数svcmgr_handler()进行处理。

根据不同的传输语义码(txn->code) 来进行相应的操作:查询服务,注册服务,以及列举所服务

源码如下:


int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   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;
    uint32_t dumpsys_priority;

    //获取binder_parse 过来的binder_transaction_data 数据
    struct binder_transaction_data *txn = &txn_secctx->transaction_data;

    if (txn->target.ptr != BINDER_SERVICE_MANAGER)
        return -1;

    if (txn->code == PING_TRANSACTION)
        return 0;


    //从 msg的 binder_io.data的起始地址读取4个字节的内容,存入strict_policy,strict_policy现在不需要使用,可以直接忽略
    //然后msg->data 往后偏移4个字节,即忽略了开头的strict_policy;msg->data_avail 缓冲区的剩余可用字节数减去4个字节。
    //msg->data0一直不变,指向数据缓冲区的起始地址
    strict_policy = bio_get_uint32(msg);
    bio_get_uint32(msg);  // 继续偏移4个字节,忽略了header

     //先读取msg->data接下来的4个字节,即为length,然后读取((len + 1) * sizeof(uint16_t))字节的内容存入s。这里len+1的目的:字符串都要包含最后一个’\0’,需要把这个偏移量加入
    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;
    }

    //检查SELinux环境是否有任何改变
    if (sehandle && selinux_status_updated() > 0) {
#ifdef VENDORSERVICEMANAGER
        struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
#else
        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
#endif
        if (tmp_sehandle) {
            selabel_close(sehandle);
            sehandle = tmp_sehandle;
        }
    }

    //根据不同code进行逻辑处理
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        //获取服务名字和长度,分别存在s和len
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //根据服务名称查找相应服务
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
                                 (const char*) txn_secctx->secctx);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;

    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;
        dumpsys_priority = bio_get_uint32(msg);
        //注册指定服务
        if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
                           txn->sender_pid, (const char*) txn_secctx->secctx))
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
        uint32_t n = bio_get_uint32(msg);
        uint32_t req_dumpsys_priority = bio_get_uint32(msg);
        //扫描服务列表
        if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) {
            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                    txn->sender_euid);
            return -1;
        }
        si = svclist;
        // walk through the list of services n times skipping services that
        // do not support the requested priority
        while (si) {
            if (si->dumpsys_priority & req_dumpsys_priority) {
                if (n == 0) break;
                n--;
            }
            si = si->next;
        }
        if (si) {
            bio_put_string16(reply, si->name);
            return 0;
        }
        return -1;
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}

其中有几个binder_io 数据获取的方法要注意:

1) uint32_t bio_get_uint32(struct binder_io *bio)

  作用:获取bio->data开始后的4个字节。

  说明:bio->data 往后偏移4个字节,bio-->data_avail 缓冲区的剩余可用字节数减去4个字节。bio-->data0一直不变,指向数据缓冲区的起始地址

2) uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz)

  作用:在svcmgr_handler()主要是用来获取服务名和length

  说明:先读取一个32bits的整数,这个整数值就是字符串的长度。然后再读取((len + 1) * sizeof(uint16_t))字节。

  这里len+1的目的:字符串都要包含最后一个’\0’,需要把这个偏移量加入

3) static void *bio_get(struct binder_io *bio, size_t size)

  作用:根据传入的size,获取对应的ptr内容

  说明:bio->data 往后偏移4个字节,bio-->data_avail 缓冲区的剩余可用字节数减去4个字节。bio-->data0一直不变,指向数据缓冲区的起始地址

4) uint32_t bio_get_ref(struct binder_io *bio)

  作用:获取一个服务端的handle

  说明:从bio->data 中读出一个flat_binder_object的结构数据,返回 flat_binder_object->handle

5) void bio_put_ref(struct binder_io *bio, uint32_t handle)

  作用:在svcmgr_handler中主要是把handler存入reply

  说明:bio->data 偏移size(flat_binder_object), 其中存入 flat_binder_object obj的数据,并把hanlder存入obj

6) void bio_put_string16(struct binder_io *bio, const uint16_t *str)

  作用:把service的名称 存入reply

svcmgr_handler() 对binder驱动传来的binder_transaction_data进行解析读取后,根据不同的code,执行不同的操作,下面一起看看“注册服务”和“查找服务”是如何操作的。

5.7 ServiceManager 是如何管理service信息的?

通过前面的一些信息,我们了解到,ServiceManager用来管理服务信息,那么它是如何进行管理的呢?

在ServiceMnager的进程里有一个全局性的svclist变量,服务信息都存在于这里

struct svcinfo *svclist = NULL;

结构:

struct svcinfo
{
    struct svcinfo *next;        // 下一个"服务的信息"
    uint32_t handle;                //实际上记录的就是系统service对应的binder句柄值
    struct binder_death death;
    int allow_isolated;
    uint32_t dumpsys_priority;
    size_t len;                        //服务名的长度
    uint16_t name[0];                //Service的名字,最后要保留一个\0的位置,buffer长度为 (len + 1) * sizeof(uint16_t)
};

 

5.8 注册服务

根据传入的code:SVC_MGR_ADD_SERVICE得知,本次binder流程想要进行服务注册。

步骤:

  1. 从binder_io  msg中获取服务名称和长度
  2. 从binder_io msg 中获取handle
  3. 检查该服务是否有注册的selinx权限
  4. 查询服务列表svclist 是否存在该handle,如果有handle,就更新该服务的handle信息,通过这个handle我们最终就能找到远端的service实体
  5. 如果svclist不存在该服务,申请一个svcinfo的空间,把服务名、长度、handle等信息存入其中
  6. 把svcinfo 加入svclist的链表中
  7. 再以BC_ACQUIRE命令,handle为目标的信息,通过ioctl发送给binder驱动
  8. 最后以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通过ioctl发送给binder驱动,主要用于清理内存等收尾工作
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    ...
    case SVC_MGR_ADD_SERVICE:
        //获取服务的名字s和长度len
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //获取handle,通过这个handle我们最终就能找到远端的service实体
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        dumpsys_priority = bio_get_uint32(msg);
        //注册服务
        if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
                           txn->sender_pid, (const char*) txn_secctx->secctx))
            return -1;
        break;
    ...
}


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

     //service的name长度不能大于127
    if (!handle || (len == 0) || (len > 127))
        return -1;

    //最终调用selinux_check_access() 进行selinux的权限检查,检查服务是否有进行服务注册
    if (!svc_can_register(s, len, spid, sid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }
    //查询是否包含该name的svcinfo
    si = find_svc(s, len);
    if (si) {
        if (si->handle) {
            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                 str8(s, len), handle, uid);
            svcinfo_death(bs, si); //服务已注册时,释放相应的服务
        }
        //更新服务 handle
        si->handle = handle;
    } else {
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        if (!si) {
                 //内存不足,无法分配足够内存
            ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
                 str8(s, len), handle, uid);
            return -1;
        }

        // 对svcinfo进行初始化赋值
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death; //注册死亡回调,当进程死亡后,进行通知
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->dumpsys_priority = dumpsys_priority;
        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;
}

5.9 查找服务

根据传入的code:SVC_MGR_GET_SERVICE、SVC_MGR_CHECK_SERVICE得知,本次binder流程想要进行服务获取。

步骤:

  1. 获取服务的名字s和长度len
  2. 从svclist中根据名字查到一个svcinfo的结构
  3. 进行selinx检查
  4. 检查通过,返回该服务的handle
  5. 把handle放入binder_io 的reply的data中
  6. 最后在binder_parse()中调用binder_send_reply(),以BC_REPLY命令, 通过ioctl发送给binder驱动,最终转到client进程

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        //获取服务的名字s和长度len
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //从svclist中查找服务名称对应的handle
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
                                 (const char*) txn_secctx->secctx);
        if (!handle)
            break;
        //把handle存入binder_io reply的信息中
        bio_put_ref(reply, handle);
        return 0;
}

uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid)
{
    //查询相应的服务
    struct svcinfo *si = find_svc(s, len);

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

    if (!si->allow_isolated) {
        // If this service doesn't allow access from isolated processes,
        // then check the uid to see if it is isolated.
        uid_t appid = uid % AID_USER;
        //检查该服务是否允许孤立于进程而单独存在
        if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
            return 0;
        }
    }
     //调用selinux_check_access()进行检查服务是否满足查询条件,是否有selinx权限问题
    if (!svc_can_find(s, len, spid, sid, uid)) {
        return 0;
    }
    //返回服务的handle
    return si->handle;
}

查到服务的handle后,存入binder_io reply的data中,其中存入的type为HANDLE: BINDER_TYPE_HANDLE

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->hdr.type = BINDER_TYPE_HANDLE;
    obj->handle = handle;
    obj->cookie = 0;
}

服务的handle被放入到binder_io的reply中后,我们回到binder_parse(),当svcmgr_handler()处理完成后,执行binder_send_reply()的过程。

binder_send_reply()主要是将BC_FREE_BUFFER和BC_REPLY命令协议发送给Binder驱动,向client端发送reply. 其中data的数据区中保存的是TYPE为HANDLE

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;        // reply命令
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {  //svcmgr_handler执行成功,把reply的数据组装进入txn
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
       //向Binder驱动通信
    binder_write(bs, &data, sizeof(data));
}

 

6.总结

ServiceManager 注册成为上下文管理者-守护进程,负责管理系统中的所有服务。ServiceManger进程跟所有向其注册服务的死亡通知建立联系, 那么当服务所在进程死亡后, 会只需告知ServiceManager.每个Client通过查询ServiceManager可获取Server进程的情况,降低所有Client进程直接检测会导致负载过重。

ServiceManager的整体流程如下:

  1. 打开设备,mmap一个128K的内存空间,进行用户空间和内核空间的内存绑定
  2. 注册成为上下文的管理者--守护进程
  3. 陷入死循环,标注线程的looper状态为enter
  4. 不停的操作binder读写过程
  5. 解析binder数据,进行查询服务、注册服务、列出服务的核心操作
  6. 由于在进行服务注册的时候,把svcinfo_death()注册到了binder_death的中,当收到BR_DEAD_BINDER时,表明应用层向Binder驱动发送Binder调用时,Binder应用层的另一个端已经死亡,进行死亡通知操作,调用svcinfo_death(),最终调用binder_release()发送BC_RELEASE 到内核进行处理。

7.代码路径:

frameworks/native/cmds/servicemanager/binder.c

frameworks/native/cmds/servicemanager/service_manager.c

frameworks/native/cmds/servicemanager/servicemanager.rc

我的微信公众号:IngresGe

参考:

《启动ServiceManager》

《Binder(ServiceManager篇)》

《浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路》

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

发布了19 篇原创文章 · 获赞 44 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/yiranfeng/article/details/105210069
今日推荐