关于Parcel

关于Parcel

1. 概念: 

Parcel是一个容器,他可以包含数据或者是对象引用,并且能够用于Binder的传输。同时支持序列化以及跨进程之后进行反序列化。在android系统中用途非常广泛,主要功能就是用来进行IPC的通信,用来序列化的Parcelable接口,还有aidl其实也就是封装了Parcel的数据传递。

2. 数据结构: 
Parcel底层维护了一个用来存储数据的变量: 

uint8_t* mData;

对应Java层的是marshall方法返回的结果。 
以及一个‘游标’,用来指示数据的位置: 

mutable size_t mDataPos; 

uint8 其实就是一个unsigned char的数据类型。写进去的数据大小取决于数据类型,比如以基本数据类型为例,int,float是4个字节,long是8个字节,boolean其实是写int,也占4个字节,走的都是一个泛型函数,字符串不一样,用张图来描述: 

如果字符串是空,则写入writeInt(-1),代表无值。其他的数据类型比如数组,binder对象等,差不多都是像字符串的写入过程。反过来读数据也是差不多这么个过程,所以我们可以看到,Parcel本地写入数据的过程必须得和读数据的顺序是一样的,如果中间多或少了一个数据,或者读多读少一个数据,后面的数据就会全部打乱,导致数据都是错的(这种方法有点像对称加密)。

3.应用 
四大组件都是用parcel从本地向远处服务AMS传递相关的数据,下面是startService的过程(代码来源: ActivityManagerNative.java , 7.1版本),理论上,我们在native层只要按照顺序写入我们自己的数据,提交给远程系统服务,也一样可以启动我们的service,这样还可以缩短时间。

​
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    service.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeString(callingPackage);
    data.writeInt(userId);
    mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    ComponentName res = ComponentName.readFromParcel(reply);
    data.recycle();
    reply.recycle();
    return res;
}

​

4.7.0和8.0差异 

在探索的过程中,发现7.0和8.0的源码相差甚远,在8.0上ActivityManagerNative已经被废弃了,只有寥寥几行代码,相关的代码都转移到了ServiceManager上,在源码里面已经没有startService的parcel数据写入过程,但IActivityManager还是个aidl,既然是个aidl,那么编译的时候自然也会有java文件编译出来,在8.0编译过的源码上,执行:

find . -name IActivityManager.java

会在一个叫framework_immediate文件下找到这个文件,8.0 startService的parcel数据写入过程:

@Override public android.content.ComponentName startService(android.app.IApplicationThread caller, android.content.Intent service, java.lang.String resolvedType, boolean requireForeground, java.lang.String callingPackage, int userId) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.content.ComponentName _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((caller!=null))?(caller.asBinder()):(null)));
if ((service!=null)) {
_data.writeInt(1);
service.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeString(resolvedType);
_data.writeInt(((requireForeground)?(1):(0)));
_data.writeString(callingPackage);
_data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_startService, _data, _reply, 0);

跟7.0还是有点差别,因为只要参数列表多了一个,那么除非去看源码,不然也不好判断它把这个参数插在哪个位置上,目前来看,还是按照参数列表的顺序在写,不清楚其他版本会不会有差异。根据这些理论,我们可以从native层去startService,探索高版本的保活:

《从native层实现startService(android7.1原生系统保活)》

《从native层实现startService(7.1非原生系统保活)》

猜你喜欢

转载自blog.csdn.net/aa642531/article/details/83684615