前文中曾經遇到過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的數據模型如下: