Android中Binder学习

关于Binder,我们需要知道,为什么需要跨进程通信(IPC),怎么做到跨进程通信?为什么是Binder?

由于Android是基于Linux内核的,因此有些知识需要我们先了解:

进程隔离

进程隔离是为保护操作系统中进程互不干犹而设计的一组不同硬件和软件的技术。这个技术是为了避免进程A写入进程B的情况发生。进程的隔离实现,使用了虚拟地址空间。进程A的虚拟地址和进程B的虚拟地址不同,这样就放置进程A将数据信息写入进程B。

ps:虚拟地址就是逻辑地址。

操作系统的不同进程之间,数据不共享,对于每个进程来说,它都以为自己独享了整个系统,完全不知道其他进程的存在,因此一个进程需要个另一个进程通信,需要某种系统机制才能完成。

用户空间/内核空间

Linux Kernel 是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。

对于Kernel这么一个高安全级别的东西,显然是不容许其他的应用随便调用或访问的,所以需要对Kernel提供一定的保护机制,这个保护机制用来告诉那些应用程序,只可以访问某些许可的资源,不许可的资源是拒绝访问的,也是就把Kernel和上层应用程序抽象的隔离开,分别称为内核空间和用户控件。

系统调用/内核态/用户态

虽然从逻辑上抽离出用户空间和内核空间,但是不可避免的是,有一些用户空间需要访问内核的资源,比如应用程序访问文件,网络等,怎么办呢?
用户空间访问内核空间的唯一方式就是系统调用;通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。

当一个任务(进程)执行系统调用而进入内核代码中执行时,我们就称进程处于内核运行行态(或简称为内核态),此时处理器处于特权级最高的内核代码中执行。当进程在执行用户自己的代码时,就称其处于用户运行状态,即此时处理器在特权级最低的用户代码中运行。
处理器在特权等级最高的时候才能执行那些特权CPU指令。

内核模块/驱动

通过系统调用,用户控件可以访问内核空间,那么如果一个用户空间想与另一个用户空间进行通信,怎么办呢?那么肯定是通过操作系统内核了,通过添加操作系统内核的支持;传统的Linux通信机制,比如Socket,管道等都是内核支持的。
Binder并不是Linux内核的一部分,那么它是怎么做到访问内核空间的呢?Linux的动态可加载内核模块机制解决了这个问题;
模块是具有独立功能的程序,它可以被单独被编译,但是不能独立运行。它在运行时被连接到内核作为内核的哟部分在内核空间运行。这样,Android系统可以通过天际一个内核模块运行在内核空间,用户进程之间的通信就通过这个模块作为桥梁。

在Android系统中,这个运行在内核空间,负责各个用户进程通过Binder通信的内核模块叫做BInder驱动。

为什么使用Binder?

Android使用的Linux内核拥有着非常多的跨进程通信机制,比如管道,System V,Socket等;为什么还需要单独搞一个Binder出来呢?主要有两点,性能和安全。在移动设备上,广泛地使用跨进程通信肯定对通信机制本身提出了严格的要求;Binder相对出传统的Socket方式,更加高效;另外,传统的进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造;而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。这个也是Android权限模型的基础。

Binder相较于传统IPC来说更适合于Android系统,具体原因的包括如下三点:

  1. Binder本身是C/S架构的,这一点更符合Android系统的架构
  2. 性能上更有优势:管道,消息队列,Socket的通讯都需要两次数据拷贝,而Binder只需要一次。要知道,对于系统底层的IPC形式,少一次数据拷贝,对整体性能的影响是非常之大的
  3. 安全性更好:传统IPC形式,无法得到对方的身份标识(UID/GID),而在使用Binder IPC时,这些身份标示是跟随调用过程而自动传递的。Server端很容易就可以知道Client端的身份,非常便于做安全检查
Binder的架构

在这里插入图片描述

  • Binder通信采用C/S架构,从组建视角来讲,包含Client,Server,ServiceManager以及Binder驱动,其中ServiceManager用于管理系统中各种Service。
  • Binder在framwork层进行了封装,通过JNI技术调用Native层的Binder架构。
  • Binder在Native层以ioctl的方式与Binder驱动通信。
