前面两篇文章分别介绍了getService中"请求的发送"和"请求的处理"这两部分,本文将介绍getService请求的最后一部分–请求的反馈。下面继续分析分析MediaPlayer收到请求反馈之后的处理流程。
注意:本文是基于Android 7.xx版本进行介绍的!
从前面的篇章我们可以知道,MediaPlayer在发送getService请求之后,会阻塞在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是目标节点。
//这里,MediaPlayerService请求的目标是Service Manager
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;
}
此时MediaPlayer客户端进程被Service Manager从睡梦中唤醒,同时它的待处理事务队列中有Service Manager添加的事务,此时,binder_has_thread_work()为true,因此,MediaPlayer客户端进程会继续往下执行。下面我们逐步分析MediaPlayer客户端进程被唤醒后的处理流程:
(1) 进入while循环后,首先取出待处理事务。
(2) 事务的类型是BINDER_WORK_TRANSACTION,得到对应的binder_transaction类型指针t之后,跳出switch语句。很显然,此时t不为NULL,因此继续往下执行。下面的工作的目的,是将t中的数据转移到tr中(tr是事务交互数据包结构体binder_transaction_data对应的指针),然后将指令和tr数据都拷贝到用户空间,让MediaPlayer读取后进行处理。此时的指令为BR_REPLY!
(3) 最后,更新consumed的值,即更新bwr.read_consumed的值。
binder_thread_read()执行完毕之后,共反馈了两个指令到用户空间:BR_NOOP和BR_REPLY。
之后的流程应该都比较熟悉了,首先返回到binder_ioctl()中,接着将ServiceManager反馈的数据拷贝到用户空间。接下来的工作就交给MediaPlayer客户端进程进行处理了。
从Binder驱动返回后,首先回到talkWithDriver()中,接着便返回到waitForResponse()中。在waitForResponse()会对反馈数据进行解析。
现在回到MediaPlayer客户端进程位于用户层空间。它会逐个解析Binder驱动反馈的指令。对于BR_NOOP,MediaPlayerService不会做任何实质性的动作。对于BR_REPLY,我们细看代码分析MediaPlayerService的处理流程。让我们来一起分析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函数。
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();
}
函数入参data就是ServiceManager返回来的数据,数据中包含一个flat_binder_object对象,因此objectsCount则为1。先通过freeDataNoInit()将原始的数据清空,然后再给mData和mObjects赋值,这样就将数据保存到了Parcel中。
为什么objectsCount的值是1呢?请返回查看一下Service Manager在执行binder_send_reply()即可知,这里就不再多说。
waitForResponse()执行完BR_REPLY之后,便返回到IPCThreadState::transact()中;然后层层返回,直到退回到checkService()。
virtual sp<IBinder> checkService( const String16& name) const
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
}
该代码定义在frameworks/native/libs/binder/IServiceManager.cpp中,分析至此,通过transact()来获取MediaPlayerService的事务已经执行完毕!MediaPlayerService的接入点(即我们要获取的handle值)已经保存在replay中。接下来的工作就是调用reply.readStrongBinder()来从replay中解析出所需要的数据,即MediaPlayerService在Biner驱动中的Binder引用描述,也就是C++层的句柄。
status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{
return unflatten_binder(ProcessState::self(), *this, val);
}
sp<IBinder> Parcel::readStrongBinder() const
{
sp<IBinder> val;
readStrongBinder(&val);
return val;
}
readStrongBinder()会调用unflatten_binder()来解析Parcel中的数据,这个和addService中调用flatten_binder将数据打扁写入Parcel是相对应的一个流程。
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, wp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
...
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
*out = proc->getWeakProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->unsafe_get()), *flat, in);
}
}
return BAD_TYPE;
}
readObject()的作用是从Parcel中读取出它所保存的flat_binder_object类型的对象。该对象的类型是BINDER_TYPE_HANDLE,因此会指向BINDER_TYPE_HANDLE对应的switch分支。让我们详解一下该分支:
(1) 这里的proc是ProcessState对象,执行proc->getStrongProxyForHandle()会将句柄(MediaPlayerService的Binder引用描述)保存到ProcessState的链表中,然后再创建并返回该句柄的BpBinder对象(即Binder的代理)。在 Android Binder机制(五) defaultServiceManager()的实现中有getStrongProxyForHandle()的详细说明,下面只给出getStrongProxyForHandle()代码。
(2) finish_unflatten_binder()中只有return NO_ERROR。
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
//在矢量数组中查找"句柄值为handle的handle_entry对象"
//找到的话,则直接返回;找不到的话,则新建handle对应的handle_entry
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
// 当handle==0(即是Service Manager的句柄)时,尝试去ping Binder驱动。
if (handle == 0) {
Parcel data;
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
return NULL;
}
//新建BpBinder代理
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
这样,getService()的内容就全部执行完毕。getService()的返回结果IBinder=BpBinder对象,该对象包含了"MediaPlayerService(在Binder驱动)中的Binder引用的描述",该描述在C++层而言就是个整型句柄。之后,若MediaPlayer要向MediaPlayerService发送请求,就根据该IBinder对象和"MediaPlayerService"进行通信。
上面只是执行完了getService(),它返回了IBinder对象。但是,getMediaPlayerService()并没有执行完毕。下面继续回到getMediaPlayerService()中。
const sp<IMediaPlayerService>
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.player"));//参见章节2
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
在成功获取MediaPlayerService对应的IBinder对象(binder)之后,可以通过interface_cast(binder)获取它的代理。
是不是对interface_cast()很熟悉!不错,在 Android Binder机制(五) defaultServiceManager()的实现中就是通过该宏获取IServiceManager的代理的。
让我们将对应的宏展开,看看最终展开的代码:
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
下面直接给出下面直接给出IMediaPlayerService::asInterface()的代码。
android::sp<IMediaPlayerService> IMediaPlayerService::asInterface(
const android::sp<android::IBinder>& obj)
{
android::sp<IMediaPlayerService> intr;
if (obj != NULL) {
intr = static_cast<IMediaPlayerService*>(
obj->queryLocalInterface(
IMediaPlayerService::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
asInterface()代码真的非常简单,就是调用调用new BpMediaPlayerService()新建BpServiceManager对象,并返回给对象。但是到这一步的千辛万苦,真的是不能给常人道也啊。
至此,MediaPlayer客户端进程的getService请求就全部介绍完毕了。如果读者有仔细阅读了addService的篇章,那么addService就是手到擒来了。如果没有看到也没有关系,Binder这个就像美女,得慢慢沟通交流最好才能深入的,如果没有耐心是吃不了热豆腐的。