AMS Startup Flowchart

Let’s take a look at this flowchart first. It helps us to understand the startup process of Android AMS from time to time and form a general startup process in mind, which includes the following contents:

  • SystemServer Startup process
  • AMS concrete method start logic
  • Start the APP process
  • StartActivity startup process

In startBootstrapServices mActivityManagerService. Set SystemServer setSystemProcess, here also set various services set by ServiceManager, Communicate with Binder for future service.

Data structures related to Activity management
ActivityRecord (An entry in the history stack, representing An activity)

There are a number of member variables in an ActivityRecord that contain all the information about an Activity. The member variable task in ActivityRecord represents the TaskRecord in which it is located, so you can see that ActivityRecord is associated with the TaskRecord

TaskRecord

An ArrayList

is used to store an ActivityRecord. The mStack in a TaskRecord represents the ActivityStack in which it is located. StartActivity () also creates a TaskRecord

ActivityStackSupervisor
ActivityStack mHomeStack; // Manage launcher related tasks
ActivityStack mFocusedStack; // Manage tasks that are not Launcher related
Copy the code

Some Android services

Android system through SystemServiceManager management Java layer of various services startup, about 80 more than services, into an ArrayList

If you look at some of the Android services,The reference here is from a book with an in-depth understanding of Android, not an official standard distinction.

· The first category is the core Android services, such as ActivityManagerService, WindowManagerService, etc.

· The second category is communication-related services, such as wifi-related services and Telephone services.

· The third category is related to system functions, such as AudioService, MountService, UsbService, etc.

· The fourth category is BatteryService, VibratorService and other services.

· The fifth category is EntropyService, DiskStatsService, Watchdog and other relatively independent services.

· The sixth category is Bluetooth services.

· The seventh category is UI services, such as status bar services, notification management services, etc.

Related to the Activity startup process

ApplicationThread is a Binder that communicates across threads through handlers, leading to ActivityThread

When the app is not started, clicking on the app must call startProcessLocked(), fork an app process with Zogyte, and then it’s a matter of both the app process and AMS. The application process calls AMS methods, such as startActivity, which are easy to call. Because AMS is a named Binder service, proxy classes can be picked up anywhere by searching for them in ServiceManager(SM). The first thing the application process does after fork is attach, which is to hand my proxy class to AMS. The application process has a proxy object for AMS, and AMS has a proxy object for the application process, and the two can then communicate. This is the startActivity startup process.

SM’s acquisition of Acitivity’s Binder services

The setup process for the agent

Activity Manager consists of the following parts:

1. Service Proxy: Implemented by ActivityManagerProxy, it is used for interprocess communication with system services provided by the Server

2. Service hub :ActivityManagerNative inherits from Binder and implements IActivityManager, which provides interconversion between service interfaces and Binder interfaces, stores the service proxy image internally and provides getDefault method to return the service proxy

3.Client: Part of the service interface is encapsulated by ActivityManager for the Client to invoke. Internally, ActivityManager calls ActivityManagerNative’s getDefault method to get a reference to an ActivityManagerProxy object, which can then be used to invoke methods on the remote service

4.Server: Implemented by ActivityManagerService, provides system services on the Server

Sequence diagram

An Activity in AMS is an ActivityRecord object, an Activity corresponds to an ActivityRecord, there are a large number of member variables in the ActivityRecord, containing all the information of an Activity. The member variable task in ActivityRecord represents its TaskRecord. The ActivityRecord is created during startActivity.

Related knowledge:
  1. Wechat double open, select which wechat, or WHEN the PDF file is opened, there are multiple choices, is collected here, according to the intent action

  1. Instrumentation to understand

Android instrumentation is the Android system inside a set of control methods or “hooks”, these hooks can be in the normal life cycle (normal is operating system control) control Android control operation, In fact, refers to the Instrumentation class to provide a variety of process control methods.

App -> Instrumentation -> AMS -> APP, automated testing is able to operate activities by operating instrumentation, the instrumentation is equivalent to the design of a unified entrance.

ActivityThread is not a Thread class, but runs in the main UI Thread. In activityThead. main, there is a looper in the main UI Thread

Binder communication correlation

A natural UID with the caller’s identity information verifies that the caller has this permission

