Activity.startActivity 方法。如果没有装Intent的flag为FLAG

  至此,问题1算凡是找到了答案。

public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

   看这么的 Log,说明我们就打响的 Hook 了
Context.startActivity()。而且 doContextStartHook()
方法只有以先后开始之时刻调用一糟糕即可,后面在次外的 Activity 中调用
Context.startActivity() 时此拦截工作均只是生效,这是因
Context.startActivity() 在执行启动 Activity 的操作时调是经过
ActivityTread 获取到 Instrumentation,然后重新调用
Instrumentation.execStartActivity() 方法,而 ActivityTread
在程序中凡是坐单例的款型是的,这就是是由。所以说调用
doContextStartHook() 方法极其好之机会该是置身 Application 中。

交此处Activity就启动起来了。下面我们来回顾一下点的兼具手续流程:
1、首先我们设启动之Activity会去ActivityManagerService中校检是否合法,如果不合法则委来怪。
2、然后经过回调ActivityThread中内部类ApplicationThread的scheduleLaunchActivity方法并发送一个信息及ActivityThread中的其中类H中,而H是一个Handler类。
3、最后经反射创建Activity对象同Application对象,并回调其中的生命周期方法,从而将Activity启动起来。

  以搭下的解析中待查阅 Android 源码,先引进两单翻 Android
源码的网站:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {        
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {       
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

  源码地址:

其间调用了performLaunchActivity方法创建了一个Activity对象,然后回调Activity的生命周期方法,我们看一下performLaunchActivity方法:

    其实代码也不难理解,跟 Hook Activity 的 startActivity()
方法是一个思路,只是 Hook 的触及不同而已。下面我们以 MainActivity
的 attachBaseContext() 方法中调用 doContextStartHook()
方法,并加上相关测试代码,具体代码如下:

@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

    updateProcessState(procState, false);

    ActivityClientRecord r = new ActivityClientRecord();

    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;

    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;

    r.startsNotResumed = notResumed;
    r.isForward = isForward;

    r.profilerInfo = profilerInfo;

    r.overrideConfig = overrideConfig;
    updatePendingConfiguration(curConfig);

    sendMessage(H.LAUNCH_ACTIVITY, r);
}
 1 public static void doActivityStartHook(Activity activity){
 2         try {
 3             Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation");
 4             mInstrumentationField.setAccessible(true);
 5             Instrumentation originalInstrumentation = (Instrumentation)mInstrumentationField.get(activity);
 6             mInstrumentationField.set(activity, new EvilInstrumentation(originalInstrumentation));
 7         } catch (Exception e) {
 8             e.printStackTrace();
 9         }
10 }
@Override
public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();

    // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
    // generally not allowed, except if the caller specifies the task id the activity should
    // be launched in.
    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
            && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
        throw new AndroidRuntimeException(
                "Calling startActivity() from outside of an Activity "
                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                + " Is this really what you want?");
    }
    mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity) null, intent, -1, options);
}

  MainActivity 的代码如下:

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
        ...省略
    }
}

  startActivityForResult(Intent intent, int requestCode, Bundle
options) 源码如下:

此的行逻辑和方面mParent为空时的实行逻辑是同等的。都是调用了Instrumentation类的execStartActivity方法。看到此,我们更比一下尽开始说到的ContextImpl里面的startActivity方法,同样都是实施了Instrumentation类的execStartActivity方法。所以我们便集中地看Instrumentation类的execStartActivity方法吧,Instrumentation类的execStartActivity方法来多单重构的法,它们的逻辑都是同的,所以我们就是拿里面的一个看好了。源码如下:

   [EvilInstrumentation]
: 请注意! startActivity已经被hook了!

