关注微信公众号:“编码美丽”技术干货实时推荐,欢迎访问我的github:点击进入

Android系统篇之—-Binder机制和远程服务调用机制分析

Android技术篇 jiangwei212 5375℃ 0评论

一、前景概要

最近要实现Android中免注册Activity就可以运行的问题,那么结果是搞定了,就是可以不用在AndroidManifest.xml中声明这个Activity即可运行,主要是通过骗取系统,偷龙转凤技术的,这个知识点后面会详细讲解的,因为在研究了这个问题过程中遇到了很多知识点,当然最重要也是最根本的就是Android中的Binder机制和远程服务调用机制,而关于Binder机制的话,在Android中算是一个非常大的系统架构模块了,光这篇文章是肯定不能讲解到全部的,而且本人也不是非常的熟悉Binder机制,只是发表个人的理解,并且会用最简单的语言讲解最核心的知识,因为现在有很多知识都在介绍Binder机制,但是大部分说的都太抽象了大部分都看不懂。

二、具体需求

接下来几篇主要是介绍关于Android中的应用启动流程,通过Hook机制拦截Activity的启动流程,达到我们想要的功能,同时可以实现Activity无需在AndroidManifest.xml中声明即可运行的效果。

三、Android中远程服务调用分析

我们如果做过多进程之间通信的话都了解远程服务的使用了,使用也是非常简单的,下面来看一下如何定义自己的远程服务:

1、定义一个AIDL文件

类似于定义接口类型,这个AIDL文件将在本地和远端都要使用到


2、定义远端服务

在远程服务中的onBind方法,实现AIDL接口的具体方法,并且返回Binder对象


3、本地创建连接对象

本地创建一个服务连接对象,实现ServiceConnection接口,在连接成功之后,会得到一个远端传递过来的Binder对象,就是上面的远端服务onBind方法返回的,得到Binder对象之后在进行转化就可以得到AIDL对象,然后即可调用方法


4、连接服务

连接服务也是比较简单的,这时候把上面的连接对象传递进去即可


到这里我们就看到完成了本地端和远端的通信了,如果把DemoService远端服务定义在另外一个进程中,那么这里就可以实现多进程通信了。看到上面的步骤很简单,但是有一个核心的地方就是Demo.Stub类,这个类起着重要的作用,下面来分析一下他的实现,每次定义了AIDL接口文件之后,编译一下就会在gen目录中产生对应的java文件了:


这里的实现稍微有点复杂了,但是这些内容其实都是编译工具帮我们实现的,而这个实现都是有一定规则的:

1、AIDL接口必须实现IInterface接口

关于IInterface接口的实现也很简单:


这里可以把当前的AIDL对象转化成一个IBinder类型对象

2、AIDL接口中肯定有一个静态实现类Stub

这个类必须实现Binder类,以及本身的AIDL接口类型。那么这个类就具备了Binder类中的四个功能:

1》可以将Binder对象转化成AIDL对象,调用asInterface方法,可以看到这个方法其实和上面的asBinder方法对立的

2》通信方法onTransact实现,这个方法是最核心的用于通信之间的逻辑实现

3》通过queryLocalInterface方法可以根据类的描述符(字符串可以唯一标识这个远端服务的名称即可)获取到对应的AIDL对象(其实是IInterface类型的)

4》在构造方法中必须调用Binder中的attachInterface方法把当前服务对象和描述符进行关联

上面看完了Stub类之后,发现他其实是远端服务Binder对象的一个中间者,用来和客户端进行交互的,下面再来看一下Proxy类:


这个代理对象其实就是远端传递过来Binder对象的一个代理,他是客户端这边用户和服务端交互的中间者。我们在前面的Stub类中可以看到:


这里会把远端传递过来的Binder对象转化成一个本地对象,发现先是通过queryLocalInterface方法借助服务描述符来获取对象:


而这个mOwner和mDescriptor之间的关系就在attachInterface方法中进行初始化的,也就是在Stub类的构造方法中


那么现在就清楚了,如果客户端和服务端是在一个进程中,那么其实queryLocalInterface获取的就是Stub对象,如果不在一个进程queryLocalInterface查询的对象肯定为null,因为不同进程有不同虚拟机,肯定查不到mOwner对象的,所以这时候其实是返回的Proxy对象了。通过上面的讲解之后,发现多进程服务通信基准就是借助Binder对象,先传递Binder对象,然后在把Binder转成可以使用的原生对象即可调用了,而对于Stub类和Proxy类其实就是相当于是服务端和客户端的中间者,把一些逻辑封装起来,这种设计也会显得不是那么凌乱:

