美文网首页
从AIDL分析Framework层中的跨进程通信--Binder

从AIDL分析Framework层中的跨进程通信--Binder

作者: simonenfp | 来源:发表于2020-07-16 18:28 被阅读0次

    AIDL简介

    AIDL是Android Interface Definition Language的缩写,即Android接口定义语言。它是Android的进程间通信比较常用的一种方式,其原理是通过Binder机制实现进程间通信的

    从一个简单的AIDL实例开始分析

    由于Binder机制的进程间通信是基于C/S架构的,这里先看下客户端需要创建的文件以及代码---

    • 客户端(Client)应用创建:在客户端定义一个Book实体,如果在Binder通信中传递非基本类型,那么除了需要创建一个实现Parcelable接口的实体类外,还需要再建个和实体类命名一样的实体AIDL文件,如下:
    // Book.aidl
    package com.example.client;
    parcelable Book;
    

    在同一个包下定义一个AIDL接口文件,其中有两个方法:

    // IBookManager.aidl
    package com.example.client;
    //注意即使在同一个包下也需要导入
    import com.example.client.Book;
    
    interface IBookManager {
        //添加书本
        void addBook(in Book person);
        //返回图书列表
        List<Book> getBookList();
    }
    

    绑定远程服务端代码(获取远程Binder引用):

        private void bindService() {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.example.client", "com.xx.leo_service.LeoAidlService"));
            bindService(intent, connection, Context.BIND_AUTO_CREATE);
        }
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                iBookManager = IBookManager.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                iBookManager = null;
            }
        };
    
    • 服务端(Server)应用创建,注意需要把Book实体类和AIDL接口文件完全拷贝过来,包名也需要一致,除此之外,再新建一个服务,其内部创建一个Binder,继承IBookManager.Stub,实现远程方法:
      void addBook(in Book book);
      List<Book> getBookList();
      并通过onBind()返回给客户端
      如下:
    public class BookManagerService extends Service {
        private ArrayList<Book> books;
        private String TAG = "BookManagerService";
        @Override
        public IBinder onBind(Intent intent) {
            books = new ArrayList<>();
            Log.e(TAG, "success onBind:"+getApplicationInfo().processName);
            return iBinder;
        }
    
        private IBinder iBinder = new IBookManager.Stub() {
            @Override
            public void addBook(Book book) throws RemoteException {
                Log.e(TAG, "addBook:"+ Thread.currentThread().getName());
                books.add(book);
            }
    
            @Override
            public List<Book> getBookList() throws RemoteException {
                Log.e(TAG, "getBookList:"+ Thread.currentThread().getName());
                return books;
            }
        };
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e(TAG, "onCreate: success");
        }
    }
    

    接下来将两个应用跑起来就可以在Client和Server间进行进程间通信了,在客户端调用以下方法即会调用到服务端的具体实现方法:
    iBookManager.addBook(new Book("历史", 1));
    List<Book> books = iBookManager.getBookList();

    简单分析下AIDL为我们生成的文件

    /*
     * This file is auto-generated.  DO NOT MODIFY.
     */
    package com.example.client;
    
    public interface IBookManager extends android.os.IInterface {
        public static abstract class Stub extends android.os.Binder implements com.example.client.IBookManager {
            private static final java.lang.String DESCRIPTOR = "com.example.client.IBookManager";
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
            public static com.example.client.IBookManager asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.example.client.IBookManager))) {
                    return ((com.example.client.IBookManager) iin);
                }
                return new com.example.client.IBookManager.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_addBook: {
                        data.enforceInterface(DESCRIPTOR);
                        com.example.client.Book _arg0;
                        if ((0 != data.readInt())) {
                            _arg0 = com.example.client.Book.CREATOR.createFromParcel(data);
                        } else {
                            _arg0 = null;
                        }
                        this.addBook(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                    case TRANSACTION_getBookList: {
                        data.enforceInterface(DESCRIPTOR);
                        java.util.List<com.example.client.Book> _result = this.getBookList();
                        reply.writeNoException();
                        reply.writeTypedList(_result);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.example.client.IBookManager {
                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 void addBook(com.example.client.Book book) throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        if ((book != null)) {
                            _data.writeInt(1);
                            book.writeToParcel(_data, 0);
                        } else {
                            _data.writeInt(0);
                        }
                        mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
    
                @Override
                public java.util.List<com.example.client.Book> getBookList() throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    java.util.List<com.example.client.Book> _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.createTypedArrayList(com.example.client.Book.CREATOR);
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }
    
            static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        }
    
        public void addBook(com.example.client.Book book) throws android.os.RemoteException;
    
        public java.util.List<com.example.client.Book> getBookList() throws android.os.RemoteException;
    }
    
    

    这里主要生成了两个类,一个Stub和一个Proxy,都实现了IBookManager接口,很显然这是代理模式

    • 从客户的绑定服务开始,可以看到在ServiceConnection的onServiceConnected方法中返回了一个IBinder参数service,通过传入方法IBookManager.Stub.asInterface(service)中获取一个客户端代理类,
    • 在Stub的asInterface()中,queryLocalInterface作用是判断是否是在同一个进程中,如果是直接返回当前对象,无需跨进程,如果不是,则将远程返回的BinderProxy(也实现了IBinder)传入Proxy,创建一个本地客户端代理类
    • 当调用代理类的iBookManager.addBook(new Book("历史", 1));时,将对象序列化入参调用mRemote.transact()(这个函数三个重要参数:int code、Parcel data、Parcel reply,分别对应了被调函数编号、参数包、响应包),这里就通过Binder驱动,调到了远程服务端的Stub(继承Binder实现IBookManger)的onTransact()方法,根据入参定位具体方法,进而调用到服务端的Binder中addBook()方法。getBookList()方法调用方式一样

    整体流程很简单,通过绑定远程服务-->获取远程BinderProxy代理引用-->作为入参创建本地客户端Proxy-->调用本地Proxy方法--->mRemote.transact()-->通过Binder底层驱动处理-->调用到远程Stub的onTransact方法-->最终调用到远程服务的目标实现方法。虽然服务端和客户端有着一模一样的代码,可以看出Stub主要给服务端使用,而Proxy主要是给客户端使用

    类关系图如下: AIDL类关系图.png

    bindService()如何获取到远程服务Binder引用

    进程间通信客户端如何从服务端获取Binder引用是关键,拿到了引用才能调用其远程方法,接下来简单分析下bindService()源码(Android-23)。

    @Override
        public boolean bindService(Intent service, ServiceConnection conn,
                int flags) {
            return mBase.bindService(service, conn, flags);
        }
    
    
    • mBase是上下文context,而调用context的bindService()实际上实际上是调用它的实现类ContextImpl的bindService()方法,所以最终会调用到ContextImpl的bindServiceCommon()方法: bindServiceCommon.png

    1.构建IServiceConnection后面用于给客户端回传服务端Binder引用。
    2.ActivityManagerNative.getDefault()获取的其实是ActivityManagerService本地客户端(即当前用户进程)的代理类,调用ActivityManagerService代理类的bindService()方法,最终通过Binder驱动会跨进程调用Systemserver进程中ActivityManagerServicebindService()方法。

    • 看下ActivityManagerNative.getDefault()
    ActivityManagerNative.getDefault().png
    1. 由于AMS所在进程是Systemserver进程,因此应用进程调用系统服务ActivityManagerService的方法也需要跨进程,那就必须拿到(即Systemserver进程)ActivityManagerService服务的Binder引用,才能跨进程通信,所以上面代码1处即通过ServiceManager获取ActivityManagerService在远程服务端的Binder引用,然后在2处传入本地客户端代理,最终返回客户端代理类ActivityManagerProxy,当调用代理类的bindService()时即会跨进程调用服务端ActivityManagerService方法绑定服务。可以看出这里是一次跨进程调用,应用进程与Systemserver进程的通信,通过系统服务ActivityManagerService去绑定一个服务
    2. 而1处是如何拿到AMS远程服务的Binder引用的呢?
    • 接着看下ServiceManager.getService("activity")
     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;
        }
    private static IServiceManager getIServiceManager() {
            if (sServiceManager != null) {
                return sServiceManager;
            }
            // Find the service manager
            sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
            return sServiceManager;
        }
    

    根据系统服务名,先从Cache中查找是否已有,没有的话则通过getIServiceManager().getService(name)获取,
    getIServiceManager()中通过BinderInternal.getContextObject()获取远程服务端ServiceManager服务的Binder引用,并创建本地代理类,通过代理类的getService()方法调用远程服务的getService()方法获取ActivityManagerService系统服务。
    可以看到这里又是一次进程间通信,通过ServiceManager获取系统服务ActivityManagerService的Binder引用还需要与ServiceManager所在进程跨进程通信,那么与ServiceManager服务通信的Binder引用又从哪里来呢???
    这里简单介绍下ServiceManager服务,进程间通信的客户端想要获取服务端的 Bind引用都需要通过它来获取,而所有服务端都需要向ServiceManager注册自己的Binder以供客户端使用,由于其他进程与ServiceManager服务进程通信也必须获取它的Binder引用,所以ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager时 Binder 驱动会自动为它创建 Binder 实体,其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向ServiceManager注册自己的 Binder 就必须通过这个 0 号引用Binder和 ServiceManager服务进行通信。

    • 回到ActivityManagerNative.getDefault().bindService(),经过上面的分析可以知道其实这里调用是用户应用进程的ActivityManagerService代理类(其实现IActivityManager接口)的bindService(),即是:ActivityManagerProxy.bindService()
    public int bindService(IApplicationThread caller, IBinder token,
                Intent service, String resolvedType, IServiceConnection connection,
                int flags,  String callingPackage, int userId) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            data.writeStrongBinder(token);
            service.writeToParcel(data, 0);
            data.writeString(resolvedType);
            data.writeStrongBinder(connection.asBinder());
            data.writeInt(flags);
            data.writeString(callingPackage);
            data.writeInt(userId);
            mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
            reply.readException();
            int res = reply.readInt();
            data.recycle();
            reply.recycle();
            return res;
        }
    

    其实就是AIDL中代理类的方法,mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);最终会通过这个方法调用到远程服务ActivityManagerServiceonTransact()方法,ActivityManagerService继承自ActivityManagerNative,并实现了IActivityManager中相应的方法。
    所以先看下ActivityManagerNative中的transact()方法中对应的BIND_SERVICE_TRANSACTION位置:

    ActivityManagerNative.png
    上述代码1处即调用ActivityManagerServicebindService()方法,至此,从用户进程切换到了AMS所在进程进行绑定服务操作。
    AMS服务中的相关AIDL类关系: image.png

    进入到ActivityManagerService中,看下bindService具体实现流程

    由于细节太多,这里仅仅列出主线,可自行根据主线方法名跟入源码仔细研究

    • ActivityManagerService#bindService会再调用ActiveServices#bindServiceLocked,
      ActiveServices#bindServiceLocked中如果目标进程会去创建目标进程,如果服务未启动,会启动服务
    • ActiveServices中启动服务端调用流程:bindServiceLocked()-->bringUpServiceLocked()-->realStartServiceLocked()-->app.thread.scheduleCreateService()在这里app.thread其实又是一个跨进程通信,从AMS进程进入到目标用户进程,其AIDL接口是IApplicationThread,Stub是ApplicationThreadNative,Proxy是ApplicationThreadProxy,远程服务端实现是ActivityThread内部类ApplicationThread,它继承自ApplicationThreadNative(这里用户进程变成了服务端,AMS服务所在进程变成了客户端),所以先看下客户端代理类的ApplicationThreadProxy#scheduleCreateService方法: image.png

    可以看到调用了s.onBind,就是我们在服务端服务中定义的onBind方法
    获取我们的Binder对象,接着又调用ActivityManagerNative.getDefault()publishService(),这里非常眼熟,与前面的bindService就很类似,又是跨进程通信,拿到AMS的客户端代理,执行它的publishService(),最终又会交给AMS去执行,不再重复看

    • 直接到ActivityManagerService中找publishService()方法,发现它又会调用ActiveServices#publishServiceLocked()
     void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
            final long origId = Binder.clearCallingIdentity();
            try {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                        + " " + intent + ": " + service);
                if (r != null) {
                    Intent.FilterComparison filter
                            = new Intent.FilterComparison(intent);
                    IntentBindRecord b = r.bindings.get(filter);
                    if (b != null && !b.received) {
                        b.binder = service;
                        b.requested = true;
                        b.received = true;
                        for (int conni=r.connections.size()-1; conni>=0; conni--) {
                            ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                            for (int i=0; i<clist.size(); i++) {
                                ConnectionRecord c = clist.get(i);
                                if (!filter.equals(c.binding.intent.intent)) {
                                    if (DEBUG_SERVICE) Slog.v(
                                            TAG_SERVICE, "Not publishing to: " + c);
                                    if (DEBUG_SERVICE) Slog.v(
                                            TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
                                    if (DEBUG_SERVICE) Slog.v(
                                            TAG_SERVICE, "Published intent: " + intent);
                                    continue;
                                }
                                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                                try {
                                    c.conn.connected(r.name, service);
                                } catch (Exception e) {
                                    Slog.w(TAG, "Failure sending service " + r.name +
                                          " to connection " + c.conn.asBinder() +
                                          " (in " + c.binding.client.processName + ")", e);
                                }
                            }
                        }
                    }
    
                    serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
                }
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
        }
    

    可以看到核心部分c.conn.connected(r.name, service),(其实这里涉及到一开始bindServiceCommon中构建的IServiceConnection,在LoadedApk中的ServiceDispatcher.InnerConnection中,其实本质又是一次跨进程通信,感兴趣的可以看下源码)这里就会再调用到我们在客户端绑定服务时传入的ServiceConnection的onServiceConnected方法,最终客户端拿到服务端IBinder的引用!!!
    至此,终于结束了,可以发现为了实现自定义的一次跨进程通信,其内部经历了无数次跨进程。。。

    相关文章

      网友评论

          本文标题:从AIDL分析Framework层中的跨进程通信--Binder

          本文链接:https://www.haomeiwen.com/subject/lvlgcktx.html