在源碼分析(一)的結尾,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驅動層。