本文主要是介绍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是NULLif (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到。所以还是得理解为主,理解设计者的思路,然后跟着代码解读。多看,多分析一定会有意想不到的效果。最后让我们奉上整个时序图,重温一遍。
这篇关于Android Binder框架实现之Native层addService详解之请求的反馈的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!