
在某些虚拟化,免安装,打点,环境检测,拦截器等场景,针对
Android系统服务接口的拦截是常用的技术方案。通常只是针对正向的接口调用,如果涉及被动的服务回调拦截,则实现起来就有些许麻烦。
由于我们容器产品的特性,需要将应用完整的运行起来,所以必须要对各系统服务(超过100+系统服务)通信进行拦截过滤,修正和还原接口通信的数据。让系统和应用可以无感知运行,实现免安装运行的效果。
整个方案基本上都聚焦在服务模块主动调用的拦截上,系统回调的拦截涉及较少,但随着功能的深入,越来越需要对服务回调的接口(Binder for Oneway)进行拦截。在这里将整个通用的拦截方案和实现过程分享出来,希望对大家有益。
Binder的Oneway回调机制,即应用进程向系统服务注册回调(通常注册和取消注册成对出现),当服务端有相应时间时,可以直接回调给改Binder对象实例。
如常见的AMS服务接口:
// source code: /frameworks/base/core/java/android/app/IActivityManager.aidl interface IActivityManager { // ... Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String requiredPermission, int userId, int flags ); void unregisterReceiver(in IIntentReceiver receiver); // ... } 我们的目标:
AMS的registerReceiver方法,将参数receiver通过Proxy创建一个新的扩展类对象传递出去。android.content.IIntentReceiver)由于通常系统接口类(如:IActivityManager.aidl,IPackageManager.aidl等)均为隐藏类,因此很自然的想法是将系统的aidl源文件导入到工程中。

配置好目录:
sourceSets { main { aidl.srcDirs = ['src/main/aidl'] } } 编译后我们就可以连接该类,并进行继承扩展了,如:
public class StubIntentReceiver extends IIntentReceiver.Stub { Object mOrigin; protected StubIntentReceiver(Object org) { this.mOrigin = org; } private static Method sMethod_performReceive; public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException { // TODO something here ... if (null == sMethod_performReceive) { sMethod_performReceive = ReflectUtils.getDeclaredMethod( mOrigin, "performReceive", Intent.class, int.class, String.class, Bundle.class, boolean.class, boolean.class, int.class ); } sMethod_performReceive.invoke(mOrigin, intent, resultCode, data, extras, ordered, sticky, sendingUser); } } 对于IIntentReceiver.aidl的回调接口来说,这样就可以解决了,因为他满足了几个特性:
9.0 ~ 14.0接口名和参数都一致。然而更多的接口并非如此,接口类函数不仅仅是多个,而且不同版本类方法各异,同函数参数也都不相同,这才是常态,所以我们自然的解决方案就是:flavor。
既然每个版本可能不一致,那就编译多版本就可以解决了,如:

这样确实能解决多版本系统接口变化的问题,但同时带来了新的问题:
Parcelable的对象,而该对象又为隐藏类,因此又需要继续导入关联的类确保编译运行正常,导致越来越臃肿。我们对于复杂的方案生来恐惧,越复杂越做不稳定,所以我们的目标:
于是我们通过编译后的源码我们目标锁定在Binder的onTransact函数,如:
public interface IIntentReceiver extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements android.content.IIntentReceiver { private static final java.lang.String DESCRIPTOR = "android.content.IIntentReceiver"; /** Construct the stub at attach it to the interface. */ @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 TRANSACTION_performReceive: { data.enforceInterface(DESCRIPTOR); Intent _arg0; if (0 != data.readInt()) { _arg0 = Intent.CREATOR.createFromParcel(data); } else { _arg0 = null; } int _arg1 = data.readInt(); String _arg2 = data.readString(); Bundle _arg3; if (0 != data.readInt()) { _arg3 = (Bundle)Bundle.CREATOR.createFromParcel(data); } else { _arg3 = null; } boolean _arg4 = 0 != data.readInt(); boolean _arg5 = 0 != data.readInt(); int _arg6 = data.readInt(); // call function here !!! this.performReceive(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } } 于是我们的方案:

IIntentReceiver.aidl),该接口无方法,仅保持名字一致,目的只是为了编译出IIntentReceiver.class类。onTransact方法,仅处理感兴趣的code(aidl文件编译后函数对应的编号),其他的默认调用原对象方法。于是我们扩展实现类为:
import android.content.IIntentReceiver; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.text.TextUtils; public class OnewayIIntentReceiver extends IIntentReceiver.Stub { private final Object mArgument; private static int TRANSACTION_performReceive = -1; public OnewayIIntentReceiver(Object org) { mArgument = org; if (TRANSACTION_performReceive < 0) { TRANSACTION_performReceive = ReflectUtils.getMethodCode(org, "TRANSACTION_performReceive"); } } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException { if (TRANSACTION_performReceive == code) { data.enforceInterface(getInterfaceDescriptor()); Intent _arg0; if (0 != data.readInt()) { _arg0 = Intent.CREATOR.createFromParcel(data); } else { _arg0 = null; } int _arg1 = data.readInt(); String _arg2 = data.readString(); Bundle _arg3; if (0 != data.readInt()) { _arg3 = (Bundle)Bundle.CREATOR.createFromParcel(data); } else { _arg3 = null; } boolean _arg4 = 0 != data.readInt(); boolean _arg5 = 0 != data.readInt(); int _arg6 = data.readInt(); // do call origin Method method = ReflectUtils.getDeclaredMethod( mArgument.mOrigin, "performReceive", Intent.class, int.class, String.class, Bundle.class, boolean.class, boolean.class, int.class ); method.invoke(mOrigin, _arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6); reply.writeNoException(); return true; } return doTransact(code, data, reply, flags); } public boolean doTransact(int code, Parcel data, Parcel reply, int flags) { Method method = ReflectUtils.getDeclaredMethod( mOrigin, "onTransact", int.class, Parcel.class, Parcel.class, int.class ); } try { return (Boolean) method.invoke(mOrigin, code, data, reply, flags); } catch (Throwable e) { Logger.e(e); } return false; } } 至此,我们找到了相对简单,兼容性好的系统接口回调的拦截方案。
如果该服务为Native实现,则需要参考我们的另一篇文章 深入 Binder 拦截 来解决。