其间又调用了handleLaunchActivity方法:

 1 public class EvilInstrumentation extends Instrumentation {
 2     private Instrumentation instrumentation;
 3     public EvilInstrumentation(Instrumentation instrumentation) {
 4         this.instrumentation = instrumentation;
 5     }
 6     public ActivityResult execStartActivity(
 7             Context who, IBinder contextThread, IBinder token, Activity target,
 8             Intent intent, int requestCode, Bundle options) {
 9         Logger.i(EvilInstrumentation.class, "请注意! startActivity已经被hook了!");
10         try {
11             Method execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class,
12                     IBinder.class, IBinder.class, Activity.class,
13                     Intent.class, int.class, Bundle.class);
14             return (ActivityResult)execStartActivity.invoke(instrumentation, who, contextThread, token, target,
15                     intent, requestCode, options);
16         } catch (Exception e) {
17             e.printStackTrace();
18         }
19 
20         return null;
21     }
22 }
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, bOptions,
            UserHandle.getCallingUserId());
}

  startActivity(Intent intent) 源码如下:

咱俩看一下Singleton类的概念:

  Activity.attach() 的源码如下(注意第8履及第26行):

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    if (r.profilerInfo != null) {
        mProfiler.setProfiler(r.profilerInfo);
        mProfiler.startProfiling();
    }

    // Make sure we are running with the most recent config.
    handleConfigurationChanged(null, null);

    if (localLOGV) Slog.v(
        TAG, "Handling launch of " + r);

    // Initialize before creating the activity
    WindowManagerGlobal.initialize();

    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

        if (!r.activity.mFinished && r.startsNotResumed) {      
            performPauseActivityIfNeeded(r, reason);

            if (r.isPreHoneycomb()) {
                r.state = oldState;
            }
        }
    } else {
        // If there was an error, for any reason, tell the activity manager to stop us.
        try {
            ActivityManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

   程序运行之后,点击启动 Activity 的按钮将出口以下 Log:

public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
        int requestCode, @Nullable Bundle options) {
    options = transferSpringboardActivityOptions(options);
    Instrumentation.ActivityResult ar =
        mInstrumentation.execStartActivity(
            this, mMainThread.getApplicationThread(), mToken, child,
            intent, requestCode, options);
    if (ar != null) {
        mMainThread.sendActivityResult(
            mToken, child.mEmbeddedID, requestCode,
            ar.getResultCode(), ar.getResultData());
    }
    cancelInputsAndStartExitTransition(options);
}
1 public void startActivity(Intent intent) {
2         this.startActivity(intent, null);
3 }

2、接着通过调用r.packageInfo.makeApplication方法判断Application对象是不是有,存在则回,不存则新建一个Application对象并回调其onCreate方法:

  注意!前方惊现彩蛋一朵!!

拖欠办法的源码有硌长,我们分层次来拘禁:
1、首先通过调用mInstrumentation.newActivity方法创建了一个Activity对象,而mInstrumentation.newActivity方法创建对象的进程为老粗略,直接通过反射创建一个初的对象,如下所示:

1 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
2 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3 getApplicationContext().startActivity(intent);
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }

    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
            mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }

    if (r.activityInfo.targetActivity != null) {
        component = new ComponentName(r.activityInfo.packageName,
                r.activityInfo.targetActivity);
    }

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
        if (localLOGV) Slog.v(
                TAG, r + ": app=" + app
                + ", appName=" + app.getPackageName()
                + ", pkg=" + r.packageInfo.getPackageName()
                + ", comp=" + r.intent.getComponent().toShortString()
                + ", dir=" + r.packageInfo.getAppDir());

        if (activity != null) {
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (r.overrideConfig != null) {
                config.updateFrom(r.overrideConfig);
            }
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                    + r.activityInfo.name + " with config " + config);
            Window window = null;
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);

            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            checkAndBlockForNetworkAccess();
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

            activity.mCalled = false;
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " did not call through to super.onCreate()");
            }
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onPostCreate()");
                }
            }
        }
        r.paused = true;

        mActivities.put(r.token, r);

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to start activity " + component
                + ": " + e.toString(), e);
        }
    }

    return activity;
}
1 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
2         startActivityForResult(intent, requestCode, null);
3 }

最终通过调用了mH的sendMessage方法将一个Message对象上加至信息队列,因为mH的品类的Handler类,所以最终会以mH的handleMessage方法被拍卖该消息,我们看一下handleMessage方法中对msg.what为H.LAUNCH_ACTIVITY的拍卖逻辑:

   代码如达到,布局文件特别粗略就未糊出来了,就是少单按钮,一个测试
