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实现)