Android(安卓)代理拦截系统层代码
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和跨进程通信,可以参考这篇文章
现在,就可以参照上面说的方法,试着实现代理:
-
反射拿到IActivityManagerSingleton里面的mInstance对象
-
实现一个实现了IActivityManager接口的子类,并代理mInstance
-
通过反射,将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); }}
-
Star是一个接口,有一个doAction的方法
-
Jack实现了该接口,并实现了doAction方法,输出action参数
-
StarProxy类代理任意一个Star对象,并有一个getProxy方法用于获取代理对象
-
getProxy()方法中,使用Proxy.newProxyInstance()方法创建了一个,实现了Star接口的动态代理对象,并使用StarProxy自身作为InvocationHandler回调
-
InvocationHandler的invoke方法是代理对象被调用其实现的接口的方法时,被调用的方法,在调用被代理对象的相应方法前,会输出一个log
-
输出结果:在"sing"和"dance"输出前,分别会先输出一个"proxy prepare"
由此可见,动态代理Proxy的api的要点:
-
需要一个classLoader用来加载接口类
-
需要动态代理对象实现的接口(一定要注意只有接口才可以哦)类class对象
-
需要一个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相关的接口方法调用时,进行拦截了~
更多相关文章
- Android使用SharedPreferences存储数据的实现方法
- android给 TextView 加上效果和事件响应 判断是否点击函数
- android UiAutomator生成测试报告的基本方法封装
- Android(安卓)4.1-4.2 默认窗体旋转180 度代码
- Android开发之《Android应用开发揭秘》UI事件汇总
- Android之intent传值的三种方法
- Android中ActivityLifecycleCallbacks监听Activity们的生命周期
- Android多媒体编程——MediaPlayer播放音乐
- android bind service and remote service(aidl)
随机推荐
- TextView 设置 Ellipsize 属性,但它不工作
- Eclipse 开发 Android, Hello FormStuff(
- android列出目录下的所有图片
- Android(安卓)媒体:网络视频播放器的基本
- [Android] conversion to dalvik format
- Android(安卓)Unable to execute dex: ja
- Android(安卓)强制横屏或竖屏设置
- Android: Performing Network Operations
- android开发模式LiveData+ViewModel+Room
- Android(安卓)SQLiter cursor的使用