Android Binder框架实现之Java层Binder服务跨进程调用源码分析

Android Binder框架实现之Java层Binder服务跨进程调用源码分析


Android Binder框架实现目录:

Android Binder框架实现之Binder的设计思想
Android Binder框架实现之何为匿名/实名Binder
Android Binder框架实现之Binder中的数据结构
Android Binder框架实现之Binder相关的接口和类
Android Binder框架实现之Parcel详解之基本数据的读写
Android Binder框架实现之Parcel read/writeStrongBinder实现
Android Binder框架实现之servicemanager守护进程
Android Binder框架实现之defaultServiceManager()的实现
Android Binder框架实现之Native层addService详解之请求的发送
Android Binder框架实现之Native层addService详解之请求的处理
Android Binder框架实现之Native层addService详解之请求的反馈
Android Binder框架实现之Binder服务的消息循环
Android Binder框架实现之Native层getService详解之请求的发送
Android Binder框架实现之Native层getService详解之请求的处理
Android Binder框架实现之Native层getService详解之请求的反馈
Android Binder框架实现之Binder Native Service的Java调用流程
Android Binder框架实现之Java层Binder整体框架设计
Android Binder框架实现之Framework层Binder服务注册过程源码分析
Android Binder框架实现之Java层Binder服务跨进程调用源码分析
Android Binder框架实现之Java层获取Binder服务源码分析


前言

  兜兜转转几个春秋,通过前面的博客Android Binder框架实现之Framework层Binder服务注册过程源码分析Android Binder框架实现之Java层获取Binder服务源码分析我们终于将Android Framework层的Binder框架整体分析完毕了。通过前面的基本功修炼,我们通过ServiceManagerProxy的getService获取远程Java BInder服务的代理端IXXXServiceProxy了,而我们获取服务端代理端的最终目的并不是为了获取而获取,我们得到它的最终目的是达到使用远程Java Binder服务提供的各种功能。而我们今天的博客将来了结了这最后一段逆缘,即在我们获取远程Java Binder服务的的代理端IXXXServiceProxy以后怎么通过它跨进程调用到Java Binder服务实体端从而使用Java Binder服务(这里我们以AMP跨进程调用AMS为例说明)。

  • 注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:
framework/base/core/java/android/os/
  ---IInterface.java
  ---IServiceManager.java
  ---ServiceManager.java
  ---ServiceManagerNative.java(内含ServiceManagerProxy类)

framework/base/core/java/android/os/
  ---IBinder.java
  ---Binder.java(内含BinderProxy类)
  ---Parcel.java
frameworks/native/libs/binder/IPCThreadState.cpp

framework/base/core/java/com/android/internal/os/
  ---BinderInternal.java

framework/base/core/jni/
  ---AndroidRuntime.cpp
  ---android_os_Parcel.cpp
  ---android_util_Binder.cpp
  
frameworks/native/libs/binder/BpBinder.cpp
frameworks/native/include/binder/IBinder.h
frameworks/native/libs/binder/Binder.cpp
frameworks/native/include/binder/Parcel.h
frameworks/native/libs/binder/Parcel.cpp
frameworks/base/core/jni/core_jni_helpers.h
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks//base/core/java/android/app/ActivityThread.java

  • 为了后续的书写方便会将ActivityManagerService简述为AMS,ServiceManager简述为SM,ServiceManagerProxy简述为SMP,ServiceManagerNative简述为SMN,ActivityManagerProxy简称AMP, ActivityManagerNative简称AMN以上特此申明!并且在这里附上Java跨进程调用Framework Binder服务整体流程图:

在这里插入图片描述



一.AMP跨进程调用AMS服务流程分析


1.1 AMP服务代理端的获取

  还记得我们在前面博客中Android Binder框架实现之Java层获取Binder服务源码分析中我们以AMS服务为例,讲述了怎么获取其远程服务代理端AMP,在正式开始流程分析前我们先看看AMS的家族关系的类图结构(包括服务端和远程服务代理端的),如下:
在这里插入图片描述

  • 这里我们可以看到AMS服务代理端AMP继承于接口业务类IActivityManager,从而可以使第三方应用在获取到AMS远程代理端以后可以忽略跨进程调用如同使用本地对象一样使用AMS提供的业务逻辑方法。而其AMP的数据传输最后都是通过mRemote的transact方法来实现的,而这个mRemote又是AMP在构造的时候new AMP(BinderProxy(BpBidner(handle)))传入的。

  • 而我们的BinderProxy由对应的BpBinder构造而来,且BinderProxy与BpBinder相互引用,BinderProxy的transact()方法会通过JNI层调用到对应的成员变量mObject中的BpBinder的transact()方法;

  • 通过JNI层的支持,native层的JavaBBinder对象对java层的Binder持有引用,在Binder初始化时即通过JavaBBinderHolder持有了Java Binder的引用,Java Binder的引用保存在JavaBBinder的成员变量mObject中,因此AMP响应时是由JavaBBinder这个BBinder的子类通过JNI层通知上层Java Binder继而实现java层服务的响应请求。

总之关于这三者的具体关系的获取,可以参见Android Binder框架实现之Java层获取Binder服务源码分析博客,而三者之间的关系可以使用如下的示意图来表示,如下:

在这里插入图片描述

1.2 AMP跨进程调用AMS远程服务

//ActivityManagerNative.java
class ActivityManagerProxy implements IActivityManager
	    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
	            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
	            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
	        Parcel data = Parcel.obtain();
	        Parcel reply = Parcel.obtain();
	        data.writeInterfaceToken(IActivityManager.descriptor);
	        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
			..
			//这里的mRemote指向BinderProxy
	        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
	        ...
	        reply.readException();
	        int result = reply.readInt();
	        reply.recycle();
	        data.recycle();
	        return result;
	    }
}

  这里我们以AMP的startActivity方法的跨进程调度为例,这个流程和前面博客Java层获取Binder服务的调度逻辑是一样的,其基本操作逻辑如下:

  • 先执行BinderProxy对象的transact()方法通过JNI层的传递调用到Native层中对应的BpBinder(handle)的transact()方法
  • 接下来就是通过BpBinder中的IPCThreadState与Binder驱动进行交互了

上述的流程我们已经烂熟于心了,这个不是重点,重点是AMS怎么获取Binder服务传递过来的信息,并相应服务请求!


1.2 AMS服务怎么响应远程端请求

  在前面的博客 Android系统启动之SystemServer大揭秘中我们分析到了在system_server进程的其中过程中会调用如下的代码逻辑:

	//app_main.cpp
    virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();//开启binder线程,是不是有中似曾相识的熟悉感觉
    }

ProcessState::self()是单例模式,主要工作是调用open()打开/dev/binder驱动设备,再利用mmap()映射内核的地址空间,将Binder驱动的fd赋值ProcessState对象中的变量mDriverFD,用于交互操作。startThreadPool()是创建一个新的binder线程,不断进行talkWithDriver(),在binder系列文章中Android Binder框架实现之Binder服务的消息循环有关于该部分的详细的讲解,这里就不过多阐述了。这样将当前线程注册到Binder驱动程序中,这样我们创建的线程就加入了Binder线程池中,这样新创建的system_server进程就支持Binder进程间通信了。即我们system_server进程中IPCThreadState线程会不断向Binder驱动查看是否有请求数据的到来,而此时AMP的数据通过Binder驱动传递过来了,我们的AMS服务的进程就会通过IPCThreadState线程接收到。至于这其中的复杂调用逻辑过程,就不是本篇的重点可以参见Android Binder框架实现之之Binder Native Service的Java调用流程

1.2.1 AMS读取Binder驱动发送来的请求数据

  通过前面的章节我们知道system_server进程会调用IPCThreadState::joinThreadPool()进入消息循环的,而joinThreadPool()又会通过getAndExecuteCommand()调用到talkWithDriver()来和Binder驱动交互的。因此,Binder驱动返回到用户空间之后,会进入talkWithDriver()。

//IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    ...
    do {
        ...
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            ...
        ...
    } while (err == -EINTR);

    ...

    if (err >= NO_ERROR) {
        // 清空已写的数据
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        // 设置已读数据
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        ...
        return NO_ERROR;
    }

    return err;
}

