Binder 服务注册进程情形剖析之C言语篇

本文基于 Android 10 源码环境。

示例程序思路参阅自韦东山老师开源代码,在此基础上针对 Android10 渠道重写调试适配。

在Binder 示例程序之 C 言语篇中咱们介绍了运用 C 言语完结的最简略的 Binder 示例程序,接下来咱们开端详细剖析这个示例中服务注册的进程。

服务的注册进程分为以下几个步骤:

  • 启动 ServiceManager
  • Binder Server 建议服务注册恳求
  • ServiceManager 收到服务注册恳求,完结服务注册,发送应对消息给 Server
  • Server 收到并处理应对数据

1. Binder 服务注册进程第一阶段——ServiceManager 启动

ServerManager由体系完结,对应的代码在:frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char** argv)
{
    struct binder_state *bs;
    char *driver;
    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }
    //重视点1,初始化 binder 驱动
    bs = binder_open(driver, 128*1024);
    if (!bs) {
        //省掉 VENDORSERVICEMANAGER 相关代码 ......
        ALOGE("failed to open binder driver %s\n", driver);
        return -1;
    }
    //重视点2, 注册当时进程为 context_manager
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    // 省掉 selinux 相关代码 ......
    //重视点3
    //进入循环,等候长途调用
    //svcmgr_handler 是一个函数指针,是长途调用的回调函数
    binder_loop(bs, svcmgr_handler);
    return 0;
}
  • ServiceManager 调用 open() 函数,翻开 Binder 驱动,而后调用 mmap() 完结内存的申请与映射,这一进程封装为 binder_open 函数
  • 经过 ioctl 将当时线程注册为 contextmanager,这一进程封装为 binder_become_context_manager 函数
  • 进入循环,调用 ioctl 函数等候承受数据,并进入待机状态,这一进程封装为 binder_loop 函数

全体作业流程如下图所示:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

重视点1调用 binder_open 完结 Binder 驱动初始化。

binder_open 的运用方法如下:

bs = binder_open("/dev/binder", 128*1024);

binder_open 的完结如下:

// driver 通常是 "/dev/binder"
// mapsize 是需求 mmap 的内存的巨细
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs; //用于存需求回来的值
    struct binder_version vers; 
    bs = malloc(sizeof(*bs)); 
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }
    //翻开 /dev/binder,拿到内核回来的句柄
    bs->fd = open(driver, O_RDWR | O_CLOEXEC); 
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }
    //查询版别
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        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;
    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;
}

其间 struct binder_state *bs 结构如下:

struct binder_state
{
    int fd;
    void *mapped;
    size_t mapsize;
};

用于保存 binder_open 的回来成果。

binder_open 的作业比较简略,分为以下几步:

  1. 经过体系调用 open() 来翻开 /dev/binder,获得一个文件句柄信息。
  2. 经过 ioctl 获取 binder 的版别信息,比较 binder 协议版别是否相同,不同则跳出。
  3. 经过 mmap 内存映射 128K 的内存空间,即把 binder 驱动文件的 128K 字节映射到了内存空间。

许多面试喜爱问 binder 数据传输巨细的约束,答案就在 binder mmap 函数的第二个参数,关于 ServiceManager 来说约束便是 128k。

重视点2调用 binder_become_context_manager 函数,将当时进程注册为整个体系中仅有的上下文办理器。

binder_become_context_manager 将当时进程注册为整个体系中仅有的 ServiceManager。详细完结如下:

int binder_become_context_manager(struct binder_state *bs)
{
    //构建需求发送的数据 flat_binder_object
    struct flat_binder_object obj;
    memset(&obj, 0, sizeof(obj));
    obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
    //向 Binder 驱动发送数据
    int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);
    //假如失,运用原始方法再次调用 ioctl
    // fallback to original method
    if (result != 0) {
        android_errorWriteLog(0x534e4554, "121035042");
        result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
    }
    return result;
}