Binder的通信模型

两个运行在用户空间的进程要完成通信,必须借助内核的帮助,这个运行在内核里面的程序叫做Binder驱动,它的功能类似于基站;ServiceManager(简称SM)的功能类似于通信录。

在这里插入图片描述

通信的步骤:

  1. ServiceManager建立:首先有一个进程向驱动提出申请SM,驱动同意后,SM进程负责管理Service
  2. 各个Service向SM注册:每个Server端进程启动后,向SM报告,我是XXX,如果要找我就返回0x1234(类比)。其他Server进程也是如此;这样SM就建立了一张表,对应着各个Server的名字和地址。
  3. Client想要与Server通信,首先询问SM,请告诉我如何联系zhangsan,SM收到后给他一个号码0x1234;Client收到之后,开心滴用这个号码拨通了Server的电话,于是就开始通信了。

那么Binder驱动干什么去了呢?这里Client与SM的通信,以及Client与Server的通信,都会经过驱动,驱动在背后默默无闻,但是做着最重要的工作。

Binder机制跨进程原理

由上面的知识,我们可以知道,两个进程想要通信,肯定要通过内核进行中转,要知道,内核是可以访问所有进程的数据的,我们直接可以想到,加入进程A,B,进程A要给进程B发送消息,那么可以将A的数据复制一份到内核,内核再将对应的数据复制到B。

但是Binder不是这样做的。Binder驱动为我们做了一切。

假设Client进程想要调用Server进程的Object对象的一个add方法,
首先,Server进程向SM注册:告诉自己是谁,有什么能力,正这个场景中,Server告诉SM,它叫zhangsan,它有一个object对象,可以执行add操作;于是SM就建立了一张表,zhangsan这个名字就对应了这个Server。

然后Client向SM查询:我需要联系一个名字叫zhangsan的进程里面的Object对象;这个时候,内核里面的驱动不会给Client进程返回一个真正的Object对象,而是返回一个看起来跟Object一模一样的代理对象objectProxy,这个objectProxy也有有个add方法,但是这个add方法没有Servier进程里面Object对象中的add方法那个能力;objectProxy的add只是一个傀儡,它唯一做的事情就是把参数包装起来转发给Binder驱动。

驱动收到这个消息,发现是这个objectProxy;一查表就明白了:我之前用objectProxy替换了object发送给Client了,它真正应该要访问的是object对象的add方法;于是Binder驱动通知Server进程,调用你的object对象的add方法,然后把结果发给我,Sever进程收到这个消息,照做之后将结果返回驱动,驱动然后把结果返回给Client进程;于是整个过程就完成了。

通过这个,我们可以知道,Binder跨进程传输并不是真的把一个对象传输到了另外一个进程;传输过程好像是Binder跨进程穿越的时候,它在一个进程留下了一个真身,在另外一个进程幻化出一个影子(这个影子可以很多个);Client进程的操作其实是对于影子的操作,影子利用Binder驱动最终让真身完成操作。

对于Binder的访问,如果是在同一个进程中,那么直接返回原始的Binder实体,如果是在不同进程,那么就给它一个代理影子;

另外我们为了简化整个流程,隐藏了SM这一部分驱动进行的操作;实际上,由于SM与Server通常不在一个进程,Server进程向SM注册的过程也是跨进程通信,驱动也会对这个过程进行暗箱操作:SM中存在的Server端的对象实际上也是代理对象,后面Client向SM查询的时候,驱动会给Client返回另外一个代理对象。Sever进程的本地对象仅有一个,其他进程所拥有的全部都是它的代理。

一句话总结就是:Client进程只不过是持有了Server端的代理;代理对象协助驱动完成了跨进程通信。

BInder的面对对象

