Android Binder框架实现之何为匿名/实名Binder

    Android Binder框架实现之何为匿名/实名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框架实现系列在我奋发图强下基本也告一段落了,准备完结了,但是有个小伙伴私信我询问我何为匿名/实名 Binder!说实话刚看到这个问题,刚开始的时候是有点懵的,Android的Binder不都是以实名注册到servicemanager进程中的吗,但是大概思索一番后发现也不尽然发现还有一些特例并不是如此,就有一些独立于上述框架而存在着。有哪些呢,我们Android源码中比较常见的(我后面一搜源码发现还比较多),这里我列举两个大伙应该耳熟能详的案例:

  • 还记得我们我们启动Android四大组件的Service的两种方式吗,有一种就是通过bindService获取了远程Service的代理端IBinder,而此时远程Service端并没有注册到servicemanager进程中,但是我们确实获取到了远程Service的代理端,然后调用它实现了远程RPC调用(这其中还牵涉到另外一个匿名Binder就是IServiceConnection)

  • 另外一个就是在Android应用进程创建的过程中会调用ActivityThread中的attach方法将ApplicationThread Binder服务代理端attach到AMS中,逻辑如下:

  final ApplicationThread mAppThread = new ApplicationThread();
  final IActivityManager mgr = ActivityManagerNative.getDefault();
  try {
      mgr.attachApplication(mAppThread);
  } catch (RemoteException ex) {
      throw ex.rethrowFromSystemServer();
  }

示意图如下,这里不做过多分析,感兴趣的小伙伴们可以参见博客Android应用进程创建流程大揭秘,在这里小伙们有这个概念就好了。

在这里插入图片描述

  前面说了这么一大堆,我想小伙伴们应该也知道了我本篇的重点了,这里我将从整体上重点介绍一下何为匿名Binder,及其怎么使用。当然匿名Binder也并不是玩去了什么新花样,只是在原来的Binder框架基础上扩展了一下罢了,仅此而已。

注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:

kernel/drivers/staging/android/binder.c

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
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java


一.实名Binder概述

1.1 Binder框架概述

  在正式开始介绍实名Binder我们先回顾回顾Binder的整体架构图,如下所示:
在这里插入图片描述

  对于该框架,我们不重点分析(因为本篇的重点不在于此),通过这个框架图我们可以看到Binder框架基本可以分为四个模块,Client端,Service端,servicemanager进程端,Binder驱动端。而Binder框架从功能上划分可以分为:

  • Service通过Binder驱动向servicemanager进程注册服务
  • Client端通过Binder驱动向servicemanager进程获取服务
  • Clinet端通过获取的Binder服务代理端,RPC远程Binder服务业务逻辑

大伙有发现没有Binder框架的三个基本功能都离不开servicemanager进程的协助,符合上述Binder的都可以认为是正统上的实名Binder。


1.2 何为实名Binder

  这个吗很好理解,只要遵循了Android Binder的基本框架即OK了,既满足了向servicemanager注册服务,通过servicemanager进程获取服务的都可以被称之为正统的实名Binder(即能带入下面的场景就OK了)。
在这里插入图片描述


1.3 怎么查询实名Binder

  什么,实名Binder你还是不理解,那只能上绝招了。如果通过Android内置的命令service list可以列举出来的都是实名Binder,那service是用来干啥的呢,service的格式如下:

XXX:# service -h
Usage: service [-h|-?]
       service list
       service check SERVICE
       service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR ] ...
Options:
   i32: Write the 32-bit integer N into the send parcel.
   i64: Write the 64-bit integer N into the send parcel.
   f:   Write the 32-bit single-precision number N into the send parcel.
   d:   Write the 64-bit double-precision number N into the send parcel.
   s16: Write the UTF-16 string STR into the send parcel.

有那些实名Binder呢(太多了,这里我只是随便挑选了几个)

XXX:# service list | grep activity
130     activity: [android.app.IActivityManager]
XXX# service list | grep package
133     package_native: [android.content.pm.IPackageManagerNative]
134     package: [android.content.pm.IPackageManager]
XXX# service list | grep window
106     window: [android.view.IWindowManager]
XXX# service list | grep media
44      media.camera.proxy: [android.hardware.ICameraServiceProxy]
45      media_projection: [android.media.projection.IMediaProjectionManager]
50      media_router: [android.media.IMediaRouterService]
51      media_session: [android.media.session.ISessionManager]
68      midi: [android.media.midi.IMidiManager]
70      audio: [android.media.IAudioService]
142     media.resource_manager: [android.media.IResourceManagerService]
143     media.sound_trigger_hw: [android.hardware.ISoundTriggerHwService]
144     media.audio_policy: [android.media.IAudioPolicyService]
146     media.player: [android.media.IMediaPlayerService]
148     media.extractor: [android.media.IMediaExtractorService]
149     media.audio_flinger: [android.media.IAudioFlinger]
150     media.metrics: [android.media.IMediaAnalyticsService]
151     media.camera: [android.hardware.ICameraService]
155     media.drm: [android.media.IMediaDrmService]