流程如下:

  • 构建需求发送的数据 flat_binder_object
  • 经过 ioctl 将结构好的数据发送给 Binder 驱动,运用的协议是 BINDER_SET_CONTEXT_MGR_EXT
  • 假如失利,运用原始方法再次调用 ioctl

重视点3 调用 binder_loop 进入循环,等候长途调用。

binder_loop 的完结如下:

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    //ioctl 读写数据类型
    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;
    //ioctl 的根本封装
    binder_write(bs, readbuf, sizeof(uint32_t));
    for (;;) {
        //结合上面 bwr 的赋值,这儿是要读数据
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //向驱动建议读操作
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        //解析收到的数据,func 是解析好数据后的回调函数
        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;
        }
    }
}

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

  1. 调用 ioctl + BC_ENTER_LOOPER 奉告驱动,应用进程行将进入循环
  2. 进入 for 循环,ioctl + BINDER_WRITE_READ 向驱动读取数据,读到的数据格局为 binder_write_read
  3. 调用 binder_parse 会解析 binder_write_read,并将其转换为 binder_transaction_data 和 binder_io
  4. 调用 func 函数指针,并将解析好的数据 binder_transaction_data 和 binder_io 传给 func 函数
  5. 进入下一个循环周期,持续从驱动读取数据

进程调用 binder_loop 后,会进入读取数据,解析数据的循环。这样 Binder Server 才能一向运转下去。

ServiceManager中,binder_loop 的调用方法如下:

binder_loop(bs, svcmgr_handler);

其间 svcmgr_handler 是一个函数指针,是收到长途调用后的回调,其完结如下:

//txn_secctx msg 是调用方发来的数据
//reply 是回来给调用方的数据
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    //......
}

调用 binder_loop 后,进程收到的 binder 数据格局为 binder_write_read,将数据解析为 binder_transaction_data_secctx binder_io 后就会调用 binder_loop 传入的函数指针,并传入解析好的数据。

接下来咱们先剖析 binder_loop 涉及到的数据结构:

binder_io 能够理解为一个数据集合,数据发送端将数据依照一定的次序写入集合,数据承受端依照相同的次序读取数据。

binder_io 的运用方法如下:

数据发送端:

unsigned iodata[512/4];
struct binder_io msg;
/* 结构binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0);  // strict mode header
bio_put_string16_x(&msg, "IHelloService");
/* 放入参数 */
bio_put_string16_x(&msg, name);

数据接纳端:

strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);  //"IHelloService"
s = bio_get_string16(msg, &len);  // name

接下来咱们看看 binder_io 的详细完结:

struct binder_io
{
    char *data;            /* pointer to read/write from */
    binder_size_t *offs;   /* array of offsets */
    size_t data_avail;     /* bytes available in data buffer */
    size_t offs_avail;     /* entries available in offsets array */
    char *data0;           /* start of data buffer */
    binder_size_t *offs0;  /* start of offsets buffer */
    uint32_t flags;
    uint32_t unused;
};

binder_io 的初始化进程:

//初始化进程
unsigned iodata[512/4];
struct binder_io msg;
//初始化 binder_io 
bio_init(&msg, iodata, sizeof(iodata), 4);
void bio_init(struct binder_io *bio, void *data,
              size_t maxdata, size_t maxoffs)
{
    size_t n = maxoffs * sizeof(size_t);
    //溢出处理
    if (n > maxdata) {
        bio->flags = BIO_F_OVERFLOW;
        bio->data_avail = 0;
        bio->offs_avail = 0;
        return;
    }
    //将 bio 一分为二
    bio->data = bio->data0 = (char *) data + n;
    bio->offs = bio->offs0 = data;
    bio->data_avail = maxdata - n;
    bio->offs_avail = maxoffs;
    bio->flags = 0;
}

从 binder_io 的界说和初始化进程中能够看出,binder_io 用于办理一块内存,一起将内存分为了两部分办理:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

为方便叙述,本文称这两个区为偏移区和数据区。

maxdata 是这块内存总的字节数,偏移区的巨细为 n 字节,其间 n = maxoffs * sizeof(size_t),数据区的巨细为 maxdata – n

两块内存怎么运用?

接下来咱们看看怎么将一个 unit32_t 数据存入 binder_io:

void bio_put_uint32(struct binder_io *bio, uint32_t n)
{
    //分配内存
    uint32_t *ptr = bio_alloc(bio, sizeof(n));
    if (ptr)
        *ptr = n;
}
//在 binder_io 的第二部分分配 size 巨细的内存
static void *bio_alloc(struct binder_io *bio, size_t size)
{   
    //size 终究等于 4,8,12,16,20 ......
    //size 的值比原始的值大
    size = (size + 3) & (~3);
    //溢出操作
    if (size > bio->data_avail) {
        bio->flags |= BIO_F_OVERFLOW;
        return NULL;
    } else {
        //分配方位
        void *ptr = bio->data;
        bio->data += size;
        bio->data_avail -= size;
        return ptr;
    }
}

写入一个 32 位整数的进程如下:

  • 在数据区分配 4 字节倍数的数据
  • 将整数值写入已分配的内存

写入后一个 uint32_t n 后,内存结构如下图所示:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

字符串的写入略微复杂一点,但是本质原理和写入 unit_32 相同,有爱好的同学能够自行剖析下面的代码:

void bio_put_string16_x(struct binder_io *bio, const char *_str)
{
    unsigned char *str = (unsigned char*) _str;
    size_t len;
    uint16_t *ptr;
    if (!str) {
        bio_put_uint32(bio, 0xffffffff);
        return;
    }
    len = strlen(_str);
    if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
        bio_put_uint32(bio, 0xffffffff);
        return;
    }
    /* Note: The payload will carry 32bit size instead of size_t */
    bio_put_uint32(bio, len);
    ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t));
    if (!ptr)
        return;
    while (*str)
        *ptr++ = *str++;
    *ptr++ = 0;
}

接下来咱们看看,怎么在 binder_io 中存一个指针数据。(在服务注册场景下,这个指针指向一个函数,是 server 端收到调用后的回调函数)

void bio_put_obj(struct binder_io *bio, void *ptr)
{
    struct flat_binder_object *obj;
    //分配内存
    obj = bio_alloc_obj(bio);
    if (!obj)
        return;
    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj->hdr.type = BINDER_TYPE_BINDER;
    //ptr 保存在 flat_binder_object 的 binder 成员中
    obj->binder = (uintptr_t)ptr;
    obj->cookie = 0;
}
static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio)
{
    struct flat_binder_object *obj;
    //在数据区分配内存
    obj = bio_alloc(bio, sizeof(*obj));
    //在第一部分保存偏移量
    if (obj && bio->offs_avail) {
        bio->offs_avail--;
        *bio->offs++ = ((char*) obj) - ((char*) bio->data0);
        return obj;
    }
    bio->flags |= BIO_F_OVERFLOW;
    return NULL;
}
struct flat_binder_object {
	struct binder_object_header	hdr;
	__u32				flags;
	/* 8 bytes of data. */
	union {
		binder_uintptr_t	binder;	/* local object */
		__u32			handle;	/* remote object */
	};
	/* extra data associated with local object */
	binder_uintptr_t	cookie;
};
struct binder_object_header {
	__u32        type;
};

关于指针类型,会在数据区分配一个 flat_binder_object 结构体的数据,将指针数据保存在结构体的 binder 成员中。在偏移区将 flat_binder_object 相对 data0 的偏移值保存在 offs 指向的内存,offs 再加 1。完结数据保存后,其内存结构如下图所示:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

这儿给出一个 binder_io 结构示例,以加深理解:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

binder_transaction_data_secctx 是一个简略的封装,咱们需求的数据大部分都保存在 binder_transaction_data 中:

