如何应对Android面试官->进程通信,启动与获取SM服务

前言


image.png

本章主要围绕 ServiceManager 进行讲解,通过注册、启动等流程,玩转 ServiceManager;

ServiceManager 是什么?


客户端想要调用系统的 AMS 或者 PMS,但是客户端直接调用不到,只能获取到 AMS-IBinder 对象,AMS 在启动的时候就会把 AMS-IBinder 对象注册到 ServiceManager 中,客户端只要找到 ServiceManager 之后,就可以把 AMS-IBinder 对象返回给客户端,客户端就可以通过 AMS-IBinder 同 AMS 进行通信;

ServiceManager 它也是一个服务,但是它的句柄是确认的,也就是 handle = 0,而我们客户端在调用系统服务的时候,只需要调用这个已知道的 handle = 0 的服务就可以获取到 ServiceManager 的代理对象,然后就可以 同其他系统服务通信了;

ServiceManager 服务是如何注册的?


那么 ServiceManager 是怎么注册的呢?我们进入源码看一下,它的注册就在 init.rc 中:

// 打开 ServiceManager 这个程序,就会 调用 ServiceManager 的 main 方法
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

打开 ServiceManager 这个程序,就会 调用 servicemanager.c 的 main 方法,我们进入 main 方法看下:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    // 这个方法做了两件事情,一是 open 方法,打开 binder 驱动,二是 mmap 方法,建立映射关系
    bs = binder_open(128*1024);
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }
    // 把 ServiceManager 设置成服务大管家
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    selinux_enabled = is_selinux_enabled();
    sehandle = selinux_android_service_context_handle();
    selinux_status_open(true);

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

        if (getcon(&service_manager_context) != 0) {
            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
            abort();
        }
    }

    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    // 循环监听有没有其他服务访问
    binder_loop(bs, svcmgr_handler);

    return 0;
}

binder_open 这个方法做了两件事情,一是 open(128*1024) 方法,打开 binder 驱动,二是 mmap 方法,建立映射关系;

binder_become_context_manager 把 ServiceManager 设置成服务大管家;

binder_loop 循环监听有没有其他服务访问;

我们进入 binder_become_context_manager 这个方法看下,它是如何成为大管家的

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

直接调用的 ioctl 方法,传入的参数是 BINDER_SET_CONTEXT_MGR,而 这个方法最终调用到 binder_ioctl 中,我们进入 BINDER_SET_CONTEXT_MGR 这个 case 看下:

case BINDER_SET_CONTEXT_MGR:
    ret = binder_ioctl_set_ctx_mgr(filp);
    if (ret)
	goto err;
    break;

这个 case 中调用的是 binder_ioctl_set_ctx_mgr,我们进入这个方法看下:

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	struct binder_context *context = proc->context;

	kuid_t curr_euid = current_euid();
   // 先判断是不是已经存在了,存在则直接返回
	if (context->binder_context_mgr_node) {
		pr_err("BINDER_SET_CONTEXT_MGR already set\n");
		ret = -EBUSY;
		goto out;
	}
	ret = security_binder_set_context_mgr(proc->tsk);
	if (ret < 0)
		goto out;
   // 判断 sm 的 uid 是否是有效的,第一次肯定是无效的,所以直接进入 else
	if (uid_valid(context->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 = curr_euid;
	}
        // 通过 binder_new_node 创建 SM 的 binder_context_mgr_node 结构体
	context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
	if (!context->binder_context_mgr_node) {
		ret = -ENOMEM;
		goto out;
	}
	context->binder_cont
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值