Android Binder框架实现之Native层addService详解之请求的反馈

Android Binder框架实现之Native层addService详解之请求的反馈


Android Binder框架实现目录:

Android Binder框架实现之Binder的设计思想
Android Binder框架实现之何为匿名/实名Binder
Android Binder框架实现之Binder中的数据结构
Android Binder框架实现之Binder相关的接口和类
Android Binder框架实现之Parcel详解之基本数据的读写
Android Binder框架实现之Parcel read/writeStrongBinder实现
Android Binder框架实现之servicemanager守护进程
Android Binder框架实现之defaultServiceManager()的实现
Android Binder框架实现之Native层addService详解之请求的发送
Android Binder框架实现之Native层addService详解之请求的处理
Android Binder框架实现之Native层addService详解之请求的反馈
Android Binder框架实现之Binder服务的消息循环
Android Binder框架实现之Native层getService详解之请求的发送
Android Binder框架实现之Native层getService详解之请求的处理
Android Binder框架实现之Native层getService详解之请求的反馈
Android Binder框架实现之Binder Native Service的Java调用流程
Android Binder框架实现之Java层Binder整体框架设计
Android Binder框架实现之Framework层Binder服务注册过程源码分析
Android Binder框架实现之Java层Binder服务跨进程调用源码分析
Android Binder框架实现之Java层获取Binder服务源码分析


引言

  在前面的两篇文章Android Binder框架实现之Native层addService详解之请求的发送Android Binder框架实现之Native层addService详解之请求的处理分别介绍了addService中"请求的发送"和"请求的处理"这两部分,本文将介绍addService请求的最后一部分–请求的反馈。ServiceManager在处理完addService请求之后,添加了一个待处理事务到MediaPlayerService的事务列表中,并将MediaPlayerService唤醒。我们从上次MediaPlayerService休眠的地方开始,看看它被唤醒之后干了些什么。

注意:本文是基于Android 7.xx版本进行介绍的,其中涉及的源码路径如下:

frameworks/native/libs/binder/IServiceManager.cpp
frameworks/native/libs/binder/Static.cpp
frameworks/native/libs/binder/ProcessState.cpp
kernel/drivers/staging/android/binder.c
kernel/include/linux/list.h
kernel/drivers/staging/android/uapi/binder.h
external/kernel-headers/original/uapi/linux/android/binder.h
frameworks/native/include/binder/ProcessState.h
frameworks/native/include/binder/BpBinder.h
frameworks/native/libs/binder/BpBinder.cpp
frameworks/native/include/binder/IPCThreadState.h
frameworks/native/libs/binder/IPCThreadState.cpp
frameworks/native/include/binder/IInterface.h
frameworks/native/libs/binder/IInterface.cpp
frameworks/native/include/binder/IBinder.h
frameworks/native/libs/binder/Binder.cpp
frameworks/av/media/mediaserver/main_mediaserver.cpp
frameworks/native/cmds/servicemanager/service_manager.c
  • 在正式开始进行源码分析前,先看下Native Binder服务注册整体示意图:

在这里插入图片描述

  • 接着在正式开始进行源码分析前,先看看Native Binder类图如下:

在这里插入图片描述


1.唤醒MediaPlayerService

   从前面的篇章我们可以知道,MediaPlayerService在发送addService请求之后,会阻塞在Binder驱动wait_event_interruptible_exclusive等待被唤醒,那么我们从此处被唤醒说起,继续往下分析。

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)
{
	...
	if (wait_for_proc_work) {
		...
		if (non_block) {
			...
		} else
			//阻塞式读取,阻塞等待事务的发生,此时会被唤醒
			ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
	} else {
		...
	}
	...
	while (1) {
		uint32_t cmd;
		struct binder_transaction_data tr;
		struct binder_work *w;
		struct binder_transaction *t = NULL;

		//如果当前线程的“待完成工作”不为空,则取出待完成工作
		if (!list_empty(&thread->todo))
			w = list_first_entry(&thread->todo, struct binder_work, entry);
		else if (!list_empty(&proc->todo) && wait_for_proc_work)
			w = list_first_entry(&proc->todo, struct binder_work, entry);
		else {
			...
		}

		...

		switch (w->type) {
		case BINDER_WORK_TRANSACTION: {
			t = container_of(w, struct binder_transaction, work);
		} break;
			...
		}

		if (!t)
			continue;

		//此时t-buffer-target_node是NULL
		if (t->buffer->target_node) {
			...
		} else {
			tr.target.ptr = 0;
			tr.cookie = 0;
			cmd = BR_REPLY;
		}
		//交易码
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);

		if (t->from) {
			struct task_struct *sender = t->from->proc->tsk;
			tr.sender_pid = task_tgid_nr_ns(sender,
							task_active_pid_ns(current));
		} else {
			tr.sender_pid = 0;
		}

		//数据大小
		tr.data_size = t->buffer->data_size;
		//数据中对象的偏移数组的大小(即对象的个数)
		tr.offsets_size = t->buffer->offsets_size;
		//数据
		tr.data.ptr.buffer = (binder_uintptr_t)(
					(uintptr_t)t->buffer->data +
					proc->user_buffer_offset);
		//数据中对象的偏移数组
		tr.data.ptr.offsets = tr.data.ptr.buffer +
					ALIGN(t->buffer->data_size,
					    sizeof(void *));

		//将cmd指令写入到ptr,即传递到用户空间
		if (put_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		//将tr数据拷贝到用户空间
		ptr += sizeof(uint32_t);
		if (copy_to_user(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);

		...
		//删除已处理的事务
		list_del(&t->work.entry);
		t->buffer->allow_user_free = 1;
		//设置回复信息
		if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
			...
		} else {
			t->buffer->transaction = NULL;
			kfree(t);
			binder_stats_deleted(BINDER_STAT_TRANSACTION);
		}
		break;
	}

done:
	//更新bwr.read_comsumed的值
	*consumed = ptr - buffer;
	...
	return 0;
}

