Binder native层源码分析(二):Parcel类

Parcel是Binder通信的数据包,通过分析Parcel类,我们就可以知道Binder通信中究竟在传输什么数据,进而对Binder有一个更深入的理解。

在上篇博文中,addService函数往Parcel data写入了以下数据。

data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);

我们看这些函数干了什么。在此之前,有几个Parcel的重要成员变量和数据类型需要我们先有个初步的认识,有助于我们更好地理解代码。

成员变量

mData:数据指针

mDataCapacity:可读写的区域大小

mDataPos:读写位置,每次读或每次写都会改变这个变量的值。在写完数据之后,会把这个变量值零,供下次读使用。

mObjectSize:Object的数量

mObjects:类型为binder_size_t,该类型定义为typedef __u32 binder_size_t,其实就是一个4字节数,代表flat_binder_object在mData中的偏移

Parcel的数据类型

Parcel的内容只有四种类型:

  1. 字符串型:前4个字节记录字符的数目,然后才是字符内容,一个字符占两个字节。最后还要进行4字节对齐,不足4字节的部分填充0。
  2. InterfaceToken:位于mData开头,记录目标服务的服务名,由两部分组成:strict_mode和name。其中strict_mode的类型是int,name的类型是字符串型。
  3. Object:结构体flat_binder_object的序列化表示,所以占的大小就是结构体的大小。在mObjects中会存储Object在mData中的偏移
  4. 整型:bool、char和byte的数据序列化时会强制转换为32位,然后才写入mData指向的内存空间,以保证写入的数据是4字节对齐的。

writeInterfaceToken

//data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
status_t Parcel::writeInterfaceToken(const String16& interface)
{
    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
               STRICT_MODE_PENALTY_GATHER);
    // currently the interface identification token is just its name as a string
    return writeString16(interface);
}

传进来的参数是IServiceManager::getInterfaceDescriptor(),先看该函数干了什么,这个函数的定义在IInterface.h的IMPLEMENT_META_INTERFACE宏里面,具体可见源码分析一。

const android::String16 IServiceManager::descriptor("android.os.IServiceManager");             
const android::String16&                                            
        IServiceManager::getInterfaceDescriptor() const {              
    return IServiceManager::descriptor;                                
}     

该函数返回了字符串"android.os.IServiceManager"。而writeInterfaceToken先是写入了一个32位整数代表mode(具体是干嘛的暂时不清楚),然后才把"android.os.IServiceManager"作为interfaceToken写入。这里可以看到其实interfaceToken就是目标服务端的名字。

writeString16

writeString有很多重载函数,这里只选一个看其是怎么实现的。

status_t Parcel::writeString16(const char16_t* str, size_t len)
{
    if (str == NULL) return writeInt32(-1);//空字符串写入-1

    status_t err = writeInt32(len);//写入长度
    if (err == NO_ERROR) {
        len *= sizeof(char16_t);
        //找到写的位置
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
        if (data) {
        //赋值字符串
            memcpy(data, str, len);
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}

先调用writeInt32()把长度写入,然后调用writeInplace()找到写的位置,这里传入的参数是(len+1)*sizeof(char16_t),考虑了结束符所以长度len要加1。然后memcpy复制字符串,并在字符串的最后写入两个字节的0作为结束标志。

pad_size和writeInplace初始化写空间

我们来分析一下writeInplace()是怎么获取写入位置的。在此之前,先看以下pad_size函数,这个函数用于Parcel中数据4字节对齐,在writeInplace中会用到该函数。

static size_t pad_size(size_t s) {
    if (s > (SIZE_T_MAX - 3)) {
        abort();
    }
    return PAD_SIZE_UNSAFE(s);//#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
}

pad_size 接收一个长度,返回将这个长度对齐(4字节对齐)后的值。4字节对齐即要求长度二进制表示的最后两位为0。当最后两位不为0时,增加长度值直到最后两位为0为止。

整个过程其实可以这样来表述:当二进制值的第0位和第1位不为0时,在二进制值的第3位产生一个进位,然后将第0位和第1位清零。

所以计算式子为:(s+3)&~3。3的二进制是00…011,若s的第0位和第1位不为0,它加上3后必定会产生一个进位,否则则不会。加上3之后再把第0位和第1位置0即可。

void* Parcel::writeInplace(size_t len)
{
//INT32_MAX=2147483647,如果大于这个数说明其二进制符号位为1,是个负数
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return NULL;
    }

    const size_t padded = pad_size(len);//4字节对齐后的值

    // sanity check for integer overflow
    if (mDataPos+padded < mDataPos) {//如果padded是溢出,成了负数
        return NULL;
    }

    if ((mDataPos+padded) <= mDataCapacity) {
restart_write:
        //printf("Writing %ld bytes, padded to %ld\n", len, padded);
        uint8_t* const data = mData+mDataPos;

        // Need to pad at end?
        if (padded != len) {//若进入这个分支,则padded必不等于len
#if BYTE_ORDER == BIG_ENDIAN
            static const uint32_t mask[4] = {
                0x00000000, 0xffffff00, 0xffff0000, 0xff000000
            };
#endif
#if BYTE_ORDER == LITTLE_ENDIAN//小端,一般情况下手机默认都是小端
            static const uint32_t mask[4] = {
                0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
            };
#endif
//如果pad-len=1,则令最后一字节为0。若等于2,则令最后两字节为0,以此类推。
            *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
        }

        finishWrite(padded);//更新mDataPos+=padded
        return data;
    }
//调用continueWrite(((mDataSize+len)*3)/2)来增长空间。
    status_t err = growData(padded);
    if (err == NO_ERROR) goto restart_write;
    return NULL;
}

正常情况下,首先得到4字节对齐后长度padded,将mDataPos+len到mDataPos+padded的填充位置零,更新mDataPos+=padded,最后返回的是mDataPos未更新前的位置。

如果mDataCapacity不够的情况下,该函数会调用growData增长空间,然后才进行正常情况的流程。

也就是说该函数找到了数据应该写入的空间(如果原空间不够则分配新空间),并对该空间尾部做了填充处理,然后返回该空间的地址。该函数完成了数据复制前的一切准备工作。

finishWrite更新mDataPos

status_t Parcel::finishWrite(size_t len)
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    //printf("Finish write of %d\n", len);
    mDataPos += len;
    ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
    if (mDataPos > mDataSize) {
        mDataSize = mDataPos;
        ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
    }
    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
    return NO_ERROR;
}

似乎没什么值得说的,就是更新mDataPos。

continueWrite修改分配给mData的空间

growData中调用了该函数来修改分配给mData空间。

//continueWrite(((mDataSize+len)*3)/2)
//注意参数,增长的空间为当前需要空间的1.5倍
status_t Parcel::continueWrite(size_t desired)
{
    if (desired > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    // If shrinking, first adjust for any objects that appear
    // after the new data size.
    size_t objectsSize = mObjectsSize;
    if (desired < mDataSize) {//缩减空间的情况
    //减少object的数量直到空间符合要求为止。
        if (desired == 0) {
            objectsSize = 0;
        } else {
            while (objectsSize > 0) {
                if (mObjects[objectsSize-1] < desired)
                    break;
                objectsSize--;
            }
        }
    }
//mOwner是一个释放函数,当其不为NULL时说明该Parcel空间需要释放(不确定,猜的)
    if (mOwner) {
        // If the size is going to zero, just release the owner's data.
        if (desired == 0) {
            freeData();
            return NO_ERROR;
        }

        // If there is a different owner, we need to take
        // posession.
        uint8_t* data = (uint8_t*)malloc(desired);//分配新空间
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }
        binder_size_t* objects = NULL;

        if (objectsSize) {
        //分配新objects
            objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
            if (!objects) {
                free(data);

                mError = NO_MEMORY;
                return NO_MEMORY;
            }

            // Little hack to only acquire references on objects
            // we will be keeping.
            size_t oldObjectsSize = mObjectsSize;
            mObjectsSize = objectsSize;
            acquireObjects();
            mObjectsSize = oldObjectsSize;
        }

        if (mData) {//复制mData
            memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
        }
        //仅仅复制objectSize数量的object。
        //注意objectSize在 if (desired < mDataSize)中可能减少
        if (objects && mObjects) {
            memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
        }
        //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
        //释放旧的空间。
        mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
        mOwner = NULL;

        LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;
        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
	//给Parcel赋予新的数据空间
        mData = data;
        mObjects = objects;
        mDataSize = (mDataSize < desired) ? mDataSize : desired;
        ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
        mDataCapacity = desired;
        mObjectsSize = mObjectsCapacity = objectsSize;
        mNextObjectHint = 0;

    } else if (mData) {//当mOwner为NULL,且mData不为NULL的情况
    //即if (desired < mDataSize) ,空间减少的情况
        if (objectsSize < mObjectsSize) {
            // Need to release refs on any objects we are dropping.
            const sp<ProcessState> proc(ProcessState::self());
            //把多余的object释放掉
            for (size_t i=objectsSize; i<mObjectsSize; i++) {
            //从这里可以看成,mObject记录的是object在mData中的偏移
                const flat_binder_object* flat
                    = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
                if (flat->type == BINDER_TYPE_FD) {
                    // will need to rescan because we may have lopped off the only FDs
                    mFdsKnown = false;
                }
                release_object(proc, *flat, this, &mOpenAshmemSize);
            }
            //缩减mOjbect的空间
            binder_size_t* objects =
                (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
            if (objects) {
                mObjects = objects;
            }
            mObjectsSize = objectsSize;
            mNextObjectHint = 0;
        }

        // We own the data, so we can just do a realloc().
        if (desired > mDataCapacity) {//空间增大的情况,直接分配
            uint8_t* data = (uint8_t*)realloc(mData, desired);
            if (data) {
                LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
                        desired);
                pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
                //修正Parcel的全局空间分配值。
                gParcelGlobalAllocSize += desired;
                gParcelGlobalAllocSize -= mDataCapacity;
                pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
                mData = data;
                mDataCapacity = desired;
            } else if (desired > mDataCapacity) {//分配失败且要求增大空间的情况
                mError = NO_MEMORY;
                return NO_MEMORY;
            }
        } else {//减少空间的情况,不realloc,让size和pos等于desired
            if (mDataSize > desired) {
                mDataSize = desired;
                ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
            }
            if (mDataPos > desired) {
                mDataPos = desired;
                ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
            }
        }

    } else {//mOwner等于NULL且data等于NULL的情况
        // This is the first data.  Easy!
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }

        if(!(mDataCapacity == 0 && mObjects == NULL
             && mObjectsCapacity == 0)) {
            ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired);
        }

        LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
        //更改全局分配值
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;
        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);

        mData = data;
        mDataSize = mDataPos = 0;
        ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
        ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
        mDataCapacity = desired;
    }

    return NO_ERROR;
}

函数稍微有点长, 但代码都很简单。做个总结,当释放函数mOwner不为NULL的时候,将旧空间的数据复制到新空间后释放旧空间(新空间的大小等于我们期望的大小,所以数据可能会被截断)。当mOwner为NULL且要求增长空间的时候,直接realloc mData;当为减少的时候释放掉多余的object但不realloc mData

writeInt32和writeAligned

status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}

调用了writeAligned来写入整型。

template<class T>
status_t Parcel::writeAligned(T val) {
//断言,写入的数据必须4字节对齐
    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;
        return finishWrite(sizeof(val));
    }
//空间不够时,分配更多空间
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}

好像也没什么值得说的,就是简单地将数据写入。也就强制类型转换然后再赋值的写法可以学习一下。

writeStrongBinder和flatten_binder_object

writeStrongBinder从名字上看是写入一个Binder对象的意思,我们看代码。

//传入的对象类型为MediaPlayerService
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

参数是IBinder类型,IBinder的继承者有BpBinder和BBinder,再源码分析(一)中,我们可以看到传入的是MediaPlayerService对象,它其实是BBinder的派生类。

sp和wp是智能指针,关于sp和wp可以看我的另一篇文章 android智能指针sp和wp,继续看代码。

// \external\kernel-headers\original\uapi\linux\android\binder.h
struct flat_binder_object {
	/* 8 bytes for large_flat_header. */
	__u32		type;
	__u32		flags;

	/* 8 bytes of data. */
	union {
		binder_uintptr_t	binder;	/* local object */
		__u32			handle;	/* remote object */
	};

	/* extra data associated with local object */
	binder_uintptr_t	cookie;
};

再看flatten_binder函数之前,先看一下该函数用到的数据结构flat_binder_object的定义。它代表一个Binder对象。

//传进来的是MediaPlayerService,是BBinder的派生类
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) {//进入该if
        IBinder *local = binder->localBinder();
     //BBinder的localBinder返回this(代码在Binder.cpp中),
     //故local不为NULL,不会进入该if
        if (!local) {
            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 {//进入该else
            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);
}

由于MediaPlayerService是BBinder的派生类,故走的是else分支,这里要注意构造的flat_binder_object类型是BINDER_TYPE_BINDER,cookie成员自身指针,二binder成员为自身的弱引用类型(weakref_type类型),关于这个类型见android智能指针sp和wp

finish_flatten_binder和writeObject

flatten_binder的最后调用了finish_flatten_binder

inline static status_t finish_flatten_binder(
    const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

调用了writeObject。

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;

        // remember if it's a file descriptor
        if (val.type == BINDER_TYPE_FD) {
            if (!mAllowFds) {
                // fail before modifying our object index
                return FDS_NOT_ALLOWED;
            }
            mHasFds = mFdsKnown = true;
        }

        // Need to write meta-data?
        //进入这个if
        if (nullMetaData || val.binder != 0) {
            mObjects[mObjectsSize] = mDataPos;
            acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
            mObjectsSize++;
        }

        return finishWrite(sizeof(flat_binder_object));
    }

    if (!enoughData) {//调用growData增长mData空间
        const status_t err = growData(sizeof(val));
        if (err != NO_ERROR) return err;
    }
    if (!enoughObjects) {//重新分配object空间
        size_t newSize = ((mObjectsSize+2)*3)/2;
        if (newSize < mObjectsSize) return NO_MEMORY;   // overflow
        binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
        if (objects == NULL) return NO_MEMORY;
        mObjects = objects;
        mObjectsCapacity = newSize;
    }

    goto restart_write;
}

就是将flat_binder_object写入mData中。

总结

Parcel中可以存放四种数据类型的数据:整型,字符串,interfacetoken和object,这些数据都放在mData空间中。其中object指的是flat_binder_object,由IBinder对象构造而来。

本篇文章到此结束。下篇文章将分析BpBinder通信类。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值