Activity.startActivity() 方法,一个测试 Context.startActivity()
方法,然后以 MainActivity 的 onCreate() 中调用了 doActivityStartHook()
在 MyApplication 里面调用了 doContextStartHook(),
目前总的来说代码很正规,符合我们地方的思路,但楼主在点击按钮发现 Log
输出如下:

每当平常开行Activity时,我们经常因此的是于Activity中一直调用startActivity(intent)就得启动Activity,或者是透过context来启动Activity。归根结底调用的凡ContextImpl类的startActivity(intent)方法。所以我们一直打ContextImpl的startActivity(intent)方法开始,而startActivity(intent)方法最终调用了startActivity(intent,
bundle)。以下是源码(本篇文章的源码的基于Android
26底,不同版本代码间可能小出入,但是原理是一致的):

 1   final void attach(Context context, ActivityThread aThread,
 2             Instrumentation instr, IBinder token, int ident,
 3             Application application, Intent intent, ActivityInfo info,
 4             CharSequence title, Activity parent, String id,
 5             NonConfigurationInstances lastNonConfigurationInstances,
 6             Configuration config, String referrer, IVoiceInteractor voiceInteractor,
 7             Window window) {
 8         attachBaseContext(context);
 9 
10         mFragments.attachHost(null /*parent*/);
11 
12         mWindow = new PhoneWindow(this, window);
13         mWindow.setWindowControllerCallback(this);
14         mWindow.setCallback(this);
15         mWindow.setOnWindowDismissedCallback(this);
16         mWindow.getLayoutInflater().setPrivateFactory(this);
17         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
18             mWindow.setSoftInputMode(info.softInputMode);
19         }
20         if (info.uiOptions != 0) {
21             mWindow.setUiOptions(info.uiOptions);
22         }
23         mUiThread = Thread.currentThread();
24 
25         mMainThread = aThread;
26         mInstrumentation = instr;
27         mToken = token;
28         mIdent = ident;
29         mApplication = application;
30         mIntent = intent;
31         mReferrer = referrer;
32         mComponent = intent.getComponent();
33         mActivityInfo = info;
34         mTitle = title;
35         mParent = parent;
36         mEmbeddedID = id;
37         mLastNonConfigurationInstances = lastNonConfigurationInstances;
38         if (voiceInteractor != null) {
39             if (lastNonConfigurationInstances != null) {
40                 mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
41             } else {
42                 mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
43                         Looper.myLooper());
44             }
45         }
46 
47         mWindow.setWindowManager(
48                 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
49                 mToken, mComponent.flattenToString(),
50                 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
51         if (mParent != null) {
52             mWindow.setContainer(mParent.getWindow());
53         }
54         mWindowManager = mWindow.getWindowManager();
55         mCurrentConfig = config;
56     }

大凡未是盼了一个死熟稔的老,have you declared this activity in your
AndroidManifest.xml?,这个非常的意思是咱从来不在AndroidManifest中注册我们的Activity,所以抛出异常了。这证明了开行之Activity会通过ActivityManagerService进行一些校验,如果不符合要求就会丢掉来异常。好了,让我们继承进入ActivityManager类可以望getService方法如下:

  源码地址:

判断了一晃如是从外表启动Activity,但是也未曾装Intent
的flag为FLAG_ACTIVITY_NEW_TASK的,会弃来怪。我们平素也会用Application作为Context去启动Activity,如果没设置Intent的flag为FLAG_ACTIVITY_NEW_TASK,会丢掉来地方的万分,这点是急需小心的。
健康的流程下是倒mMainThread.getInstrumentation().execStartActivity方法。先记住这等同步,到下还要回到看。我们先行去看一下Activity底startActivity方法与Context的startActivity方法发生什么区别,Activity的startActivity方法最终调用了startActivityForResult的方式,我们直接扣该方法好了:

   照着(一)中之姿势点入 startActivity() 方法中,由于 Context