Binder使用Client-Server通信方式:一个进程作为Server提供如视频/音频解码,视频捕获,地址本查询,网络连接等服务;多个进程作为Client向Server发起服务请求,获得所需要的服务。对Binder而言,Binder可以看成Server提供的实现某个特定服务的访问接入点,Client通过这个‘地址’向Server发送请求来使用该服务;对Client而言,Binder可以看成是通向Server的入口,要想和某个Server通信,必须先建立这个入口并获取这个入口。

与其他IPC不同,Binder使用了面对对象的思想来描述访问接入点的Binder及其在Client中的入口:Binder是一个实体位于Server中的对象,该对象提供了一套用以实现对服务的请求,就像类的成员函数。在Client看来,通过Binder‘指针’调用其提供的方法和通过指针调用其他任何本地对象的方法并没有什么区别,尽管前者的实体位于远端的Server中,而后者的实体位于本地内存中。从通信的角度来看,Client中的Binder也可以看做是Server Binder的‘代理’,在本地代表远端Server为Client提供服务。

面向对象思想的引入将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。最诱人的是,这个引用和java里引用一样既可以是强类型,也可以是弱类型,而且可以从一个进程传给其它进程,让大家都能访问同一Server,就象将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder在英文里的原意。

理解Java层的Binder
IBinder/IInterface/Binder/BinderProxy/Stub

我们在使用AIDL接口的时候,经常会接触到这些类,那么每个类代表的是什么呢?

  • IBinder:是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递,这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会失败IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。
  • IBinder负责数据传输,那么client与server端的调用契约(这里不用接口避免混淆)呢?这里的IInterface代表的就是远程server对象具有什么能力。具体来说,就是aidl里面的接口。
  • Java层的Binder类,代表的其实就是Binder本地对象。BinderProxy类是Binder类的一个内部类,它代表远程进程的Binder对象的本地代理;这两个类都继承自IBinder,因为都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。
  • 在使用AIDL的时候,编译工具会给我们生成一个Stub的静态内部类;这个类继承了Binder, 说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力;Stub是一个抽象类,具体的IInterface的相关实现需要我们手动完成,这里使用了策略模式。
AIDL过程分析

首先定义一个简单的AIDL接口:

interface ICalculate {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
   int add(int first,int second);
   int sub(int first,int second);
}

经过编译后,会在app/build/generated/source/aidl下生成一个ICalculate.java的Interface

public interface ICalculate extends android.os.IInterface{
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements 
	com.example.asus1.remoteservice.ICalculate{
		private static final java.lang.String DESCRIPTOR = 	
					"com.example.asus1.remoteservice.ICalculate";
		/** Construct the stub at attach it to the interface. */
		public Stub(){
		this.attachInterface(this, DESCRIPTOR);
		}
		/**
 		* Cast an IBinder object into an com.example.asus1.remoteservice.ICalculate interface,
 		* generating a proxy if needed.
 		*/
		public static com.example.asus1.remoteservice.ICalculate asInterface(android.os.IBinder obj){
		if ((obj==null)) {
			return null;
		}
		android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
		if (((iin!=null)&&(iin instanceof com.example.asus1.remoteservice.ICalculate))) {
			return ((com.example.asus1.remoteservice.ICalculate)iin);
		}
		return new com.example.asus1.remoteservice.ICalculate.Stub.Proxy(obj);
	}
		@Override public android.os.IBinder asBinder()
		{
			return this;
		}
		@Override public boolean onTransact(int code, android.os.Parcel data, 		android.os.Parcel reply, int flags) throws android.os.RemoteException
		{
			switch (code)
		{
			case INTERFACE_TRANSACTION:
		{
			reply.writeString(DESCRIPTOR);
			return true;
		}
			case TRANSACTION_add:
		{
			data.enforceInterface(DESCRIPTOR);
			int _arg0;
			_arg0 = data.readInt();
			int _arg1;
			_arg1 = data.readInt();
			int _result = this.add(_arg0, _arg1);
			reply.writeNoException();
			reply.writeInt(_result);
			return true;
		}
			case TRANSACTION_sub:
		{
			data.enforceInterface(DESCRIPTOR);
			int _arg0;
			_arg0 = data.readInt();
			int _arg1;
			_arg1 = data.readInt();
			int _result = this.sub(_arg0, _arg1);
			reply.writeNoException();
			reply.writeInt(_result);
			return true;
		}
	}
		return super.onTransact(code, data, reply, flags);
	}
private static class Proxy implements com.example.asus1.remoteservice.ICalculate
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int add(int first, int second) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(first);
_data.writeInt(second);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int sub(int first, int second) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(first);
_data.writeInt(second);
mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int add(int first, int second) throws android.os.RemoteException;
public int sub(int first, int second) throws android.os.RemoteException;
}

系统会帮我们生成这个文件,之后我们只需要继承ICalculate.Stub这个抽象类,实现它的方法,然后在Service的onBinder方法里面返回就实现了AIDL。

Stub类继承自Binder,以为着这个Stub其实自己就是一个Binder本地对象,然后实现了ICalculate接口,ICalculate本身就是一个IInterface,因此它携带某种客户端需要的功能(这里是add方法和sub方法)。此类里面还有一个内部类Proxy,也就是Binder代理对象。

在Stub类的构造方法中,我们可以看到:


private static final java.lang.String DESCRIPTOR = "com.example.asus1.remoteservice.ICalculate";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}

    public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }
    

