Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android插件開發-就是你了!啟動吧!插件的activity(二)

android插件開發-就是你了!啟動吧!插件的activity(二)

編輯:關於Android編程

這篇博客是上篇的延續,在閱讀之前先閱讀第一部分:第一部分
我們在啟動插件的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
這裡寫圖片描述
看下是否是最前面的一個任務活動棧(很好理解,因為你不可能一次只開一個應用,你可能在使用手機時打開QQ,微信,微博,他們都各自對應很多個活動棧),是的話就准備響應他<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KCjxwPnN0YWNrLnJlc3VtZVRvcEFjdGl2aXR5TG9ja2VkOiA8YnI+CjxpbWcgc3JjPQ=="/uploadfile/Collfiles/20160415/20160415090907261.png" alt="這裡寫圖片描述" title="\">
最後都轉發到了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);

在本例中就是:
這裡寫圖片描述

這裡prev.appToken是一個IBinder對象,其他的都是Boolean還有Configuration類型,我們不難得出,AMS區分activity是誰就是通過這個IBinder對象(Token是IBinder的實現類)!!!

這裡寫圖片描述

為了驗證我們的猜想,還是繼續閱讀源碼:
這裡寫圖片描述

Handler中的處理代碼:
這裡寫圖片描述
IBinder被保存在Message的obj中,之後調用handlePauseActivity函數:
這裡寫圖片描述

 //mActivities是一個map,通過IBinder映射activity client record!
 final ArrayMap mActivities = new ArrayMap<>();

這裡通過IBinder找到要停止的類,!看來AMS就是通過這個IBinder類區分要操作的activity!那麼,我們只要能夠替換掉本該給StubActivity的IBinder,那麼PluginActivity是不是就名正言順的獲得了AMS的認可,間接也就獲得了聲明周期呢!

之後的操作:
這裡寫圖片描述

回到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
這裡寫圖片描述
還是和之前一樣,把AMS返回的資源封裝到ActivityClientRecord之後,發送message,並把資源存儲在Message的obj中。

之後進入H的處理函數:
這裡寫圖片描述
它的handleMessage函數
這裡寫圖片描述

這裡有個小知識,在handler調用handleMessage,會調用dispathMessage用來分發Message,我們從下面的源碼可以看出,如果我們的message自己有callback,那麼就調用Message自己的callback,否則看下handler本身的mCallback,如果mCallback不為空,那麼就調用mCallback的handleMessage,並且如果返回true則直接結束,否則調用我們重載的handleMessage函數。
這裡寫圖片描述
那麼思路來了,我們首先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);
    }
}

效果:
這裡寫圖片描述

代碼下載

猛擊此處

這裡解釋下我們是如何hookAMS的,考慮之前我們分析如何hook一個系統服務的例子:例子,當我們啟動一個activity的時候,會調用:
這裡寫圖片描述

我們可以看下:
這裡寫圖片描述
而這個gDefault呢是一個Singleton對象:
這裡寫圖片描述
而他的具體實現在:
這裡寫圖片描述
可見只要我們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;
            }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved