摘要:本节主要来讲解Android10.0 Binder中守护进程ServiceManager是如何启动、注册、获取服务
阅读本文大约需要花费35分钟。
文章首发微信公众号:IngresGe
专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!
[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析
[Android取经之路] 系列文章:
《系统启动篇》
- Android系统架构
- Android是怎么启动的
- Android 10.0系统启动之init进程
- Android10.0系统启动之Zygote进程
- Android 10.0 系统启动之SystemServer进程
- Android 10.0 系统服务之ActivityMnagerService
- Android10.0系统启动之Launcher(桌面)启动流程
- Android10.0应用进程创建过程以及Zygote的fork流程
- Android 10.0 PackageManagerService(一)工作原理及启动流程
- Android 10.0 PackageManagerService(二)权限扫描
- Android 10.0 PackageManagerService(三)APK扫描
- Android 10.0 PackageManagerService(四)APK安装流程
《日志系统篇》
- Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
- Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
- Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
- Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现
《Binder通信原理》
- Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
- Android10.0 Binder通信原理(二)-Binder入门篇
- Android10.0 Binder通信原理(三)-ServiceManager篇
- 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流程想要进行服务注册。
步骤:
- 从binder_io msg中获取服务名称和长度
- 从binder_io msg 中获取handle
- 检查该服务是否有注册的selinx权限
- 查询服务列表svclist 是否存在该handle,如果有handle,就更新该服务的handle信息,通过这个handle我们最终就能找到远端的service实体
- 如果svclist不存在该服务,申请一个svcinfo的空间,把服务名、长度、handle等信息存入其中
- 把svcinfo 加入svclist的链表中
- 再以BC_ACQUIRE命令,handle为目标的信息,通过ioctl发送给binder驱动
- 最后以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流程想要进行服务获取。
步骤:
- 获取服务的名字s和长度len
- 从svclist中根据名字查到一个svcinfo的结构
- 进行selinx检查
- 检查通过,返回该服务的handle
- 把handle放入binder_io 的reply的data中
- 最后在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的整体流程如下:
- 打开设备,mmap一个128K的内存空间,进行用户空间和内核空间的内存绑定
- 注册成为上下文的管理者--守护进程
- 陷入死循环,标注线程的looper状态为enter
- 不停的操作binder读写过程
- 解析binder数据,进行查询服务、注册服务、列出服务的核心操作
- 由于在进行服务注册的时候,把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
参考:
《浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路》
《SEAndroid安全机制对Binder IPC的保护分析》