是一个抽象类,所以我们得找到她的实现类似才能够看出实际的代码,通过翻
Android 源码我们好于 ActivityTread 中只是知 Context 的贯彻类似是
ContextImpl。(在这边大家先了解就同一沾就是执行,具体的调用细节将会见于产同样首博文被详细介绍)

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
    ...省略
    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info,
                    // TODO: Have this take the merged configuration instead of separate global and
                    // override configs.
                    mergedConfiguration.getGlobalConfiguration(),
                    mergedConfiguration.getOverrideConfiguration(), r.compat,
                    r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                    r.persistentState, results, newIntents, !andResume,
                    mService.isNextTransitionForward(), profilerInfo);
    ...省略
}
 1 public class MainActivity extends Activity {
 2     private Button btn_start_by_context;
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.activity_main);
 7         btn_start_by_context = (Button) findViewById(R.id.btn_start_by_context);
 8         btn_start_by_context.setOnClickListener(new View.OnClickListener() {
 9             @Override
10             public void onClick(View v) {
11                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
12                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
13                 getApplicationContext().startActivity(intent);
14             }
15         });
16     }
17     @Override
18     protected void attachBaseContext(Context newBase) {
19         super.attachBaseContext(newBase);
20         ActivityThreadHookHelper.doContextStartHook();
21     }
22 }
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;
        }
    };

  我们先来一步步剖析 startActivity 的源码,随手写一个 startActivity
的言传身教,按停 command 键( windows 下按停 control )用鼠标点击
startActivity的主意即可跳反到方法中。

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    Uri referrer = target != null ? target.onProvideReferrer() : null;
    if (referrer != null) {
        intent.putExtra(Intent.EXTRA_REFERRER, referrer);
    }
    if (mActivityMonitors != null) {
        synchronized (mSync) {
            final int N = mActivityMonitors.size();
            for (int i=0; i<N; i++) {
                final ActivityMonitor am = mActivityMonitors.get(i);
                ActivityResult result = null;
                if (am.ignoreMatchingSpecificIntents()) {
                    result = am.onStartActivity(intent);
                }
                if (result != null) {
                    am.mHits++;
                    return result;
                } else if (am.match(who, null, intent)) {
                    am.mHits++;
                    if (am.isBlocking()) {
                        return requestCode >= 0 ? am.getResult() : null;
                    }
                    break;
                }
            }
        }
    }
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

  调试发现,我们于
attachBaseContext(..) 里面执行完毕 doActivityStartHook(…) 方法后审将
Activity 的 mInstrumentation 变量换成了我们团结的
EvilInstrumentation,但程序执行到 onCreate() 方法后便会意识这候
mInstrumentation 变成了系统协调之 Instrumentation
对象了。这时候我们得以确信的凡 mInstrumentation 变量一定是于
attachBaseContext() 之后为初始化或者赋值的。带在此目标我们十分自在就以
Activity 源码的 attach() 方法被找到如下代码:

如果mParent不为空,则执行mParent的startActivityFromChild方法:

  我们不妨先猜想一下,一定是 Activity 的 mInstrumentation
对象在咱们轮换之前即既改成了 EvilInstrumentation, 然后我们又以
Activity.onCreate 方法调用了同一坏 doActivityStartHook(), 相当给我们以因故
EvilInstrumentation 又重新写了 EvilInstrumentation 的 startActivity()
方法,所以导致 log 信息输出了区区软。

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    ...省略

    try {
        java.lang.ClassLoader cl = getClassLoader();
        if (!mPackageName.equals("android")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    "initializeJavaContextClassLoader");
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        if (!mActivityThread.mInstrumentation.onException(app, e)) {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            throw new RuntimeException(
                "Unable to instantiate application " + appClass
                + ": " + e.toString(), e);
        }
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!instrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    }

    ...省略

    return app;
}

  http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ContextImpl.java#ContextImpl.startActivity%28android.content.Intent%29

同时调用了startActivityAsUser方法,而startActivityAsUser方法又调用了那重构的同名方法,而那重构的方以调用了ActivityStarter的startActivityMayWait方法,经过内部频繁调用,然后又调用了ActivityStackSupervisor类的道,经过了ActivityStarter类和ActivityStackSupervisor类的累累调用,最后执行之是ActivityStackSupervisor类中的realStartActivityLocked方法,那么我们直接扣之主意吧:

  那问题而来了,为什么 Activity 的 mInstrumentation