此时system_server进程从Binder驱动中返回后,继续处理,talkWithDriver()会清除已经发送的数据。然后,便返回到getAndExecuteCommand()中,我们接着继续分析getAndExecuteCommand()的处理逻辑。

status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;

    // 和Binder驱动交互,此时已经从该逻辑中返回
    result = talkWithDriver();
    if (result >= NO_ERROR) {
        ...
        // 读取mIn中的数据
        cmd = mIn.readInt32();
        ...

        // 调用executeCommand()对数据进行处理。
        result = executeCommand(cmd);
        ...
    }

    return result;
}

getAndExecuteCommand()会取出Binder反馈的指令,然后再调用executeCommand()根据指令进行解析,还能啥呢,老白干继续干呗!

status_t IPCThreadState::executeCommand(int32_t cmd) 
{
    BBinder* obj; 
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;

    switch (cmd) {
        ...
        case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            ...

            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(size_t), freeBuffer, this);

            ...

            Parcel reply;
            ...
            if (tr.target.ptr) {
                sp<BBinder> b((BBinder*)tr.cookie);
                const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
                if (error < NO_ERROR) reply.setError(error);

            } else {
                ...
            }

            if ((tr.flags & TF_ONE_WAY) == 0) {
                sendReply(reply, 0);
            } else {
                ...
            }
            ...

        }
        break;

        ...
    }

    ...
    return result;
}

这里可以看到最终会调用到BBinder的transact()处理请求进而调用虚函数onTransact(),而我们知道AMS服务通过JNI在C++层创建了和Java层Binder对应的JavaBBinder(C++)重写了onTransact(),在正式分析JavaBBinder之前,我们先来看看Binder(Java)和JavaBBinder(C++)之间的关系图:
在这里插入图片描述

1.2.2 AMS调用JavaBBinder的onTransact()响应请求

	//android_util_binder.cpp
    virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {
        JNIEnv* env = javavm_to_jnienv(mVM);

        ALOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);

        IPCThreadState* thread_state = IPCThreadState::self();
        const int32_t strict_policy_before = thread_state->getStrictModePolicy();

		//通过JNI调用Binder(Java)中的execTransact方法
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

        if (env->ExceptionCheck()) {
            jthrowable excep = env->ExceptionOccurred();
            report_exception(env, excep,
                "*** Uncaught remote exception!  "
                "(Exceptions are not yet supported across processes.)");
            res = JNI_FALSE;

            /* clean up JNI local ref -- we don't return to Java code */
            env->DeleteLocalRef(excep);
        }
        ...
        return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
    }

  这里没有什么好说的了,通过前面的博客我们知道AMS,J avaBBinderHolder,JavaBBinder存在相互引用关系,三者之间的关系详见前面的示意图,即三者之间可以通过纽带找到对方。有了以上信息的加持,我们知道最终通过JNI调用Binder(Java)中的execTransact方法,

1.2.3 JavaBBinder通过JNI调用到Java层的Binder的execTransact方法

	//Binder.java
    private boolean execTransact(int code, long dataObj, long replyObj,
            int flags) {
        Parcel data = Parcel.obtain(dataObj);
        Parcel reply = Parcel.obtain(replyObj);
        boolean res;

        try {
        	//此时会调用子类重写之后的onTransact方法
            res = onTransact(code, data, reply, flags);
        } catch (RemoteException|RuntimeException e) {
        	...
        }
    }

  这里没有什么,直接调用onTransact方法进行下一步的处理,但是通常不会由JavaBBinder进行处理,而是交由其子类进行相关的处理。

1.2.4 JavaBBinder子类Stub处理onTransact方法

  按照通常逻辑一般是交由服务端的Stub类中进行处理onTransact方法的,但是我们的AMS服务比较奇怪并没有接着aidl语言生成Stub接口(这里说明一个道理,我们可以不通过aidl也可以实现Java Binder),而是直接手动硬编写出了Stub类型的子类AMN,我们看看其onTransact处理方法:

    @Override
    //ActivityManagerNative.java
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {	
        ....
        int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
        }
   }