The ID needs to be restored, so there needs to be a process.

Hook Activity Startup process

First of all, we need to understand the above Activity startup process, a brief introduction is as follows:

  1. AMS (verifies that the Activity is registered), cannot pass targetActivity

    Pass a fake StubActivity, return success, and StubActivity replaces targetActivity back

  2. After returning to the app process (Instrumentation create Activity)

  3. AMS controls the Instrumentation execution Activity lifecycle

We need to spoof AMS and then launch the real TargetActivity, with the Hook having a start point and an end point. There are two hooks to look for: replacing an Activity in an Intent (hookIActivityTaskManager) and restoring an Activity in an Intent (hookHandler).

import android.app.Activity;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.jackie.activityhookdemo.StubActivity;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

public class HookHelper {
    private static final String TAG = "Jackie";

    public static final String EXTRA_TARGET_INTENT = "extra_target_intent";

    public static void hookAMSAidl(a){
        if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
            hookIActivityTaskManager();
        }else{ hookIActivityManager(); }}public static void hookIActivityTaskManager(a){
        try{
            Field singletonField = null; Class<? > actvityManager = Class.forName("android.app.ActivityTaskManager");
            singletonField = actvityManager.getDeclaredField("IActivityTaskManagerSingleton");
            singletonField.setAccessible(true);
            Object singleton = singletonField.get(null);
            // Take the IActivityManager objectClass<? > singletonClass = Class.forName("android.util.Singleton");
            Field mInstanceField = singletonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            // The original IActivityTaskManager
            final Object IActivityTaskManager = mInstanceField.get(singleton);

            Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
                    , new Class[]{Class.forName("android.app.IActivityTaskManager")},new InvocationHandler() {
                        @Override
                        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// Log.i(TAG, "invoke: " + method.getName());

                            // Change the pillar
                            // The target of the activity to actually start
                            Intent raw = null;
                            int index = -1;
                            if ("startActivity".equals(method.getName())) {
                                Log.i(TAG, Invoke: startActivity ready to start);
                                for (int i = 0; i < args.length; i++) {
                                    if(args[i] instanceof  Intent){
                                        raw = (Intent)args[i];
                                        index = i;
                                    }
                                }
                                Log.i(TAG, "invoke: raw: " + raw);
                                // Replace the Intent
                                Intent newIntent = new Intent();
                                newIntent.setComponent(new ComponentName("com.jackie.activityhookdemo", StubActivity.class.getName()));
                                newIntent.putExtra(EXTRA_TARGET_INTENT,raw);

                                args[index] = newIntent;

                            }

                            returnmethod.invoke(IActivityTaskManager, args); }});// 7. IActivityManagerProxy integrates into the framework
            mInstanceField.set(singleton, proxy);

        }catch(Exception e){ e.printStackTrace(); }}public static void hookIActivityManager(a) {

        try{
            Field singletonField = null;
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Class<? > actvityManager = Class.forName("android.app.ActivityManager");
                singletonField = actvityManager.getDeclaredField("IActivityManagerSingleton");
            } else{ Class<? > actvityManager = Class.forName("android.app.ActivityManagerNative");
                singletonField = actvityManager.getDeclaredField("gDefault");
            }
            singletonField.setAccessible(true);
            Object singleton = singletonField.get(null);
            // Take the IActivityManager objectClass<? > actvityManager = Class.forName("android.util.Singleton");
            Field mInstanceField = actvityManager.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            // The original IActivityManager
            final Object rawIActivityManager = mInstanceField.get(singleton);
            // Create an IActivityManager proxy object
            Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
                    , new Class[]{Class.forName("android.app.IActivityManager")},new InvocationHandler() {
                        @Override
                        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// Log.i(TAG, "invoke: " + method.getName());

                            // Change the pillar
                            // The target of the activity to actually start
                            Intent raw = null;
                            int index = -1;
                            if ("startActivity".equals(method.getName())) {
                                Log.i(TAG, Invoke: startActivity ready to start);
                                for (int i = 0; i < args.length; i++) {
                                    if(args[i] instanceof  Intent){
                                        raw = (Intent)args[i];
                                        index = i;
                                    }
                                }
                                Log.i(TAG, "invoke: raw: " + raw);
                                // Replace the Intent
                                Intent newIntent = new Intent();
                                newIntent.setComponent(new ComponentName("com.jackie.activityhookdemo", StubActivity.class.getName()));
                                newIntent.putExtra(EXTRA_TARGET_INTENT,raw);

                                args[index] = newIntent;

                            }

                            returnmethod.invoke(rawIActivityManager, args); }});// 7. IActivityManagerProxy integrates into the framework
            mInstanceField.set(singleton, proxy);

        }catch(Exception e){ e.printStackTrace(); }}public static void hookHandler(a) {
        try{ Class<? > atClass = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThreadField = atClass.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThreadField.setAccessible(true);
            Object sCurrentActivityThread = sCurrentActivityThreadField.get(null);
            //ActivityThread an app process has only one, get its mH
            Field mHField = atClass.getDeclaredField("mH");
            mHField.setAccessible(true);
            final Handler mH = (Handler) mHField.get(sCurrentActivityThread);

            / / get mCallback
            Field mCallbackField = Handler.class.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);

            mCallbackField.set(mH, new Handler.Callback() {

                @Override
                public boolean handleMessage(Message msg) {
                    Log.i(TAG, "handleMessage: " + msg.what);
                    switch (msg.what) {
                        case 100: {}break;
                        case 159: {
                            Object obj = msg.obj;
                            Log.i(TAG, "handleMessage: obj=" + obj);
                            try {
                                Field mActivityCallbacksField = obj.getClass().getDeclaredField("mActivityCallbacks");
                                mActivityCallbacksField.setAccessible(true);
                                List mActivityCallbacks = (List) mActivityCallbacksField.get(obj);
                                Log.i(TAG, "handleMessage: mActivityCallbacks= " + mActivityCallbacks);
                                Size =0 for the first time
                                // Before Android O
                                //public static final int LAUNCH_ACTIVITY = 100;
                                //public static final int PAUSE_ACTIVITY = 101;
                                //public static final int PAUSE_ACTIVITY_FINISHING= 102;
                                //public static final int STOP_ACTIVITY_SHOW = 103;
                                //public static final int STOP_ACTIVITY_HIDE = 104;
                                //public static final int SHOW_WINDOW = 105;
                                //public static final int HIDE_WINDOW = 106;
                                //public static final int RESUME_ACTIVITY = 107;
                                //public static final int SEND_RESULT = 108;
                                //public static final int DESTROY_ACTIVITY = 109;
                                //end
                                // Reconstructs the state mode from AndroidP
                                //public static final int EXECUTE_TRANSACTION = 159;
                                // First an app has only one ActivityThread and then only one mH
                                // All activity lifecycle processing in our app is handled in the handleMessage of mH
                                // Prior to Android 8.0, different life cycles correspond to different msg.what processing
                                // In Android 8.0 this was changed to all EXECUTE_TRANSACTION
                                // So the first mActivityCallbacks here are the MainActivity lifecycle callbacks
// handleMessage: 159
// handleMessage: obj=android.app.servertransaction.ClientTransaction@efd342
// handleMessage: mActivityCallbacks= []
// invoke: method activityPaused
// handleMessage: 159
// handleMessage: obj=android.app.servertransaction.ClientTransaction@4962
// handleMessage: mActivityCallbacks= [WindowVisibilityItem{showWindow=true}]
// handleMessage: size= 1
// handleMessage: 159
// handleMessage: obj=android.app.servertransaction.ClientTransaction@9e98c6b
// handleMessage: mActivityCallbacks= [LaunchActivityItem{intent=Intent { cmp=com.zero.activityhookdemo/.StubActivity (has extras) }, ident = 168243404, info = ActivityInfo {5 b8d769 com. Zero. Activityhookdemo. StubActivity}, curConfig = {1.0 310 McC260mnc [en_US] ldltr sw411dp w411dp h659dp 420dpi nrml port finger qwerty/v/v -nav/h winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=Rect(0, 0 - 1080, 1794) mWindowingMode=fullscreen mActivityType=undefined} s.6},overrideConfig={1.0 310mcc260mnc [en_US] LDLTR sw411dp w411dp h659dp 420dpi nrml port finger qwerty/v/v -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 1794) mAppBounds=Rect(0, 0-1080. 1794) mWindowingMode=fullscreen mActivityType=standard} s.6},referrer=com.zero.activityhookdemo,procState=2,state=null,persistentState=null,pendingResults=null,pendingNewIntent s=null,profilerInfo=null}]
// handleMessage: size= 1
                                if (mActivityCallbacks.size() > 0) {
                                    Log.i(TAG, "handleMessage: size= " + mActivityCallbacks.size());
                                    String className = "android.app.servertransaction.LaunchActivityItem";
                                    if (mActivityCallbacks.get(0).getClass().getCanonicalName().equals(className)) {
                                        Object object = mActivityCallbacks.get(0);
                                        Field intentField = object.getClass().getDeclaredField("mIntent");
                                        intentField.setAccessible(true); Intent intent = (Intent) intentField.get(object); Intent targetIntent = intent.getParcelableExtra(EXTRA_TARGET_INTENT); intent.setComponent(targetIntent.getComponent()); }}}catch(Exception e) { e.printStackTrace(); }}break;
                    }
                    mH.handleMessage(msg);
                    return true; }}); }catch (Exception e) {
            Log.e(TAG, "hookHandler: "+ e.getMessage()); e.printStackTrace(); }}public static void hookInstrumentation(Activity activity) {
        //TODO:Class<? > activityClass = Activity.class;// Get the mInstrumentation field via activity. class
        Field field = null;
        try {
            field = activityClass.getDeclaredField("mInstrumentation");
            field.setAccessible(true);
            // Get the Instrumentation object based on the mInstrumentation field in the activity
            Instrumentation instrumentation = (Instrumentation) field.get(activity);
            // Create a proxy object. Note that since Instrumentation is a class, not an interface, we can only use static proxies.
            Instrumentation instrumentationProxy = new ProxyInstrumentation(instrumentation);
            // Make the substitution
            field.set(activity, instrumentationProxy);
        } catch(Exception e) { e.printStackTrace(); }}static class ProxyInstrumentation extends Instrumentation {

        private static final String TAG = "Zero";
        // Save the original object in ActivityThread
        Instrumentation mBase;

        public ProxyInstrumentation(Instrumentation base) {
            mBase = base;
        }

        public ActivityResult execStartActivity(
                Context who, IBinder contextThread, IBinder token, Activity target,
                Intent intent, int requestCode, Bundle options) {

            Log.d(TAG, "StartActivity executed with the following parameters:" + "who = [" + who + "]." +
                    "contextThread = [" + contextThread + "], token = [" + token + "]." +
                    "target = [" + target + "], intent = [" + intent +
                    "], requestCode = [" + requestCode + "], options = [" + options + "]");

            // Since this method is hidden, reflection calls are required; Find this method first
            //execStartActivity has an overload
            try {
                Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                        "execStartActivity",
                        Context.class, IBinder.class, IBinder.class, Activity.class,
                        Intent.class, int.class, Bundle.class);
                execStartActivity.setAccessible(true);
                return (ActivityResult) execStartActivity.invoke(mBase, who,
                        contextThread, token, target, intent, requestCode, options);
            } catch (Exception e) {
                throw new RuntimeException("do not support!!! pls adapt it"); }}/**
         * 重写newActivity 因为newActivity 方法有变
         * 原来是:(Activity)cl.loadClass(className).newInstance();
         *
         * @param cl
         * @param className
         * @param intent
         * @return
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws ClassNotFoundException
         */
        @Override
        public Activity newActivity(ClassLoader cl, String className, Intent intent)
                throws InstantiationException, IllegalAccessException,
                ClassNotFoundException {

            returnmBase.newActivity(cl, className, intent); }}public static void hookActivityThreadInstrumentation(a) {
        //TODO:
        try {
            Get the current ActivityThread objectClass<? > activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            //currentActivityThread is a static function so it can be invoked without instance arguments
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);

            // Get the original mInstrumentation field
            Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
            mInstrumentationField.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
            // Create a proxy object
            Instrumentation proxyInstrumentation = new ProxyInstrumentation(mInstrumentation);
            // Change the pillar
            mInstrumentationField.set(currentActivityThread, proxyInstrumentation);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

Reference article: juejin.cn/post/684490…