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的时候会更新。

相关推荐
GEEKVIP1 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model20053 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏6893 小时前
Android广播
android·java·开发语言
与衫4 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql
500了10 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵11 小时前
Android Debug Bridge(ADB)完全指南
android·adb
小雨cc5566ru16 小时前
uniapp+Android面向网络学习的时间管理工具软件 微信小程序
android·微信小程序·uni-app
bianshaopeng17 小时前
android 原生加载pdf
android·pdf
hhzz17 小时前
Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)
android·linux·jar
火红的小辣椒18 小时前
XSS基础
android·web安全