Binder - Parcel的结构

一、前言

我们在BpServierManageraddService方法中,有一段初始化Parcel的代码,我们借此来分析一下Parcel的结构

二、源码分析

我们看下addServiceParcel的使用:

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 初始化

mOutIPCThreadState的成员变量,在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字节内存,将返回的地址存储到mDatamDataSizemDataPos初始化为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我们不再深究,结果是:写入了一个int32char16

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

最后写入一个boolint32

我们之前都忽略了容量不足的情况,现在来看一下:

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、mDataParcel存放数据的内存起始地址

  • 2、mDataPos是当前数据位置

  • 3、mDataCapacity是最大数据容量,适时的时候需要进行缩容与扩容

  • 4、mDataSize指的是当前数据尺寸

  • 5、mObjects是一个指针,存放的是objectmData中的偏移地址

  • 6、mObjects可以通过mObject[index]这种形式取不同objectoffset,这是C++基础的指针操作,与数组类似

  • 7、mObjectSizemData中存储的object数量,我们在writeObject的时候会更新。

相关推荐
子非衣10 分钟前
MySQL修改JSON格式数据示例
android·mysql·json
openinstall全渠道统计3 小时前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
双鱼大猫4 小时前
一句话说透Android里面的ServiceManager的注册服务
android
双鱼大猫4 小时前
一句话说透Android里面的查找服务
android
双鱼大猫4 小时前
一句话说透Android里面的SystemServer进程的作用
android
双鱼大猫4 小时前
一句话说透Android里面的View的绘制流程和实现原理
android
双鱼大猫5 小时前
一句话说透Android里面的Window的内部机制
android
双鱼大猫5 小时前
一句话说透Android里面的为什么要设计Window?
android
双鱼大猫5 小时前
一句话说透Android里面的主线程创建时机,frameworks层面分析
android
苏金标6 小时前
android 快速定位当前页面
android