android之binder学习攻克

《深入理解Android内核设计思想》笔记之Binder

2017-02-21  本文已影响868人  Hohohong

本篇文章是《深入理解Android内核设计思想》的读书笔记,可能就没书本来得详细。

image.png

Binder分为四个部分

一.Binder驱动

binder-open{
生成binder_proc对象
binder_proc初始化
把它添加到Binder的全局管理中(资源互斥添加)
}

到这里,Binder驱动已经为用户创建了一个它自己的binder_proc实体,之后用户对Binder的设备操作将以这个对象为基础。

二.ServiceManager - Binder Service

int main{
binder_open打开Binder设备,做好初始化(各种内存地址)
binder_become_context_manager将自己设为Binder大管家,整个Android程序中只允许一个ServiceManager存在
binder_loop进入主循环
}

* binder_open初始化
>    * binder_state 记录SM中有关于Binder的所有信息
     * mmap由Binder驱动决定被映射到进程空间中的内存起始地址
     * 映射区大小为128K,只读
     * 从文件的起始地址开始映射
* binder_become_context_manager初始化
>    * 调用到ioctl方法
     * ioctl方法中发送向Binder Driver发送 **BINDER_SET_CONTEXT_MGR**的ioctl命令使其成为大管家。(只要向Binder驱动发送BINDER_SET_CONTEXT_MGR的ioctl命令即可,因为ServicerManager启动的很早,能确保它是系统中第一个向Binder驱动注册成管家的程序)
* binder_loop循环等待客户端请求
>    * binder_write_read 执行**BINDER_WRITE_READ**所需的数据格式
     * 开始循环读取消息(它的消息是从Binder驱动那里获取的)
     * 从Binder驱动读取消息,通过**BINDER_WRITE_READ**命令
     * binder_parse处理消息
     * 不断循环,而且永远不会主动退出,除非出现致命错误

ServiceManager的功能架构比较简洁,内部维护着一个svclist列表,用于存储所有Server(Binder Service)的相关信息(以svcinfo为数据结构),查询和注册都是基于这个表展开

访问SM服务流程

1.打开Binder设置
2.执行mmap内存映射
3.通过Binder驱动向SM发送请求(SM的handle为0)
4.获取结果

每个进程只允许打开一次Binder设备,且只做一次内存映射---所有需要使用Binder驱动的线程共享这一资源。

获取SM服务流程图

获取SM服务

进程中ProcessState和IPCThreadState这两个类专用与Binder驱动通信,所以Java层代码使用Binder驱动实际上是基于他们来完成的,我们称为BpBinder。

应用程序通过代理ServiceManagerProxy(能够跨进程调用)来获取到SM服务,它所能提供的服务和服务端的SM必须是一致的,也就是它也要实现服务端SM对应的接口。
这里的SM指的是Binder层面的ServiceManager。

对于获取SM服务的代理ServiceManagerProxy,Android系统在ServiceManagerProxy上又加了一层封装ServiceManager.java

2.1 获取SM服务

首先调用ServiceManager.getService(name)

 public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

其中sCache用于记录getService的历史查询结果,加快查询速度。如果不在则调用getIServiceManager().getService(name);
getIServiceManager方法用于获取一个IServiceManager对象,而IServiceManager接口的实现类则是ServiceManagerProxy

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

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

调用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);
    }

先查询本地是否已经有了IServiceManager,如果没有则新建一个。

最终则是调用到了ServiceManagerProxy.getService

public IBinder getService(String var1) throws RemoteException {
        Parcel var2 = Parcel.obtain();
        Parcel var3 = Parcel.obtain();
        var2.writeInterfaceToken("android.os.IServiceManager");
        var2.writeString(var1);
        this.mRemote.transact(1, var2, var3, 0);
        IBinder var5 = var3.readStrongBinder();
        var3.recycle();
        var2.recycle();
        return var5;
    }

该函数分为以下3部分

那么IBinder内部是怎么实现的呢?

2.2 IBinder内部

前面我们的IBinder对象是通过BinderInternal.getContextObject()获取到的
而它则是一个native方法

public static final native IBinder getContextObject();

