其他分享
首页 > 其他分享> > Android10.0 Binder通信原理(三)-ServiceManager篇

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安装流程

《日志系统篇》

  1. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
  2. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
  4. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入门篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++实例分析

 

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的保护分析》 

 

标签:txn,Android10.0,handle,struct,bio,binder,Binder,ServiceManager,data
来源: https://blog.csdn.net/yiranfeng/article/details/105210069