Binder學(xué)習(xí)筆記(五)—— Parcel是怎么打包的?

前文中曾經(jīng)遇到過Parcel,從命名上知道他負(fù)責(zé)數(shù)據(jù)打包。在checkService的請求/響應(yīng)體系中,Parcel只打包了基本數(shù)據(jù)類型,如Int32、String16……后面還要用于打包抽象數(shù)據(jù)類型flat_binder_object,這會稍微復(fù)雜一些,因此有必要拿出來單獨研究。我們從Parcel::writeInterfaceToken(…)追起,它的層層調(diào)用關(guān)系如下,這些函數(shù)都在frameworks/native/libs/binder/Parcel.cpp文件中,行數(shù)和函數(shù)名為:

582 writeInterfaceToken(…)
748 Parcel::writeInt32(int32_t val)
1149 Parcel::writeAligned(val)

所有的基本數(shù)據(jù)類型的打包最后都由writeAligned(…)實現(xiàn)的,其內(nèi)部邏輯也非常簡單,
frameworks/native/libs/binder/Parcel.cpp:1149

template<class T>
status_t Parcel::writeAligned(T val) {
    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; // 將val追加到mData
        return finishWrite(sizeof(val));
    }

    status_t err = growData(sizeof(val));  // 如果mData空間不夠,則先擴容
    if (err == NO_ERROR) goto restart_write;
    return err;
}

mData是一塊內(nèi)存棧,writeXXX則把數(shù)據(jù)寫入棧,如果mData空間不夠,先給mData擴容,并把原先的數(shù)據(jù)搬到新的空間,再把新數(shù)據(jù)寫入棧。
Parcel::writeStrongBinder(…)的邏輯更復(fù)雜一些,它的調(diào)用關(guān)系如下:
frameworks/native/libs/binder/Parcel.cpp

872 Parcel::writeStrongBinder(const sp<IBinder>& val)
205 Parcel::flatten_binder(const sp<ProcessState>& /proc/, const sp<IBinder>& binder =val, Parcel* out=this)

來看flatten_binder(…),frameworks/native/libs/binder/Parcel.cpp:205

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) {
        IBinder *local = binder->localBinder();
        if (!local) { // remote類型的binder封裝邏輯
            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 {       // local類型的binder封裝邏輯
            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);
}

它根據(jù)傳入binder的類型做不同的數(shù)據(jù)封裝,在frameworks/native/include/binder/IBinder.h:139,可以看到IBinder聲明了兩個虛函數(shù):

class IBinder : public virtual RefBase
{
public:
    ……
    virtual BBinder*        localBinder();
    virtual BpBinder*       remoteBinder();
    ……
};

并在frameworks/native/libs/binder/Binder.cpp:47定義了默認(rèn)實現(xiàn):

BBinder* IBinder::localBinder()
{
    return NULL;
}

BpBinder* IBinder::remoteBinder()
{
    return NULL;
}

flat_binder_object這個數(shù)據(jù)結(jié)構(gòu)在《Binder學(xué)習(xí)筆記(四)—— ServiceManager如何響應(yīng)checkService請求》研究ServiceManager如何組織reply數(shù)據(jù)時遇到過,它定義在external/kernel-headers/original/uapi/linux/binder.h:57。對于不同的binder封裝成的數(shù)據(jù)示意圖如下:


remote類型的binder封裝形式

local類型的binder封裝形式

然后flatten_binder(…)調(diào)用finish_flatten_binder(…),frameworks/native/libs/binder/Parcel.cpp:199

inline static status_t finish_flatten_binder(
    const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

繼續(xù)調(diào)用writeObject(…),frameworks/native/libs/binder/Parcel.cpp:1035

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:
        // 如果空間足夠,他把前面組裝的flat_binder_object實體追加到mData里
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
        ……
        if (nullMetaData || val.binder != 0) {
      // mObjects記錄每次向mData追加的flat_binder_object的偏移位置
            mObjects[mObjectsSize] = mDataPos; 
            acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
            mObjectsSize++;
        }

        return finishWrite(sizeof(flat_binder_object));
    }
    ……
}

總結(jié)一下:Parcel的數(shù)據(jù)區(qū)域分兩個部分:mData和mObjects,所有的數(shù)據(jù)不管是基礎(chǔ)數(shù)據(jù)類型還是對象實體,全都追加到mData里,mObjects是一個偏移量數(shù)組,記錄所有存放在mData中的flat_binder_object實體的偏移量。Parcel的數(shù)據(jù)模型如下:

Parcel的數(shù)據(jù)模型
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容