admin 管理员组文章数量: 1184232
一:binder的全面介绍
1、binder的出现
Binder是Android中最重要的一种进程间通信机制,基于开源的OpenBinder
George Hoffman当时任Be公司的工程师,他启动了一个名为OpenBinder的项目,在Be公司被ParmSource公司收购后,OpenBinder 由Dinnie Hackborn继续开发,后来成为管理ParmOS6 cobalt Os的进程的基础。在Hackborn加入谷歌后,他在OpenBinder的基础上开发出了Android Binder(以下简称Binder),用来完成Android的进程通信。
2、为什么要学习binder
作为一名Android开发,我们每天都在和Binder打交道,虽然可能有的时候不会注意到,譬如:
- startActivity的时候,会获取AMS服务,调用AMS服务的startActivity方法
- startActivity传递的对象为什么需要序列化
- bindService为什么回调的是一个Ibinder对象
- 多进程应用,各个进程之间如何通信
- AIDL的使用
- …
它们都和Binder有着莫切关系,当碰到上面的场景,或者一些疑难问题的时候,理解Binder机制是非常有必要的。我们知道Android 应用程序是由Activity、Service、Broadcast Receiver 和 Content Provide 四大组件中的一个或者多个组成的。有时这些组件运行在同一进程,有时运行在不同的进程。这些进程间的通信就依赖于Binder IPC机制。不仅如此,Android 系统对应用层提供的各种服务如:ActivityManagerService、PackageManagerService等都是基于 Binder IPC 机制来实现的。Binder 机制在 Android 中的位置非常重要!
3、为什么安卓选择binder
Android 系统是基于 Linux 内核的,Linux 已经提供了管道、消息队列、共享内存和 Socket 等 IPC机制。那为什么Android 还要提供Binder 来实现IPC呢?主要是基于性能、稳定性和安全性几方面的原因。
3.1、常见进程间通信
3.1.1、共享内存
共享内存是进程间通信中最简单的方式之一,共享内存允许两个或更多进程访问同一块内存,当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改,原理图如下:
因为共享内存是访问同一块内存,所以数据不需要进行任何复制,是IPC几种方式中最快,性能最好的方式。但相对应的,共享内存未提供同步机制,需要我们手动控制内存间的互斥操作,较容易发生问题。同时共享内存由于能任意的访问和修改内存中的数据,如果有恶意程序去针对某个程序设计代码,很可能导致隐私泄漏或者程序崩溃,所以安全性较差。
3.1.2、 管道
管道分为命名管道和无名管道,它是以一种特殊的文件作为中间介质,我们称为管道文件,它具有固定的读端和写端,写进程通过写段向管道文件里写入数据,读进程通过读段从读进程中读出数据,构成一条数据传递的流水线,它的原理如下图所示:
管道一次通信需要经历2次数据复制(进程A>管道文件,管道文件>进程B)。管道的读写分阻塞和非阻塞,管道创建会分配一个缓冲区,而这个缓冲区是有限的,如果传输的数据大小超过缓冲区上限,或者在阻塞模式下没有安排好数据的读写,会出现阻塞的情况。管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式。
3.1.3、 消息队列
消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示。消息队列允许多个进程同时读写消息,发送方与接收方要约定好消息体的数据类型与大小。消息队列克服了信号承载信息量少、管道只能承载无格式字节流等缺点,消息队列一次通信同样需要经历2次数据复制(进程A消息队列,消息队列>进程B),它的原理如下图所示:
3.1.4、 Socket
Socket原本是为了网络设计的,但也可以通过本地回环地址(127.0.0.1)进行进程间通信,后来在Socket的框架上更是发展出一种IPC机制,名叫UNIX Domain Socket。Socket是一种典型的C/S架构,一个Socket会拥有两个缓冲区,一读一写,由于发送/接收消息需要将一个socket缓冲区中的内容拷贝至另一个socket 缓冲区,所以socket 一次通信也是需要经历2次数据复制,它的原理如下图所示:
3.1.5、binder
跨进程通信是需要内核空间做支持的。传统的IPC机制如管道、Socket都是内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是Binder 并不是Linux 系统内核的一部分,那怎么办呢?这就得益于 Linux的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。
在Android.系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫
Binder驱动(Binder Dirver)。
那么在Android系统中用户进程之间是如何通过这个内核模块(Binder驱动)来实现通信的呢?难道是和前面说的传统IPC机制一样,先将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程,通过两次拷贝来实现吗?显然不是,否则也不会有开篇所说的Binder在性能方面的优势了。这就不得不说 Linux 下的另一个概念:内存映射。
Binder IPC机制中涉及到的内存映射通过mmap()来实现,mmap()是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
比如进程中的用户区域是不能直接和物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘->内核空间->用户空间);通常在这种场景下mmap()就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。而Binder 并不存在物理介质,因此Binder 驱动使用mmap()并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。
一次完整的 Binder IPC 通信过程通常是这样:
- 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
- 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
- 发送方进程通过系统调用copyfromuser()将数据copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
如下图:
3.2、小结
3.2.1、性能
首先说说性能上的优势。Socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。Binder 只需要一次数据拷贝,性能上仅次于共享内存
3.2.2、稳定性
再说说稳定性,Binder基于C/S架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。共享内存虽然无需拷贝,但是控制负责,难以使用。从稳定性的角度讲,Binder 机制是优于内存共享的。
3.2.3、安全性
另一方面就是安全性。Android作为一个开放性的平台,市场上有各类海量的应用供用户选择安装,因此安全性对于Android 平台而言极其重要。作为用户当然不希望我们下载的APP偷偷读取我的通信录,上传我的隐私数据,后台偷跑流量、消耗手机电量。传统的IPC没有任何安全措施,完全依赖上层协议来确保。首先传统的IPC接收方无法获
得对方可靠的进程用户ID/进程ID(UID/PID),从而无法鉴别对方身份。Android为每个安装好的APP分配了自己的UID,故而进程的UID是鉴别进程身份的重要标志。传统的IPC只能由用户在数据包中填入UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由IPC机制在内核中添加。其次传统的IPC访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。同时Binder 既支持实名 Binder,又支持匿名 Binder,安全性高。
基于上述原因,Android需要建立一套新的IPC机制来满足系统对稳定性、传输性能和安全性方面的要求,这就是Binder
4、Binder架构
Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。其中 Service Manager 和Binder 驱动由系统提供,而Client、Server 由应用程序来实现。Client、Server 和 ServiceManager 均是通过系统调用open、mmap和ioctl来访问设备文件/dev/binder,从而实现与Binder 驱动的交互来间接的实现跨进程通信。
Client、Server、ServiceManager、Binder 驱动这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder驱动)之前的关系。
Client 先去ServiceManager中拿到Server的binder(BpBinder),然后Client再通过这个binder来“调用"Server端的代码。当然这个“调用”是跨进程的过程,需要通过ioctl来支持,也就是需要Binder驱动支持。具体的流程如下图所示:
二:如何设计一个binder
1、binder的设计方案
在理解Binder架构前,我们来考虑下,如果是你,该如何设计一个Binder的进程间通信机制。
要实现一个IPC通信那么需要几个核心要素:
- 发起端:肯定包括发起端所从属的进程,以及实际执行传输动作的线程
- 接收端:接收发送端的数据。
- 待传输的数据
- 内存映射,内核态
首先先画一个最简单的IPC通信图:
进程Process1和进程Process2 通过IPC通信机制进行通信。
再进行扩展调整,把IPC机制换成Binder机制,那么就变成如下的图形:
由于Android存在进程隔离,那么两个进程之间是不能直接传输数据的,Process1需要得到Process2的代理,Process2需要一个实体。
为了实现RPC,我们的代理都是提供接口,称为“接口代理”,实体需要提供“接口实体”,如下图所示:
我们把代理改成BpBinder,实体改成BBinder,接口代理改成BpInterface,接口实现体改成BnInterface。我们都知道两个进程的数据共享,需要陷入内核态,那就需要一个驱动设备“/dev/binder”,同时需要一个守护进行来进行service管理,我们成为ServiceManager。
进一步演变为:
假如我们想要把通过Process1的微信信息发送给Process2的微信,我们需要做下面几步:
- Process2在腾讯服务器中进行注册(包括微信名称、当前活动的IP地址等)
- Process1从朋友列表中中查找到Process2的名称,这就是Process2的别名:“service_name”
- Process1编写消息消息内容,点击发送。这里的消息内容就是IPC数据
- 数据会发送到腾讯的服务器,服务器理解为Binder驱动
- 服务器从数据库中解析出IPC数据,找到Process2信息,转到Process2注册的地址,数据库理解为ServiceManager
- 把数据发给Process2,完成Process1和Process2的通信
我们可以简单的把上面的顺序内容进行转换:
- Binder驱动–腾讯服务器
- 数据库-ServiceManager
- Service_name: Process2的微信名称
- IPC数据:Process1发送的微信消息
Native C/C++和内核进行通信需要通过系统调用,ServiecManager的主要用来对Service管理,提供了add\find\list等操作。Native进程的数据直接可以通过系统调用陷入内核态,进入图像转换,变为如下:
上面列举的是Native C/C++空间的进程进行Binder通信机制,那么Java层是如何通信的呢,Native层的Binder提供的是libbinder.so,那么JAVA到Native需要经过JNI、Framework层的封装,JNI层的命名通常为android_util_xxx,我们这里是binder机制,那么JNI层的文件为android_util_binder,同时Native的BBinder不能直接传给JAVA层,在JNI里面转换了一个Java BBinder对象。Framework层给应用层提供时,其实提供的也是一个代理,我们也称之为BinderProxy。在JAVA侧要对应一个Binder的实体,称之为Binder。JAVA侧的服务进行也需要一个管理者,类似于Native创建了JAVA的ServiceManager,那么设计如下:
相信到现阶段,大家已经对binder的运行机制有了一点的理解了,但是仍旧会有很多懵逼的地方,大家如果有这种感觉,不要急,回过头再看一遍Binder通信模型就理解了。我们接下来分析一下AIDL对binder的一个封装。
2、Binder何时初始化
我们已经讲了这么多关于binder的通信的案例了,那么Binder到底是在什么时候初始化呢?
Binder初始化一般是指binder驱动的初始化,大家在使用binder的过程中,我们从来没有执行过new Binder的方式来实现Binder初始化,原因很简单:binder初始化有它自身独立的特点。
每一个应用进程启动的时候,都是通过zygote fork产生的,所以,当fork产生进程后app进程的代码就开始执行,就开始运行的地方如下:
// ZygoteInit.java
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
// 初始化运行环境
RuntimeInit.commonInit();
// 启动进程binder,方法在androidRuntime.cpp中注册
ZygoteInit.nativeZygoteInit();
// 通过反射创建诚信入口函数的Method对象,并返回Runnable对象
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
// RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true);
// We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
大家可以看到,会执行Zygotelnit.nativeZygotelnit()函数,而nativeZygotelnit函数执行appRuntime的onZygotelnit代码,也就是App_main.cpp中的onZygotelnit()函数,函数如下:
virtual void onzygoteInit()
{
sp<PorcessState> proc = PorcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
在ProcessState的self函数里面就会初始化ProcessState(),而这个初始化的一个非常重要的动作就是启动binder驱动并构建binder的Map映射。具体代码如下:
Processstate:Processstate(const char "driver)
:noriverName(string8(driver))
,moriverro(open_driver(driver))/打开binder的虚驱动
,mMStart(MAP_FAILED)
,mThreadcountLock(PTHREAD_MUTEX_INITIALIZER)
,mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
,mtxecutingThreadscount(0)
,mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
,mstarvationstartTimeMs(0)
,mBindercontextcheckFunc(nullptr)
,maindercontextuseroata(nullptr)
,mThreadpoolstarted(false)
,mThreadPoolSeq(1)
,mcallRestriction(callRestriction::NONE)
{
// T0D0(b/139016109): enforce in build system
#if defined(_ANDROID_APEX_)
LOG_ALMAYS_FATAL("Cannot use libbinder in APEX (only system,ing libbinder) since it isnot stable.");
#end if
if(mDriverD >= 0)
{
//用mmap接口向minder驱动中申请内核空间的内存
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mvstart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE|MAP_NORESERVE, moriverD, 0);
if (mVMStart == MAR_FAILED)
{
//"sigh
ALOGE(using Xs failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(moriverFD);
mDriverFD = -1:
mDriverName.clear();
}
}
#ifdef _ANOROID_
LOG_ALMAYS_FATAL_IF(mDriverFD < 0, "inder driver 'Xs' could not be opened.Terminating.", driver);
#endif
}
所以,总的来说,Binder的初始化是在进程已创建就完成了。创建进程后会第一时间为这个进程打开一个binder驱动,并调用mmap接口向Binder驱动中申请内核空间的内存。
三:Binder通信模型
1、Binder通信模型
要讲解Binder的通信模型,我们首先需要了解网络的通信模型。
1.1、网络请求的通信模型
通常我们访问一个网页的步骤是这样的:首先在浏览器输入一个地址,如http://www.google然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问DNS域名服务器,域名服务器中保存了http://www.google对应的ip地址10.249.23.13,然后通过这个ip地址才能放到到http://www.google对应的服务器。
它的通信模型是这样的:
- Server端向DNS服务器注册自己的域名和IP,因此NDS服务器中存储了所有的域名和IP的信息表;
- Client端先通过域名去访问网络服务,但是域名却不能给client端打通服务,因为访问服务是通过IP进行;
- 这个时候要通过域名找到IP地址,所以首先会通过路由器先去访问DNS服务器,通过DNS服务器的域名解析拿到IP地址;
- client端在拿到DNS给的IP地址后,通过IP地址由路由器去访问服务器,完成网络请求。
1.2、切换到binder中的说明
Binder通信的模型就和上面的网络通信模型有着异曲同工之妙。在角色方面,Client进程就等同于网络请求的Client端,服务器就是Server进程,路由器相当于Binder驱动,而DNS服务器相当于ServiceManager进程。Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
ServiceManager和DNS类似,作用是将字符形式的 Binder名字转化成 Client中对该Binder的引用,使得Client能够通过Binder的名字获得对Binder 实体的引用。注册了名字的Binder 叫实名 Binder,就像网站一样除了有IP地址以外还有自己的网址(域名)。Server创建了Binder,并为它起一个可读易记得名字,比如AMS的binder,我们就取了一个名字叫做“activity”,将这个Binder实体连同名字一起以数据包的形式通过Binder驱动发送给ServiceManager,通知ServiceManager 注册一个名为“activity”的 Binder,它位于某个Server中。而ServcieManager中就存储了一个表格,这个表格中就有名字和binder引用对应的信息item。
Client 获得实名 Binder的引用
在这个代码ServiceManager.getService(Context.ACTIVITY_SERVICE)/String ACTIVITY_SERVICE=“activity”,这段代码是Client去ServcieManager中获取server进程的binder的代码,这个地方获取的是AMS的binder实体的代码。代码的设计逻辑就是去ServcieManager的查找表容器中去获取名字为activity的binder实体。
Server向ServiceManager 中注册了 Binder 以后,Client就能通过名字获得Binder的引用了。Client也利用保留的0号引用向ServiceManager 请求访问某个Binder:我申请访问名字叫张三的Binder 引用。ServiceManager收到这个请求后从请求数据包中取出Binder名称,在查找表里找到对应的条目,取出对应的Binder引用作为回复发送给发起请求的Client。从面向对象的角度看,Server中的Binder实体现在有两个引用:一个位于ServiceManager中,一个位于发起请求的Client中。如果接下来有更多的Client请求该Binder,系统中就会有更多的引用指向该 Binder,就像java中一个对象有多个引用一样。
2、Binder通信流程
Binder框架定义了四个角色:Server,Client,ServiceManager(以后简称SMgr)以及Binder驱动。其中Server,Client,SMgr运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似:Server是服务器,Cllent是客户终端,SMgr是域名服务器(DNS),驱动是路由器。
在网络通信中域名服务器的地址是一个固定的地址,所以很方便的通过这个固定地址拿到。那么在Binder中,SMgr是一个进程,Server是另一个进程,Server向SMgr注册Binder必然会涉及进程间通信。当前实现的是进程间通信却又要用到进程间通信,这就好象蛋可以孵出鸡前提却是要找只鸡来孵蛋。Binder的实现比较巧妙:预先创造一只鸡来孵蛋:SMgr和其它进程同样采用Binder通信,SMgr是Server端,有自己的Binder对象(实体),其它进程都是Client,需要通过这个Binder的引用来实现Binder的注册,查询和获取。SMgr提供的Binder比较特殊,它没有名字也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成SMgr时Binder驱动会自动为它创建
Binder实体(这就是那只预先造好的鸡)。其次这个Binder的引用在所有Client中都固定为0而无须通过其它手段获得。也就是说,一个Server若要向SMgr注册自己Binder就必需通过0这个引用号和SMgr的Binder通信。类比网络通信,0号引用就好比域名服务器的地址,你必须预先手工或动态配置好。要注意这里说的Client是相对SMgr而言的,一个应用程序可能是个提供服务的Server,但对SMgr来说它仍然是个Client。
有了以上的知识铺垫,我们可以比较清晰的把BInder通信的流程梳理出来:
- 首先,一个进程使用BINDER_SET_CONTEXT_MGR命令通过 Binder 驱动将自己注册成为ServiceManager;
- Server通过驱动向ServiceManager 中注册Binder(Server中的Binder实体),表明可以对外提供服务。驱动为这个Binder 创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字以及新建的引用打包传给ServiceManager,ServiceManger 将其填入查找表;
- Client 通过名字,在Binder 驱动的帮助下从ServiceManager 中获取到对Binder实体的引用;
- 通过这个Binder实体引用,Client实现和Server 进程的通信。
四、AIDL
AIDL可以生成C++文件,不仅仅是可以生成java文件,当然,要在android的比较新的版本上面才能够实现这点,目前需要在api-29之后。
概述
aidl是常用的android IPC方式,本文将根据一个demo来解析下AIDL的原理。
为了便于读者理解,本文不会探究Binder的实现细节,可以认为Binder在此文的分析中被看做是一个“黑盒”。有一定经验的读者可以直接到文末看总结,最终流程图如下:
public class MainActivity extends AppcompatActivity {
private Iserver iserver;
private serviceconnection serviceconnection = new Serviceconnection(){
@override
public void onserviceconnected(ComponentName name, IBinder service){
iserver =Iserver.Stub.asInterface(service);
system.out.println("onserviceconnected");
try{
int a = iserver. testFunction("test string");
Log.i("test", "after testFunction a:"+ a);
}catch (Exception e){
e.printstackTrace();
}
}
@override
public void onserviceDisconnected(ComponentName name){
Log.i("test", "onServiceDisconnected");
};
@override
protected void oncreate(Bundle savedInstancestate){
super.oncreate(savedInstancestate);
setContentview(R.layout.activity_main);
Intent intent =new Intent(MainActivity.this, RemoteTestservice.class);
bindservice(intent, serviceConnection, Context. BIND_AUTO_CREATE);
}
}
接下来看RemoteTestService.java
public class RemoteTestservice extends service {
private IServer. Stub serverBinder = new Iserver.stub(){
@Override
public int testFunction(String s) throws RemoteException {
Log.i("test","testFunction s= "+ s);
return 0;
}
};
@Nullable @override
public IBinder onBind(Intent intent){
return serverBinder;
}
}
再看|Server.aidl
interface Iserver {
int testFunction(String s);
}
再看AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android/apk/res/android" package="com.example.bindservicetest">
<application
android: allowBackup="true"
android:icon="@mipmap/ic_launcher"
android: label="@string/app_name"
android: roundIcon="@mipmap/ic_launcher_round"
android: supportsRtl="true"
android:theme="@style/AppTheme">
<activity android: name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service
android:name=".RemoteTestService"
android: process=": remote"/>
</application>
</manifest>
未完。。。
ServiceManager的启动
所有的系统服务都是需要在ServiceManager中进行注册的,而ServiceManager作为一个起始的服务,是通过init.rc
来启动的。
system\core\rootdir\init.rc
//启动的服务,这里是用的服务名称。服务名称是在对应的rc文件中注册并启动的
start servicemanager
具体的服务信息是在servicemanger.rc命名并定义的
/\frameworks\native\cmds\servicemanager\servicemanager.rc
service servicemanager /system/bin/servicemanager
class core animation
user system//说明以用户system身份运行
group system readproc
//说明servicemanager是系统中的关键服务,
//关键服务是不会退出的,如果退出了,系统就会重启,当系统重启时就会启动用onrestart关键字修饰的进程,
//比如zygote、media、surfaceflinger等等。
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
onrestart restart keystore
onrestart restart gatekeeperd
onrestart restart thermalservice
...
servicemanager的入口函数在frameworks\native\cmds\servicemanager\main.cpp中
代码如下:
int main(int argc, char"* argu) {
//根据上面的rc文件,argc == 1,argv[0] =="/system/bin/servicemanager
if (arge > 2) {
LOG(FATAL) « "usage:" «< argv[0] << " [binder driver]";
}
//此时,要使用的binder驱动为/dev/binder
const char* driver = argc == 2 ? argv[1) : "/dev/binder";
//初始化binder驱动
sp<ProcessState> ps = ProcessState::initwithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps-›setcallRestriction(ProcessState:: CallRestriction: :FATAL_IF_NOT_ONEWAY);
//实例化serviceManager,传入Access类用鉴权
sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
//将自身作为服务添加
if (Imanager-›addService("manager", manager, false /*allowisolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()){
LOG(ERROR) « "Could not self register servicemanager";
}
//设置服务業Bbinder对象
IPCThreadstate::self()-›setTheContextObject(manager);
//设置成为binder驱动的context manager,成为上下文的管理者
ps->becomeContextManager (nullptr, nullptr);
//通过Looper epoll机制处理binder事务
sp<Loopers looper - Looper::prepare(false /*allowsonCallbacks*/);
//从通知驱动BC_ENTER_LOOPER,监听驱动fd,有消息时回调到handleEvent处理binder调用
BinderCallback::setupTo(looper);
//服务的注册监听相关
ClientBinderCallback: setupTo(looper, manager);
//无服循环等消息
while(true) {
looper->pollAll(-1);
}
// should not be reached
return EXIT_FAILURE;
}
具体的逻辑整理如下的流程图
Framework层中的Binder 客户端通信以ServiceManager为例的源码分析
- 获取ServiceManager对象
从ServiceManager作为一个进程为例来说明如何通过Binder来具体完通信的
private static IServiceManager getIServiceManager(){
if (sServiceManager != null){
return sServiceManager;
}
// Find the service manager
sServiceManager =ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager; //ServiceManagerProxy
BinderInternal.getContextObject(),返回值是一个IBinder对象,它经历了几层封装,具体逻辑如下:
首先是在Native层创建了一个BpBinder对象,然后BpBinder对象在JNI层被封装成为BinderProxy对象并返回,所以
BinderInternal.getContextObject()返回的是BinderProxy对象。
然而当看到上面的Binder.allowBlocking的如下源码的时候,发现这其中并没有实质性的代码:
// Binder
public static IBinder allowBlocking(IBinder binder) {
try {
if (binder instanceof BinderProxy) {
((BinderProxy) binder).mWarnOnBlocking = false;
} else if (binder != null && binder.getInterfaceDescriptor() != null
&& binder.queryLocalInterface(binder.getInterfaceDescriptor()) == null) {
Log.w(TAG, "Unable to allow blocking on interface " + binder);
}
} catch (RemoteException ignored) {
}
return binder;
}
接下来,我们再分析一下ServiceManagerNative.asInterface函数
// ServiceManagerNative
static public IServiceManager asInterface(IBinder obj){
if (obj == null) {
return null;
}
IServiceManager in = (IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
以上代码的核心逻辑在于将obj封装成为了ServiceManagerProxy对象,看一下它的代码
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
mServiceManager =IServiceManager.Stub.asInterface(remote);
}
public IBinder asBinder() {
return mRemote;
}
@UnsupportedAppUsage
public IBinder getService(String name) throws RemoteException {
return binder;
}
public IBinder checkService(String name) throws RemoteException {
return binder;
}
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException {
}
public String[] listServices(int dumpPriority) throws RemoteException {
return array;
}
public void setPermissionController(IPermissionController controller)
throws RemoteException {
}
@UnsupportedAppUsage
private IBinder mRemote;
}
以上代码其实就是ServiceManagerProxy通过手动的方案实现了IServiceManager,而IServiceManager是IServiceManager.aidl生成的IServiceManager.java接口,我们来看一下它生成出的IServiceManager.Stub.asInterface方法:
public static android.os.IServiceManager asInterface(android.os.IBinder obj){
if ((obj == null)){
return null;
}
android.os.IInterface iin=obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) &&(iin instanceof android.os.IServiceManager))){
return((android.os.IServiceManager) iin);
}
return new android.os.IServiceManager.Stub.Proxy(obj);
}
这里我们传入的IBinder是BinderProxy,它的queryLocalInterface永远返回null,所以这里返回的是
IServiceManager.Stub.Proxy对象。
本文标签: Binder ServiceManager
版权声明:本文标题:binder驱动-ServiceManager的启动 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1765177240a3355070.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论