一、前言
我们在BpServierManager
的addService
方法中,有一段初始化Parcel
的代码,我们借此来分析一下Parcel
的结构
二、源码分析
我们看下addService
中Parcel
的使用:
c++
::android::Parcel _aidl_data;
_aidl_data.markForBinder(remoteStrong());
::android::Parcel _aidl_reply;
::android::status_t _aidl_ret_status = ::android::OK;
::android::binder::Status _aidl_status;
_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
_aidl_ret_status = _aidl_data.writeUtf8AsUtf16(name);
_aidl_ret_status = _aidl_data.writeStrongBinder(service);
_aidl_ret_status = _aidl_data.writeBool(allowIsolated);
_aidl_ret_status = _aidl_data.writeInt32(dumpPriority);
2.1 初始化
mOut
是IPCThreadState
的成员变量,在IPCThreadState
构造函数中设置:
c++
mOut.setDataCapacity(256);
设定初始大小:
c++
status_t Parcel::setDataCapacity(size_t size)
{
if (size > mDataCapacity) return continueWrite(size);
return NO_ERROR;
}
status_t Parcel::continueWrite(size_t desired)
{
// This is the first data. Easy!
uint8_t* data = (uint8_t*)malloc(desired);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
mData = data;
mDataSize = mDataPos = 0;
mDataCapacity = desired;
return NO_ERROR;
}
从内存中申请256字节内存,将返回的地址存储到mData
,mDataSize
与mDataPos
初始化为0,代表没有任何数据,mDataCapacity
代表数据容量,初始化为256。
我们接下来依次看下Parcel
被调用的各个方法
2.2 markForBinder
c++
void Parcel::markForBinder(const sp<IBinder>& binder) {
LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
}
}
void Parcel::markForRpc(const sp<RpcSession>& session) {
LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
"format must be set before data is written OR on IPC data");
LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
mSession = session;
}
这个方法并不涉及主体数据的修改,只是保存mSession
2.3 writeInterfaceToken
c++
status_t Parcel::writeInterfaceToken(const String16& interface)
{
return writeInterfaceToken(interface.string(), interface.size());
}
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
if (CC_LIKELY(!isForRpc())) {
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid()
: IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
}
// currently the interface identification token is just its name as a string
return writeString16(str, len);
}
看下writeInt32
:
c++
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
template<class T>
status_t Parcel::writeAligned(T val) {
static_assert(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;
}
status_t Parcel::finishWrite(size_t len)
{
//更新mDataPos(与mDataSize)
mDataPos += len;
if (mDataPos > mDataSize) {
mDataSize = mDataPos;
}
return NO_ERROR;
}
writeAligned
是个模板方法,包含了数据写入与扩容操作,写操作结束之后,更新mDataPos
,再看下writeString16
:
c++
status_t Parcel::writeString16(const String16& str)
{
return writeString16(str.string(), str.size());
}
与上面的类似,只是写入长度不同,到目前为止,我们写入了三个int32
,一个string16
。
2.4 writeUtf8AsUtf16
c++
status_t Parcel::writeUtf8AsUtf16(const std::string& str) {
const uint8_t* strData = (uint8_t*)str.data();
const size_t strLen= str.length();
const ssize_t utf16Len = utf8_to_utf16_length(strData, strLen);
status_t err = writeInt32(utf16Len);
if (err) {
return err;
}
void* dst = writeInplace((utf16Len + 1) * sizeof(char16_t));
utf8_to_utf16(strData, strLen, (char16_t*)dst, (size_t) utf16Len + 1);
return NO_ERROR;
}
从名字可以看出来,是将UTF-8
的字符串写入为UTF-16
的字符串,writeInplace
我们不再深究,结果是:写入了一个int32
与char16
。
2.5 writeStrongBinder
c++
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flattenBinder(val);
}
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
flat_binder_object obj;
obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != nullptr) {
BBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
const int32_t handle = proxy ? proxy->getPrivateAccessorForId().binderHandle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
obj.flags |= schedBits;
status_t status = writeObject(obj, false);
return finishFlattenBinder(binder);
}
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;
//给mObjects赋值,值为当前object的offset
if (nullMetaData || val.binder != 0) {
mObjects[mObjectsSize] = mDataPos;
acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
mObjectsSize++;
}
return finishWrite(sizeof(flat_binder_object));
}
//容量不够
if (!enoughData) {
const status_t err = growData(sizeof(val));
if (err != NO_ERROR) return err;
}
// ... exception
goto restart_write;
}
status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder)
{
internal::Stability::tryMarkCompilationUnit(binder.get());
auto category = internal::Stability::getCategory(binder.get());
return writeInt32(category.repr());
}
将Binder对象"展开"为flat_binder_object
,然后将其写入,然后写入一个int32
最后写入一个bool
和int32
。
我们之前都忽略了容量不足的情况,现在来看一下:
2.6 growData
c++
status_t Parcel::growData(size_t len)
{
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
: continueWrite(std::max(newSize, (size_t) 128));
}
扩容计算公式为:(旧容量+新增长度) * 3 / 2,举个例子:如果旧容量为256,新增长度为4,那么结果为(256+4) * 3 / 2 = 390,然后执行continueWrite
:
c++
status_t Parcel::continueWrite(size_t desired)
{
size_t objectsSize = mObjectsSize;
//缩容逻辑,如果目标小于当前size,则缩容到相应大小
if (desired < mDataSize) {
if (desired == 0) {
objectsSize = 0;
} else {
while (objectsSize > 0) {
if (mObjects[objectsSize-1] < desired)
break;
objectsSize--;
}
}
}
if (mData) {
if (objectsSize < mObjectsSize) {
//缩容之后,对应的Object释放等逻辑
}
//容量不够,申请修改内存大小
if (desired > mDataCapacity) {
uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (data) {
mData = data;
mDataCapacity = desired;
} else {
mError = NO_MEMORY;
return NO_MEMORY;
}
} else {
//fix当前offset尺寸与位置
if (mDataSize > desired) {
mDataSize = desired;
}
if (mDataPos > desired) {
mDataPos = desired;
}
}
}
return NO_ERROR;
}
三、总结
经过上面这些阶段,我们基本上可以理解Parcel
里面一些成员变量的含义,以及Parcel
的一些内存布局,及其之间的对应关系,成图之后大概如下所示:
图3.1 - Parcel的基本结构
可以得到以下几个信息:
-
1、
mData
是Parcel
存放数据的内存起始地址 -
2、
mDataPos
是当前数据位置 -
3、
mDataCapacity
是最大数据容量,适时的时候需要进行缩容与扩容 -
4、
mDataSize
指的是当前数据尺寸 -
5、
mObjects
是一个指针,存放的是object
在mData
中的偏移地址 -
6、
mObjects
可以通过mObject[index]
这种形式取不同object
的offset
,这是C++基础的指针操作,与数组类似 -
7、
mObjectSize
是mData
中存储的object
数量,我们在writeObject
的时候会更新。