Parcel是Binder通信的數據包,通過分析Parcel類,我們就可以知道Binder通信中究竟在傳輸什麼數據,進而對Binder有一個更深入的理解。
在上篇博文中,addService函數往Parcel data寫入了以下數據。
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
我們看這些函數幹了什麼。在此之前,有幾個Parcel的重要成員變量和數據類型需要我們先有個初步的認識,有助於我們更好地理解代碼。
成員變量
mData:數據指針
mDataCapacity:可讀寫的區域大小
mDataPos:讀寫位置,每次讀或每次寫都會改變這個變量的值。在寫完數據之後,會把這個變量值零,供下次讀使用。
mObjectSize:Object的數量
mObjects:類型爲binder_size_t,該類型定義爲typedef __u32 binder_size_t,其實就是一個4字節數,代表flat_binder_object在mData中的偏移。
Parcel的數據類型
Parcel的內容只有四種類型:
- 字符串型:前4個字節記錄字符的數目,然後纔是字符內容,一個字符佔兩個字節。最後還要進行4字節對齊,不足4字節的部分填充0。
- InterfaceToken:位於mData開頭,記錄目標服務的服務名,由兩部分組成:strict_mode和name。其中strict_mode的類型是int,name的類型是字符串型。
- Object:結構體flat_binder_object的序列化表示,所以佔的大小就是結構體的大小。在mObjects中會存儲Object在mData中的偏移
- 整型:bool、char和byte的數據序列化時會強制轉換爲32位,然後才寫入mData指向的內存空間,以保證寫入的數據是4字節對齊的。
writeInterfaceToken
//data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
status_t Parcel::writeInterfaceToken(const String16& interface)
{
writeInt32(IPCThreadState::self()->getStrictModePolicy() |
STRICT_MODE_PENALTY_GATHER);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
傳進來的參數是IServiceManager::getInterfaceDescriptor(),先看該函數幹了什麼,這個函數的定義在IInterface.h的IMPLEMENT_META_INTERFACE宏裏面,具體可見源碼分析一。
const android::String16 IServiceManager::descriptor("android.os.IServiceManager");
const android::String16&
IServiceManager::getInterfaceDescriptor() const {
return IServiceManager::descriptor;
}
該函數返回了字符串"android.os.IServiceManager"。而writeInterfaceToken先是寫入了一個32位整數代表mode(具體是幹嘛的暫時不清楚),然後才把"android.os.IServiceManager"作爲interfaceToken寫入。這裏可以看到其實interfaceToken就是目標服務端的名字。
writeString16
writeString有很多重載函數,這裏只選一個看其是怎麼實現的。
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
if (str == NULL) return writeInt32(-1);//空字符串寫入-1
status_t err = writeInt32(len);//寫入長度
if (err == NO_ERROR) {
len *= sizeof(char16_t);
//找到寫的位置
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
if (data) {
//賦值字符串
memcpy(data, str, len);
*reinterpret_cast<char16_t*>(data+len) = 0;
return NO_ERROR;
}
err = mError;
}
return err;
}
先調用writeInt32()把長度寫入,然後調用writeInplace()找到寫的位置,這裏傳入的參數是(len+1)*sizeof(char16_t),考慮了結束符所以長度len要加1。然後memcpy複製字符串,並在字符串的最後寫入兩個字節的0作爲結束標誌。
pad_size和writeInplace初始化寫空間
我們來分析一下writeInplace()是怎麼獲取寫入位置的。在此之前,先看以下pad_size函數,這個函數用於Parcel中數據4字節對齊,在writeInplace中會用到該函數。
static size_t pad_size(size_t s) {
if (s > (SIZE_T_MAX - 3)) {
abort();
}
return PAD_SIZE_UNSAFE(s);//#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
}
pad_size 接收一個長度,返回將這個長度對齊(4字節對齊)後的值。4字節對齊即要求長度二進制表示的最後兩位爲0。當最後兩位不爲0時,增加長度值直到最後兩位爲0爲止。
整個過程其實可以這樣來表述:當二進制值的第0位和第1位不爲0時,在二進制值的第3位產生一個進位,然後將第0位和第1位清零。
所以計算式子爲:(s+3)&~3。3的二進制是00…011,若s的第0位和第1位不爲0,它加上3後必定會產生一個進位,否則則不會。加上3之後再把第0位和第1位置0即可。
void* Parcel::writeInplace(size_t len)
{
//INT32_MAX=2147483647,如果大於這個數說明其二進制符號位爲1,是個負數
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return NULL;
}
const size_t padded = pad_size(len);//4字節對齊後的值
// sanity check for integer overflow
if (mDataPos+padded < mDataPos) {//如果padded是溢出,成了負數
return NULL;
}
if ((mDataPos+padded) <= mDataCapacity) {
restart_write:
//printf("Writing %ld bytes, padded to %ld\n", len, padded);
uint8_t* const data = mData+mDataPos;
// Need to pad at end?
if (padded != len) {//若進入這個分支,則padded必不等於len
#if BYTE_ORDER == BIG_ENDIAN
static const uint32_t mask[4] = {
0x00000000, 0xffffff00, 0xffff0000, 0xff000000
};
#endif
#if BYTE_ORDER == LITTLE_ENDIAN//小端,一般情況下手機默認都是小端
static const uint32_t mask[4] = {
0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
};
#endif
//如果pad-len=1,則令最後一字節爲0。若等於2,則令最後兩字節爲0,以此類推。
*reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
}
finishWrite(padded);//更新mDataPos+=padded
return data;
}
//調用continueWrite(((mDataSize+len)*3)/2)來增長空間。
status_t err = growData(padded);
if (err == NO_ERROR) goto restart_write;
return NULL;
}
正常情況下,首先得到4字節對齊後長度padded,將mDataPos+len到mDataPos+padded的填充位置零,更新mDataPos+=padded,最後返回的是mDataPos未更新前的位置。
如果mDataCapacity不夠的情況下,該函數會調用growData增長空間,然後才進行正常情況的流程。
也就是說該函數找到了數據應該寫入的空間(如果原空間不夠則分配新空間),並對該空間尾部做了填充處理,然後返回該空間的地址。該函數完成了數據複製前的一切準備工作。
finishWrite更新mDataPos
status_t Parcel::finishWrite(size_t len)
{
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return BAD_VALUE;
}
//printf("Finish write of %d\n", len);
mDataPos += len;
ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
if (mDataPos > mDataSize) {
mDataSize = mDataPos;
ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
}
//printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
return NO_ERROR;
}
似乎沒什麼值得說的,就是更新mDataPos。
continueWrite修改分配給mData的空間
growData中調用了該函數來修改分配給mData空間。
//continueWrite(((mDataSize+len)*3)/2)
//注意參數,增長的空間爲當前需要空間的1.5倍
status_t Parcel::continueWrite(size_t desired)
{
if (desired > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return BAD_VALUE;
}
// If shrinking, first adjust for any objects that appear
// after the new data size.
size_t objectsSize = mObjectsSize;
if (desired < mDataSize) {//縮減空間的情況
//減少object的數量直到空間符合要求爲止。
if (desired == 0) {
objectsSize = 0;
} else {
while (objectsSize > 0) {
if (mObjects[objectsSize-1] < desired)
break;
objectsSize--;
}
}
}
//mOwner是一個釋放函數,當其不爲NULL時說明該Parcel空間需要釋放(不確定,猜的)
if (mOwner) {
// If the size is going to zero, just release the owner's data.
if (desired == 0) {
freeData();
return NO_ERROR;
}
// If there is a different owner, we need to take
// posession.
uint8_t* data = (uint8_t*)malloc(desired);//分配新空間
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
}
binder_size_t* objects = NULL;
if (objectsSize) {
//分配新objects
objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
if (!objects) {
free(data);
mError = NO_MEMORY;
return NO_MEMORY;
}
// Little hack to only acquire references on objects
// we will be keeping.
size_t oldObjectsSize = mObjectsSize;
mObjectsSize = objectsSize;
acquireObjects();
mObjectsSize = oldObjectsSize;
}
if (mData) {//複製mData
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
//僅僅複製objectSize數量的object。
//注意objectSize在 if (desired < mDataSize)中可能減少
if (objects && mObjects) {
memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
//釋放舊的空間。
mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
mOwner = NULL;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
//給Parcel賦予新的數據空間
mData = data;
mObjects = objects;
mDataSize = (mDataSize < desired) ? mDataSize : desired;
ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
mDataCapacity = desired;
mObjectsSize = mObjectsCapacity = objectsSize;
mNextObjectHint = 0;
} else if (mData) {//當mOwner爲NULL,且mData不爲NULL的情況
//即if (desired < mDataSize) ,空間減少的情況
if (objectsSize < mObjectsSize) {
// Need to release refs on any objects we are dropping.
const sp<ProcessState> proc(ProcessState::self());
//把多餘的object釋放掉
for (size_t i=objectsSize; i<mObjectsSize; i++) {
//從這裏可以看成,mObject記錄的是object在mData中的偏移
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
if (flat->type == BINDER_TYPE_FD) {
// will need to rescan because we may have lopped off the only FDs
mFdsKnown = false;
}
release_object(proc, *flat, this, &mOpenAshmemSize);
}
//縮減mOjbect的空間
binder_size_t* objects =
(binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
if (objects) {
mObjects = objects;
}
mObjectsSize = objectsSize;
mNextObjectHint = 0;
}
// We own the data, so we can just do a realloc().
if (desired > mDataCapacity) {//空間增大的情況,直接分配
uint8_t* data = (uint8_t*)realloc(mData, desired);
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
//修正Parcel的全局空間分配值。
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocSize -= mDataCapacity;
pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
} else if (desired > mDataCapacity) {//分配失敗且要求增大空間的情況
mError = NO_MEMORY;
return NO_MEMORY;
}
} else {//減少空間的情況,不realloc,讓size和pos等於desired
if (mDataSize > desired) {
mDataSize = desired;
ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
}
if (mDataPos > desired) {
mDataPos = desired;
ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
}
}
} else {//mOwner等於NULL且data等於NULL的情況
// This is the first data. Easy!
uint8_t* data = (uint8_t*)malloc(desired);
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
}
if(!(mDataCapacity == 0 && mObjects == NULL
&& mObjectsCapacity == 0)) {
ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired);
}
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
//更改全局分配值
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataSize = mDataPos = 0;
ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
mDataCapacity = desired;
}
return NO_ERROR;
}
函數稍微有點長, 但代碼都很簡單。做個總結,當釋放函數mOwner不爲NULL的時候,將舊空間的數據複製到新空間後釋放舊空間(新空間的大小等於我們期望的大小,所以數據可能會被截斷)。當mOwner爲NULL且要求增長空間的時候,直接realloc mData;當爲減少的時候釋放掉多餘的object但不realloc mData。
writeInt32和writeAligned
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
調用了writeAligned來寫入整型。
template<class T>
status_t Parcel::writeAligned(T val) {
//斷言,寫入的數據必須4字節對齊
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
//這種寫法可以學一下
*reinterpret_cast<T*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
}
//空間不夠時,分配更多空間
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
好像也沒什麼值得說的,就是簡單地將數據寫入。也就強制類型轉換然後再賦值的寫法可以學習一下。
writeStrongBinder和flatten_binder_object
writeStrongBinder從名字上看是寫入一個Binder對象的意思,我們看代碼。
//傳入的對象類型爲MediaPlayerService
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
參數是IBinder類型,IBinder的繼承者有BpBinder和BBinder,再源碼分析(一)中,我們可以看到傳入的是MediaPlayerService對象,它其實是BBinder的派生類。
sp和wp是智能指針,關於sp和wp可以看我的另一篇文章 android智能指針sp和wp,繼續看代碼。
// \external\kernel-headers\original\uapi\linux\android\binder.h
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
__u32 type;
__u32 flags;
/* 8 bytes of data. */
union {
binder_uintptr_t binder; /* local object */
__u32 handle; /* remote object */
};
/* extra data associated with local object */
binder_uintptr_t cookie;
};
再看flatten_binder函數之前,先看一下該函數用到的數據結構flat_binder_object的定義。它代表一個Binder對象。
//傳進來的是MediaPlayerService,是BBinder的派生類
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {//進入該if
IBinder *local = binder->localBinder();
//BBinder的localBinder返回this(代碼在Binder.cpp中),
//故local不爲NULL,不會進入該if
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {//進入該else
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
return finish_flatten_binder(binder, obj, out);
}
由於MediaPlayerService是BBinder的派生類,故走的是else分支,這裏要注意構造的flat_binder_object類型是BINDER_TYPE_BINDER,cookie成員自身指針,二binder成員爲自身的弱引用類型(weakref_type類型),關於這個類型見android智能指針sp和wp。
finish_flatten_binder和writeObject
flatten_binder的最後調用了finish_flatten_binder
inline static status_t finish_flatten_binder(
const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
return out->writeObject(flat, false);
}
調用了writeObject。
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = mObjectsSize < mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
//強制類型轉換後寫入
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// remember if it's a file descriptor
if (val.type == BINDER_TYPE_FD) {
if (!mAllowFds) {
// fail before modifying our object index
return FDS_NOT_ALLOWED;
}
mHasFds = mFdsKnown = true;
}
// Need to write meta-data?
//進入這個if
if (nullMetaData || val.binder != 0) {
mObjects[mObjectsSize] = mDataPos;
acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
mObjectsSize++;
}
return finishWrite(sizeof(flat_binder_object));
}
if (!enoughData) {//調用growData增長mData空間
const status_t err = growData(sizeof(val));
if (err != NO_ERROR) return err;
}
if (!enoughObjects) {//重新分配object空間
size_t newSize = ((mObjectsSize+2)*3)/2;
if (newSize < mObjectsSize) return NO_MEMORY; // overflow
binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == NULL) return NO_MEMORY;
mObjects = objects;
mObjectsCapacity = newSize;
}
goto restart_write;
}
就是將flat_binder_object寫入mData中。
總結
Parcel中可以存放四種數據類型的數據:整型,字符串,interfacetoken和object,這些數據都放在mData空間中。其中object指的是flat_binder_object,由IBinder對象構造而來。
本篇文章到此結束。下篇文章將分析BpBinder通信類。