struct binder_transaction_data_secctx {
	struct binder_transaction_data transaction_data;
	binder_uintptr_t secctx;
};
struct binder_transaction_data {
	union {
		__u32	handle;
		binder_uintptr_t ptr;
	} target;
	binder_uintptr_t	cookie;
	__u32		code;
	__u32	        flags;
	pid_t		sender_pid;
	uid_t		sender_euid;
	binder_size_t	data_size;
	binder_size_t	offsets_size;
	union {
		struct {
			binder_uintptr_t	buffer;
			binder_uintptr_t	offsets;
		} ptr;
		__u8	buf[8];
	} data;
};

接下来再回头来看 binder_loop 详细完结:

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    //ioctl 读写数据类型
    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;
    //ioctl 的根本封装
    binder_write(bs, readbuf, sizeof(uint32_t));
    for (;;) {
        //结合上面 bwr 的赋值,这儿是要读数据
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //向驱动建议读操作
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
      //省掉后续代码......
    }
}
//binder_write 是对 ioctl + BINDER_WRITE_READ 写操作的简略封装
int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    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(errno));
    }
    return res;
}

binder_loop 调用 ioctl 读取数据,当时线程堵塞在此处直到有 ioctl 收到服务相关的长途调用恳求。

2. Binder 服务注册进程第二阶段——Server 端建议服务注册恳求

Server 端建议服务注册恳求的流程如下图所示:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

Server 端建议服务注册恳求的代码:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
    uint32_t handle;
	int ret;
    //翻开驱动
    bs = binder_open("/dev/binder", 128*1024);
    if (!bs) {
        fprintf(stderr, "failed to open binder driver\n");
        return -1;
    }
	//增加服务
	ret = svcmgr_publish(bs, svcmgr, "hello", hello_service_handler);
    if (ret) {
        fprintf(stderr, "failed to publish hello service\n");
        return -1;
    }
    binder_loop(bs, test_server_handler);
    return 0;
}

整个流程分为一下几步;

  • binder_open 初始化
  • svcmgr_publish 注册函数,需求供给一个参数 hello_service_handler,也便是咱们自己写的回调函数
  • 调用 binder_loop 进入循环,等候长途调用

binder_open 和 binder_loop 在上一节已做剖析,这儿咱们接着剖析 svcmgr_publish 的完结:

int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
    int status;
    unsigned iodata[512/4];
    struct binder_io msg, reply;
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_uint32(&msg, 0);
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    bio_put_obj(&msg, ptr);
    bio_put_uint32(&msg, 0);
    bio_put_uint32(&msg, 0);
    //经过 binder_call 建议长途函数调用
    if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)) {
        //fprintf(stderr, "svcmgr_public 长途调用失利\n");
        return -1;
    }
    //解析回来值
    status = bio_get_uint32(&reply); //调用成功回来0
    //长途调用完毕,告诉驱动整理内存
    binder_done(bs, &msg, &reply);
    return status;
}

svcmgr_publish 作业流程如下:

  • 经过 binder_io 结构需求发送的数据 msg
  • 经过 binder_call 建议长途调用
  • 解析回复的数据,经过 binder_done 函数告诉驱动通讯完结,整理内存

其间心功用经过 binder_call 完结,binder_call 用于建议长途进程调用,其调用进程如下:


//调用进程
// bs 是 binder_open 的回来值
// msg 是上面代码结构的 binder_io 结构体
// reply 是被调用方的回来数据
// target 是一个整型变量,用于指定要访问哪个进程
// SVC_MGR_ADD_SERVICE 表明咱们要调用长途进程的 addservice 函数
binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)
//函数结构
int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    //......
}

接下来咱们来剖析 binder_call 的详细完结:

//binder_call 中的结构进程
int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    int res;
    //重视点1 binder_io *msg 转为 binder_write_read
    //声明数据
    struct binder_write_read bwr;
    //binder_write_read 内部成员 write_buffer 的结构
    struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    unsigned readbuf[32];
    //...... 省掉非中心代码
    //结构 binder_write_read 内部结构 writebuf
    // BC_TRANSACTION 表明当时数据是用于建议长途调用
    writebuf.cmd = BC_TRANSACTION;
    // target 用于找到长途进程,即咱们要调用哪个进程的函数
    writebuf.txn.target.handle = target;
    // code 表明调用哪个函数
    writebuf.txn.code = code;
    writebuf.txn.flags = 0;
    writebuf.txn.data_size = msg->data - msg->data0;
    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
    //data 是数据区,指向一个 binder_io 结构体
    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
    //给 write 相关变量赋值
    //表明当时进程是写入数据,即发送数据
    bwr.write_size = sizeof(writebuf);
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) &writebuf;
    hexdump(msg->data0, msg->data - msg->data0);
    for (;;) {
        //重视点2 写的一起也要读数据
        //给 read 相关变量赋值
        //一起,咱们也要读取回来的成果值
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //重视点3 建议读写操作
        //发送 binder_write_read 数据
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
            goto fail;
        }
        //省掉部分代码 ......
        //重视点4 解析收到的数据 readbuf
        // res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
        // if (res == 0) return 0;
        // if (res < 0) goto fail;
    }
fail:
    memset(reply, 0, sizeof(*reply));
    reply->flags |= BIO_F_IOERROR;
    return -1;
}
//binder_write_read 结构体界说如下
struct binder_write_read {
	binder_size_t		write_size;	/* bytes to write */
	binder_size_t		write_consumed;	/* bytes consumed by driver */
	binder_uintptr_t	write_buffer;
	binder_size_t		read_size;	/* bytes to read */
	binder_size_t		read_consumed;	/* bytes consumed by driver */
	binder_uintptr_t	read_buffer;
};
struct binder_transaction_data {
	union {
		__u32	handle;
		binder_uintptr_t ptr;
	} target;
	binder_uintptr_t	cookie;
	__u32		code;
	__u32	        flags;
	pid_t		sender_pid;
	uid_t		sender_euid;
	binder_size_t	data_size;
	binder_size_t	offsets_size;
	union {
		struct {
			binder_uintptr_t	buffer;
			binder_uintptr_t	offsets;
		} ptr;
		__u8	buf[8];
	} data;
};

binder_write_read 是应用程序与驱动相互传递的数据,由两部分组成:

  • write 部分:发送给驱动的数据
    • write_buffer:指针,指向一个 writebuf 结构体
    • write_size:指针指向数据的巨细
    • write_consumed:指针指向的数据已运用区域的数据巨细
  • read 部分:用于承受长途调用的回来值
    • read_buffer:指针,指向一段内存,用于接纳数据
    • read_size:指针指向数据的巨细
    • read_consumed:指针指向的数据已运用区域的数据巨细

重视点 1 处,主要是运用 binder_io 构建 binder_write_read 的 write_buffer 部分:

//声明
struct {
    uint32_t cmd;
    struct binder_transaction_data txn;
} __attribute__((packed)) writebuf;
//结构 writebuf
writebuf.cmd = BC_TRANSACTION;
writebuf.txn.target.handle = target;
writebuf.txn.code = code;
writebuf.txn.flags = 0;
writebuf.txn.data_size = msg->data - msg->data0;
writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
bwr.write_size = sizeof(writebuf);
bwr.write_consumed = 0;
//赋值
bwr.write_buffer = (uintptr_t) &writebuf;

writebuffer 的结构如下图所示:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

重视点 2 处,主要给 read_buffer 赋值,表明写的一起也要读取数据即等候回来值:

unsigned readbuf[32];
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;

重视点 3 处,建议写操作,程序进入堵塞状态,直到收到长途调用的回来数据:

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

3. Binder 服务注册进程第三阶段—— ServiceManager 收到服务注册恳求

ServiceManager 收到 Server 发送的数据,并从第一阶段的堵塞休眠中唤醒过来:

流程如下图所示:

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

详细代码如下:

binder.c 中 binder_loop 源码:

void binder_loop(struct binder_state *bs, binder_handler func)
{
    //......
    bwr.read_size = sizeof(readbuf);
    bwr.read_consumed = 0;
    bwr.read_buffer = (uintptr_t) readbuf;
    //从这儿唤醒,收到的数据保存在 bwr 变量中
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    //解析收到的数据,回调 func 方法,func 传入的是 svcmgr_handler
    res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
    //......
}

