Binder學習筆記(五)—— Parcel是怎么打包數據的?


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

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

所有的基本數據類型的打包最后都由writeAligned(…)實現的,其內部邏輯也非常簡單,

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是一塊內存棧,writeXXX則把數據寫入棧,如果mData空間不夠,先給mData擴容,並把原先的數據搬到新的空間,再把新數據寫入棧。

Parcel::writeStrongBinder(…)的邏輯更復雜一些,它的調用關系如下:

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);
}

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

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

並在frameworks/native/libs/binder/Binder.cpp:47定義了默認實現:

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

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

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

  

然后flatten_binder(…)調用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);
}

繼續調用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));
    }
    ……
}

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM