安卓的Binder通信与Aidl的简单理解

1,053 阅读6分钟

Binder通信

由于目前自己对于Binder通信的理解还不够到位,没有深入到源码,因此还是看别人的为主吧: 一个我觉得讲的比较详细的帖子,具体到代码上很细致,都有涉及到。 blog.csdn.net/qq_38998213…

总结一下就是:为了实现跨进程通信(IPC), 安卓系统为我们定义了一种Binder的通信方法。架构是C/S架构,也就是一边Client, 一边Server。 Server在Binder驱动里面注册自己的方法或者数据, 而Client可以通过向Binder驱动注册来申请使用Server提供的服务。

Aidl

什么是Aidl?

Android Interface defination language(Aidl) 是安卓提供给用户使用的一种接口设计语言,其本意就是方便用户设计跨进程通信。不用每次都从头自己写一堆代码,只需要新建一个aidl文件,规定里面的数据还有方法,IDE会自动帮忙生成一个通信的基本架构,即Binder驱动,服务器以及客户端之间的联系。用户需要实现的是:

  • 实现Server的具体功能
  • 在客户端中把自己跟需要使用的服务关联起来

具体实施

在一个新建的安卓项目里面新建一个aidl文件,Android Studio会自动生成一个初始的aidl文件:

// IMyAidlInterface.aidl
package com.ronghua.deviceselfcheck;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

aidl在这里告诉了你一些你可以使用的基本类型,如果需要传输的数据内容超出了这些范围,由于Binder通信只能传输序列化数据,需要自己通过实现Parcelable接口来定义。 这里的方法,数据类型都可以改,后面以我自己在做的项目中的一段代码举例,这个MagiskDetect的方法来源于一个博客darvincitech.wordpress.com/2019/11/04/…

首先列出一个文件,大家不想看可以直接划过去

interface IIsolatedProcess {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    boolean detectMagiskHide();
}

build一下项目,IDE会生成如下代码,是我们需要使用的:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.ronghua.deviceselfcheck;
// Declare any non-default types here with import statements

public interface IIsolatedProcess extends android.os.IInterface {
    /**
     * Default implementation for IIsolatedProcess.
     */
    public static class Default implements com.ronghua.deviceselfcheck.IIsolatedProcess {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        @Override
        public boolean detectMagiskHide() throws android.os.RemoteException {
            return false;
        }

        @Override
        public android.os.IBinder asBinder() {
            return null;
        }
    }

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.ronghua.deviceselfcheck.IIsolatedProcess {
        private static final java.lang.String DESCRIPTOR = "com.ronghua.deviceselfcheck.IIsolatedProcess";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.ronghua.deviceselfcheck.IIsolatedProcess interface,
         * generating a proxy if needed.
         */
        public static com.ronghua.deviceselfcheck.IIsolatedProcess asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.ronghua.deviceselfcheck.IIsolatedProcess))) {
                return ((com.ronghua.deviceselfcheck.IIsolatedProcess) iin);
            }
            return new com.ronghua.deviceselfcheck.IIsolatedProcess.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_detectMagiskHide: {
                    data.enforceInterface(descriptor);
                    boolean _result = this.detectMagiskHide();
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.ronghua.deviceselfcheck.IIsolatedProcess {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public boolean detectMagiskHide() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_detectMagiskHide, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().detectMagiskHide();
                    }
                    _reply.readException();
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static com.ronghua.deviceselfcheck.IIsolatedProcess sDefaultImpl;
        }

        static final int TRANSACTION_detectMagiskHide = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        public static boolean setDefaultImpl(com.ronghua.deviceselfcheck.IIsolatedProcess impl) {
            if (Stub.Proxy.sDefaultImpl == null && impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.ronghua.deviceselfcheck.IIsolatedProcess getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public boolean detectMagiskHide() throws android.os.RemoteException;
}

文件的内容很长,但是我们可以省略一部分代码,重点需要关注两个部分

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.ronghua.deviceselfcheck;
// Declare any non-default types here with import statements

public interface IIsolatedProcess extends android.os.IInterface {
   //一堆。。。。。。不用看
    public static abstract class Stub extends android.os.Binder implements com.ronghua.deviceselfcheck.IIsolatedProcess {
        //一堆。。。。。。不用看
        
        public static com.ronghua.deviceselfcheck.IIsolatedProcess asInterface(android.os.IBinder obj) {
            //返回一个客户端可以使用的 Proxy
        }
        //一堆。。。。。。不用看
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            //blablabla一堆
            boolean _result = this.detectMagiskHide();
            //然后返回结果
            }
        }
        private static class Proxy implements com.ronghua.deviceselfcheck.IIsolatedProcess {
            private android.os.IBinder mRemote;
            @Override
            public boolean detectMagiskHide() throws android.os.RemoteException {
                //blablabla一堆
                boolean _status = mRemote.transact(Stub.TRANSACTION_detectMagiskHide, _data, _reply, 0);
                //blablabla一堆
            }

        }

       //一个default的Stub实现,但我们不关注
       
    public boolean detectMagiskHide() throws android.os.RemoteException;
}

这样精简完了之后,我们需要关注的内容就少了很多:

  • Stub类
  • Proxy类
  • IIsolatedProcess本身

首先是Proxy类。Proxy类的存在就是为了Client能好好地使用Server端的功能,我给你一个Proxy,你使用就完事了。那Client如何能获取Proxy呢?

我们这时候可以看到Stub里面的一个static方法asInterface能做到这样的事。也就是说,Client端只需要在合适的时机调用asInterface方法,就能获取到我需要的Proxy,通过Proxy,我们就可以调用binder驱动使唤Server。

Stub类就是上述的binder驱动,关联两端的通信。但是Stub里面没有帮你实现接收到调用detectMagiskHide请求的时候的逻辑代码。也就是说,如果我们能把Stub在服务端给实现了,那么我们就完成了整个过程。

目前我们的思路是这样的:

  • 建立一个Service,这个Service实现了我们Stub里面detectMagiskHide真实逻辑功能,并把这个Stub作为自己的binder驱动。
  • Client为了使用服务,对Service进行绑定,希望获得binder驱动
  • 绑定成功,Service会把刚刚实现好的Stub接口(Binder驱动)告诉Client,Client可以直接使用,但是很麻烦,因为进程间通信使用的不是普通的数据类型,因此我们需要asInterface转换成方便我们使用的Proxy

将上述的逻辑实现成代码看起来就是这样的:

  • Service.class
package com.ronghua.deviceselfcheck;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.Nullable;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class IsolatedService extends Service {
    private static String[] blackListMountPaths = {"/sbin/.magisk/",
            "/sbin/.core/mirror",
            "/sbin/.core/img",
            "/sbin/.core/db-0/magisk.db"};
    private static String TAG = "DetectMagisk";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private IBinder mBinder = new IIsolatedProcess.Stub() {
        @Override
        public boolean detectMagiskHide() throws RemoteException {
            try{
                Native.isLibLoaded();
            }catch(UnsatisfiedLinkError e){
                Log.i(TAG, "Native lib is not loaded successfully");
                return true;
            }
            boolean isMagiskDetect = false;
            int count = 0;
            try {
                FileReader fr = new FileReader("/proc/self/mounts");
                BufferedReader br = new BufferedReader(fr);
                String line = "";
                while((line = br.readLine()) != null){
                    for(String path:blackListMountPaths){
                        Log.i(TAG, "Checking mount path: " + path);
                        if(line.contains(path)){
                            count++;
                            Log.i(TAG, "Magisk path detected: " + path);
                            break;
                        }
                    }
                }
                if(count > 0)
                    isMagiskDetect = true;
                else{
                    isMagiskDetect = Native.detectMagiskNative();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return isMagiskDetect;
        }
    };


}

IsolatedService实现了Service,然后将我们希望返回的Binder驱动利用onBind方法返回。这样Service被绑定的时候会就告诉MainActivity 驱动是什么。

  • MainActivity.class
package com.ronghua.deviceselfcheck;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IBinder;


public class MainActivity extends AppCompatActivity {

    private boolean isBound = false;
    private static String TAG = "DetectMagisk";
    private IIsolatedProcess mServiceBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, IsolatedService.class);
        getApplicationContext().bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mServiceBinder = IIsolatedProcess.Stub.asInterface(service);
            mRemote = service;
            isBound = true;
            Log.i(TAG, "service is bound");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };


 
}

MainActivity onStart()的时候对刚刚的Service进行绑定,并且在绑定成功onServiceConnected()的时候把获取到的Binder驱动保存下载,方便使用。刚刚提到,这个Binder由于数据格式问题,用起来并不方便,因此我们通过asInterface获取Proxy。后面我们什么时候想让Service做任务,就可以通过Proxy使唤了。

(如果是不同进程,Binder驱动会对onBind做处理。同进程asInterface会直接获取到实现的Stub(Service 与 MainActivity 同进程),所以可以直接调用Stub实现的方法,也就类似于同进程实现Binder,获取Service实例。如果是不同进程,我们是不能直接获取到obj这种实例,因此会给我们一个BinderProxy来通过Binder驱动调用Service实现)