android 插件开发 如何启动插件activity,android插件开发-就是你了!启动吧!插件的activity(二)...

这篇博客是上篇的延续,在阅读之前先阅读第一部分:第一部分

我们在启动插件的activity时,通过替换component成功欺骗AMS获得了启动一个activity所必须的一些资源。不过,我们还没有把获取的那些资源都转移到插件的activity之下。这一节就是解决这个问题。

所有的答案都是分析源码之后得到的,所以我们还和之前一样继续分析源码,看下AMS是怎么把资源关联到一个activity上的,这样我们才有可能转移这些资源到插件的activity之下。

在上一篇博文中我们分析到了startActivityLocked函数。在这个函数里面,我们在获得启动一个activity的信息,和要启动的activity信息之后,我们转到了startActivityUncheckedLocked函数:

final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,

IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,

boolean doResume, Bundle options, TaskRecord inTask) {

...

//用来判断是否需要重新创建一个新的任务栈来启动这个activity

//在我们这里例子里面 我们newTask会一直是false

boolean newTask = false;

boolean keepCurTransition = false;

...

// Should this be considered a new task?

if (r.resultTo == null && inTask == null && !addingToTask

&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {

...

} else if (sourceRecord != null) {

} else ....

....

//调用ActivityStack的成员函数

targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);

...

return ActivityManager.START_SUCCESS;

}

之后调用的是startActivityLocked方法:

final void startActivityLocked(ActivityRecord r, boolean newTask,

boolean doResume, boolean keepCurTransition, Bundle options) {

...

if (!newTask) {

// If starting in an existing task, find where that is...

boolean startIt = true;

for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {

task = mTaskHistory.get(taskNdx);

if (task.getTopActivity() == null) {

// All activities in task are finishing.

continue;

}

if (task == r.task) {

// Here it is! Now, if this is not yet visible to the

// user, then just add it without starting; it will

// get started when the user navigates back to it.

if (!startIt) {

if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "

+ task, new RuntimeException("here").fillInStackTrace());

task.addActivityToTop(r);

r.putInHistory();

mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,

r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,

(r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,

r.userId, r.info.configChanges, task.voiceSession != null,

r.mLaunchTaskBehind);

if (VALIDATE_TOKENS) {

validateAppTokensLocked();

}

ActivityOptions.abort(options);

return;

}

break;

} else if (task.numFullscreen > 0) {

startIt = false;

}

}

}

...

//把要启动的activity压入活动栈中

task.addActivityToTop(r);

task.setFrontOfTask();

...

//我们显然是要让activity显示 所以这里一定会执行

if (doResume) {

mStackSupervisor.resumeTopActivitiesLocked(this, r, options);

}

}

这个函数做的事情很简单,就是把要启动的activity压到活动栈的栈顶,这里又证实了我们之前的猜想:

AMS解析Intent,获得ActivityRecord,而ActivityRecord用来表示一个activity,至于如何解析Intent获得ActivityRecord,内容和网上一些intent解析过程一样intent解析过程 ,而我们在代码中是显示启动一个activity,所以我们替换ComponeneName就可以

接下来我们继续转到StackSupervisor中去查看resumeTopActivitiesLocked

0818b9ca8b590ca3270a3433284dd417.png

看下是否是最前面的一个任务活动栈(很好理解,因为你不可能一次只开一个应用,你可能在使用手机时打开QQ,微信,微博,他们都各自对应很多个活动栈),是的话就准备响应他

stack.resumeTopActivityLocked:

0818b9ca8b590ca3270a3433284dd417.png

最后都转发到了resumeTopActivityInnerLocked

查看下:

final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {

...

// We need to start pausing the current activity so the top one

// can be resumed...

boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;

boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);

if (mResumedActivity != null) {

if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);

//调用start[

pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);

}

...

return true;

}

在确认一切无误之后调用startPausingLocked方法

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,

boolean dontWait) {

...

if (prev.app != null && prev.app.thread != null) {

if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);

try {

...

//prev.app.thread返回一个IApplicationThread

prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,

userLeaving, prev.configChangeFlags, dontWait);

} catch (Exception e) {

...

}

} else {

...

}

