Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出

移步系列Android跨进程通信IPC系列
Parcel类详解
每一个java端的Binder对象(服务端)在初始化时都会对应一个native对象,类型是BBinder,它继承于IBinder类

1 Binder对象的写入

时序图


Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出_第1张图片
5713484-9635f897d3851952.jpeg

通过 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(mData+mDataPos) = val; 写入mObject

最后调用finishWrite()函数,就是调整调整 mDataPos 和 mDataSize,上面已经说过了,我就跟踪了。至此整体写入流程已经完成了。

2 Binder对象的读出

Parcel对象的读出,首先还是在Parcel.java这个类里面,对应的方法是

时序图如下:


Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出_第2张图片
5713484-ad4370e8a1fdf7a8.png

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意味着是跨进程

再来分下一下这个函数,主要就是两个两个流程

    1. 从Binder驱动中读取一个flat_binder_object对象flat
    1. 根据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

Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出_第3张图片
5713484-13bb6c483859927f.png

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

你可能感兴趣的:(Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出)