收到的数据 readbuf 的格局如下:

struct {
    uint32_t cmd;
    struct binder_transaction_data txn;
    //可能存在多个数据组
    uint32_t cmd;
    struct binder_transaction_data txn;
    uint32_t cmd;
    struct binder_transaction_data txn;
    //.......
}

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

接下来咱们看看数据的解析进程:

binder.c 中 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) {
        //解分出 readbuf 中的 cmd
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        switch(cmd) {
        //...... 省掉非相关代码
        // cmd == BR_TRANSACTION
        case BR_TRANSACTION_SEC_CTX:
        case BR_TRANSACTION: {
            struct binder_transaction_data_secctx txn;
            if (cmd == BR_TRANSACTION_SEC_CTX) {
                //...... 省掉非相关代码
            } else /* BR_TRANSACTION */ {
                if ((end - ptr) < sizeof(struct binder_transaction_data)) {
                    ALOGE("parse: txn too small (binder_transaction_data)!\n");
                    return -1;
                }
                //将 readbuf 中的数据解释为 binder_transaction_data 结构体
                memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
                ptr += sizeof(struct binder_transaction_data);
                txn.secctx = 0;
            }
            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);
                //将 binder_transaction_data 数据中的 data 数据区解析到 binder_io msg 中
                bio_init_from_txn(&msg, &txn.transaction_data);
                //调用回调函数,即 svcmgr_handler 函数
                res = func(bs, &txn, &msg, &reply);
                if (txn.transaction_data.flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
                } else { //走这儿
                    //将成果发送给 server 进程
                    binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
                }
            }
            break;
        }
        // ...... 省掉非相关代码
        }
    }
    return r;
}

总结一下便是解分出 binder_transaction_data binder_io, 然后调用传入的回调函数 svcmgr_handler

接下来剖析回调函数 svcmgr_handler 的完结:

// bs 是 binder_open 中构建的结构体
//txn_secctx 和 msg 是收到的数据
//reply 用于servicemanager 向 server 回复数据
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_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 中第一个数据一般是一个 32 位的 0
    strict_policy = bio_get_uint32(msg);
    // msg 中的第二个数据 ,一般也是 32 位的 0
    bio_get_uint32(msg); 
    // msg 中的第三个数据,一个字符串,正常情况是 android.os.IServiceManager
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }
    // svcmgr_id 是一个字符数组,内容是 android.os.IServiceManager
    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 相关代码 省掉 ......
    //code 代表需求调用哪个函数
    switch(txn->code) {
    //获取服务
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
       //...... 省掉无关代码
    //增加服务
    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;
    // list 已注册的服务
    case SVC_MGR_LIST_SERVICES: {
       // ...... 省掉无关代码 
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }
    //写入回来值
    bio_put_uint32(reply, 0);
    return 0;
}

上面的代码从 binder_transaction_data_secctx 中获取到了code,code 代表了 server 端要执行的操作,在当时服务注册情形中,code 的值为 SVC_MGR_ADD_SERVICE,表明增加服务。

接下来咱们来剖析一下增加服务相关的代码:

    //增加服务
    case SVC_MGR_ADD_SERVICE:
        //获取到服务的姓名
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //获取到服务在内核中的句柄 handle
        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;

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, uint32_t dumpsys_priority, pid_t spid, const char* sid) {
    struct svcinfo *si;
    if (!handle || (len == 0) || (len > 127))
        return -1;
    //一些权限判别,暂时能够不论
    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;
    }
    //源码中界说了一个单向链表用保存 service
    // 链表的节点是 svcinfo,是 servicemanager 对一个服务的描绘或表明,链表头是 svclist,是一个全局变量
    //这儿从链表中查找服务,这儿是注册服务,链表中没有当时服务,查找到的值是 null
    si = find_svc(s, len); // si 为 null
    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);
        }
        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;
        }
        // 保存数据,并加入链表
        si->handle = 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 = si;
    }
    //binder_ref强引证加1操作,留到内核部分解说,这儿暂时不论
    binder_acquire(bs, handle);
    //注册死亡告诉,留到内核部分解说,这儿暂时不论
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

