这个binder面试题你会吗?-学员作业

背景:

上一篇文章给大家布置了一个binder面试题相关的作业,具体看如下文章:
这个binder面试题你会吗?-学员作业

很多同学也对这个callingpid为0的情况感觉到很奇怪,也不太清楚为啥。
在这里插入图片描述

最后的作业要求一共就3个部分:

1、剖析出uid和pid是如何在kernel中被binder驱动进行设置的

2、解释什么场景调用pid就会设置为0

3、解释清楚为啥同样的captureDisplay接口,aosp14和aosp16会有差异。

下面本文将对上面3个疑问进行挨个剖析讲解清楚。
在这里插入图片描述

CallingUid,Pid的数据来源分析

先看看场景调用getCallingPid,getCallingUid对应的值:

pid_t IPCThreadState::getCallingPid() const
{
    checkContextIsBinderForUse(__func__);
    return mCallingPid;
}

uid_t IPCThreadState::getCallingUid() const
{
    checkContextIsBinderForUse(__func__);
    return mCallingUid;
}

那么这里的mCallingPid,mCallingUid是在哪里进行的赋值呢?

这块经过寻找发现是在executeCommand方法中BR_TRANSACTION时候进行的赋值。


status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;

    switch ((uint32_t)cmd) {
//省略部分

    case BR_TRANSACTION_SEC_CTX:
    case BR_TRANSACTION:
        {
            binder_transaction_data_secctx tr_secctx;
            binder_transaction_data& tr = tr_secctx.transaction_data;

            if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
                result = mIn.read(&tr_secctx, sizeof(tr_secctx));
            } else {
                result = mIn.read(&tr, sizeof(tr));
                tr_secctx.secctx = 0;
            }

            mCallingPid = tr.sender_pid;
            mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
            mCallingUid = tr.sender_euid;

可以看到这里的赋值来源就是binder驱动中传递过来的sender_pid,sender_euid。

kernel中分析CallingUId和Pid的设置

通过对比发现无论aosp14还是aosp16在kernel这块对sender_pid和sender_euid的设置代码都是一样的,所以对于kernel这块都是使用一份代码就可以。

发起跨进程调用会执行到binder_transaction方法中,这里会创建对应的binder_transaction,而且会给binder_transaction进行相关的赋值,然后到目标进程的binder_thread_read进行使用

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply,
			       binder_size_t extra_buffers_size)
{
//省略部分
//可以看到这里会对flag进行判断,如果不是ONE_WAY调用那么就会赋值thread,如果ONE_WAY那就赋值NULL
	if (!reply && !(tr->flags & TF_ONE_WAY))
		t->from = thread;
	else
		t->from = NULL;
	t->from_pid = proc->pid;
	t->from_tid = thread->pid;
	//这里直接赋值调用进程的uid
	t->sender_euid = task_euid(proc->tsk);

在目标进程binder_thread_read方法中,会读取上面的对sender_pid和sender_euid进行设置,返回到app层面

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
//省略部分
		trd->flags = t->flags;
		trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);

		t_from = binder_get_txn_from(t);
		if (t_from) {
			struct task_struct *sender = t_from->proc->tsk;

			trd->sender_pid =
				task_tgid_nr_ns(sender,
						task_active_pid_ns(current));
			trace_android_vh_sync_txn_recvd(thread->task, t_from->task);
		} else {
			trd->sender_pid = 0;
		}

通过查看binder驱动中源码,得出以下结论:

1、sender_euid的值就是发起binder调用的进程对于uid值

2、对于sender_pid这块需要考虑2种情况。
a、binder调用是oneway(异步调用)情况那就是pid = 0
b、如果调用是同步的,非oneway情况,那么pid就是原来进程的pid值。

那么到此就回答了昨天面试题作业的前2问,剩下最后一问:

解释清楚为啥同样的captureDisplay接口,aosp14和aosp16会有差异。

那么下面重点来分析这块14,16的差异。

aosp16与aosp14上captureLayers接口对比

首先从binder驱动部分源码可以看出,影响pid最核心的参数就是传递进来的flag
drivers/android/binder.c
在这里插入图片描述这个flag本质是app层面在IPCThreadStated的transact调用时候进行传递的。
在这里插入图片描述可以看到这里还有log进行打印,但是一般默认这个log不开放,这里需要去修改开放:

在这里插入图片描述
然后在日志调查:

aosp16打印发现属于同步跨进程调用

12-25 11:40:03.197   487   719 D WindowManager: captureDisplay
12-25 11:40:03.197   487   719 D ipc     : >>>> SEND from pid 487 uid 1000 READ REPLY

aosp16打印发现属于oneway这种异步跨进程调用

12-24 11:40:47.892   751  1135 D WindowManager: captureDisplay 10150 pid 0
12-24 11:40:47.892   751  1135 D ipc     : >>>> SEND from pid 751 uid 1000 ONE WAY

也可以抓一下2个版本对于的Perfetto进行分析

aosp14上captureLayers是同步跨进程调用方式:
在这里插入图片描述
aosp16上captureLayers是异步跨进程调用方式,也就是oneway方式:

在这里插入图片描述
最后看看aidl文件:
aosp14这个captureLayers接口:


    /**
     * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
     * This requires READ_FRAME_BUFFER permission. This function will fail if there
     * is a secure window on screen
     */
    void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);

aosp16这个captureLayers接口确实oneway:

frameworks/native/libs/gui/aidl/android/gui/ISurfaceComposer.aidl


    /**
     * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
     * This requires READ_FRAME_BUFFER permission. This function will fail if there
     * is a secure window on screen
     */
    oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);


到此你应该可以把这个一波三折的binder面试题得到满分了吧。

原文地址:
https://mp.weixin.qq.com/s/6iiNNzq_37g_wS8dNOqRzw

更多framework实战开发干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值