...

}

prev.app.thread返回一个IApplicationThread,它通知Ui线程可以终止当前正在响应的activity了,我要开启新的activity了!

prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,

userLeaving, prev.configChangeFlags, dontWait);

在本例中就是:

0818b9ca8b590ca3270a3433284dd417.png

这里prev.appToken是一个IBinder对象,其他的都是Boolean还有Configuration类型,我们不难得出,AMS区分activity是谁就是通过这个IBinder对象(Token是IBinder的实现类)!!!

0818b9ca8b590ca3270a3433284dd417.png

为了验证我们的猜想,还是继续阅读源码:

0818b9ca8b590ca3270a3433284dd417.png

Handler中的处理代码:

0818b9ca8b590ca3270a3433284dd417.png

IBinder被保存在Message的obj中,之后调用handlePauseActivity函数:

0818b9ca8b590ca3270a3433284dd417.png

//mActivities是一个map,通过IBinder映射activity client record!

final ArrayMap mActivities = new ArrayMap<>();

这里通过IBinder找到要停止的类,!看来AMS就是通过这个IBinder类区分要操作的activity!那么,我们只要能够替换掉本该给StubActivity的IBinder,那么PluginActivity是不是就名正言顺的获得了AMS的认可,间接也就获得了声明周期呢!

之后的操作:

0818b9ca8b590ca3270a3433284dd417.png

回到AMS中:

@Override

public final void activityPaused(IBinder token) {

final long origId = Binder.clearCallingIdentity();

synchronized(this) {

//通过这个IBinder找到任务栈

ActivityStack stack = ActivityRecord.getStackLocked(token);

if (stack != null) {

//开始准备启动新的activity了

stack.activityPausedLocked(token, false);

}

}

Binder.restoreCallingIdentity(origId);

}

NEXT:

//ActivityStack.java

final void activityPausedLocked(IBinder token, boolean timeout) {

if (DEBUG_PAUSE) Slog.v(

TAG, "Activity paused: token=" + token + ", timeout=" + timeout);

final ActivityRecord r = isInStackLocked(token);

if (r != null) {

//正常停止了就要移出检测停止activity超时的消息

mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);

if (mPausingActivity == r) {

if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r

+ (timeout ? " (due to timeout)" : " (pause complete)"));

//完成停止activity

completePauseLocked(true);

} else {

EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,

r.userId, System.identityHashCode(r), r.shortComponentName,

mPausingActivity != null

? mPausingActivity.shortComponentName : "(none)");

}

}

}

由于篇幅限制,我们只分析到这里,之后,AMS就准备真正启动activity了:

final boolean realStartActivityLocked(ActivityRecord r,

ProcessRecord app, boolean andResume, boolean checkConfig)

throws RemoteException {

...

ProfilerInfo profilerInfo = profileFile != null

? new ProfilerInfo(profileFile, profileFd, mService.mSamplingInterval,

mService.mAutoStopProfiler) : null;

app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);

//回调到Ui 第二个参数就是之前分析的IBinder

app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,

System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),

r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,

r.icicle, r.persistentState, results, newIntents, !andResume,

mService.isNextTransitionForward(), profilerInfo);

...

return true;

}

这里回调ui线程开始真正启动一个activity

ActivityThread.java

0818b9ca8b590ca3270a3433284dd417.png

还是和之前一样,把AMS返回的资源封装到ActivityClientRecord之后,发送message,并把资源存储在Message的obj中。

之后进入H的处理函数:

0818b9ca8b590ca3270a3433284dd417.png

它的handleMessage函数

0818b9ca8b590ca3270a3433284dd417.png

这里有个小知识,在handler调用handleMessage,会调用dispathMessage用来分发Message,我们从下面的源码可以看出,如果我们的message自己有callback,那么就调用Message自己的callback,否则看下handler本身的mCallback,如果mCallback不为空,那么就调用mCallback的handleMessage,并且如果返回true则直接结束,否则调用我们重载的handleMessage函数。

0818b9ca8b590ca3270a3433284dd417.png

那么思路来了,我们首先hook H这个类的的mCallbak域,替换成我们自己的callback,当检测到处理的消息是针对StubActivity时,我们获得原来的启动插件的Intent,然后替换下component(和原来逆向的过程)

主要代码:

/** * Created by chan on 16/4/8. */

public class HookApplication extends Application {

@TargetApi(Build.VERSION_CODES.KITKAT)

@Override

protected void attachBaseContext(Context base) {

super.attachBaseContext(base);

try {

//获得ActivityManagerNative

Class> serviceManagerClz = Class.forName("android.app.ActivityManagerNative", false, getClassLoader());

//获得ActivityManagerNative.getDefault静态方法

Method getDefaultMethod = serviceManagerClz.getDeclaredMethod("getDefault");

//获得原始的IActivityManager对象

Object rawIActivityManagerInterface = getDefaultMethod.invoke(null);

//我们自己的Hook的对象

Object hookIActivityManagerInterface = Proxy.newProxyInstance(

getClassLoader(),

new Class[]{Class.forName("android.app.IActivityManager", false, getClassLoader())},

new AMSHook(rawIActivityManagerInterface)

);

//反射ActivityManagerNative的gDefault域

Field gDefaultField = serviceManagerClz.getDeclaredField("gDefault");

gDefaultField.setAccessible(true);

Object gDefaultObject = gDefaultField.get(null);

//他的类型是Singleton

Class> singletonClz = Class.forName("android.util.Singleton", false, getClassLoader());

//把他的mInstance域替换掉 成为我们自己的Hook对象

Field mInstanceField = singletonClz.getDeclaredField("mInstance");

mInstanceField.setAccessible(true);

mInstanceField.set(gDefaultObject, hookIActivityManagerInterface);

Class> activityThreadClz = Class.forName("android.app.ActivityThread", false, getClassLoader());

Method method = activityThreadClz.getDeclaredMethod("currentActivityThread");

Object activityThreadObject = method.invoke(null);

Field mHField = activityThreadClz.getDeclaredField("mH");

mHField.setAccessible(true);

Object mHObject = mHField.get(activityThreadObject);

Field handlerCallbackField = Handler.class.getDeclaredField("mCallback");

for(Field f : Handler.class.getDeclaredFields()) {

Log.d("chan_debug", f.getName());

}

handlerCallbackField.setAccessible(true);

Object callbackObject = handlerCallbackField.get(mHObject);

Object hookHObject = new MessageHook(callbackObject, getClassLoader());

handlerCallbackField.set(mHObject, hookHObject);

} catch (ClassNotFoundException | IllegalAccessException |

NoSuchMethodException | InvocationTargetException | NoSuchFieldException e) {

e.printStackTrace();

}

}

}

package com.chan.hook.handle;

import android.content.ComponentName;

import android.content.Intent;

import android.os.Handler;

import android.os.Message;

import com.chan.hook.StubActivity;

import com.chan.hook.util.Constant;

import java.lang.reflect.Field;

/** * Created by chan on 16/4/14. */

public class MessageHook implements Handler.Callback {

private Handler.Callback m_base;

private static final int LAUNCH_ACTIVITY = 100;

private Field m_intentField;

public MessageHook(Object base, ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException {

m_base = (Handler.Callback) base;

//获取ActivityClientRecord的class

Class> activityClientRecordClz = Class.forName("android.app.ActivityThread$ActivityClientRecord", false, classLoader);

//获得它的intent

m_intentField = activityClientRecordClz.getDeclaredField("intent");

m_intentField.setAccessible(true);

}

@Override

public boolean handleMessage(Message msg) {

//检测到时启动一个activity

if (msg.what == LAUNCH_ACTIVITY) {

try {

//msg.obj是android.app.ActivityThread$ActivityClientRecord对象,请参考前面的源码解析

Intent intent = (Intent) m_intentField.get(msg.obj);

ComponentName componentName = intent.getComponent();

//检测到是启动StubActivity

if(componentName != null &&

componentName.getClassName().equals(StubActivity.class.getCanonicalName())) {

//获得之前启动插件的intent

Intent raw = intent.getParcelableExtra(Constant.EXTRA_RAW_INTENT);

//替换成插件的component

intent.setComponent(raw.getComponent());

}

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

//之后的操作还是和原来一样

return m_base != null && m_base.handleMessage(msg);

}

}

package com.chan.hook.am;

import android.content.ComponentName;

import android.content.Intent;

import com.chan.hook.StubActivity;

import com.chan.hook.util.Constant;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

/** * Created by chan on 16/4/13. */

public class AMSHook implements InvocationHandler {

private Object m_base;

public AMSHook(Object base) {

m_base = base;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//拦截startActivity方法

if ("startActivity".equals(method.getName())) {

//查找原始的intent对象

Intent raw = null;

final int size = (args == null ? 0 : args.length);

int i = 0;

for (; i < size; ++i) {

if (args[i] instanceof Intent) {

raw = (Intent) args[i];

break;

}

}

//看下是否是启动插件中的activity

if (raw.getBooleanExtra(Constant.EXTRA_INVOKE_PLUGIN, false)) {

//获得原始的ComponentName

ComponentName componentName = raw.getComponent();

//创建一个新的Intent

Intent intent = new Intent();

//把Component替换为StubActivity的 这样就不会被系统检测到 启动一个没有在AndroidManifest.xml

//中声明的activity

intent.setComponent(new ComponentName(componentName.getPackageName(),

StubActivity.class.getCanonicalName()));

//保存原始的intent

intent.putExtra(Constant.EXTRA_RAW_INTENT, raw);

//替换为新的Intent

args[i] = intent;

}

}

//还是按往常一样调用各种函数

return method.invoke(m_base, args);

}

}

package com.chan.hook.util;

/** * Created by chan on 16/4/13. */

public interface Constant {

String EXTRA_INVOKE_PLUGIN = "com.chan.hook.util.invoke_plugin";

String EXTRA_RAW_INTENT = "com.chan.hook.util.raw_intent";

}

package com.chan.hook.util;

import android.app.Activity;

import android.content.Intent;

/** * Created by chan on 16/4/14. */

public class Utils {

public static void invokePluginActivity(Activity activity, Class> who) {

Intent intent = new Intent(activity, who);

intent.putExtra(Constant.EXTRA_INVOKE_PLUGIN, true);

activity.startActivity(intent);

}

}

效果:

0818b9ca8b590ca3270a3433284dd417.png

代码下载

这里解释下我们是如何hookAMS的,考虑之前我们分析如何hook一个系统服务的例子:例子,当我们启动一个activity的时候,会调用:

0818b9ca8b590ca3270a3433284dd417.png

我们可以看下:

0818b9ca8b590ca3270a3433284dd417.png

而这个gDefault呢是一个Singleton对象:

0818b9ca8b590ca3270a3433284dd417.png

而他的具体实现在:

0818b9ca8b590ca3270a3433284dd417.png

可见只要我们hook mInstance就行,而它被实例化IActivityManager对象,之后我们只需拦截其中的IActivityManager对象的startActivity方法,检测是否是标识启动一个插件activity,然后替换其中的component。

//看下是否是启动插件中的activity

if (raw.getBooleanExtra(Constant.EXTRA_INVOKE_PLUGIN, false)) {

//获得原始的ComponentName

ComponentName componentName = raw.getComponent();

//创建一个新的Intent

Intent intent = new Intent();

//把Component替换为StubActivity的 这样就不会被系统检测到 启动一个没有在AndroidManifest.xml

//中声明的activity

intent.setComponent(new ComponentName(componentName.getPackageName(),

StubActivity.class.getCanonicalName()));

//保存原始的intent

intent.putExtra(Constant.EXTRA_RAW_INTENT, raw);

//替换为新的Intent

args[i] = intent;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值