Android Binder跨进程与非跨进程的传输异同源码分析

Posted _houzhi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Binder跨进程与非跨进程的传输异同源码分析相关的知识,希望对你有一定的参考价值。

前两天看到Service的onBind函数返回值的三种情况(扩展Binder对象,Messenger,aidl),一直在想他们内部的实现有什么不一样的,网上很多文章都介绍了Service的绑定过程,但是并没有介绍对于跨进程与非跨进程,对于不同的返回值,其具体有什么区别,以及具体是怎么实现的。这篇文章就根据源码分析android究竟是在哪部分来控制跨进程与非跨进程Binder的传输的,Binder究竟是怎么传输的。

首先看一下Service的绑定中,Binder跨进程与非跨进程的区别代码中的体现。

Service跨进程绑定与非跨进程绑定的表象

我们都知道如果使用aidl跨进程在onServiceConnection函数中需要这样使用new TestInterface.Stub.Proxy(binder)(TestInterface是aidl声明的接口,并且在Service的onBind中返回new TestInterface.Stub())。如果是普通扩展Binder对象,那么直接强制类型转换就可以了TestBinder(binder)(TestBinder是Binder子类,并且在Service的onBind中返回new TestBinder())。如果想知道onServiceConnection的参数binder具体是怎么样的,其实直接在ServiceConnection的onServiceConnection回调函数中,以及在Service的onBind中分别打印一下Binder就好了,如下代码

//Service onBind
public Binder onBind()
    Binder binder ; //这里并没有初始化,可以使扩展Binder,也可以是aidl的Stub
    Log.i("TestLog","onBind: "+binder.hashCode()+","+binder.toString());
    return binder;


//ServiceConnection
ServiceConnection connection = new ServiceConnection()
    public void onServiceConnection(ComponentName component, Binder binder)
        Log.i("TestLog","onServiceConnection: "+binder.hashCode()+","+binder.toString());//跨进程时会binder是一个BinderProxy对象,非跨进程时跟onBind返回的对象一模一样。
    

通过设置Service的android:process,可以将Service远程进程与相同进程进行实验。

通过log可以发现,如果是跨进程的话,在ServiceConnection的onServiceConnection返回的结果是一个BinderProxy对象,与onBind中打印的结果是不一致的。如果是非跨进程,在onServiceConnection与onBind的打印的内容是一模一样的。使用aidl跨进程与非跨进程,使用扩展Binder非跨进程都是一样的。这里跟onBind是返回扩展Binder,返回扩展aidl自动产生Stub类还是Messenger是没有关系的,只跟是否跨进程有关

从这里可以看出,如果是跨进程则返回BinderProxy,非跨进程则返回原来在onBind中返回的对象。我困惑的问题是,Android是怎么实现这个根据不同的进程来返回不同的结果呢?因为是Service绑定,所以先看一下Service的绑定过程。

Service绑定过程

绑定过程

首先简单介绍一下Service的绑定过程:
1. Activity通过bindService请求绑定相应的Service(由Intent指定),并设置了ServiceConnection回调接口。
2. 实际上会调用ContextImpl.bindService,然后调用ActivityManagerNative.getDefault().bindService,也就是通过IPC调用ActivityManagerService的bindService函数,之后会调用ActiveServices的bindServiceLocked。
3. 在bindServiceLocked中,先通过retrieveServiceLocked创建ServiceRecord,这个就是对应着我们想要绑定的Service。
4. 然后根据结果创建ConnectionRecord,将ServiceConnection保存在这里面。
5. 判断指定的Service是否启动,如果没有则启动服务,通过ApplicationThreadProxy(app.thread)远程调用ApplicationThread的scheduleCreateService来发送Message给主线程消息循环(ActivityThread,ActivityThread.H),启动服务。如果是远程进程服务,ActivityManagerService会开一个新的进程(startProcessLocked),然后将Service运行在新的Process中。
6. 绑定服务,通过ApplicationThreadProxy(app.thread)远程调用ApplicationThread的scheduleBindService来发送Message给主线程消息循环(ActivityThread,ActivityThread.H),绑定服务(handleBindService)。这里看一下源码:

