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:
- 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
- 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:
-
AMS (verifies that the Activity is registered), cannot pass targetActivity
Pass a fake StubActivity, return success, and StubActivity replaces targetActivity back
-
After returning to the app process (Instrumentation create Activity)
-
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…