总结一下便是:

  • 构建 svcinfo 链表节点
  • 根据收到的数据,给 svcinfo 赋值
  • 将 svcinfo 增加到 svclist 链表中

svcinfo 是 servicemanager 中对一个服务的描绘,其间最重要的数据是 handle,用于标识一个 service,其值由驱动确定,并发送给了 servicemanger,保存在 svclist 链表中。

4. ServiceManager 奉告 Server 服务注册完毕,Server 收到应对数据

在 svcmgr_handler 最后部分,写入回来数据:

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    //......
    //写入回来数据
    bio_put_uint32(reply, 0);
    return 0;
}

在 binder_parse 中调用 binder_send_relpy 发送回来的数据。

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    //两块数据,不同的 cmd 关于不同的数据格局
    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;
    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 {
        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_write(bs, &data, sizeof(data));
}

流程根本和 server 注册服务一向,仅仅这儿有两块数据,一起发送给 binder 驱动。第一块数据由驱动处理,第二块数据会回来给 Server 端,Server 端从堵塞中康复:

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
        //省掉部分代码 ......
        //Server 从堵塞中康复,一起收到数据
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
            goto fail;
        }
        //解析收到的数据
        res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
        if (res == 0) return 0;
        if (res < 0) goto fail;
    //......
}

解析回来数据:

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);
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        //...... 省掉部分代码
        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 中,即上一层传入的 reply
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                /* todo FREE BUFFER */
            }
            ptr += sizeof(*txn);
            r = 0;
            break;
        }
        //省掉部分代码 ......
        }
    }
    return r;
}

binder_parse 回来 0,收到的数据保存在 reply 中:

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
        //binder_parse 回来
        res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
        if (res == 0) return 0; // 回来值是 0
        if (res < 0) goto fail;
    //......
}

binder_call 回来到 svcmgr_publish

int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
    //......
    //binder_call 回来 0
    if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)) {
        //fprintf(stderr, "svcmgr_public 长途调用失利\n");
        return -1;
    }
    //解析回来值
    //解分出的值是 0 表明调用成功
    status = bio_get_uint32(&reply); //调用成功回来0
    //长途调用完毕,告诉驱动整理内存
    binder_done(bs, &msg, &reply);
    return status; //回来 0 给 main 主程序,调用成功
}

最后调用 binder_done 告诉驱动整理内存:

void binder_done(struct binder_state *bs,
                 __unused struct binder_io *msg,
                 struct binder_io *reply)
{
    struct {
        uint32_t cmd;
        uintptr_t buffer;
    } __attribute__((packed)) data;
    if (reply->flags & BIO_F_SHARED) {
        data.cmd = BC_FREE_BUFFER;
        data.buffer = (uintptr_t) reply->data0;
        binder_write(bs, &data, sizeof(data));
        reply->flags = 0;
    }
}

到这儿,服务的注册进程就剖析完了,后边的文章咱们会持续剖析服务的获取和调用进程。

参阅资料

  • 韦东山 Android 体系教程

  • 《Android 框架解密》

  • 《Android 源代码情形剖析》

  • 《深化理解 Android 》体系图书

  • Android Binder 魅族团队

  • IT先森 Binder 系列博客

  • 芦半山 Binder 剖析文章

  • Binder中的SEAndroid控制

  • 快乐安卓 Android Binder通讯

  • Binder Driver缺点导致定屏的实战剖析

  • Binder | 代理对象的泄露及其检测

关于

假如你对 Android Framework 感爱好,能够持续重视:

  • 渠道个人技术博客
  • 我的个人公众号

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇

  • 我的个人微信(能够加我微信进 Android Framework 沟通群)

写给应用开发的 Android Framework 教程——学穿 Binder 篇之 Binder 服务注册过程情景分析之C语言篇