Android底层:通熟易懂的分析binder--3. 探究binder全流程通信之回复篇
前言
Android底层:通熟易懂的分析binder--3. 探究binder全流程通信之请求篇已经把请求流程分析完毕了,接下来分析回复流程。
本篇内容
方法调用栈
收到回复数据并发送
回复数据到达driver层
server收尾工作
client收到回复数据
全流程总结
01
—
方法调用栈
在分析回复流程之前需要把请求流程后,各个环节现在所处的方法调用栈讲清楚,这样可以作为我们后面分析的基础。
分别用clientInvokeStack(client进程方法调用栈),serverInvokeStack(server进程方法调用栈),clientDriInvokeStack(“driver层client代理“方法调用栈),serverDriInvokeStack(”driver层server代理“方法调用栈)表示方法调用栈,位于方法栈前面的就是最近调用的方法。
serverInvokeStack的方法有:
server.methodXX(“真正”方法执行者)
Stub的onTransact
JavaBBinder的onTransact
BBinder的transact
IPCThreadState的execCommand
IPCThreadState的getAndExecuteCommand
IPCThreadState的joinThreadPool
serverDriInvokeStack中暂时没有任何方法,因为driver层把请求数据传递给上层后就结束了。
clientDriInvokeStack的方法有:
binder_thread_read (进入等待状态,等待回复消息)
binder_ioctl_write_read
binder_ioctl
clientInvokeStack的方法有:
IPCThreadState::self()-> waitForResponse(进入等待状态,等待回复消息)
IPCThreadState::self()->transact
BpBinder::transact
android_os_BinderProxy_transact (android_util_Binder.cpp)
binderProxy.transactNative
binderProxy.transact
serverProxy.methodXX
在分析回复流程中,其实是上面的方法栈的方法出栈的过程,因此上面的方法栈会伴随着我们的分析流程。
02
—
收到回复数据并发送
发生于server进程
下面分析用到了serverInvokeStack方法调用栈。
收到回复数据是指server.methodXX方法已经处理完,并且把回复数据写入reply(Parcel)中,native层收到了这个回复数据,发送回复数据是指native层开始把回复发送给client。
2.1 收到回复数据
先上Stub代码
public static abstract class Stub extends android.os.Binder implements IXXX {
private static final java.lang.String DESCRIPTOR
= "...IXXX";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_methodXX: {
data.enforceInterface(descriptor);
// xxx 代表某一类型, data.readXXX代表读取参数
xxx _result = this.methodXX(data.readXXX());
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
}
}
上面代码,Stub的子类对象(server)的methodXX方法处理完毕后,把回复数据写入reply(Parcel)中,这两方法从serverInvokeStack中出栈,进而进入JavaBBinder的onTransact和BBinder的transact方法,这两方法也基本没做啥处理,直接返回出栈,那现在serverInvokeStack中的方法有:
IPCThreadState的execCommand
IPCThreadState的getAndExecuteCommand
IPCThreadState的joinThreadPool
那我们进入execCommand方法,看下它未执行完的代码
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:
{
省略代码...
// ptr有值,进入这
if (tr.target.ptr) {
if (reinterpret_cast<RefBase::weakref_type*>(
tr.target.ptr)->attemptIncStrong(this)) {
// tr.cookie其实就是BBinder对象,这时候开始调用BBinder的transact方法,这是通往上层的入口
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
} else {
error = UNKNOWN_TRANSACTION;
}
} else {
省略代码...
}
// 同步请求则开始把结果返回
if ((tr.flags & TF_ONE_WAY) == 0) {
if (error < NO_ERROR) reply.setError(error);
sendReply(reply, 0);
} else {
省略代码...
}
省略代码...
}
break;
省略代码...
}
return result;
}
上面代码,执行完
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
后,若没有error,并且是同步请求,则调用sendReply发送回复数据,reply中存储了回复数据,sendReply方法入栈,那现在serverInvokeStack中的方法有:
IPCThreadState的sendReply
IPCThreadState的execCommand
IPCThreadState的getAndExecuteCommand
IPCThreadState的joinThreadPool
2.2 发送回复数据
进入sendReply方法,看下它的代码
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
status_t err;
status_t statusBuffer;
// 把数据写入binder_transaction_data中,准备写入driver层
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
if (err < NO_ERROR) return err;
// waitForResponse中最终调用talkWithDriver方法把数据传递给driver层
return waitForResponse(NULL, NULL);
}
上面代码把cmd为BC_REPLY(这时候给driver层的命令是BC_REPLY),reply等写入binder_transaction_data中后,调用waitForResponse方法,注意它的参数是null,null。waitForResponse调用talkWithDriver方法后把回复数据写入driver层,waitForResponse进入等待状态(等待driver层返回命令)
waitForResponse方法入栈,那现在serverInvokeStack中的方法有:
IPCThreadState的waitForResponse(进入等待状态)
IPCThreadState的sendReply
IPCThreadState的execCommand
IPCThreadState的getAndExecuteCommand
IPCThreadState的joinThreadPool
小结
到此server进程已经把回复数据发送给了driver层,server进程内的binder线程进入等待状态,停留于waitForResponse方法,后面还会进入这方法。
03
—
回复数据到达driver层
发生于“driver层server代理”
我使用“driver层xx代理”来代表在driver层中记录的上层进程的信息(binder_proc,binder_thread等)的总称,xx是上层进程的名字(实在没想出一个好名字)。“driver层client代理”与client进程是对应关系,同理“driver层server代理”与server进程是对应关系。
下面分析用到了serverDriInvokeStack方法调用栈。
下面的流程分析起来就特别容易了,和请求流程基本是一模一样的,因此只把不一样的地方着中介绍下。
和请求流程一样binder_ioctl,binder_ioctl_write_read,binder_thread_write方法入serverDriInvokeStack方法调用栈中。
binder_thread_write
进入binder_thread_write方法,看下回复相关代码
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
省略代码
// thread->return_error == BR_OK 这个很关键, BR_OK代表还没把信息返回给上层
while (ptr < end && thread->return_error == BR_OK) {
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
省略代码...
switch (cmd) {
case BC_REPLY: {
struct binder_transaction_data tr;
// 拷贝 binder_transaction_data
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
}
省略代码...
}
return 0;
}
因为从上层传递的cmd是BC_REPLY,因此进入binder_transaction方法,是不是和BC_TRANSACTION cmd的流程是一样的。binder_transaction方法进入serverDriInvokeStack方法调用栈中。
3.1 查找接收回复数据的“目标”
这里的“目标”当然是“driver层client代理”,下面来看下查找过程,查找代码依然在binder_transaction方法中,只展示与回复相关代码
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
// t里面的数据会存放到目标binder_proc或binder_thread的队列中
struct binder_transaction *t;
// tcomplete用来告诉client数据已经成功的交给了目标
struct binder_work *tcomplete;
// 用来对binder服务,binder服务引用做转换
binder_size_t *offp, *off_end;
binder_size_t off_min;
// 目标
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
// 回复相关
struct binder_transaction *in_reply_to = NULL;
省略代码...
// 回复结果
if (reply) {
// transaction_stack存放 事务栈, 主要用于回复阶段
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
binder_user_error("%d:%d got reply transaction with no transaction stack\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_empty_call_stack;
}
binder_set_nice(in_reply_to->saved_priority);
// 判断是否是一样的binder_thread
if (in_reply_to->to_thread != thread) {
binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, in_reply_to->debug_id,
in_reply_to->to_proc ?
in_reply_to->to_proc->pid : 0,
in_reply_to->to_thread ?
in_reply_to->to_thread->pid : 0);
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
goto err_bad_call_stack;
}
thread->transaction_stack = in_reply_to->to_parent;
// 找到target_thread
target_thread = in_reply_to->from;
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
// 容错判断
if (target_thread->transaction_stack != in_reply_to) {
binder_user_error("%d:%d got reply transaction with bad target transaction stack %d, expected %d\n",
proc->pid, thread->pid,
target_thread->transaction_stack ?
target_thread->transaction_stack->debug_id : 0,
in_reply_to->debug_id);
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
// 找到目标binder_proc
target_proc = target_thread->proc;
}
}
上面代码很简单,主要做了以下几件事情:
因为当前是reply是true,进入reply环节
thread->transaction_stack保存了请求时的事务
查找“目标”binder_thread, binder_proc
查找到“目标”后,与请求流程一样,会把回复数据拷贝(对BBinder,BpBinder进行转换)后组装成type为BINDER_WORK_TRANSACTION,的binder_work放入“目标”的todo队列中。同时把type为
BINDER_WORK_TRANSACTION_COMPLETE的binder_work放入当前binder_thread的todo队列中。除此之外回复流程还做了从thread->transaction_stack中移除reply事务的操作。
binder_transaction,binder_thread_write方法从serverDriInvokeStack中出栈。
小结
到此回复数据已经放入了“目标”的binder_thread(“driver层client代理”)的todo队列中(注意此时的binder_transaction->buffer->target_node为null)
“driver层server代理”的binder_thread的todo队列放入type为BINDER_WORK_TRANSACTION_COMPLETE的binder_work(它的binder_transaction为null)。
因此这个时候形成了“driver层client代理”与“driver层server代理”并行执行的情况。我们先把“driver层server代理”的流程分析完毕,再来专注分析“driver层client代理”这条线。
serverDriInvokeStack方法调用栈包含的方法有:
binder_ioctl
binder_ioctl_write_read
04
—
server收尾工作
发生于server进程和“driver层server代理”
会用到serverInvokeStack,serverDriInvokeStack方法调用栈。
“driver层server代理”把回复数据传递给了“driver层client代理”,server进程还需要做一些收尾工作。
4.1 发送“complete”事件给上层
与处理请求的逻辑一样,“driver层server代理”收到BINDER_WORK_TRANSACTION_COMPLETE的binder_wrok后,会发送BR_TRANSACTION_COMPLETE的cmd给上层,binder_thread_read,binder_ioctl_write_read,binder_ioctl方法先后从serverDriInvokeStack出栈,serverDriInvokeStack方法调用栈中没有任何方法,“driver层server代理”的处理流程结束。
4.2 server收尾工作
收到BR_TRANSACTION_COMPLETE cmd后进入server的收尾工作。先回顾下
serverInvokeStack中的方法有哪些:
IPCThreadState的waitForResponse(进入等待状态)
IPCThreadState的sendReply
IPCThreadState的execCommand
IPCThreadState的getAndExecuteCommand
IPCThreadState的joinThreadPool
咱们依次从这些方法入手,看执行收尾工作的流程
回到waitForResponse,先看下相关代码
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
因为reply和acquireResult都是null,因此直接finish。从serverInvokeStack中出栈
其他的sendReply,execCommand,getAndExecuteCommand方法也先后从serverInvokeStack中出栈,
回到joinThreadPool,看下相关代码
void IPCThreadState::joinThreadPool(bool isMain)
{
省略代码...
status_t result;
do {
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
mProcess->mDriverFD, result);
abort();
}
// Let this thread exit the thread pool if it is no longer
// needed and it is not the main process thread.
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
省略代码...
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
若getAndExecuteCommand方法执行的结果result正常,则进入
if(result == TIMED_OUT && !isMain) {
break;
}
这块代码分为两个逻辑:
若isMain为false(不是binder主线程),则从do循环中跳出,往mOut中写入BC_EXIT_LOOPER cmd通过talkWithDriver方法传递给driver层,注意talkWithDriver方法这时候的参数是false,false代表不需要等待driver层的返回数据,只要把数据成功发送给driver层后就直接结束。当前的binder线程结束,joinThreadPool方法从serverInvokeStack中出栈,serverInvokeStack中没有任何方法。
若isMain为true(binder主线程),则继续与driver层通信,driver层最终进入binder_thread_read方法进入等待状态,监听新的事务。
小结
到此,binder进程通信中的server进程回复阶段处理完毕,server进程中的binder线程若为非主线程,则一般会直接结束,否则浪费资源;若为主线程,则继续与driver层通信,等待着新的事务(最起码得有一个binder线程来接收事务)。
05
—
client收到回复数据
发生于client进程和“driver层client代理”
会用到clientInvokeStack,clientDriInvokeStack方法调用栈。
clientDriInvokeStack的方法有:
binder_thread_read (进入等待状态,等待回复消息)
binder_ioctl_write_read
binder_ioctl
5.1 “driver层client代理”发送回复数据给上层
3.1 节 中"driver层client代理“的binder_thread的todo队列中收到回复binder_work后,binder_thread_read方法被唤醒,拿到回复binder_transaction数据后把其中相关的数据赋值给binder_transaction_data结构体,把cmd为BR_REPLY和
binder_transaction_data数据发送给上层,binder_thread_read,binder_ioctl_write_read,binder_ioctl方法先后从clientDriInvokeStack出栈,clientDriInvokeStack没有任何方法。
5.2 client收到回复数据
先来看下clientInvokeStack方法调用栈,包含的方法有哪些:
IPCThreadState::self()-> waitForResponse(进入等待状态,等待回复消息)
IPCThreadState::self()->transact
BpBinder::transact
android_os_BinderProxy_transact (android_util_Binder.cpp)
binderProxy.transactNative
binderProxy.transact
serverProxy.methodXX
咱们依次从这些方法入手,来分析收到回复数据流程。
client native层在waitForResponse方法中处于等待状态,等待回复数据,看下相关代码
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
uint32_t cmd;
int32_t err;
while (1) {
省略代码...
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 {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
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), this);
}
} else {
freeBuffer(NULL,
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), this);
continue;
}
}
goto finish;
}
省略代码...
}
因为driver层传递的cmd是BR_REPLY,进入该case,最终进入下面的代码流程
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);
}
}
reply(Parcel对象)调用ipcSetDataReference方法把driver层传递的回复数据写入reply中,进而结束waitForResponse方法的调用,该方法执行出栈操作。
IPCThreadState::self()->transact,BpBinder::transact,android_os_BinderProxy_transact,binderProxy.transactNative,binderProxy.transact先后执行出栈操作,最终在serverProxy.methodXX方法中,把reply回复数据读取出来。
clientInvokeStack内没有任何方法。
至此,整个回复流程结束,因为有请求流程作为基础,因此回复流程分析起来相对简单了很多。
06
—
全流程总结
到此binder进程通信全流程分析完毕,我用一张图来总结下整个流程
图中解析
1.黑色直线箭头代表client请求server的过程,绿色直线箭头代表server处理完
毕返回结果给client的过程
2.红色的框和红色字体代表每层之间传递的数据的变化
3.粉色框代表哪层,比如framework层
4. 蓝色字体是对关键信息的说明
5. client是c/s中的client是一个进程,同理server就是server端,也是一个进程
上面这张图看上去确实比较复杂(即时我去掉了一些无关紧要的流程),它展示了client请求server服务及server把结果返回给client的过程。在这个过程中,每层(app,framework,jni,native,driver)之间协议的变化,每层之间是怎么联系起来的,native与driver层都做了哪些处理等。