然后看看asInterface方法,我们在bind一个Service之后,onServiceConnection的回调里面,就是通过这个方法拿到一个远程的service的IBinder(远程的,就是BinderProxy),所以这个方法做了什么呢?

public static com.example.asus1.remoteservice.ICalculate asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//看本地有没有
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.asus1.remoteservice.ICalculate))) {
return ((com.example.asus1.remoteservice.ICalculate)iin);
}
return new com.example.asus1.remoteservice.ICalculate.Stub.Proxy(obj);
}


  public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

首先看方法的参数IBinder类型的obj,这个对象是驱动给我们的,如果是Binder本地对象,那么它就是Binder类型,如果是Binder代理对象,那就是BinderProxy类型;然后,它会试着查找Binder本地对象,如果找到,说明Client和Server都在同一个进程中,这个参数直接就是本地对象,也就是Stub,直接强制类型转换然后返回。如果找不到,说明是远程对象(处于另一个进程),那么就需要创建一个Binder代理对象,让这个Binder代理实现对于远程对象的访问。一般来说,如果是与一个远程Servie对象进行通信,那么这里返回的一定是一个Binder代理对象,这个IBinder参数实际上是BinderProxy;

Proxy(android.os.IBinder remote)
{
mRemote = remote;
}

然后我们在看看我们AIDL中的add方法的实现:在Stub类里面,add是一个抽象方法,我们需要继承这个类并实现它;如果Client和Server在同一个进程,那么直接就是调用这个方法;
如果是远程调用,这中间发生了什么呢?Client是如何调用到到Server的方法的呢?

根据上面分析,我们知道,对于远程方法的调用,肯定是通过Binder代理完成的,在这个例子中,就是Proxy类:

private static class Proxy implements com.example.asus1.remoteservice.ICalculate
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int add(int first, int second) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(first);
_data.writeInt(second);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int sub(int first, int second) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(first);
_data.writeInt(second);
mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

它首先用parcel把数据序列化,然后调用了transact方法,这个transact方法是干嘛的呢?这个Proxy类在asInterface方法里面被创建,前面提到过,如果是Binder代理那么说明驱动返回的IBinder实际是BinderProxy, 因此我们的Proxy类里面的mRemote实际类型应该是BinderProxy;我们看看BinderProxy的transact方法:(Binder.java的内部类)

 public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

        if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
            // For now, avoid spamming the log by disabling after we've logged
            // about this interface at least once
            mWarnOnBlocking = false;
            Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
                    new Throwable());
        }

        final boolean tracingEnabled = Binder.isTracingEnabled();
        if (tracingEnabled) {
            final Throwable tr = new Throwable();
            Binder.getTransactionTracker().addTrace(tr);
            StackTraceElement stackTraceElement = tr.getStackTrace()[1];
            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
        }
        try {
            return transactNative(code, data, reply, flags);
        } finally {
            if (tracingEnabled) {
                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
            }
        }
    }


 public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

