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的内容只有四种类型:
- 字符串型:前4个字节记录字符的数目,然后才是字符内容,一个字符占两个字节。最后还要进行4字节对齐,不足4字节的部分填充0。
- InterfaceToken:位于mData开头,记录目标服务的服务名,由两部分组成:strict_mode和name。其中strict_mode的类型是int,name的类型是字符串型。
- Object:结构体flat_binder_object的序列化表示,所以占的大小就是结构体的大小。在mObjects中会存储Object在mData中的偏移
- 整型: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通信类。