Android 代理拦截系统层代码

本文探讨了如何通过手动代理和动态代理技术来拦截和处理Android系统层的异常。通过分析系统源码,利用反射修改ActivityThread的mCallback以实现手动代理,以及通过动态代理方式创建IActivityManager接口的代理对象,从而实现对系统API的拦截和自定义操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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 = true
val singletonDefault = singletonDefaultField.get(null)

//获取Singleton的原始mInstance对象
val singleton = Class.forName("android.util.Singleton")
val mInstanceField = singleton.getDeclaredField("mInstance")
mInstanceField.isAccessible = true
val rawIActivityManager = mInstanceField.get(singletonDefault)

//获取IActivityManager接口的类对象
val iActivityManagerInterface = Class.forName("android.app.IActivityManager")

//创建动态代理对象,并赋值给mInstance
val 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相关的接口方法调用时,进行拦截了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值