我们可以看到,在transact方法中,最终会调用transactNative方法,这是一个本地方法,它的实现在native层,具体来说在frameworks/base/core/jni/android_util_Binder.cpp文件,里面进行了一系列的函数调用;要知道的是它最终调用到了talkWithDriver函数;看这个函数的名字就知道,通信过程要交给驱动完成了;这个函数最后通过ioctl系统调用,Client进程陷入内核态,Client调用add方法的线程,挂起等待返回;驱动完成一系列的操作之后唤醒Server进程,调用了Server进程本地对象的onTransact方法(实际上由Server端线程池完成)。我们再看Binder本地对象onTransact方法(这里就是Stub类里面的方法)

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_sub:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sub(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

在Server进程里面,onTransact根据调用号(每个AIDL函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数;在这个例子中,调用了Binder本地对象的add方法;这个方法将结果返回给驱动,驱动唤醒挂起的Client进程里面的线程并将结果返回。于是一次跨进程调用就完成了。

AIDL,它总是那么一种固定的模式:一个需要跨进程传递的对象一定继承自IBinder,如果是Binder本地对象,那么一定继承Binder实现IInterface,如果是代理对象,那么就实现了IInterface并持有了IBinder引用;

Proxy与Stub不一样,虽然他们都既是Binder又是IInterface,不同的是Stub采用的是继承(is 关系),Proxy采用的是组合(has 关系)。他们均实现了所有的IInterface函数,不同的是,Stub又使用策略模式调用的是虚函数(待子类实现),而Proxy则使用组合模式。为什么Stub采用继承而Proxy采用组合?事实上,Stub本身是一个IBinder(Binder),它本身就是一个能跨越进程边界传输的对象,所以它得继承IBinder实现transact这个函数从而得到跨越进程的能力(这个能力由驱动赋予)。Proxy类使用组合,是因为他不关心自己是什么,它也不需要跨越进程传输,它只需要拥有这个能力即可,要拥有这个能力,只需要保留一个对IBinder的引用。
如果把这个过程做一个类比,在封建社会,Stub好比皇帝,可以号令天下,他生而具有这个权利(不要说宣扬封建迷信。。)如果一个人也想号令天下,可以,“挟天子以令诸侯”。为什么不自己去当皇帝,其一,一般情况没必要,当了皇帝其实限制也蛮多的是不是?我现在既能掌管天下,又能不受约束(Java单继承);其二,名不正言不顺啊,我本来特么就不是(Binder),你非要我是说不过去,搞不好还会造反。最后呢,如果想当皇帝也可以,那就是asBinder了。在Stub类里面,asBinder返回this,在Proxy里面返回的是持有的组合类IBinder的引用。

在这里插入图片描述

前面有一直提到ServiceManager,我们先看看ServiceManager的源码:

public final class ServiceManager {
	private static final String TAG = "ServiceManager";

	private static IServiceManager sServiceManager;
    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();

    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

    /**
     * Returns a reference to a service with the given name.
     *
     * @param name the name of the service to get
     * @return a reference to the service, or <code>null</code> if the service doesn't exist
     */
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(getIServiceManager().getService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

    /**
     * Returns a reference to a service with the given name, or throws
     * {@link NullPointerException} if none is found.
     *
     * @hide
     */
    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
        final IBinder binder = getService(name);
        if (binder != null) {
            return binder;
        } else {
            throw new ServiceNotFoundException(name);
        }
    }

    /**
     * Place a new @a service called @a name into the service
     * manager.
     *
     * @param name the name of the new service
     * @param service the service object
     */
    public static void addService(String name, IBinder service) {
        try {
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

    /**
     * Place a new @a service called @a name into the service
     * manager.
     *
     * @param name the name of the new service
     * @param service the service object
     * @param allowIsolated set to true to allow isolated sandboxed processes
     * to access this service
     */
    public static void addService(String name, IBinder service, boolean allowIsolated) {
        try {
            getIServiceManager().addService(name, service, allowIsolated);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

    /**
     * Retrieve an existing service called @a name from the
     * service manager.  Non-blocking.
     */
    public static IBinder checkService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(getIServiceManager().checkService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in checkService", e);
            return null;
        }
    }

    /**
     * Return a list of all currently running services.
     * @return an array of all currently running services, or <code>null</code> in
     * case of an exception
     */
    public static String[] listServices() {
        try {
            return getIServiceManager().listServices();
        } catch (RemoteException e) {
            Log.e(TAG, "error in listServices", e);
            return null;
        }
    }

    /**
     * This is only intended to be called when the process is first being brought
     * up and bound by the activity manager. There is only one thread in the process
     * at that time, so no locking is done.
     *
     * @param cache the cache of service references
     * @hide
     */
    public static void initServiceCache(Map<String, IBinder> cache) {
        if (sCache.size() != 0) {
            throw new IllegalStateException("setServiceCache may only be called once");
        }
       sCache.putAll(cache);
    }

    /**
     * Exception thrown when no service published for given name. This might be
     * thrown early during boot before certain services have published
     * themselves.
     *
     * @hide
     */
    public static class ServiceNotFoundException extends Exception {
        public ServiceNotFoundException(String name) {
            super("No service published for: " + name);
        }
    }
}

我们可以看到,在ServiceManager中,有一个HashMap sCache来保存我们的IBinder,但是在addService方法中,可以看到是用的是IServiceManager.addService,在getService方法中,是先在sCahce中查找,找不到再去IServiceManager中找,所以核心还是IServiceManager。根据上面的知识,我们可以知道,IServiceManager应该是aidl接口的实现:

public interface IServiceManager extends IInterface
{
    /**
     * Retrieve an existing service called @a name from the
     * service manager.  Blocks for a few seconds waiting for it to be
     * published if it does not already exist.
     */
    public IBinder getService(String name) throws RemoteException;

    /**
     * Retrieve an existing service called @a name from the
     * service manager.  Non-blocking.
     */
    public IBinder checkService(String name) throws RemoteException;

    /**
     * Place a new @a service called @a name into the service
     * manager.
     */
    public void addService(String name, IBinder service, boolean allowIsolated)
                throws RemoteException;

    /**
     * Return a list of all currently running services.
     */
    public String[] listServices() throws RemoteException;

    /**
     * Assign a permission controller to the service manager.  After set, this
     * interface is checked before any services are added.
     */
    public void setPermissionController(IPermissionController controller)
            throws RemoteException;

    static final String descriptor = "android.os.IServiceManager";

   int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
    int CHECK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
    int ADD_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
    int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
    int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
    int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
}
ServiceManager启动

在这里插入图片描述

  • ServiceManager分为framework层和native层,framework层只是对native层进行了封装方便调用,图上展示的是native层的ServiceManager启动过程。
  • ServiceManager的启动时系统在开机的时候,init进程解析init.rc文件调用service_manager.c中的main()方法入口启动的。native层有一个binder.c封装了一些与Binder驱动交互的方法。
  • ServiceManager的启动分为三步,首先打开驱动创建全局链表binder_procs,然后将自己当前进程信息保存到binder_procs链表,最后开启loop不断的处理共享内存中的数据,并处理BR_xxx命令(ioctl 的命令,BR 可以理解为 binder reply 驱动处理完的响应)。
一次完整的通信:

在这里插入图片描述

我们在使用Binder的时候基本都是调用framework层封装好的方法,AIDL就是framework层提供的傻瓜式使用方法。

参考:https://blog.csdn.net/freekiteyu/article/details/70082302

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其binder是重之重,都说无binderAndroidbinde是Android系统的任督二脉。课程水平循序渐进,由级再到高级,满足各个层次水平的android开发者。1、灵活使用binder跨进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行跨进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种跨进程通信实战使用8、针对android源码使用的socket源码轻松掌握9、android系统源码最常见的socketpair双向跨进程通信10、使用socket实现一个可以让app执行shell命令的程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值