Stub类是服务端的中间者,一般是实现了AIDL接口类型和继承了Binder类,具备将Binder对象转化成原生对象的能力

Proxy类是客户端的中间者,一般是实现了AIDL接口类型


四、分析系统服务调用流程

这种图就够形象了,所以后面会看到系统中的一些服务使用的时候其实也是跨进程使用,比如下面来看一下著名的PackageManager,IPackageManager,PackageManagerService体系:


这个就是IPackageManager.aidl文件,因为我们还没有编译源码,所以这里可能需要AIDL工具单独编译才能看到IPackageManager.java了:


这里看到了熟悉的远端服务中间者Stub和本地端的中间者Proxy类了,而这两个类的规则都和上面一样的。

下面来看一下远端服务实现代码PackageManagerService.java:


实现了上面的的Stub类功能。

下面我们再走一遍获取PackageManager的流程:


而这个getPackageManager方法是在ContextImpl.java中实现的:


看到了吧,这里先从ServiceManager的getService中获取到一个PackageManager远端的IBinder对象,然后在使用Stub的asInterface方法进行转化成本地的PackageManager对象,其实就是那个Proxy对象。然后就可以通过PackageManager来调用方法和远端的PackageManagerService服务进行通信了。

通过上面的PackageManager案例可以分析,我们在使用系统中的服务的时候的流程都是如此:


每个应用在使用系统服务的时候,都会走这么几步:

1、调用getSystemService(String serviceName)方法获取服务对象

2、而getSystemService一般都是在ContextImpl类中实现的,其实是调用了ServiceManager的getService方法

3、调用ServiceManager的getService方法获取远端服务的IBinder对象

4、有了远端服务的IBinder对象之后,在使用远端服务的中间者类Stub进行转化对象asInterface方法

5、因为系统中的服务获取都是肯定是跨进程的,远端服务都是在system_server进程中的,所以asInterface方法中返回的是Proxy代理对象,也就是本地端的中间者。

6、最后返回的对象其实就是这个Proxy对象,而这个对象内部使用了静态代理方式,内部有一个来自远端的mRemote变量即IBinder对象。然后直接调用方法其实就是调用mRemote的transact方法进行通信了。

所以在这个过程中可以看到有两个对象很重要,一个是ServiceManager,一个是IBinder对象。下面再来一一介绍

五、服务大管家ServiceManager

那么现在又有几个问题了,这个ServiceManager是干嘛的?如何能够通过服务描述符就可以获取到对应服务的IBinder对象?这个就要看一下ServiceManager的功能了:

首先看看他获取远端服务的IBinder对象方法,他本身会维护一个IBinder缓存池,也是为了效率高考虑,对于一个应用频繁的使用一些服务的话效率就会高很多。


然后最核心的获取服务的方法是getIServiceManager方法:


这个方法看到了,又是一个远程通信获取Binder对象,而这次是IServiceManager对象了:


看到这里的ServiceManager也是通过远端服务获取到他的IBinder对象,然后在转化成本地对象进行使用的。那么刚刚看到系统的服务都是通过ServiceManager管理获取的,而现在ServiceManager本身是怎么获取到的IBinder对象的呢?这个就要从系统启动的时机看了,众所周知系统启动的时候是根据init.rc文件进行操作的:


这里会启动一个servicemanager服务,那么就要去service_manager.c程序中的入口程序看了:


这个入口其实包含了Binder机制的重要信息,而主要就是三件事:

1、打开底层的Binder驱动程序,这个后面介绍Binder机制在介绍

2、通过向binder程序发送命令:BINDER_SET_CONTEXT_MGR,告诉binder程序,我要成为大管家

3、进入循环监听上层应用的服务请求处理,所以这里可以看到其实ServiceManager是一个守护进程在后台默默监听

在第二步中成为大管家的代码深入看一看:


其实这里的逻辑也是比较简单的,首先创建一个属于servicemanager的binder节点,然后在创建一个binder链表,而这个链表的作用就是存放上层中需要系统服务的所有binder对象的节点,这样ServiceManager就可以实现了服务的增加和查询操作了。

再来看看ServiceManager的添加服务操作:


添加服务比较复杂,首先查看这个服务有没有注册权限限制,不是所有的服务都能注册的,然后在查看这个服务是不是已经被注册过了,最后在通知binder驱动程序注册一个服务即可。

然后在来看看ServiceManager的查找服务功能:


