在前面的Binder注册与查找一文中,已经介绍过Binder机制在Java层的通信架构。我们知道,在通信过程中,请求进程要一个业务代理和BinderProxy代理,服务进程要实现业务代理并继承Binder类。本文主要介绍一下底层数据的通信流程,先看一下Java层的通信架构图。

public int startActivity(IApplicationThread caller,...) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
...
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
...
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
...
return result;
}
将参数写入Parcel对象,普通类型int、char、float、boolean等分别调用Parcel的writeXxx对应方法,复杂类型,如Binder类型,IApplicationThread是这种类型,调用Parcel的writeStrongBinder方法。所有数据都写入底层Parcel。最后,调用BinderProxy的transact方法,向底层发送数据。
再看一下服务进程,ActivityManagerNative类重写Binder类的onTransact方法。
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case START_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
...
int result = startActivity(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
reply.writeInt(result);
return true;
}
...//其他业务code判断。
}
return super.onTransact(code, data, reply, flags);
}
该方法通过业务code,区别每一个业务方法。然后,调用子类ActivityManagerService对应的业务方法。这是服务进程的逻辑,其中,Binder的execTransact方法将触发该方法。
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
boolean res;
try {
res = onTransact(code, data, reply, flags);
} catch (RemoteException e) {
} catch (RuntimeException e) {
} catch (OutOfMemoryError e) {
}
reply.recycle();
data.recycle();
StrictMode.clearGatheredViolations();
return res;
}
该方法在Binder类,将调用业务类重写的onTransact方法,Binder本身不关心业务,因此,每一个继承Binder的业务类,都需要重写onTransact方法,根据业务code查找业务方法。execTransact方法是由底层调用的。
总之,当请求进程调用某个业务方法时,会将code和参数一起,传入底层。服务进程底层得知有请求发生时,将code和参数交给服务进程上层,execTransact方法就是底层调用上层的入口方法。下面看一下底层的通信流程。
请求进程底层流程
先看一下请求进程BinderProxy的transact方法,它将调用JNI#transactNative方法,进入底层代码。
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
return transactNative(code, data, reply, flags);
}
transactNative方法,底层JNI对应android_os_BinderProxy_transact方法。
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) {
// throws RemoteException
//dataObj为空抛出异常
Parcel* data = parcelForJavaObject(env, dataObj);
Parcel* reply = parcelForJavaObject(env, replyObj);
//data,reply为返回false
IBinder* target = (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
//target为空抛出异常
...
status_t err = target->transact(code, *data, reply, flags);
...
return JNI_FALSE;
}
首先,根据上层Parcel参数内部mNativePtr指针,获取底层Parcel对象,根据上层BinderProxy内部mObject指针,获取底层BpBinder对象。
然后,调用底层BpBinder的transact方法。
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
该方法中,入参是上层传入code、flag和底层Parcel,我们在上层向Parcel写入传递的数据时,其实是写入引用的底层Parcel中。调用IPCThreadState的transact方法,每个线程IPCThreadState是一个单独对象,属于线程私有变量。mHandle是底层BpBinder句柄,在BpBinder创建时初始化其值,通过该值可以查找服务进程的底层Binder。每一个请求进程底层都会有一个对应的BpBinder。看一下IPCThreadState的transact方法。
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
...
if (err == NO_ERROR) {
err = writeTransactionData(BC_TRANSACTION, flags, handle,
code, data, NULL);
}
...
if ((flags & TF_ONE_WAY) == 0) {
...
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
....
} else {
//携带TF_ONE_WAY标志,无需回复
err = waitForResponse(NULL, NULL);
}
return err;
}
调用IPCThreadState的writeTransactionData方法,将code,handle,底层Parcel以及通信命令BC_TRANSACTION,写入binder_transaction_data结构体。
上层flag带有TF_ONE_WAY标志,代表一次通信,不需要服务进程回复消息。当没有该标志位时,调用waitForResponse方法,入参是回复Parcel,等待服务进程回复。
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) {
binder_transaction_data tr;
tr.target.ptr = 0;
tr.target.handle = handle;
tr.code = code;
tr.cookie = 0;
....
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
....
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
将binder_transaction_data结构体和通信命令cmd一起写入mOut,即输出Parcel,它存储向Binder驱动发送的数据。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
...
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
if (err != NO_ERROR) goto finish;
if (reply) {
} else {
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
//error
}
return err;
}
该方法是一个无限循环,中间状态会判断退出。首先,它调用talkWithDriver方法,此方法包含真正与Binder驱动通信的系统调用。当从talkWithDriver唤醒时,查看输入Parcel,即mIn是否有数据,如果包含输入数据,读取通信命令,处理数据。
status_t IPCThreadState::talkWithDriver(bool doReceive) {
//mDriverFD必须大于0,否则返回错误
binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
binder_write_read数据读写结构体,保存mIn和mOut的数据。当发送数据时,用mOut数据的指针初始化binder_write_read结构体写缓冲区write_buffer和写入write_size大小。接收数据时,用mIn数据的指针初始化该结构体读缓冲区read_buffer和读取read_size大小。
如果读写大小都是空,不需要与驱动交互,直接返回,说明什么都没有做。如果binder_write_read有一个buffer不空,系统调用ioctl方法,陷入Binder驱动执行代码。
ioctl方法返回后,根据write_consumed与read_consumed,重设mOut与mIn对象。当请求进程在内核态执行ioctl系统调用时,会因等待数据挂起或数据到来而被唤醒,因此,在ioctl方法处会有休眠,等待数据返回。若被唤醒,回到waitForResponse方法,从mIn中读取数据。
系统调用ioctl方法,mDriverFD是文件描述符,BINDER_WRITE_READ是读写命令,内核态执行的是binder_ioctl方法。请求进程通信流程图。

任重而道远
网友评论