对象在我们轮换之前就曾改成了 EvilInstrumentation? 

每当realStartActivityLocked方法被调用了app.thread.
scheduleLaunchActivity方法,而app.thread的色是IApplicationThread,其实现类是ActivityThread内部类ApplicationThread,所以我们去ApplicationThread中找到scheduleLaunchActivity方法:

 1 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 2         ...
 3             if (activity != null) {
 4                 Context appContext = createBaseContextForActivity(r, activity);
 5                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
 6                 Configuration config = new Configuration(mCompatConfiguration);
 7         ...
 8 }
 9         ...
10 private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
11          ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
12          appContext.setOuterContext(activity);
13          Context baseContext = appContext;
14          ...
15 }
public static void checkStartActivityResult(int res, Object intent) {
        if (!ActivityManager.isStartResultFatalError(res)) {
            return;
        }

        switch (res) {
            case ActivityManager.START_INTENT_NOT_RESOLVED:
            case ActivityManager.START_CLASS_NOT_FOUND:
                if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                    throw new ActivityNotFoundException(
                            "Unable to find explicit activity class "
                            + ((Intent)intent).getComponent().toShortString()
                            + "; have you declared this activity in your AndroidManifest.xml?");
                throw new ActivityNotFoundException(
                        "No Activity found to handle " + intent);
            case ActivityManager.START_PERMISSION_DENIED:
                throw new SecurityException("Not allowed to start activity "
                        + intent);
            case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
                throw new AndroidRuntimeException(
                        "FORWARD_RESULT_FLAG used while also requesting a result");
            case ActivityManager.START_NOT_ACTIVITY:
                throw new IllegalArgumentException(
                        "PendingIntent is not an activity");
            case ActivityManager.START_NOT_VOICE_COMPATIBLE:
                throw new SecurityException(
                        "Starting under voice control not allowed for: " + intent);
            case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
                throw new IllegalStateException(
                        "Session calling startVoiceActivity does not match active session");
            case ActivityManager.START_VOICE_HIDDEN_SESSION:
                throw new IllegalStateException(
                        "Cannot start voice activity on a hidden session");
            case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:
                throw new IllegalStateException(
                        "Session calling startAssistantActivity does not match active session");
            case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:
                throw new IllegalStateException(
                        "Cannot start assistant activity on a hidden session");
            case ActivityManager.START_CANCELED:
                throw new AndroidRuntimeException("Activity could not be started for "
                        + intent);
            default:
                throw new AndroidRuntimeException("Unknown error code "
                        + res + " when starting " + intent);
        }
    }
1 @Override
2 public void startActivity(Intent intent) {
3         warnIfCallingFromSystemProcess();
4         startActivity(intent, null);
5 }
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;
        }
    }
}

  于 Hook Activity 的 startActivity
方法之前,我们首先肯定一下我们的对象,我们先行经过追踪源码找来
startActivity
调用的真正从作用的方法,然后想办法将目标措施阻碍截掉,并出口我们的一致久 Log
信息。

源码有接触长,我们挑重点来拘禁,和开行Activity相关的就是ActivityManager.getService().startActivity方法。该措施调用了ActivityManager.getService()的startActivity方法。在入ActivityManager之前,先看一下checkStartActivityResult方法:

  就看 this.startActivity(intent, null) 方法源码:

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    if (DEBUG_MESSAGES) Slog.v(
        TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
        + ": " + arg1 + " / " + obj);
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

  这里要报告大家是,ActivityTread
即代表采取之主线程,而一个采用被仅仅出一个主线程,并且鉴于源码可知,ActivityTreadd
的目标又是因静态变量的形式在的,太好了,这正是我们只要找的 Hook
点。废话不多说了,现在咱们就待动反射通过 currentActivityThread()
方法以到 ActivityThread 的对象,然后在将 mInstrumentation 替换成
EvilInstrumentation 即可,代码如下:

