Binder native層源碼分析(二):Parcel類

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的內容只有四種類型:

  1. 字符串型:前4個字節記錄字符的數目,然後纔是字符內容,一個字符佔兩個字節。最後還要進行4字節對齊,不足4字節的部分填充0。
  2. InterfaceToken:位於mData開頭,記錄目標服務的服務名,由兩部分組成:strict_mode和name。其中strict_mode的類型是int,name的類型是字符串型。
  3. Object:結構體flat_binder_object的序列化表示,所以佔的大小就是結構體的大小。在mObjects中會存儲Object在mData中的偏移
  4. 整型: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通信類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章