Parcel是android中不同于Java Serialize新的序列化机制。
Java Serialize机制作用是能将数据对象存入字节流中,在需要时重新生成对象。主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等。
而android系统定位内存受限设备,对性能要求更高,而且系统中采用了binder ipc机制,就需要求性能更出色的对象传输方式。Parcel定位就是轻量级高效的对象序列化和反序列化机制。
源码位于
Framework/native/include/binder/parcel.h
Framework/native/libs\binder\Parcel.cpp
我们以MediaPlayer的SeriviceManager的addService为例,解析Parcel
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
从代码知,实例化了两个Parcel栈上对象data和reply,data主要是传送给servicemanager程序,而reply是从servicemanager返回的结果。
Data写入的值有InterfaceToken :“android.os.IServiceManager” String16:“media.player”
StrongBinder:MediaPlayerService, Int32: 0
我们跟踪入Parcel,看他形成了什么样的数据格式
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);
}
writeInterfaceToken写入int32的IPCThreadState的strictmode policy,还有string16的android.os.IServiceManager字符串
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
template<class T>
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(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;
}
从代码可以看出writeInt32实际上调用的是writeAligned泛型函数
writeInterfaceToken是parcel第一次被调用的函数,
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
是断言,判断读写时是否为4字节对齐#define PAD_SIZE(s) (((s)+3)&~3)
从代码可以看出,写的数据主要往mData里面,下面为创建Parcel对象时的初始值
void Parcel::initState()
{
mError = NO_ERROR;
mData = 0;
mDataSize = 0;
mDataCapacity = 0;
mDataPos = 0;
ALOGV("initState Setting data size of %p to %d\n", this, mDataSize);
ALOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
mObjects = NULL;
mObjectsSize = 0;
mObjectsCapacity = 0;
mNextObjectHint = 0;
mHasFds = false;
mFdsKnown = true;
mAllowFds = true;
mOwner = NULL;
}<p>再回到<span style="font-family:Times New Roman;">writeAligned</span><span style="font-family:宋体;">函数,此时被</span><span style="font-family:Times New Roman;">goto</span><span style="font-family:宋体;">语句指到了赋值语句,把值赋给了</span><span style="font-family:Times New Roman;">mData</span><span style="font-family:宋体;">,最后调用</span><span style="font-family:Times New Roman;">finishWrite</span></p>
很明显条件是不符合的,所以得去growData函数分配更多的内存空间
status_t Parcel::growData(size_t len)
{
size_t newSize = ((mDataSize+len)*3)/2; //newSize = ((0+4)*3)/2 = 6
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
: continueWrite(newSize);
}
可以看到内存的申请是通过continueWrite
status_t Parcel::continueWrite(size_t desired)
{
// If shrinking, first adjust for any objects that appear
// after the new data size.
size_t objectsSize = mObjectsSize;
if (desired < mDataSize) {
if (desired == 0) {
objectsSize = 0;
} else {
while (objectsSize > 0) {
if (mObjects[objectsSize-1] < desired)
break;
objectsSize--;
}
}
}
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;
}
size_t* objects = NULL;
if (objectsSize) {
objects = (size_t*)malloc(objectsSize*sizeof(size_t));
if (!objects) {
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) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
if (objects && mObjects) {
memcpy(objects, mObjects, objectsSize*sizeof(size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
mOwner = NULL;
mData = data;
mObjects = objects;
mDataSize = (mDataSize < desired) ? mDataSize : desired;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
mDataCapacity = desired;
mObjectsSize = mObjectsCapacity = objectsSize;
mNextObjectHint = 0;
} else if (mData) {
if (objectsSize < mObjectsSize) {
// Need to release refs on any objects we are dropping.
const sp<ProcessState> proc(ProcessState::self());
for (size_t i=objectsSize; i<mObjectsSize; i++) {
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);
}
size_t* objects =
(size_t*)realloc(mObjects, objectsSize*sizeof(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) {
mData = data;
mDataCapacity = desired;
} else if (desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
}
} else {
if (mDataSize > desired) {
mDataSize = desired;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
}
if (mDataPos > desired) {
mDataPos = desired;
ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
}
}
} else {
// 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: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);
}
mData = data;
mDataSize = mDataPos = 0;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
mDataCapacity = desired;
}
return NO_ERROR;
}
从代码知道mData存储着普通数据,mObjects则记录着内存块IBinder类型的数据以及FileDescriptor,而后者是通过flatten_binder()和unflatten_binder()实现的。
而我们writeInt32是mData这步,如果mData之前有分配内存走realloc,没分配走malloc,此时mDataCapacity为6,mData为分配出来的内存,mDataSize = mDataPos = 0;
template<class T>
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(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;
}
再回到writeAligned函数,此时被goto语句指到了赋值语句,把值赋给了mData,最后调用finishWrite
status_t Parcel::finishWrite(size_t len)
{
//printf("Finish write of %d\n", len);
mDataPos += len;
ALOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
if (mDataPos > mDataSize) {
mDataSize = mDataPos;
ALOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
}
//printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
return NO_ERROR;
}
mDataPos = 4, mDataSize = 4;
到这里就完成了writeInt32的操作
接着我们看writeInterfaceToken的writeString16
status_t Parcel::writeString16(const char16_t* str, size_t len)
{ // str = “android.os.IServiceManager”
if (str == NULL) return writeInt32(-1);
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t); // 104
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); // 108
if (data) {
memcpy(data, str, len);
*reinterpret_cast<char16_t*>(data+len) = 0;
return NO_ERROR;
}
err = mError;
}
return err;
}
从代码可以看到String是先在前面写了他str的个数大小,然后
void* Parcel::writeInplace(size_t len)
{
const size_t padded = PAD_SIZE(len); // 56 (54内存对齐)
// sanity check for integer overflow
if (mDataPos+padded < mDataPos) { //8+56 < 12 ?
return NULL;
}
if ((mDataPos+padded) <= mDataCapacity) { // 8+56 < 12 ?
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) { // 56 != 54
#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
//printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len],
// *reinterpret_cast<void**>(data+padded-4));
// data + 108 - 4 最后4位字节对齐后数据因大小端需保护是否正常
*reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
}
finishWrite(padded); // 把大小赋予mDataPos = 56 +8和mDataSize = 56 +8 return data;
}
status_t err = growData(padded); // 申请了(mDataSize(8) + 56 )*3/2的内存(96)
if (err == NO_ERROR) goto restart_write;
return NULL;
}
如果数据不够则申请内存,够的话,要考虑对齐后大小端问题,然后通过memcpy把str赋值给data,同时在最后加上结束符
writeInterfaceToken函数就到这,现在Parcel的mData是这样的
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
接着是data.writeString16(name); // name = “media.player”
可得出如下结构
接着是data.writeStrongBinder(service);这里的service是MediaplayerService,它是继承于IBinder的,所以写的不止是mData,还有mObject,跟踪下代码
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
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; //接收其他进程发过来的文件形式binder
if (binder != NULL) {
IBinder *local = binder->localBinder(); // mediaplayer service这项不为空
if (!local) {
// 此处为client端使用
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.handle = handle;
obj.cookie = NULL;
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = local->getWeakRefs();
obj.cookie = local;
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = NULL;
obj.cookie = NULL;
}
return finish_flatten_binder(binder, obj, out);
}
可以看出,这里涉及到flat_binder_object
从代码可以看出,mediaplayer service flat_binder_object 值如下
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; // 8个字节
obj.type = BINDER_TYPE_BINDER; // 8个字节
obj.binder = local->getWeakRefs(); // 8个字节
obj.cookie = local; // 4 个字节
inline static status_t finish_flatten_binder(
const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
{
return out->writeObject(flat, false);
}
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
// flat_binder_object本身是28字节已经对齐
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; // 96+28 <= 96
const bool enoughObjects = mObjectsSize < mObjectsCapacity; // 0 < 0
if (enoughData && enoughObjects) { // false && false
restart_write:
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// Need to write meta-data?
if (nullMetaData || val.binder != NULL) {
mObjects[mObjectsSize] = mDataPos; //mObject[0] = 96
acquire_object(ProcessState::self(), val, this); // 增加引用值
mObjectsSize++; //mObjectsSize = 1
}
// remember if it's a file descriptor
if (val.type == BINDER_TYPE_FD) {
if (!mAllowFds) {
return FDS_NOT_ALLOWED;
}
mHasFds = mFdsKnown = true;
}
return finishWrite(sizeof(flat_binder_object)); // 28 mDataPos = 96+28 ....
}
if (!enoughData) { // false
const status_t err = growData(sizeof(val)); // ((96+28)*3)/2 =186
if (err != NO_ERROR) return err;
}
if (!enoughObjects) { // false
size_t newSize = ((mObjectsSize+2)*3)/2; // (0+2)*3/2 = 3
size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); // 3*4 = 12
if (objects == NULL) return NO_MEMORY;
mObjects = objects;
mObjectsCapacity = newSize; // mObjectsCapacity = 12
}
goto restart_write;
}
void acquire_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who)
{
switch (obj.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
static_cast<IBinder*>(obj.cookie)->incStrong(who);
}
return;
case BINDER_TYPE_WEAK_BINDER:
if (obj.binder)
static_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who);
return;
case BINDER_TYPE_HANDLE: {
const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
if (b != NULL) {
LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get());
b->incStrong(who);
}
return;
}
case BINDER_TYPE_WEAK_HANDLE: {
const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
if (b != NULL) b.get_refs()->incWeak(who);
return;
}
case BINDER_TYPE_FD: {
// intentionally blank -- nothing to do to acquire this, but we do
// recognize it as a legitimate object type.
return;
}
}
ALOGD("Invalid object type 0x%08lx", obj.type);
}
通过writeObject函数把flat_binder_object 值 写入了mData,并mObject记录了flat_binder_object的起始位置
mData:
mObject:
最后data.writeInt32(allowIsolated ? 1 : 0);
最后通过remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
BpBinder(0)把Parcel data发送到ServiceManager
我们来看看读出数据,还是用addService的数据,看看怎么读取!
读取数据我们只好到servicemanager里面看下
Data的解析就在这binder_txn 的data(binder_io *msg)里面
int svcmgr_handler(struct binder_state *bs,
struct binder_txn *txn,
struct binder_io *msg, /** 是data */
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
unsigned len;
void *ptr;
uint32_t strict_policy;
int allow_isolated;
// ALOGI("target=%p code=%d pid=%d uid=%d\n",
// txn->target, txn->code, txn->sender_pid, txn->sender_euid);
if (txn->target != svcmgr_handle)
return -1;
// Equivalent to Parcel::enforceInterface(), reading the RPC
// header with the strict mode policy mask and the interface name.
// Note that we ignore the strict_policy and don't propagate it
// further (since we do no outbound RPCs anyway).
strict_policy = bio_get_uint32(msg); // 读出4个字节的strictmode
s = bio_get_string16(msg, &len); // 读出带字符串长度,并对齐的字符串android.os.IServiceManager
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { // svcmgr_id为android.os.IServiceManager
fprintf(stderr,"invalid id %s\n", str8(s));
return -1;
}
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
ptr = do_find_service(bs, s, len, txn->sender_euid);
if (!ptr)
break;
// 如果找到则填充ptr入reply
bio_put_ref(reply, ptr);
return 0;
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len); // 读出带字符串长度,并对齐的字符串media.player
ptr = bio_get_ref(msg); // 读出flat_binder_obj(带有MediaplayerService)
allow_isolated = bio_get_uint32(msg) ? 1 : 0; //读出allow_isolated
if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
unsigned n = bio_get_uint32(msg);
si = svclist;
while ((n-- > 0) && si)
si = si->next;
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
}
其中bio_get_string16,bio_get_uint32这些实际上相当于Parcel里面的readString16,readInt32!
我们把他当做parcel的成员函数来看,从代码看刚刚好是对应的,至于为什么能指向到其他进程的地址空间,那是binder driver做的
至于Java层的Parcel后面再讨论