Android 代理拦截系统层代码

  • 一.手动代理
    • 1.溯源
    • 2.手动代理
  • 二.动态代理
    • 1. 溯源
    • 2. 动态代理

有时我们的项目会遇见如下所示的崩溃堆栈:

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2101)at android.os.Handler.dispatchMessage(Handler.java:108)at android.os.Looper.loop(Looper.java:166)at android.app.ActivityThread.main(ActivityThread.java:7425)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

这种堆栈没有我们业务层代码的信息,所以我们一时间会不知所措,想try-catch捕获都捕获不了,因为都是系统层的崩溃。

那么这就引出一个问题,我们能不能捕获系统层的代码异常或普通逻辑呢?答案是可以的,我们可以用代理的方法来实现。

一.手动代理

1.溯源

这里我们以上述代码为例,尝试捕获系统层的主线程Handler抛出的异常。

首先我们根据崩溃堆栈来看系统层的实现。

public final class ActivityThread extends ClientTransactionHandler {final H mH = new H();class H extends Handler {//...}}

一个app进程的入口就在ActivityThread中,这个类中有一个叫做H,继承自Handler的类,且在ActivityThread中,创建了一个该对象叫做mH,这个Handler对象,就是上面堆栈中的那个handleMessage()方法的调用者。

再来看Handler里是如何调用handleMessage()方法的。

public void dispatchMessage(Message msg) {    if (msg.callback != null) {//Runnable        handleCallback(msg);    } else {        if (mCallback != null) {//Handler的callback对象            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);//重写该方法    }}

当Looper将Message对象,传递到Handler的dispatchMessage()方法中时,首先会看Message的callback对象是否存在,存在的话直接回调,这个callback其实是Runnable对象,是我们经常使用的handler.post({})等方法传入的回调参数,在这里由于堆栈显示,走的是Handler的handleMessage()方法,所以显然这里的callback为null,走else分支。

在else分支里,还有一个处理优先级较高的mCallback,如果该mCallback处理结果为true则直接结束处理,否则交由Handler自身的dispatchMessage()方法处理。

public class Handler {final Callback mCallback;public Handler() {        this(null, false);    }public Handler(Callback callback, boolean async) {        //...        mCallback = callback;    }}

可见,Handler的callback,是构造函数构造时传入的,而ActivityThread中创建mH时,是直接走的默认构造函数,所以这个mCallback为null,所以上面说的else分支里,会直接调用Handler的dispatchMessage()方法,我们再来看dispatchMessage()方法。

class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {//...case SCHEDULE_CRASH://flag=134                    throw new RemoteServiceException((String)msg.obj);}}}

在该方法里,如果Message的what是134的话,就会将Message的信息,抛出一个RemoteServiceException异常,也就是我们上面的堆栈信息来源。

2.手动代理

找到了源头,我们怎么解决呢?由于抛出异常的地方是系统层代码,我们无法直接在某个代码处try-catch住,于是我们想到了代理的方法,如果能找到一个可代理的对象,可以hook住系统层的mH的handleMessage()方法就好了。

于是我们想起了上面说的Handler的dispatchMessage()方法执行过程中,有一个Handler的callback可以加以利用。

public static class HookHandlerCallback implements Handler.Callback {    private final Handler.Callback delegate;    public HookHandlerCallback(Handler.Callback delegate) {        this.delegate = delegate;    }    @Override    public boolean handleMessage(Message msg) {        if (msg.what == 134 && msg.obj instanceof String && ((String) msg.obj).contains("Context.startForegroundService() did not then call Service.startForeground()")) {            //..log            return true;//直接拦截不抛出异常        }        return delegate != null ? delegate.handleMessage(msg) : false;    }}

如果我们能将ActivityThread的mH里面的mCallback替换为一个我们自定义的Callback,并代理原始mCallback,这样一来,处理Message时会优先交由我们的的callback处理,我们处理后,交由原始mCallback处理,现在mCallback为null,直接返回false,交由mH自身的handleMessage()处理即可,就可以实现代理的功能。

现在的问题就是如何获取原始的mCallback和如何改变mH的mCallback的指向,这个问题很简单,就是用反射。

我们先来看看系统源码。

public final class ActivityThread extends ClientTransactionHandler {final H mH = new H();private static volatile ActivityThread sCurrentActivityThread;private void attach(boolean system, long startSeq) {        sCurrentActivityThread = this;        //...    }}

sCurrentActivityThread在进程启动时就被赋值为当前进程主线程的ActivityThread对象。

再来看看如何获取mCallback。

// 获取全局的 ActivityThread 对象Class<?> forName = Class.forName("android.app.ActivityThread");Field currentActivityThreadField = forName.getDeclaredField("sCurrentActivityThread");currentActivityThreadField.setAccessible(true);Object currentActivityThread = currentActivityThreadField.get(null);// 获取ActivityThread 里面的 mH 对象Field handlerField = forName.getDeclaredField("mH");handlerField.setAccessible(true);Handler handlerObject = (Handler) handlerField.get(currentActivityThread);// 获取mH里面的 原始 mCallback 对象Field callBackField = Handler.class.getDeclaredField("mCallback");callBackField.setAccessible(true);Handler.Callback previousCallback = (Handler.Callback) (callBackField.get(handlerObject));//将mH的mCallback 替换成 自定义Callback,并代理原始mCallback对象callBackField.set(handlerObject, new HookHandlerCallback(previousCallback));

至此,我们就完成了hook拦截系统代码的功能,主旨就是反射+代理

二.动态代理

1. 溯源

上面的例子有一个特点,就是需要代理的对象,或者说类,都是public且不是@hide的,也就是公开的api,所以我们可以显式使用代码创建一个对象使用,那么还有一种情况是相反的,先看下面的例子。

class ContextImpl extends Context {//检查权限public int checkPermission(String permission, int pid, int uid) {        final IActivityManager am = ActivityManager.getService();        return am.checkPermission(permission, pid, uid);    }//发送广播public void sendBroadcast(Intent intent) {        ActivityManager.getService().broadcastIntent(                    mMainThread.getApplicationThread(), intent, resolvedType, null,                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,                    getUserId());    }//开启服务private ComponentName startServiceCommon(Intent service, boolean requireForeground,            UserHandle user) {        ActivityManager.getService().startService(                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(                            getContentResolver()), requireForeground,                            getOpPackageName(), user.getIdentifier());    }}

我们经常使用context进行的诸如sendBroadcast()、startService()、checkPermission()等方法,都调用了ActivityManager里面的方法,而ActivityManager是实现Activity一切行为的类,那么有时我们就会希望拦截这些方法,就可以在Activity相关api被调用时可以搞一些事情。

那如何来做呢?想来看下ActivityManager实现。

public class ActivityManager {private static final Singleton<IActivityManager> IActivityManagerSingleton =            new Singleton<IActivityManager>() {                @Override                protected IActivityManager create() {                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);                    final IActivityManager am = IActivityManager.Stub.asInterface(b);                    return am;                }            };public static IActivityManager getService() {        return IActivityManagerSingleton.get();    }}public abstract class Singleton<T> {    private T mInstance;    protected abstract T create();    public final T get() {        synchronized (this) {            if (mInstance == null) {                mInstance = create();            }            return mInstance;        }    }}
  • ActivityManager的调用其实是交给IActivityManagerSingleton的get()方法返回的对象进行

  • Singleton其实是个懒加载类,只返回第一次调用时,create()方法返回的对象

  • IActivityManagerSingleton的get()方法,返回的是系统进程中,ActivityManager的接口,IActivityManager的Binder对象,即系统进程中ActivityManagerService的Binder对象,有关Binder和跨进程通信,可以参考这篇文章

现在,就可以参照上面说的方法,试着实现代理:

  1. 反射拿到IActivityManagerSingleton里面的mInstance对象

  2. 实现一个实现了IActivityManager接口的子类,并代理mInstance

  3. 通过反射,将mInstance对象赋值为这个代理对象

这里的问题出在第二步,这个IActivityManager接口,是系统层api,我们根本无法用代码实现,那怎么办呢?可以用动态代理的方法。

2. 动态代理

我们先来简单复习一下java的动态代理机制。

public static void main(String[] args) {    Jack jack = new Jack();    Star star = new StarProxy(jack).getProxy();    star.doAction("sing");    star.doAction("dance");}static class Jack implements Star {    @Override    public void doAction(String action) {        System.out.println(action);    }}interface Star {    void doAction(String action);}static class StarProxy implements InvocationHandler {    private final Star star;    public StarProxy(Star star) {        this.star = star;    }    public Star getProxy() {        return (Star) Proxy.newProxyInstance(star.getClass().getClassLoader(), star.getClass().getInterfaces(), this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("proxy prepare");        return method.invoke(star, args);    }}
  1. Star是一个接口,有一个doAction的方法

  2. Jack实现了该接口,并实现了doAction方法,输出action参数

  3. StarProxy类代理任意一个Star对象,并有一个getProxy方法用于获取代理对象

  4. getProxy()方法中,使用Proxy.newProxyInstance()方法创建了一个,实现了Star接口的动态代理对象,并使用StarProxy自身作为InvocationHandler回调

  5. InvocationHandler的invoke方法是代理对象被调用其实现的接口的方法时,被调用的方法,在调用被代理对象的相应方法前,会输出一个log

  6. 输出结果:在"sing"和"dance"输出前,分别会先输出一个"proxy prepare"

由此可见,动态代理Proxy的api的要点:

  1. 需要一个classLoader用来加载接口类

  2. 需要动态代理对象实现的接口(一定要注意只有接口才可以哦)类class对象

  3. 需要一个InvocationHandler

第一点好说,一般任意一个类的classLoader即可;第三点创建的对象,需要我们自己将被代理的原始对象传入维护即可;主要是第二点,由于IActivityManager类是系统api,我们无法直接获取类对象,那么我们可以用反射的方法来获取class对象:

Class iActivityManagerInterface = Class.forName("android.app.IActivityManager");

于是乎,我们就可以用动态代理的方法实现hook。

//获取ActivityManager类对象val activityManagerClass = Class.forName("android.app.ActivityManager")//获取IActivityManagerSingleton对象val singletonDefaultField = activityManagerClass.getDeclaredField("IActivityManagerSingleton")singletonDefaultField.isAccessible = trueval singletonDefault = singletonDefaultField.get(null)//获取Singleton的原始mInstance对象val singleton = Class.forName("android.util.Singleton")val mInstanceField = singleton.getDeclaredField("mInstance")mInstanceField.isAccessible = trueval rawIActivityManager = mInstanceField.get(singletonDefault)//获取IActivityManager接口的类对象val iActivityManagerInterface = Class.forName("android.app.IActivityManager")//创建动态代理对象,并赋值给mInstanceval proxy = Proxy.newProxyInstance(activityManagerClass::class.java.classLoader,        arrayOf(iActivityManagerInterface), IActivityManagerHandler(rawIActivityManager))mInstanceField.set(singletonDefault, proxy)private class IActivityManagerHandler(private val rawIActivityManager: Any?) : InvocationHandler {    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {    //do something...        return if (args == null) {            method?.invoke(rawIActivityManager)        } else {            method?.invoke(rawIActivityManager, *args)        }    }}

至此,我们就可以在任何IActivityManager相关的接口方法调用时,进行拦截了~

更多相关文章

  1. Android使用SharedPreferences存储数据的实现方法
  2. android给 TextView 加上效果和事件响应 判断是否点击函数
  3. android UiAutomator生成测试报告的基本方法封装
  4. Android(安卓)4.1-4.2 默认窗体旋转180 度代码
  5. Android开发之《Android应用开发揭秘》UI事件汇总
  6. Android之intent传值的三种方法
  7. Android中ActivityLifecycleCallbacks监听Activity们的生命周期
  8. Android多媒体编程——MediaPlayer播放音乐
  9. android bind service and remote service(aidl)

随机推荐

  1. TextView 设置 Ellipsize 属性,但它不工作
  2. Eclipse 开发 Android, Hello FormStuff(
  3. android列出目录下的所有图片
  4. Android(安卓)媒体:网络视频播放器的基本
  5. [Android] conversion to dalvik format
  6. Android(安卓)Unable to execute dex: ja
  7. Android(安卓)强制横屏或竖屏设置
  8. Android: Performing Network Operations
  9. android开发模式LiveData+ViewModel+Room
  10. Android(安卓)SQLiter cursor的使用