if (activity != null) {
    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
    Configuration config = new Configuration(mCompatConfiguration);
    if (r.overrideConfig != null) {
        config.updateFrom(r.overrideConfig);
    }
    ...省略
    appContext.setOuterContext(activity);
    activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.referrer, r.voiceInteractor, window, r.configCallback);

    ...省略
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    if (!activity.mCalled) {
        throw new SuperNotCalledException(
            "Activity " + r.intent.getComponent().toShortString() +
            " did not call through to super.onCreate()");
    }
    r.activity = activity;
}

    现在咱们来查阅 ContextImpl.startActivity() 的源码。 

最后调用了sendMessage方法,而在sendMessage方法而调用了其同名重构方法sendMessage:

  问题1:在这里发生一些值得一提,我们用
doActivityStartHook(…) 方法的调用如果坐  MainActivity
的 attachBaseContext(…) 方法被替换工作将未会见收效,为什么?

public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
}
 1   public static void doContextStartHook(){
 2         try {
 3             Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
 4             Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
 5             Object activityThread = currentActivityThreadMethod.invoke(null);
 6 
 7             Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
 8             mInstrumentationField.setAccessible(true);
 9             Instrumentation originalInstrumentation = (Instrumentation)mInstrumentationField.get(activityThread);
10             mInstrumentationField.set(activityThread, new EvilInstrumentation(originalInstrumentation));
11         } catch (Exception e) {
12             e.printStackTrace();
13         }
14     }

3、回调Activity对象的attach、onCreate方法:

  从达平等步传的参数 options 为 null
我们虽可了解就同一步调用了 startActivityForResult(intent, -1) 的代码。

个中的get()方法返回了mInstance对象,而mInstance对象又由于create()方法创建的。我们得以视上面IActivityManagerSingleton对象的create()方法创建了一个IActivityManager类型的靶子。而IActivityManager是一个接口,其切实的贯彻类似是ActivityManagerService,所以最后调用的凡ActivityManagerService的startActivity方法:

  是的,Activity.startActivity 被 hook 的信输出了少于涂鸦!为什么?

她而调用了IActivityManagerSingleton类的get()方法,而IActivityManagerSingleton类是一个Singleton类型的静态单例类,如下所示:

  http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ActivityThread.java#ActivityThread.0sCurrentActivityThread

  再入 startActivity(intent, null) 查看源码如下:

   到此结束我们已经 hook 了 Activity 的 startActivity
方法,非常简单,代码量也甚少,但咱吧颇轻易之意识这种措施需要在各个一个
Activity 的 onCreate 方法中调用一不良 doActivityStartHook
方法,显然这不是一个好之方案,所以我们以摸索 hook
点时必要是专注尽量找有当经过中保障不更换或无轻被改动之变量,就如单例和静态变量。

  http://androidxref.com

  前言:于前边少首文章中分别介绍了动态代理、反射机制及Hook机制,如果对这些尚免绝了解之童鞋建议先失参考一下前片篇稿子。经过了前面两首文章的陪衬,终于可以玩点真刀实弹的了,本篇将会由此
Hook 掉 startActivity 方法的一个小例子来介绍如何寻找有相当的 Hook
切入点。 开始前我们要懂得的一些就算是,其实在 Android 里面启动一个
Activity 可以由此个别种方法实现,一栽是咱们常因此底调用
Activity.startActivity 方法,一种植是调用 Context.startActivity
方法,两种方式相比,
第一栽启动Activity的方式进一步简易,所以先以第一种乎条例。

1 public class MyApplication extends Application {
2     @Override
3     protected void attachBaseContext(Context base) {
4         super.attachBaseContext(base);
5         ActivityThreadHookHelper.doContextStartHook();
6     }
7 } 
206     private static ActivityThread sCurrentActivityThread;
207     Instrumentation mInstrumentation;
...
1597    public static ActivityThread currentActivityThread() {
1598        return sCurrentActivityThread;
1599    }
...
1797    public Instrumentation getInstrumentation()
1798    {
1799        return mInstrumentation;
1800    }

  http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ActivityThread.java#2338

  《Android应用程序的Activity启动过程大概介绍和学习计划》

  startActivityForResult(Intent intent, int requestCode) 源码如下:

1 public void startActivity(Intent intent, @Nullable Bundle options) {
2         if (options != null) {
3             startActivityForResult(intent, -1, options);
4         } else {
5             // Note we want to go through this call for compatibility with
6             // applications that may have overridden the method.
7             startActivityForResult(intent, -1);
8         }
9 }

  纵观代码,只生一个地方出问题,那就是是咱们放开
MyApplication.attachBaseContext() 方法中的 doContextStartHook()
起的意图!

  本篇文章通过拦截 Context.startActivity() 和 Activity.startActivity()
两个方法,将高达一致首文章中牵线的 Hook 技术实施 Activity
的启动流程中,同时通过这简单个小例子初步摸底了 Android
源码以及怎样去选定一个适合的 Hook 点。想使打听插件化的基本原理,熟悉
Activity 的启动流程是必需的,下一样首文章将见面详细介绍 Activity
的起步流程,感兴趣之同窗可以关心一下!

  本系列文章的代码已经上传至github,下载地址:https://github.com/lgliuwei/DroidPluginStudy 本篇文章对应之代码在 com.liuwei.proxy_hook.hook.activityhook
包内,下载下来对照代码看文章效果会另行好!

 1 public class MainActivity extends Activity {
 2     private final static String TAG = MainActivity.class.getSimpleName();
 3     private Button btn_start_by_activity;
 4     private Button btn_start_by_context;
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         btn_start_by_activity = (Button) findViewById(R.id.btn_start_by_activity);
10         btn_start_by_context = (Button) findViewById(R.id.btn_start_by_context);
11         ActivityThreadHookHelper.doActivityStartHook(this);
12         btn_start_by_activity.setOnClickListener(new View.OnClickListener() {
13             @Override
14             public void onClick(View v) {
15                 Log.i(TAG, "onClick: Activity.startActivity()");
16                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
17                 startActivity(intent);
18             }
19         });
20 
21         btn_start_by_context.setOnClickListener(new View.OnClickListener() {
22             @Override
23             public void onClick(View v) {
24                 Log.i(TAG, "onClick: Context.startActivity()");
25                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
26                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
27                 getApplicationContext().startActivity(intent);
28             }
29         });
30     }
31 }

  ActivityTread 部分代码如下:

  还是事先直接省略说一下事实的本质吧,结合上文所说,一个以内只有存在一个
ActivityTread 对象,也唯有在一个 Instrumentation
对象,这个 Instrumentation 是 ActivityTread 的积极分子变量,并以
ActivityTread 内到位初始化,在开行一个 Activity 的流水线中约在最终之职务
ActivityTread 会回调 Activity 的 attach() 方法,并以团结的
Instrumentation 对象传给 Activity。启动 Activity
的事无巨细流程以及调用细节将见面在生一致篇博文介绍,敬请期待!

  文章开始我们即便说 Android 中来个片种启动 Activity 的法,一栽是
Activity.startActivity 另一样种植是
Context.startActivity,但用专注的经常,我们在以 Context.startActivity
启动一个 Activity 的时节将 flags 指定为 FLAG_ACTIVITY_NEW_TASK。

本文链接:http://www.cnblogs.com/codingblock/p/6666239.html

   由方第四实施代码可以见到在代码中判断了 intent 的 flag
类型,如果非 FLAG_ACTIVITY_NEW_TASK
类型就见面废弃来怪。接着看红部分的根本代码,可以看先从 ActivityTread
中得到到了 Instrumentation 最后还是调用了 Instrumentation 的
execStartActivity(…) 方法,我们本欲做的哪怕是分析 ActivityTread
类,并想艺术用我们好写的 EvilInstrumentation 类将 ActivityTread 的
mInstrumentation 替换掉。

   [EvilInstrumentation] :
请注意! startActivity已经被hook了!

 1 @Override
 2 public void startActivity(Intent intent, Bundle options) {
 3         warnIfCallingFromSystemProcess();
 4         if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
 5             throw new AndroidRuntimeException(
 6                     "Calling startActivity() from outside of an Activity "
 7                     + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
 8                     + " Is this really what you want?");
 9         }