因为和Binder驱动打交道,最终都得通过JNI调用本地代码来实现。
而在native层中getContextObject方法通过ProcessState把 创建的对象BpBinder转换为Java层的IBinder对象。

IBinder只是一个接口类,
在native层,IBinder实现类为BpBinder(由ProcessState创建)
而在Java层,IBinder实现类则是BinderProxy。

前面调用IBinder.transact内部调用流程

Paste_Image.png

发现绕了个大圈子,最终处理用户的Binder请求还是通过IPCThreadState和ProcessState来实现的。

2.3 ProcessState

关键点在于

可以看到ProcessState它是一个进程中的单实例,而IPCThreadState则是线程中的单实例
可以说,IPCThreadState负责与Binder驱动进行具体的命令交互,因而它的transact函数非常重要,
而ProcessState只是负责打开了Binder节点并做mmap

三.Binder Client客户端

Binder的最大消费者是JAVA层的应用程序。一般情况下他们可以在程序代码的任何位置通过bindService,startActivity以及sendBroadcast等一系列接口方法来实现与其他进程的交互。

有了Binder驱动,Service Manager的努力,以及Android系统专门面向应用开发提供的Binder强有力的封装,才能使应用程序之间顺利地进行无缝通信。

3.1 例子bindService

应用程序如何能依托bindService来启动系统中其他进程提供的Service呢

当Activity调用bindService时,最终调用到的是ContextImpl.bindService

 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        ...
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
        ...
    }

那么,应用程序又是如何找到AMS并与之建立联系的呢?
和ServiceManager一样,AMS也同样提供了ActivityManagerNative和ActivityManagerProxy代理

//ActivityManagerNative.getDefault()
static public IActivityManager getDefault() {
        return gDefault.get();
    }

这个gDefault.get又是什么

  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            ...
            IActivityManager am = asInterface(b);
            ...
            return am;
        }
    };

可以看到这里是通过单例模式来创建一个IActivityManager对象。
之后通过ServiceManager.getService("activity")方法取得ActivityManagerService的IBinder对象。
接着调用asInterface,通过这个IBinder创建一个可用的ActivityManagerProxy

static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

此外,ActivityManagerNative的另一个作用是为ActivityManagerService的实现提供便利。AMS只需要继承自ActivityManagerNative就可以将用户的业务请求码与自己的内部实现函数连接起来。

Paste_Image.png

四.AIDL——Android接口描述语言

它是用于定义客户端/服务端通信接口的一种描述语言。

要知道Server Manager本身也是一个BinderServer,AMS,WMS也是一个BinderServer,那么从Service Manager的实现来看,构建一个Binder Server所需的工作为:

1.启动的时机

比如SM是开机的时候通过init.rc文件启动的,这就保证了它是系统中第一个注册成服务大管家的Server,而WMS,AMS这些则是在SystemServer中启动的。

2.提供一致的服务接口

一个Binder Server应该向公众暴露它所能提供的服务,而且客户端使用的服务接口和服务器实现的服务接口必须是完全一致的。

对于WMS
通过分析aidl文件以及由它转换生成的java接口文件,我们知道一个AIDL接口包括了IwindowManager,IWindowManager.Stub和IWindowManagerManager.Stub.Proxy三个重要类。
后两者分别面向与WMS的服务端和Binder Client本地代理的实现,且都继承与IWindowManager,因而就保证了Client和Server是在完全一致的服务接口上进行通讯。

3.与Binder驱动的交互

一个Binder Server需要与Binder驱动做哪些交互呢?除去一系列必要的初始化以外(open,mmap等),就是要通过不断地ioctl来循环读写数据。

比如SM就是通过binder_loop函数中的一个for死循环来完成这一工作的。
而对于AMS,PMS这些,他们在SystemServer启动的过程中会开启一个线程来循环读取消息,这点和binder_loop的主体框架基本一致。

4.外界如何才能访问到这个Server服务?

WMS可以通过addService把自己注册到Service Manager中。因而任何Binder Client都可以通过SM的getService接口获取到它的一个引用。称为实名的Server。

更简单的来构建Binder Server就是使用AIDL.

上一篇下一篇

猜你喜欢

热点阅读