try 
    if (!data.rebind) 
        IBinder binder = s.onBind(data.intent);
        ActivityManagerNative.getDefault().publishService(
            data.token, data.intent, binder);
     else 
        s.onRebind(data.intent);
        ActivityManagerNative.getDefault().serviceDoneExecuting(
            data.token, 0, 0, 0);
    
    ensureJitEnabled();
 catch (RemoteException ex) 
  1. 在绑定服务中,调用Service的onBind方法得到Binder,并且通过ActivityManagerNative.getDefault().publishService发布服务。
  2. 在publishServcie中使用ServiceRecord获取ConnectionRecord,最终调用保存在ConnectionRecord里面的ServiceConnection的onServiceConnection函数。其中ServiceRecord,ServiceConnection都是Binder类,可以通过IPC访问。

这里只是简单介绍一下绑定Service的流程,也并没有把具体对象名称写出来。如果想要更详细的了解整个过程可以去看一下老罗的博客,其实最好是照着别人的博客看一下源码。

绑定过程是否处理了不同onBind返回值

我一直觉得是在绑定服务的过程中会针对不同的onBind返回值有不同的处理,但实际上并没有。看第6步中的源码就知道了,只是把onBind的返回值当作IBinder来看。另外上面的绑定过程中,并没有针对不同的IBinder类型进行特殊处理。我想实际上在onBind函数中返回一个BinderProxy也是OK的。

既然绑定过程中没有对不同进程进行处理,那么只能看看更底层的传输过程了,Android中传输都是用Parcel类型。所以只能看看Parcel传输数据的过程中是否有特殊处理。而实际上确实是在Parcel中有体现出来了跨进程与非跨进程的区别。下面看看Parcel读取和写入Binder。

Parcel中写入读取Binder

看一下Parcel关于Binder的接口:

public final void writeStrongBinder(IBinder val) 
    nativeWriteStrongBinder(mNativePtr, val);

public final IBinder readStrongBinder() 
    return nativeReadStrongBinder(mNativePtr);

Parcel写入Binder

Parcel写入Binder是通过writeStrongBinder的,Java层的nativeWriteStrongBinder是一个native函数,对应的JNI实现为:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)

    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) 
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) 
            signalExceptionForError(env, clazz, err);
        
    

其中ibinderForJavaObject是将java层的对象转换成native层的Binder对象,实际上对应的是JavaBBinder。

然后native层的parcel会调用它的writeStrognBinder:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)

    return flatten_binder(ProcessState::self(), val, this);

最终调用flatten_binder:

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) 
        IBinder *local = binder->localBinder();   //JavaBBinder返回的是this,也就是自己
        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 // 写入JavaBBinder将会对应这一段。
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);   //把对应的JavaBBinder的指针转换成整形uintptr_t
        
     else 
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    

    return finish_flatten_binder(binder, obj, out); //finish_flatten_binder是将obj写入到out里面。

这里就根据是本地binder还是远程binder,对Binder的写入采取了两种不同的方式。Binder如果是JavaBBinder,则它的localBinder会返回localBinder,如果是BpBinder则localBinder会为null。我们写入的时候,ibinderForJavaObject就返回的是JavaBBinder。flat_binder_object是Binder写入的时候的对象,它对应着Binder。handle表示Binder对象在Binder驱动中的标志,比如ServiceManager的handle为0。type表示当前传输的Binder是本地的(同进程),还是一个proxy(跨进程)。binder,cookie保存着Binder对象的指针。finish_flatten_binder会将obj写入到out里面,最终写入到Binder驱动中,写入部分也就完了。从上面的流程可以看出,在写入的时候,Parcel会针对不同的Binder(BBinder/JavaBBinder,BpBinder)有不同的处理,而他们确实就是对应着跟Service端Binder是同一个进程的还是不同进程的情况。

Parcel读取Binder

接下来就是读取了,同样的,Java层的Parcel也是通过native函数来读取的。在这里我们从最底层开始分析,首先从unflatten_binder开始:

unflatten_binder:

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)

    const flat_binder_object* flat = in.readObject(false);

    if (flat) 
        switch (flat->type) 
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        
    
    return BAD_TYPE;

首先从Binder驱动中读取一个flat_binder_object—flat。flat的处理是关键,它会根据flat->type的值分别处理,如果是BINDER_TYPE_BINDER,则使用cookie中的值强制转换成指针。如果是BINDER_TYPE_HANDLE,则使用Proxy,getStringProxyForHandle会返回BpBinder。而调用unflatten_binder的是native层的Parcel的readStringBinder。

sp<IBinder> Parcel::readStrongBinder() const

    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;

调用readStrongBinder的是jni函数的实现:

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)

    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) 
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    
    return NULL;

javaObjectForIBinder与ibinderForJavaObject相对应,把IBinder对象转换成对应的Java层的Object。这个函数是关键。看看它的实现:

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)

     if (val == NULL) return NULL;

     if (val->checkSubclass(&gBinderOffsets))   //如果是本地的,那么会直接进入这部分代码,因为这个val是写入的时候同一个对象,gBinderOffsets也是一致。如果val是一种proxy对象,则不然,会继续往下执行找到一个Proxy对象。
         // One of our own!
         jobject object = static_cast<JavaBBinder*>(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?
     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);
    

    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);//gBinderProxyOffsets保存的是Java层BinderProxy的信息,这里也是创建BinderProxy。
    if (object != NULL) 
        LOGDEATH("objectForBinder %p: created new proxy %p !\\n", val.get(), object);
        // The proxy holds a reference to the native object.
        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<DeathRecipientList> drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));

        // Note that a new object reference has been created.
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    

    return object;
//IBinder的checkSubclass。
checkSubclass(const void* subclassID) const

    return subclassID == &gBinderOffsets;

上面的处理中,如果是跟Service是同一个进程,也就是val是JavaBBinder。那么在checkSubclass中,它所包含的gBinderOffsets指针与参数传入的gBinderOffsets的指针必然是同一个值,则满足if条件。直接将指针强制转换成JavaBBinder,返回对应的jobject。如果是不同的进程,那么val也就会是BpBinder,最终会返回一个BinderProxy。不同的进程这一部分网上很多介绍Binder的文章都介绍了,可以参阅老罗或邓凡平的书。

Parcel读取写入总结

上面介绍了Parcel整个写入读取的流程,最后代替Binder传输的是flat_binder_object。在native的Parcel中,根据跨进程还是非跨进程,flat_binder_object的值是不一样的:
1. 跨进程的时候flat_binder_object的type为BINDER_TYPE_HANDLE,
2. 非跨进程的时候flat_binder_object的type为BINDER_TYPE_BINDER。

在这里已经可以发现跨进程与非跨进程的时候传输的数据的区别了。客户端的Parcel在读取Binder的时候,根据是flat_binder_object的type的值进行区分对待,返回不同的内容。而写入的时候也是一样的,根据是否是Proxy,来决定写入HANDLE还是BINDER。

最终这些内容都会通过ioctl与Binder驱动进行数据通信。所以最终处理不同进程之间的Binder数据传输处理的也只能是Binder驱动了。

Binder驱动的处理

关于如何判断读取的时候是跟Service同一个进程还是不同的进程,脑子里根据操作系统的知识想想基本就能够想到怎么去实现了。因为每个进程进行系统调用陷入内核的时候,内核的当然是可以知道当前进入内核空间的进程的信息了啦,这样就可以判断当前请求读取信息的是跟Service同一个进程还是不同的进程了。

实际上Binder驱动保存着Service端的Binder地址和handle的信息,将两者相互映射,根据不同的服务端进程和客户端进程来区别处理。这篇文章详细介绍了Binder驱动深入分析Android Binder 驱动

总结

实际上真正控制跨进程与非跨进程返回Binder类型的,不是绑定服务的过程,而是Binder驱动。如果是与Service端同一个进程则返回Binder(在底层是Binder指针),如果是不同的进程则返回handle。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对象(相同进程)。

这样每次从Binder驱动中读取IBinder对象,都会这样:不同进程则用handle,同进程则用Service端的Binder地址。每个Binder对象的接口参数里面可能还有Binder变量,但同样按照上面的方式传输。其实如果是handle的话,通信就需要通过Binder驱动去做。

虽然是从Service的绑定过程中想到这个问题,但是Service的绑定过程并没有针对跨进程与非跨进程对Binder传输有过处理,所以Android中任何地方的Binder跨进程与非跨进程的传输都是这样的。从Binder驱动读取Binder对象的时候,Binder驱动判断请求的进程与Service端的进程是否相同,如果相同则Binder驱动传出的数据为Service端Binder的指针(flat_binder_object的type为BINDER),如果不同则Binder驱动传出的数据为Service端Binder的handle(flat_binder_object的type为HANDLE)。


积跬步,至千里;积小流,成江海

以上是关于Android Binder跨进程与非跨进程的传输异同源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 跨进程通信-Binder机制之ServiceManager对系统Service的管理

Android 跨进程通信-Binder机制传输数据限制—罪魁祸首Binder线程池

ContentProvider学习笔记

Android跨进程通信Binder机制与AIDL实例

Android跨进程通信Binder机制与AIDL实例

Carson带你学Android:全面剖析Binder跨进程通信原理