此时MediaPlayerService进程被Service Manager从睡梦中唤醒,同时它的待处理事务队列中有Service Manager添加的事务,此时,binder_has_thread_work()为true。因此,MediaPlayerService会继续往下执行。下面我们逐步分析MediaPlayerService被唤醒后的处理流程:
(1) 进入while循环,首先取出待处理事务。
(2) 事务的类型是BINDER_WORK_TRANSACTION,得到对应的binder_transaction*类型指针t之后,跳出switch语句。时t不为NULL,因此继续往下执行。下面的工作的目的,是将t中的数据转移到tr中(tr是事务交互数据包结构体binder_transaction_data对应的指针),然后将指令和tr数据都拷贝到用户空间,让MediaPlayerService读取后进行处理。此时的指令是BR_REPLY。
binder_thread_read()执行完毕之后,共反馈了两个指令到用户空间,BR_NOOP和BR_REPLY。此时MediaPlayerService从内核空间返回,进入用户空间,下面让我们重返用户空间分析。



2.MediaPlayerService重返用户空间

   现在回到MediaPlayerService位于用户空间的进程。它会逐个解析Binder驱动反馈的指令。对于BR_NOOP,MediaPlayerService不会做任何实质性的动作。对于BR_REPLY,我们细看代码分析MediaPlayerService的处理流程。让我们来一起分析IPCThreadState::waitForResponse()函数。


2.1 IPCThreadState::waitForResponse

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
		...
        cmd = (uint32_t)mIn.readInt32();
        ...
        
        switch (cmd) {
			...
         
         case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
						...
                    }
                } else {
					...
                }
            }
            goto finish;
			...
        }
    }
finish:
	...
    return err;
}

此时在在BR_REPLY分支中,先读取出数据,并保存到tr中。由于reply不为null,并且tr.flags & TF_STATUS_CODE为0;因此,会执行reply->ipcSetDataReference()。下面让我们一起分析一下ipcSetDataReference函数。


2.2 Parcel::ipcSetDataReference

void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
    const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
{
    binder_size_t minOffset = 0;
    freeDataNoInit();
    mError = NO_ERROR;
    mData = const_cast<uint8_t*>(data);
    mDataSize = mDataCapacity = dataSize;
    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
    mDataPos = 0;
    ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
    mObjects = const_cast<binder_size_t*>(objects);
    mObjectsSize = mObjectsCapacity = objectsCount;
    mNextObjectHint = 0;
    mOwner = relFunc;
    mOwnerCookie = relCookie;
    for (size_t i = 0; i < mObjectsSize; i++) {
        binder_size_t offset = mObjects[i];
        if (offset < minOffset) {
            ALOGE("%s: bad object offset %" PRIu64 " < %" PRIu64 "\n",
                  __func__, (uint64_t)offset, (uint64_t)minOffset);
            mObjectsSize = 0;
            break;
        }
        minOffset = offset + sizeof(flat_binder_object);
    }
    scanForFds();
}

通过代码可知ipcSetDataReference()是根据参数的值重新初始化Parcel的数据和对象。让我逐步分析一下。

(1) freeDataNoInit()的目的是释放原有的内存。为接下来保存Binder驱动反馈的数据做准备。
(2) 在Android Binder机制(六) addService详解02之 请求的处理中,ServiceManager反馈数据时,我们知道它对应的BR_REPLY的数据实际上是空的!因此,这里的mDataSize和mObjectsSize都是0。

实际上,Binder驱动反馈给MediaPlayerService的指令就是告诉它addService已经成功处理完毕!

在MediaPlayerService解析完Binder驱动反馈的数据之后,它会层层向上返回。这样,MediaPlayerService::instantiate()也就正式执行完了!
MediaPlayerService::instantiate()执行完毕,但是MediaPlayerService进程似乎还没有进入消息循环中等到Client的请求!那么,它是何时进入消息循环的呢?回到MediaPlayerService进程的main()函数入口中,它后面是通过startThreadPool()进入消息循环的。这部分的内容,我们下一章再来介绍。

int main(int argc __unused, char **argv __unused)
{
	...
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm(defaultServiceManager());
	...
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
	...
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}


写在最后

   写到这里,addService的整个流程也算告一段落了,读者朋友们你们都get了相关技能了吗,说实话我也木有全部get到。所以还是得理解为主,理解设计者的思路,然后跟着代码解读。多看,多分析一定会有意想不到的效果。最后让我们奉上整个时序图,重温一遍。

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值