二.匿名Binder概述


2.1 匿名Binder概念简介和框架介绍

2.1.1 匿名Binder概念简介

  有了上面对实名Binder的概述,对于匿名Binder就很好理解了,即没有向servicemanagerI进程注册的Binder都是匿名Binder(虽然没有向servicemanager进程注册,但是依然可以借助Binder框架进行通信),匿名Binder进程端可以通过已经建立的Binder连接将创建的Binder实体传给目的端,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向servicemanager进程注册名字,所以是个匿名Binder。目的端将会收到这个匿名Binder的引用,可以通过这个引用向位于匿名Binder实体进进程中的中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要目的端没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。当然通过service list也查询不到了。

2.1.2 匿名Binder框架

  在前面开篇我们就介绍了实名Binder框架的正统介绍,那匿名BInder的框架是啥呢,其实匿名Binder是建立在实名Binder的基础之上的,这个是匿名Binder的前提也是最终要素!匿名Binder可以使用如下的框架图来表示如下:

在这里插入图片描述
有点模糊不是,让我们对其中涉及的各种角色介绍一番:

  • 重要核心要素:匿名Binder 的前提基础是建立在实名Binder的基础上的,这个是关键也是一切的前提!

  • 对于匿名Binder的宿主进程而言,需要借助已经注册的实名XXXService Binder进程的服务代理端IXXXService.Stub.Proxy向目标进程传递匿名Binder服务代理端,并且目标端进程就是已实名注册的Binder进程

  • 在内核层,真正的匿名Binder服务实体binder_node节点保存在对应的宿主进程中,并且会在已注册的实名Binder进程中创建一个对匿名Binder服务的binder_node的binder_ref引用

  • 已经实名注册XXXService Binder服务进程通过Binder驱动获取匿名Binder服务代理端

  • 目标进程获取通过Binder驱动传递的匿名Binder服务的代理端

  • 宿主进程传递Binder代理对象借助的是已经实名注册的XXXService Binder服务的代理端IXXXService.Stub.Proxy


2.2 匿名Binder实现源码分析

  通过前面基本理论只是的储备和前期的磨刀,是时候来真正的砍柴了。通过前面的框架图我们知道,对于已经建立好Binder通信的Client端和Server端,Client端可以将一个Binder的引用传递给Server端,Server端可以通过这个引用来访问Client。而将这套玩得最溜的就是AMS服务和应用进程的主线程ActivityThread之间通过匿名Binder IApplicationThread来完成AMS对应用进程主线程ActivityThread的相关调度。而我们也此处为切入点,作为实例展开分析。

还记得我们在前言中说的Android应用进程创建的过程中会调用ActivityThread中的attach方法将ApplicationThread Binder服务代理端attach到AMS中,逻辑如下:

  //ActivityThread.java
  final ApplicationThread mAppThread = new ApplicationThread();
  final IActivityManager mgr = ActivityManagerNative.getDefault();
  try {
  	  //最终调用的是ActivityManagerProxy的attachApplication
      mgr.attachApplication(mAppThread);//详见章节2.2.1
  } catch (RemoteException ex) {
      throw ex.rethrowFromSystemServer();
  }

2.2.1 ApplicationThread简介

  这里的重点是ApplicationThread,它在AMS和ActivityThread的通信中起到了纽带连接的作用,我们看看其类图关系如下:
在这里插入图片描述

这里可以看到,ApplicationThreadProxy作为binder通信的客户端,ApplicationThreadNative作为Binder通信的服务端,其中ApplicationThread继承ApplicationThreadNative类,覆写其中的部分方法。

2.2.2 ActivityManagerProxy.attachApplication

  爱的魔力转圈圈,怎么从mgr.attachApplication跳转到ActivityManagerProxy.attachApplication的转变过程,请参见博客Android Binder框架实现之Java层获取Binder服务源码分析的详细分析,我们接着继续分析源码:

	//ActivityManagerNative.java
class ActivityManagerProxy implements IActivityManager {
	/**
	**注意这里的入参app是IApplicationThread的Binder实体对象,这个是关键,即最终会转换成C++层的JavaBBinder
	**/
    public void attachApplication(IApplicationThread app) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(app.asBinder());//此处是关键app.asBinder返回ApplicationThreadNative,详见章节2.2.3,这个是关键
        mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);//这里的mRemote为BinderProxy,其内部持有一个BpBinder可以通过驱动和AMS通信
        reply.readException();
        data.recycle();
        reply.recycle();
    }
}

这里有几点需要注意,我们重点备注一下:

  • 入参app为IApplicationThread的实体Binder类ApplicationThread的实例,并且其方法asBinder返回自身对象
  • 这里的mRemote为BinderProxy,其内部持有一个BpBinder可以通过驱动和AMS通信

2.2.3 writeStrongBinder

  小伙们跟着分析了这么久,是不是心里一直在嘀咕着这种匿名的Binder访问是怎么建立起来的?AMS中怎么查询找到IApplicationThread的实体对象呢然后进行通信的呢?其实答案就在第一次传递这个Binder的引用的时候的writeStrongBinder流程里,在传递过程中将在Binder驱动中保存了这个Binder实体的各种数据,创建了节点和引用!

而通过博客Android Binder框架实现之Parcel详解之read/writeStrongBinder实现我们知道了Parcel分为Java和C++层,其二者之间通过JNI串联起来,writeStrongBinder最终会调用到C++层,源码逻辑如下:

//Parcel.cpp
//此时的入参val为JavaBBinder
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}


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();//由于我们传入的是匿名Binder的实体,所以这里不是空,返回的是BBinder
        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;//远程Binder
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;//记录Binder代理的句柄
            obj.cookie = 0;
        } else {
            obj.type = BINDER_TYPE_BINDER;//本地Binder,进入该分支
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);//记录Binder实体的指针,传入驱动
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out);
}

  这里我们需要重点关注的是flat_binder_object结构体obj根据传入的参数,其成员type 最终被赋值为BINDER_TYPE_BINDER。接下来回到章节2.2.2的mRemote.transact方法,该方法经过层层传递以后最终会调用到IPCThreadState::executeCommand中进而调用talkwithdriver函数,最后调用ioctl进入内核空间中,也就是binder_ioctl中将刚刚的数据传递给Binder驱动,至于这个具体传递的流程详见博客Android Binder框架实现之Native层addService详解之请求的发送博客的3.12章节,然后在内核binder_ioctl中的调用流程如下:

binder_ioctl--->binder_thread_write--->binder_transaction

  即在binder_transaction中会对传入的binder_transaction_data数据进行解析,打包成binder_transaction传递给实名注册的XXXService Binder进程端Binder线程读取。

2.2.4 binder_transaction

  激动人心的时刻要到了,匿名Binder是怎么注册和查找的关键因素破解的时候到了,让我们接着分析binder_transaction源码,如下:

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply)
{
		...
		fp = (struct flat_binder_object *)(t->buffer->data + *offp);//此处对传入Binder驱动额数据进行解析
		switch (fp->type) {
				case BINDER_TYPE_BINDER://通过前面的场景分析,知道我们传入的type类型为BINDER_TYPE_BINDER,所以会走此分支
				case BINDER_TYPE_WEAK_BINDER: {
					struct binder_ref *ref;
					struct binder_node *node = binder_get_node(proc, fp->binder);//会进行查找,如果当前匿名Binder进程里没有节点,则会创建一个,node里保存了binder的实体地址和cookie,注意此时的proc指代的是匿名Binder在驱动层对应的进程
					if (node == NULL) {
						node = binder_new_node(proc, fp->binder, fp->cookie);
						if (node == NULL) {
							return_error = BR_FAILED_REPLY;
							goto err_binder_new_node_failed;
						}
						node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
						node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
					}
					if (fp->cookie != node->cookie) {
						binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
							proc->pid, thread->pid,
							(u64)fp->binder, node->debug_id,
							(u64)fp->cookie, (u64)node->cookie);
						goto err_binder_get_ref_for_node_failed;
					}
					if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
						return_error = BR_FAILED_REPLY;
						goto err_binder_get_ref_for_node_failed;
					}
					ref = binder_get_ref_for_node(target_proc, node);//这里的target_proc是注册的实名Binder进程在驱动层对应的进程,然后为该proc创建堆匿名binder_node对应的binder引用,有了这个引用就可以在Binder驱动中找到对应的Binder实体了,这条关系链就建立起来了
					if (ref == NULL) {
						return_error = BR_FAILED_REPLY;
						goto err_binder_get_ref_for_node_failed;
					}
					if (fp->type == BINDER_TYPE_BINDER)//修改type为handle,等待client进程去读取,创建Bpbinder。
						fp->type = BINDER_TYPE_HANDLE;
					else
						fp->type = BINDER_TYPE_WEAK_HANDLE;
					fp->binder = 0;
					fp->handle = ref->desc;//传入handle值
					fp->cookie = 0;
					binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
						       &thread->todo);
		
					trace_binder_transaction_node_to_ref(t, node, ref);
					binder_debug(BINDER_DEBUG_TRANSACTION,
						     "        node %d u%016llx -> ref %d desc %d\n",
						     node->debug_id, (u64)node->ptr,
						     ref->debug_id, ref->desc);
				} break;
			...
		}
}

