在源码分析(一)的结尾,addService函数中构造了数据包Parcel data并发送给了service manager,如下:
//将服务名和MediaPlayerService写入Parcel
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
//在interface_cast分析中我们知道BpServiceManager的remote成员是handle值
//为0的BpBinder,这里相当于BpBinder->transact。该函数完成数据的发送
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
其中remote()返回BpBinder指针。我们接下来就来分析BpBinder,看数据是怎么被交给Binder驱动的。
BpBinder
先看BpBinder的构造函数:
//\frameworks\native\libs\binder\BpBinder.cpp
BpBinder::BpBinder(int32_t handle)
: mHandle(handle)
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
//...
}
构造BpBinder时需要一个参数handle用于初始化成员变量mHandle。每个BpBinder都有唯一一个和他对应的BBinder对象,BpBinder通过这个mHandle来标识它的传输目标。可以看到在后面的transact()中,这个mHandle会作为一个参数传入,代表本次通信的目的端。而本次分析的BpBinder的mHandle值为0,代表service manager。
接下来看完成通信的transact函数。
BpBinder::transact
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
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控制服务端进入不同的分支,具体来说,在BBinder的transact()或onTransact()函数中会根据接收到的code的值进入不同的分支。data即传输的数据(很多情况下是远程函数的参数数据),reply用于接收回复,flags表示本次通信的类型,默认为0。
本次传输中,code是ADD_SERVICE_TRANSACTION,data主要是由MediaPlayerService构造而得的flat_binder_object
BpBinder的transact()函数只是将参数原封不动的交给IPCThreadState的transact()处理,并且还额外传入了mHandle表示通信的目的端。
IPCThreadState::transact
//\frameworks\native\libs\binder\IPCThreadState.cpp,删去了一些debug代码
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) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {//flags为0,会进入该if
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {//不会进入else
err = waitForResponse(NULL, NULL);
}
return err;
}
IPCThreadState的transact()将传进来的所有参数又传给了writeTransactionData(),额外传入了BC_TRANSACTION表示通信协议,然后waitForResponse()。
这两个函数的函数名已经把transact()做的事情说得很清楚了,先是发送数据,然后等待响应。继续看writeTransacctionData,看数据是怎么发送的。
writeTransacctionData
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; /* Don't pass uninitialized stack data to a remote process */
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 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) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
tr.offsets_size = 0;
tr.data.ptr.offsets = 0;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
这个函数中首先构造了一个binder_transaction_data对象tr,它的内容来源于传入的Parcel data和其它用于通信的参数例如pid,uid,handle。其中,tr.data.ptr.buffer等于data.mData,即为传输的数据。
函数的最后把协议BC_TRANSACTION和tr写进mOut。mIn和mOut是IPCThreadState的成员,类型是Parcel。从代码中可以看出,传进来的Parcel data并不直接用于传输,而是用于填充binder_transaction_data,之后将协议cmd和binder_transaction_data写入mOut,所以mOut才是Binder通信中传输的Parcel。
writeTransactData仅仅只是做了填充数据结构的工作,并没有将数据真正发送出去。事实上数据的发送和接收都在waitForResponse的talkWithDriver中进行。
waitForResponse分段1
waitForResponse中,我们只需看数据是怎么发送的即可,后面部分的代码暂时用不到,等用到时再分析。
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();
在waitForResponse()中,首先是调用talkWithDriver()。
talkWithDriver
//删去dubug代码
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
这里构造了一个binder_write_read类型变量bwr,然后根据mIn和mOut的mDataPos,mDataSize,mDataCapacity来填写bwr。
当mIn的mDataPos<mDataSize时 ,说明mIn中还有未读的数据,所以此时不需要Binder读取数据,所以needRead为false。当mDataPos>=mDataSize时,说明mIn中的数据都被我们读完了,所以needRead为true,代表我们希望从Binder中读取数据放到mIn中。也就是说,只有当mIn中的数据全部读完时,才允许从Binder中读取新数据。
对bwr的read_size,write_size的赋值很容易理解,这里不再浪费篇幅赘述,主要注意bwr的buffer指针等于mIn和mOut的数据指针,所以Binder驱动通过bwr就可以从mIn和mOut中读写数据。继续看talkWithDriver的后半部分:
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;
}
调用了ioctl,该函数属于驱动层,驱动层我放在后面的文章分析,所以在这里就到函数为止,不再继续往底层钻。
调用完ioctl之后,把mOut中已经发出去的数据移除掉,更新mIn的数据大小和读写位置(置零)。
总结
在前面的分析中,我们传入的数据data经过了层层包装和转化,最终被传给了Binder驱动,这里用一张图来直观地表现这一过程。
其中,binder_transaction_data中除了储存Parcel data的数据外,还添加了发送者和接收者等额外通信信息。
mOut和mIn是进程专门用于收发Binder通信数据的区域。数据被发送前,统一被放到mOut中,而接收的数据则统一放到mIn中。
bwr记录了mOut和mIn的相关信息,这样驱动层就可以通过这些信息直接对mOut和mIn进行读写操作。
本文到此结束,下篇文章将分析Binder驱动层。