查找服务就比较简单了,直接通过服务的描述符名称遍历binder链表节点即可。

1、Service Manager能集中管理系统内的所有服务,它能被施加权限控制,并不是任何进程都能注册服务的。
2、Service Manager支持通过字符串名称来查找对应的Service。
3、由于各种原因的影响,Server进程可能生死无常。如果有了Service Manager做统一的管理,那么Client只要向Service Manager做查询,就能得到Server的最新信息。

六、系统服务注册流程分析

下面来看一下一些系统服务是如何进程注册的,这里用MediaService来进行查看吧:

系统中的MediaService服务的启动也是在init.rc中的


查看main_mediaserver.cpp源码的main函数:


看到了系统的MediaServer依赖很多其他服务的,而这些服务必须要注册的,他们的注册操作都是在各自的初始化方法中的,这里用MediaPlayerService来看看注册操作:


看到熟悉的代码了把,这里通过ServiceManager来进行服务注册了,那么这里是如何获取到ServiceManager的?


看看ProcessState.cpp的源码:


看看getStrongProxyForHandle方法实现:


这里看到了,会使用IPCThreadState的transact方法和底层的Binder进行通信的,然后使用一个句柄handle构造一个BpBinder对象,而BpBinder对象其实就是native层实现的Binder对象,以后只要看到Bp开头的就是代理对象对应Java层的Proxy对象,Bn开头的就是native对象对应Java层的Stub对象。

在上面分析servicemanager的时候知道会维护一个binder节点链表,那里其实就有一个每个binder对应句柄handle,而后续进行通信的话都是通过这个句柄来标识是哪个服务的binder对象了,这样也就在通信的时候不会发生紊乱了,而servicemanager的句柄handle就是0。还有一个知识点就是可以看到IPC通信的时候传输数据使用的就是Parcel类,这个类就是为了跨进程通信产生的,他有一个方法readStrongBinder,就是可以从Parcel的数据中获取到Binder对象,这个也是在跨进程中传递Binder对象的核心地方。


好了,上面就通过系统的mediaserver服务来讲解了系统服务的注册流程:


到这里就分析完了Android中的远程服务调用机制逻辑以及ServiceManager这个服务大管家的作用:


1、首先跨进程通信的话,肯定会有两个对象:一个是本地端的中间者Proxy对象,一个是远程端的中间者Stub对象

2、Proxy对象通过静态代理模式维持一个远端传递过来的Binder对象,而Stub对象可以把远端传递过来的Binder对象转化成一个实际服务对象给应用使用

3、Android中在使用系统服务的时候通过getSystemService方法获取到的其实都是Stub把远端的Binder转化的对象,因为系统服务都是在system_server进程中,所以肯定是跨进程获取对象的,那么这个Binder对象其实就是上面的Proxy对象

4、系统的服务都是在一个指定的系统进程中system_server

5、服务大管家ServiceManager在系统启动的时候也是先获取自生的Binder对象,然后转化成实际操作对象,然后才可以操作系统服务的注册和查询功能

下面是系统一些服务的注册流程:


上面已经介绍了远程五福调用机制以及ServiceManager的实现原理,下面就要看看另外一个重点,也是上面提到的一个重要对象Binder,准确来说这个是Binder机制,在Android中Binder机制最复杂的一个架构系统了,它的设计很复杂,所以有很多同学在了解Binder机制的时候,总是看着看着就晕了,今天我们就直说重点,而且说得要相对明了简单。

七、Binder机制解析

第一、Android中的IPC为何要采用Binder机制

Binder是Android系统进程间通信(IPC)方式之一。Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection):管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。

Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID,PID身份,既支持实名Binder也支持匿名Binder,安全性高。对Binder而言,Binder可以看成Server提供的实现某个特定服务的访问接入点, Client通过这个‘地址’向Server发送请求来使用该服务;对Client而言,Binder可以看成是通向Server的管道入口,要想和某个Server通信首先必须建立这个管道并获得管道入口。

第二、Android中的Binder实现原理

其实Android中的Binder通信都是通过虚拟驱动设备程序/dev/binder来实现的,我们知道一些硬件都会对应一个驱动程序,而binder驱动程序没有对应的硬件,所以叫做虚拟驱动设备程序,其实他就是一个字符驱动设备,或者叫做miscdevice混杂设备驱动。

其实混杂驱动设备是字符设备的一种,它们共享一个主设备号(10),但次设备号不同,所有的混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的miscdevice设备。例如:触摸屏,LED,按键,串口。即:为了节约主设备号,将某些设备用链表的形式连接在一起,最后通过查找次设备区分。这里用主设备无法匹配出设备驱动,只能找到链表,再通过次设备号,才能找到设备驱动。而之前所学的,一般字符设备,通过主设备号,就能找到设备驱动了。我们可以通过命令查看/dev/binder驱动的主设备号:


第三、Android中Binder通信机制

先来看一张图,我们可以大体的了解到了客户端和服务端通过Binder驱动进行通信


首先不管是客户端进程还是服务端进程都是在用户空间的,而binder驱动是在内核空间的,通信的数据是有规定格式也叫作IPC数据,既然是一种通信机制,肯定是需要协议,数据格式等基础结构信息的:


上面在分析了ServiceManager的启动的时候说到了,第一步是打开驱动程序,具体打开函数在binder.c中:


在使用一个驱动之前,肯定要先打开驱动,然后把驱动程序映射到内存中,接着借助IPCTreadState.cpp和binder驱动进行通信了:


所以看到这里IPCThreadState也是需要进入后台进行监听的,处理来自客户端和服务端的数据传输消息


最后再来看一下通信时序图。

到这里我们就介绍完了Binder机制了,关于Binder机制最好不要看太深,因为越深你觉得越复杂越难理解,其实你只要了解到他是一个通信工具,通信采用的是驱动操作,通过传输IPC数据来进行通信即可。其他的关于他的详细数据格式和通信协议,感兴趣的同学可以了解一下,但是太过复杂而且在实际中也没多大用途,所以这里就不介绍了。

八、技术点概要

1、理解远程服务通信机制

通过案例先了解到本地端和服务端跨进程通信,主要就是借助Binder进行功能调用,而在这里主要有两个核心类,一个是Stub类,这个类是继承了Binder类具备了将远程传递的Binder对象转化成本地实际对象asInterface方法即可,同时实现了IXXX接口,需要实现AIDL中的功能方法,还有一个类就是Proxy类,实现了IXXX接口,同时内部保留着远端传递的Binder对象,然后通过这个对象调用远端方法。这里Stub类就是服务端的中间者,而Proxy就是本地端的中间者。

2、系统服务调用流程

通过分析了跨进程通信机制原理之后,再去看看Android系统中在使用一些服务的时候,通过getSystemService方法获取服务对象,其实这内部就是通过跨进程获取到了远端服务的Binder对象,然后转化成系统服务对象给应用调用,而这些系统服务的Binder对象在系统启动的时候服务会自动注册到ServiceManager中。

3、服务大管家ServiceManager

在整个远程服务调用过程中两个重要对象,一个是Binder对象,一个就是ServiceManager类,这个类是管理系统服务的类,他可以注册服务,查询服务,系统服务在系统启动的时候会通过addService进行服务注册,然后应用就可以通过getService进行服务查询,而在这个过程中,底层会维护一个这些服务的binder链表结构,同时每个服务的binder对象都一个句柄handle,通过这个句柄来表示通信标识,这样通信才不会紊乱。

4、底层通信核心Binder

最后分析了底层真正实现跨进程通信的机制Binder,其实是通过虚拟驱动程序/dev/binder进行通信的。一个通信机制肯定有通信协议,传输的数据结构,但是这里并没有介绍这些知识,原因是我们后面的需求并不会用到这些,其次是这些知识点太详细介绍也不好,因为会越看越乱。

九、总结

本文介绍的东东有点多,但是如果掌握了Android中的Binder机制和远程服务调用机制对后面拦截系统api做了铺垫,说到结束了才告诉大家为什么要介绍这个知识点,是因为最近在研究如何拦截系统启动Activity的事,那么就必须了解Activity的启动流程,但是在这个过程中有一个对象就是ActivityManagerService,而他就和Binder以及远程服务调用机制紧密联系了,如果不了解Binder机制,后面工作是没办法进行的,好了,说到最后再来一张神图算是总结了本文内容:


这张图非常好的表达了Android中应用使用系统服务的一个流程,也是最好的最全的解释了。看懂这张图之后,那么对Android中的binder机制和远程服务调用机制就可以掌握了,可以进行后续的拦截操作了。

 

关注微信公众号,最新技术文章干货实时推送

转载请注明:程序猿大雄 » Android系统篇之—-Binder机制和远程服务调用机制分析

喜欢 (5)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. :roll: 确实清晰易懂 赞
    kobeisgod2016-09-23 11:22 回复