10         mMainThread.getInstrumentation().execStartActivity(
11             getOuterContext(), mMainThread.getApplicationThread(), null,
12             (Activity)null, intent, -1, options);
13 }

  我们摸索着 hook 掉 Context.startActivity 方法,我们照例随手写一个
Context 方式启动 Activity 的演示,如下:

   到当下同一步我们就看到了举足轻重点,注意点代码块被红色的代码,其实
startActivity 真正调用的凡 mInstrumentation.execStartActivity(…)
方法,mInstrumentation 是
Activity 的一个私房变量。接下来的任务将移得非常简单,回忆一下直达同样篇博文《小白为会看明白插件化DroidPlugin原理(二)–
反射机制与Hook入门》遭逢之方案一,在轮换汽车发动机时我们继承原来的汽车引擎类创建了一个新类,然后在初引擎类中梗阻了无与伦比充分快的法门,这里的思路是平的,我们一直新建一个延续
Instrumentation 的新类,然后再次写 execStartActivity()
。对是来免亮的童鞋建议还看一样遍上同样首博文《小白也克看明白插件化DroidPlugin原理(二)–
反射机制及Hook入门》。代码如下:

  《Android插件化原理分析——Hook机制的动态代理》

一、Hook 掉 Activity 的 startActivity
的方法

二、Hook 掉 Context 的 startActivity
的方法

   重写工作一度召开截止了,接着我们经过反射机制用新建的
EvilInstrumentation 替换掉 Activity 的 mInstrumentation
变量,具体代码如下:

 1 public class MainActivity extends Activity {
 2     private Button btn_start_by_activity;
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.activity_main);
 7         // hook Activity.startActivity()的方法时不知道这行代码为什么放在attachBaseContext里面不行?
 8         // 调试发现,被hook的Instrumentation后来又会被替换掉原来的。
10         ActivityThreadHookHelper.doActivityStartHook(this);
11         btn_start_by_activity = (Button) findViewById(R.id.btn_start_by_activity);
12         btn_start_by_activity.setOnClickListener(new View.OnClickListener() {
13             @Override
14             public void onClick(View v) {
15                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
16                 startActivity(intent);
17             }
18         });
19     }
20 }

图片 1

  http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/

   这对于我们来说已好是如数家珍了,很快即描写了了,然后我们在 Activity
的 onCreate() 方法中需调用一下 doActivityStartHook 即可成功对
Activity.startActivity 的 hook。MainActivity 的代码如下:

  源码地址:

   点击按钮后翻看 Log 输出如下:

三、小结

 1 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
 2             @Nullable Bundle options) {
 3         if (mParent == null) {
 4             options = transferSpringboardActivityOptions(options);
 5             Instrumentation.ActivityResult ar =
 6                 mInstrumentation.execStartActivity(
 7                     this, mMainThread.getApplicationThread(), mToken, this,
 8                     intent, requestCode, options);
 9             if (ar != null) {
10                 mMainThread.sendActivityResult(
11                     mToken, mEmbeddedID, requestCode, ar.getResultCode(),
12                     ar.getResultData());
13             }
14             if (requestCode >= 0) {
15                 // If this start is requesting a result, we can avoid making
16                 // the activity visible until the result is received.  Setting
17                 // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
18                 // activity hidden during this time, to avoid flickering.
19                 // This can only be done when a result is requested because
20                 // that guarantees we will get information back when the
21                 // activity is finished, no matter what happens to it.
22                 mStartedActivity = true;
23             }
24 
25             cancelInputsAndStartExitTransition(options);
26             // TODO Consider clearing/flushing other event sources and events for child windows.
27         } else {
28             if (options != null) {
29                 mParent.startActivityFromChild(this, intent, requestCode, options);
30             } else {
31                 // Note we want to go through this method for compatibility with
32                 // existing applications that may have overridden it.
33                 mParent.startActivityFromChild(this, intent, requestCode);
34             }
35         }
36 }

  将 doContextStartHook() 方法放入到了 MyApplication
的 attachBaseContext() 里面后,代码如下:

参照文章

相关文章