移步系列Android跨进程通信IPC系列
Parcel类详解
每一个java端的Binder对象(服务端)在初始化时都会对应一个native对象,类型是BBinder,它继承于IBinder类
1 Binder对象的写入
时序图
通过 Parcel的writeStrongBinder方法将Binder对象序列化:
1.1 Parcel.writeStrongBinder(IBinder)
/Parcel.java
/**
* Write an object into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeStrongBinder(IBinder val) {
nativeWriteStrongBinder(mNativePtr, val);
}
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
调用了native的方法nativeWriteStrongBinder(long,IBinder),这个方法对应JNI的android_os_Parcel_writeStrongBinder()函数
1.2 android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
代码在android_os_Parcel.cpp 298行
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
- 这里说下ibinderForJavaObject()函数,返回的是BBinder对象(实际上是JavaBBinder,它继承自BBinder)
- 然后调用了Parcel-Native的writeStrongBinder函数
1.3 Parcel::writeStrongBinder
代码在Parcel.cpp 872行
status_t Parcel::writeStrongBinder(const sp& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
主要是调用了flatten_binder()函数
这里说一下ProcessState::self() 是获取ProcessState的单例方法
1.4 Parcel::flatten_binder(const sp& /proc/, const sp& binder, Parcel* out)
代码在Parcel.cpp 205行
status_t flatten_binder(const sp& /*proc*/,
const sp& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
//JavaBBinder返回的是this,也就是自己
IBinder *local = binder->localBinder();
//不是本地进程,即跨进程
if (!local) {
//分支一,如果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 {
//分支二,local不为空
//写入JavaBBinder将对应这一段
obj.type = BINDER_TYPE_BINDER;
// 弱引用对象
obj.binder = reinterpret_cast(local->getWeakRefs());
// this 对象,BBinder本身
obj.cookie = reinterpret_cast(local);
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
return finish_flatten_binder(binder, obj, out);
}
这里主要是分别是本地Binder还是远程Binder,对两种Binder采取了两种不同的方式。
- Binder如果是JavaBBinder,则它的localBinder会返回localBinder。
- Binder如果是BpBinder,则它的localBinder会返回null
通过上文我们知道ibinderForJavaObject就返回JavaBBinder。所以我们知道走入分支二
flat_binder_object是Binder写入对象的结构体,它对应着Binder。
flat_binder_object的handle表示Binder对象在Binder驱动中的标志,比如ServiceManager的handle为0。
flat_binder_object的type表示当前传输的Binder是本地的(同进程),还是一个Proxy(跨进程)。
通过上面代码我们知道这里取得的flat_binder_object对应的值如下
- type为BINDER_TYPE_BINDER
- binder为reinterpret_cast(local->getWeakRefs());
- cookie为reinterpret_cast(local)
binder,cookie保存着Binder对象的指针。最后调用了finish_flatten_binder()函数
1.5 Parcel::finish_flatten_binder()函数
代码在Parcel.cpp 199行
inline static status_t finish_flatten_binder(
const sp& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
return out->writeObject(flat, false);
}
finish_flatten_binder()函数主要是调用writeObject()函数将flat_binder_object写入到out里面里面,最终写入到Binder驱动中
1.6 Parcel::writeObject()函数
代码在Parcel.cpp 1035行
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:
// mObjects 数据写入
*reinterpret_cast(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 (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;
}
//分支三
if (!enoughObjects) {
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;
}
三个分支
- 如果mData和mObjects空间足够,则走分支一
- 如果mData空间不足,则扩展空间,growData()函数看前面
- 如果mObjects空间不足,则扩展空间,和mData空间扩展基本相似
调用 * reinterpret_cast
最后调用finishWrite()函数,就是调整调整 mDataPos 和 mDataSize,上面已经说过了,我就跟踪了。至此整体写入流程已经完成了。
2 Binder对象的读出
Parcel对象的读出,首先还是在Parcel.java这个类里面,对应的方法是
时序图如下:
2.1 Parcel.readStrongBinder()方法
/**
* Read an object from the parcel at the current dataPosition().
*/
public final IBinder readStrongBinder() {
return nativeReadStrongBinder(mNativePtr);
}
readStrongBinder()方法内部调用了native的nativeReadStrongBinder()方法,
2.2 Parcel.nativeReadStrongBinder()方法
private static native IBinder nativeReadStrongBinder(long nativePtr);
native方法又对应这个JNI的一个方法,通过上文我们知道,对应的是 /frameworks/base/core/jni/android_os_Parcel.cpp的android_os_Parcel_readStrongBinder()函数
2.3 android_os_Parcel_readStrongBinder()函数
代码在android_os_Parcel.cpp 429行
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast(nativePtr);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
函数里面先调用了Parcel-Native的readStrongBinder()函数,然后又用这个函数的返回值作为参数调用了javaObjectForIBinder()函数。
2.4 readStrongBinder()函数
代码在Parcel.cpp 1134行
sp Parcel::readStrongBinder() const
{
sp val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
主要就是调用了unflatten_binder()函数
readStrongBinder 其实挺简单的,是本地的可以直接用,远程的那个 getStrongProxyForHandle 也是放到后面 ServiceManager 再细说。到这里目标进程就收到原始进程传递过来的 binder 对象了,然后可以转化为 binder 的 interface 调用对应的 IPC 接口。
2.5 unflatten_binder()函数
代码在android_os_Parcel.cpp 293行
status_t unflatten_binder(const sp& proc,
const Parcel& in, sp* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
//如果是Bn的话,本地直接强转
*out = reinterpret_cast(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
//如果是Bp的话,要通过handle构造一个远程的代理对象
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
这里说下这里的两个分支
- BINDER_TYPE_BINDER分支:是Bn意味着是同一个进程
- BINDER_TYPE_HANDLE分支:是Bp意味着是跨进程
再来分下一下这个函数,主要就是两个两个流程
-
- 从Binder驱动中读取一个flat_binder_object对象flat
-
- 根据flat对象的type值来分别处理。如果是BINDER_TYPE_BINDER,则使用cookie中的值,强制转换成指针。如果是BINDER_TYPE_HANDLE,则使用Proxy,即通过getStringProxyForHandle()函数,并根据handle创建BpBinder。
ProcessState::getStrongProxyForHandle()函数后续讲解Binder的时候再详细讲解,这里就不说了。那我们来看一下finish_unflatten_binder()
2.6 finish_unflatten_binder()函数
代码在android_os_Parcel.cpp 286行
inline static status_t finish_unflatten_binder(
BpBinder* /*proxy*/, const flat_binder_object& /*flat*/,
const Parcel& /*in*/)
{
return NO_ERROR;
}
返回NO_ERROR。这时候我们回到了javaObjectForIBinder()函数。
2.7 javaObjectForIBinder()函数
javaObjectgForBinder与ibinderForJavaObject相对应的,把IBinder对象转换成对应的Java层的Object。这个函数是关键。
代码在android_os_Parcel.cpp 547行
jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
{
if (val == NULL) return NULL;
// One of our own!
if (val->checkSubclass(&gBinderOffsets)) {
//如果是本地的,那么会直接进入这个部分代码,因为这个val
//是写入的时候的同一个对象,gBinderOffsets也是一致的。如
//果val是一种Poxy对象,则不然,会继续往下执行,找到一个
//Proxy对象
// One of our own!
jobject object = static_cast(val.get())->object();
LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
// For the rest of the function we will hold this lock, to serialize
// looking/creation of Java proxies for native Binder proxies.
AutoMutex _l(mProxyLock);
// Someone else's... do we know about it?
// BpBinder没有带proxy过来
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = jniGetReferent(env, object);
if (res != NULL) {
ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
return res;
}
LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
// 创建一个proxy
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
// 给object的相关字段赋值
LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
// The proxy holds a reference to the native object.
// 把BpBinder(0) 赋值给BinderProxy的mObject
env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
val->incStrong((void*)javaObjectForIBinder);
// The native object needs to hold a weak reference back to the
// proxy, so we can retrieve the same proxy if it is still active.
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Also remember the death recipients registered on this proxy
sp drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast(drl.get()));
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}
- 首先判断是不是同一个进程,如果是同一个进程,则val是JavaBBinder。那么在checkSubclass()函数中,它所包含的gBinderOffsets指针参数传入的gBinderOffsets的指针必然是同一个值,则满足if条件,直接将指针强制转化为JavaBBinder,返回对应的jobject。如果是不是同一个进程,那么val也就是BpBinder。
- 其次,在BpBinder对象中查找是否保存相关的BinderProxy的对象,如果有,向Java层返回这个对象。如果没有,则创建一个BinderProxy对象,并将新创建的BinderProxy对象,attach到BpBinder对象中。
在构造Java对象的时候,上面用到了 binderproxy_offsets_t 结构体 ,那我们就来看下这个结构体
2.8 binderproxy_offsets_t 结构体
代码在android_os_Parcel.cpp 95行
static struct binderproxy_offsets_t
{
// Class state.
jclass mClass;
jmethodID mConstructor;
jmethodID mSendDeathNotice;
// Object state.
jfieldID mObject;
jfieldID mSelf;
jfieldID mOrgue;
} gBinderProxyOffsets;
gBinderProxyOffsets的初始化是在虚拟机启动的时候(即在 AndroidRuntime::start),最终赋值是在android_os_Parcel.cpp 的中1254行的函数
int_register_android_os_BinderProxy()中,代码如下
static int int_register_android_os_BinderProxy(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, "java/lang/Error");
1257 gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
clazz = FindClassOrDie(env, kBinderProxyPathName);
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "", "()V");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;)V");
gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
"Ljava/lang/ref/WeakReference;");
gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
return RegisterMethodsOrDie(
env, kBinderProxyPathName,
gBinderProxyMethods, NELEM(gBinderProxyMethods));
}
通过上面代码我们知道这个类型是android.os.BinderProxy,也就是说代理端Java层的对象是android.os.BinderProxy
2.9
- 每个进程都会保存多个当前调用过的BpBinder对象
- 每个BpBinder对象都会保存与之对应的Java层的BinderProxy
将创建的BinderProxy attach到BpBinder的意义在于,通过这种方式,Java应用层偏饭获取同一个Service的IBinder时,获取的是同一个BinderProxy。
3 Parcel读取写入总结
- 上面介绍了Parcel整个写入读取的流程,最后代替Binder传输的是
flat_binder_object。 - 在Parcel-Native中,根据跨进程和非跨进程,flat_binder_object的值是不一样的:跨进程的时候flat_binder_object的type是BINDER_TYPE_HANDLE;非跨进程的时候flat_binder_object的type是BINDER_TYPE_BINDER。
- 客户端的Parcel读取Binder的时候,根据flat_binder_object的type值进行区分对待,返回不同的内容。而写入的时候也是一样的,根据是否是Proxy,来决定写入HANDLE还是BINDER。
- 最终这些内容都会通过ioctl与Binder驱动进行数据通信。所以最终处理不同进程间的Binder数据传输处理也只能是Binder驱动了。
- Binder对象传入Binder驱动最底层是转化为flat_binder_object传递的。
- Parcel是根据从驱动中读取的数据做出不同的处理,如果从Binder驱动中取出的flat_binder_object的type为BINDER_TYPE_HANDLE,则创建BpBinder,在Java层创建的BinderProxy返回;
- 如果读出的flat_binder_object的type为BINDER_TYPE_BINDER则直接使用cookie的指针,将它装置转为化JavaBBinder,在Java层为原来的Service的Binder对象(相同进程)。
- 在 /frameworks/base/core/jni/android_util_Binder.cpp 中有两个函数分别是ibinderForJavaObject()函数与javaObjectForIBinder()函数是相互对应的,一个是把Native中对应的IBinder对象化为Java对象,一个是将Java的对象转为化Native中对应的IBinder对象
参考
Android跨进程通信IPC之4——AndroidIPC基础2