1.2.5 Stub子类AMN处理实际业务方法调用

  AMN中的的onTransact方法中中根据code值执行相应的方法,这里即调用startActivity方法,最终则调用PAMS类(继承自AMN)中相应的startActivity()接口方法,执行该接口方法的功能。

如上就是整个以AMP为例说明Java层Binder服务实现跨进程调用的基本流程分析,感觉有些还是没有说清楚。



总结

  关于Android Binder框架实现之Java层Binder服务跨进程调用源码分析就告一段落了,感觉依然有点意犹未尽(主要是感觉还是没有将整个脉络分析得很是清楚),但是也不得不先到这里了,在结束本篇之前我们还是老规矩总结一番!

  • Java层Binder和Native层Binder的关系是什么

  这是一个好问题,我想这也是许多小伙伴们心中所想!我们将Android Native Binder比作是一颗树根的话,那么Java层的Binder就是就是这棵树的枝叶了,枝叶是不能离开根的,而根离开了枝叶依然可以存活(不接受反驳),其实我们可以把Java层的Binder理解我对Native层Binder的一种扩展或者封装,J_Binder依赖于C_Binder而存在,但是也是由于J_Binder的存在才能将Android Binder的框架发扬光大,提供了非常完善的Android生态功能(不可能让每个应用开发者都通过C++调用Native层服务吗,不信你让Android试试可行否)。所以总之用一句话概括Java层的Binder和Native层的Binder关系就是C_Binder是根基而J_Binder是扩展,二者相辅相成进而促成了Binder在Android世界不可动摇的地位。


  • 能从整整体上概述下Java层Binder服务跨进程调用流程吗

  这个也是我最后想说的,对于前面在分析Java层Binder服务跨进程调用流程中涉及的各种类的转换和调用逻辑你是否依然晕头转向呢,对于Java层Binder服务跨进程调用我们可以使用下述示意图来高度概括(省略和Binder驱动交互的部分):
在这里插入图片描述

  • 首先第三方客户端获取servicemanager服务进程的远程代理端SMP,进而通过SMP获取XXXService端的服务代理端IXXXServiceProxy来和服务端XXXService端通过Binder驱动来交互

  • XXXService端持有一个Binder线程IPCThreadState会和Binder驱动进行交互,监听到有发向自己的请求即通过Native层的BBinder子类JavaBBinder经由JNI层通知到Java层的Binder子类IXXService.Stub,从而通知到Stub的子类Service的实现方法

  • Client端通过SMP获取到XXXService的服务代理IXXXServiceProxy,而IXXXServiceProxy持有一个BinderProxy类型的成员变量mRemote,该变量通过JNI层技术与Native层的BpBinder对象相互持有引用,而BpBinder中的IPCThreadState与Binder驱动进行交流,通过这样的联系达到Java层Client端与Binder驱动交流的目的
    在这里插入图片描述

  • JavaBBinder对象通过JavaBBinderHolder与Java层的Binder类相互引用联系,而JavaBBinder的父类BBinder与BpBinder是一一对应的,这样Client端Binder代理对象经过Binder驱动与Server端Binder对象也建立了对应的
    关系.

在这里插入图片描述

  • Java层的XXXService是Binder的子类最后通过重写onTransact方法通过不同的code标记不同的任务进行调用具体的业务方法完成了远程客户端的业务具体请求

最后套用我们伟大领袖邓工的一句话不管百贸还是黑猫抓到老鼠的都是好吗哦,而我们的Binder的架构不论采用Native语言还是Java语言,都是客户端要把请求传递到Binder驱动,然后服务端端从Binder驱动中获取数据,通进而完成整个Binder的发送请求和接收回复。但是这里关于客户端和服务端不是绝对的而是相对的,譬如XXXService在注册服务时相对于servicemanager进程就是客户端,但是它相对于使用该服务的第三方应用来说又是服务端,所以这里必须辩证的看待服务端和客户端。



写在最后

  Java层Binder服务跨进程调用源码分析,真的完结了,好了啥也不说了,各位小伙伴先告辞了。如果对你有帮助,欢迎关注和点赞,当然拍砖也是OK的,各位江湖见!

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页