Android Binder入门指南之addService详解之请求的反馈

  Android Binder入门指南之addService详解之请求的反馈


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

注意:本文是基于Android 7.xx版本进行介绍的!


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到。所以还是得理解为主,理解设计者的思路,然后跟着代码解读。多看,多分析一定会有意想不到的效果。最后让我们奉上整个时序图,重温一遍。

在这里插入图片描述

发布了89 篇原创文章 · 获赞 92 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/tkwxty/article/details/103426017