通过代码可以看到当匿名Binder实体数据传入到Binder驱动以后,将会在匿名Binder在驱动中的宿主进程中创建一个binder_node节点,并且在目的端进程(实名注册Binder进程)创建一个binder_ref的引用,最终完成了在Binder驱动中关于该匿名Binder的相关存储,而我们的目的端进程此时持有了binder_ref引用进而可以构建BpBinder,有了BpBinder那么就可以操作匿名Binder对象了,这个过程就和实名Binder类似了。即最终构成了匿名Binder的框架图,如下:
在这里插入图片描述


2.3 匿名Binder实现小结

  匿名Binder实现的整体流程分析完结了,老规矩总结一下其基本实现流程:

  • 前提条件必须是在建立在实名Binder通信的连接(基于实名注册的Binder)基础上,接着可以通过实名Binder传递Binder对象的引用给目的端(通常这个目的端就是实名Binder进程),大致的过程是:

  • 然后将匿名Binder实体对象写入Parcel数据容器,借助实名Binder和Binder驱动通信,在Binder驱动中会对传入的对象进行检查,如果发现传入的type是BINDER_TYPE_BINDER,则会在当前进程(匿名Binder进程在驱动层对应的proc)查找是否有这个节点,如果没有就用传入的Binder实体在内核空间构造一个binder_node节点,接着在目的端进程对应的proc中创建该binder_node对应的binder_ref引用,存放在transaction_data中等待目的端去获取。

  • 目的端在拿到Binder的引用后,就可以通过这个BpBinder和匿名Binder实体的BBinder进行通信了,这个通信过程和实名注册的Binder是一样的,不再赘述



三.匿名Binder和实名Binder区别联系


3.1 匿名Binder和实名Binder的联系

  联系吗,主要表现在如下几点:

  • 很简单都是Android Binder实现的一种形式,并且都借助Android Binder驱动完成了跨进程的通信,并且匿名Binder的基础条件是建立在实名Binder之上的。并且两种Bidner的实现逻辑基本相同,
  • 只是实名Binder会注册到servicemanager进程中,并且servicemanager在内核层持有一个对实名Binder的binder_node节点的引用binder_ref,而匿名Binder的引用是保存在实名Binder内核层进程proc中

3.2 匿名Binder和实名Binder区别

  就我个人觉得,匿名Binder和实名Binder最大的区别就在于对Binder传递方式的不同,其次就是使用场景和调用方式不同:

  • 我们知道实名Binder最终都会传递到servicemanager进程里进行注册(注意servicemanager进程里持有的是实名Binder的引用),其实现了key/value的方式来保存和检索服务,因此,外部可以通过名称来访问指定的Binder service,所以对于客户端来说可以通过查询servicemanager进程通过名称来查询到实名Binder服务进而使用

  • 而匿名Binder的传递就没有那么容易了,必须先建立在实名Binder的基础之上,比如attachApplication里的IApplicationThread就是建立在AMS实名Binder的的基础之上,并且IApplicationThread传递到到目的端AMS,并且它不向外告知,外部是不知道有这个IApplicationThread对象存在的,从而达到了隐匿Binder的作用。

  • 如果觉得上面的例子还不形象,还不够刺激的,那么bindService的的案例就更加清晰了,通过binService的绑定远程代理端进而远程调用服务端Service,但是从始至终服务端Service都没有注册到servicemanager进程中,这是一次匿名Binder的使用。而在binderService的实现过程中的另外一次匿名Binder服务使用就是IServiceConnection了,它的实体存储在LoadApk对象的内部类ServiceDispatcher里的,如果服务端不向外告知,外部是不知道有这个IServiceConnection对象存在的,但是它会告知实名Binder服务AMS,这也是ActivityManagerService的远程方法bindService为什么有IServiceConnection这个参数的原因,因为它需要明确获取IServiceConnection这个客户端对象,当服务绑定成功后才可以通过这个对象把服务传给远程的客户端(好吗,脱离代码感觉这么干分析是有点那么了啊,总之一句话binderService里面两次运用了匿名Binder)。

总之匿名Binder是对实名Binder的一个补充,即有些Binder并不想大张旗鼓的告诉外界,而只需要小范围的在特定的场景下传播,并且这种小范围的传播安全性更高,不会被恶意用户过度的调用,只有经过实名认证的用户才可以享用匿名Binder。



写在最后

   至此,Android Binder框架实现之何为匿名/实名Binder就介绍完了。同时这篇文章也注定了是一个小众的,因为很多小伙们可能每天都在使用着这两种Binder,却并没有留意到它二者之间的区别,我想这也是谷歌有意为之即让用屏蔽通信层,拥抱业务逻辑和具体实现。好了就到这里了,欢迎小伙伴们评论和点赞!本篇博客到这里真的要说再见了。

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