Road to Android Learning (14) Explication détaillée d'AMS et PMS

Processus de démarrage du système Android et Zygote, SystemServer

Avant d'expliquer Zygote, étant donné que le code source des différentes versions du système est différent, le code source analysé ci-dessous est basé sur Android 8.0.0.

processus d'initialisation

Lorsque le système démarre, le processus d'initialisation est le deuxième processus démarré après le démarrage du noyau Linux. C'est un processus créé dans l'espace utilisateur. Vous pouvez afficher le pid du processus d'initialisation via la commande adb shell ps :

Dans la figure ci-dessus, le PID est l'ID du processus actuel, le PPID est l'ID du processus parent et les PID du processus Linux sont triés d'avant en arrière dans l'ordre de démarrage.

Le pid du processus d'initialisation dans la figure ci-dessus est 1, tandis que le pid du noyau Linux est 0. Cela vérifie également que le processus d'initialisation est le prochain processus démarré après le démarrage du noyau Linux.

Le processus init a deux fonctions principales :

  • Démarrez les services système critiques

  • Garder les services clés. Si l'un des services clés est désactivé, le téléphone sera redémarré.

Quels types de services sont des services critiques ? Les services clés sont des services essentiels pour les téléphones mobiles, tels que les services réseau, les services Bluetooth, les services de sonnerie, etc. Vous pouvez également afficher d'autres services avec ppid=1 via adb shell ps, qui sont des services clés démarrés et gardés par le processus d'initialisation :

Dans la figure ci-dessus, par exemple, installd, servicemanager, surfaceflinger et autres ppid=1 sont tous des services clés démarrés par le processus d'initialisation.

Donc, si vous souhaitez implémenter un service système et ne voulez pas être tué, le meilleur moyen est de faire en sorte que le service soit démarré par le processus d'initialisation et devienne un service clé.

Le processus init est également un programme exécutable, il possède donc également un code associé correspondant. Le code init.c se trouve spécifiquement dans le répertoire du code source Android/system/core/init/init.cpp.

Ce que nous appelons souvent init.rc peut être compris comme une liste de tâches à effectuer par le processus init. Il s'agit en fait d'un script d'exécution :

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // 启动 zygote 的执行脚本

...

Vous pouvez voir que la liste de tâches init.rc est en fait le chemin de fichier d'autres listes de tâches importées à exécuter, y compris le script d'exécution pour démarrer zygote. ro.zygote obtiendra le fichier de script d'exécution correspondant en fonction du type de système, par exemple , Acquisition 32 bits Celui obtenu par 64 bits est init.zygote32.rc, et celui obtenu par 64 bits est init.zygote64.rc :

system/core/rootdir/init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

La première ligne d'informations de script enregistre l'emplacement d'entrée du programme à exécuter. App_main.c sous /system/bin/app_process sera exécuté en premier. -Xzygote /system/bin --zygote --start-system-server est le programme d'entrée transmis. paramètres.

Démarrer Zygote dans la couche native

Comme mentionné ci-dessus, init.rc est un script d'exécution, qui contient le fichier d'exécution init.zygote32.rc ou init.zygote64.rc qui démarre le processus Zygote. Ce fichier exécutera /frameworks/base/cmds/app_process/app_main.c La fonction main() démarre Zygote :

/frameworks/base/cmds/app_process/app_main.c

#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif

// argv 就是脚本传入的参数 -Xzygote /system/bin --zygote --start-system-server
int main(int argc, char* const argv[]) {
	...
	while (i < argc) {
		const char* arg = argv[i++];
		if (strcmp(arg, "--zygote") == 0) {
			zygote = true;
			niceName = ZYGOTE_NICE_NAME;
		} else if (strcmp(arg, "--start-system-server") == 0) {
			startSystemServer = true;
		} 
		...
	}
	...
	for (; i < argc; ++i) {
		args.add(String8(argv[i]));
	}

	// 将 app_process 修改为 zygote
	if (!niceName.isEmpty()) {
		runtime.setArgv0(niceName.string(), true /* setProcName */);
	}
	
	if (zygote) {
		// 在 system/core/rootdir/init.zygote32.rc(或 init.zygote.64.rc) 解析到参数 --zygote,所以 zygote == true,通过 runtime.start() 创建 Zygote 进程
		runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
	}
	...
}

Le code ci-dessus change app_process en Zygote, donc Zygote n'est pas ce nom au début, mais est remplacé par Zygote au démarrage.

Bien que Zygote soit créé dans la couche native du Framework en exécutant l'entrée main() du langage C, étant donné que le processus d'initialisation est dans l'espace utilisateur et que le processus d'initialisation a créé Zygote, Zygote est donc dans l'espace utilisateur.

Regardons ce que fait runtime.start() :

/frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
	...
	// 创建虚拟机,startVm() 有很多虚拟机的参数配置,比如内存大小
	// 内存调优和其他虚拟机的调优就是在这个函数处理
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

	// 动态注册 java 调用 native 的 jni
	// 我们在写 java 代码时能声明 native 方法调用 C/C++ 函数,就是因为在这里做了注册处理了映射关系
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    ...
    
    // 调用 com.android.internal.os.ZygoteInit 的 main 方法
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
        "([Ljava/lang/String;)V");
    if (startMeth == NULL) {
		ALOGE("JavaVM unable to find main() in '%s'\n", className);
	} else {
		env->CallStaticVoidMethod(startClass, startMeth, strArray);
	}
	...
}

runtime.start() fait principalement trois choses :

  • startVm() crée une machine virtuelle

  • startReg() enregistre dynamiquement Java pour appeler jni natif

  • La réflexion appelle main() de ZygoteInit

Créez une machine virtuelle via startVm(). startVm() possède de nombreuses configurations de paramètres de machine virtuelle, telles que la taille de la mémoire tas. Si vous êtes ingénieur système, le réglage de la mémoire et d'autres réglages de la machine virtuelle sont gérés par cette fonction.

startReg() enregistre dynamiquement Java pour appeler le jni natif.Lorsque nous écrivons du code Java,nous pouvons déclarer la méthode native pour appeler la fonction C/C++ car l'enregistrement est effectué ici pour traiter la relation de mappage. Prenons MessageQueue comme exemple :

// 在 startReg() 动态注册,将 java 和 C/C++ 的函数关联
static const JNINativeMethod gMessageQueueMethods[] = {
	{."nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
	...
};

int register_android_os_MessageQueue(JNIEnv* env) {
	...
}

La dernière étape consiste à appeler la méthode main() de ZygoteInit, et vous commencez ici à entrer dans le monde de Java.

Résumons brièvement le flux de traitement natif de Zygote, comme indiqué ci-dessous :

La couche Java démarre ZygoteInit

Zygote est créé en mode natif et transmet AndroidRuntime.start() de la couche native à l'entrée main() de la couche Java ZygoteInit pour continuer le traitement des processus liés à Zygote :

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

// argv 就是 init.{ro.zygote}.rc 脚本写的参数
// -Xzygote /system/bin --zygote --start-system-server
public static void main(String argv[]) {
	// 创建 ServerSocket
	ZygoteServer zygoteServer = new ZygoteServer();
	
	...
	boolean startSystemServer = false;
	String socketName = "zygote";
	String abiList = null;
	boolean enableLazyPreload = false;
	for (int i = 1; i < argv.length; i++) {
		if ("start-system-server".equals(argv[i])) {
			startSystemServer = true;
		} else if ("--enable-lazy-preload".equals(argv[i])) {
			enableLazyPreload = true;
		} else if (argv[i].startsWith(ABI_LIST_ARG)) {
			abiList = argv[i].substring(ABI_LIST_ARG.length());
		} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
			socketName = argv[i].substring(SOCKET_NAME_ARG.length());
		} else {
			throw new RuntimeException("Unknown command line argument: " + argv[i]);
		}
		...
	}
	
	zygoteServer.registerServerSocket(socketName);
	
	...
	// 加载系统类、系统资源等
	if (!enableLazyPreload) {
		...
		preload(bootTimingsTraceLog);
		...
	}
	...
	// 创建 SystemServer
	if (startSystemServer) {
		startSystemServer(abiList, socketName, zygoteServer);
	}
	
	// ZygoteServer 创建 ServerSocket 作为服务器
	// 开启循环,等待接收 socket 通信 fork app 进程的请求
	// 没有消息会一直阻塞休眠等待,Zygote 进程会一直存活运行
	zygoteServer.runSelectLoop(abiList);
	
	zygoteServer.closeServerSocket();
	...
}

La méthode de saisie main() de ZygoteInit fait principalement trois choses :

  • Précharger les ressources système, telles que les classes système, les ressources, les bibliothèques système partagées, etc.
  • La création de ZygoteServer est en fait une boucle ServerSocket attendant de notifier le processus enfant fork
  • Créer un processus SystemServer

Précharger les ressources

Voyons d'abord ce que fait preload() pour précharger les ressources :

static void preload(TimingsTraceLog bootTimingsTraceLog) {
	...
	// 系统类加载
	preloadClasses();
	...
	// 系统资源加载
	preloadResources();
	...
	// openGL加载
	preloadOpenGL();
	...
	// 系统共享库加载
	preloadSharedLibraries();
	// 文字资源加载
	preloadTextResources();
	...
}

private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";

private static void preloadClasses() {
	...	
	InputStream is;
	try {
		// 获取要加载的系统资源文件流
		is = new FileInputStream(PRELOADED_CLASSES);
	} catch (FileNotFoundException e) {
        Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
        return;
    }
    ...
	BufferedReader br 
			= new BufferedReader(new InputStreamReader(is), 256);

	int count = 0;
	String line;
	while ((line = br.readLine()) != null) {
		...
		try {
			// 读取 /system/etc/preloaded-classes 的类路径并加载
			Class.forName(line, true, null);
		}
		...
	}	
	...
}

public static final boolean PRELOAD_RESOURCES = true;

private static void preloadResources() {
	...
	mResources = Resources.getSystem();
	mResources.startPreloading();
	if (PRELOAD_RESOURCES) {
		TypedArray ar = mResources.obtainTypedArray(
				com.android.internal.R.array.preloaded_drawables);
		int N = preloadDrawables(ar);
		ar.recycle();
		ar = mResources.obtainTypedArray(
				com.android.internal.R.array.preloaded_color_state_lists);
		N = preloadColorStateLists(ar);
		ar.recycle();
		...
	}
	mResources.finishPreloading();
}

preload() précharge principalement les classes système, les ressources système, les bibliothèques système partagées, openGL, les ressources texte, etc., parmi lesquelles les classes système sont des classes préchargées qui sont lues. Comme il existe de nombreuses classes système, seule une partie du fichier est interceptée ci-dessous. Vous pouvez afficher le fichier dans le code source :

frameworks/base/preloaded-classes

...
android.app.Activity
android.app.Activity$HostCallbacks
android.app.ActivityManager
android.app.ActivityManager$1
android.app.ActivityManager$RecentTaskInfo
android.app.ActivityManager$RecentTaskInfo$1
android.app.ActivityManager$RunningAppProcessInfo
android.app.ActivityManager$RunningAppProcessInfo$1
android.app.ActivityManager$RunningServiceInfo
android.app.ActivityManager$RunningServiceInfo$1
android.app.ActivityManager$RunningTaskInfo
android.app.ActivityManager$RunningTaskInfo$1
android.app.ActivityManager$StackId
android.app.ActivityManager$TaskDescription
android.app.ActivityManager$TaskDescription$1
android.app.ActivityOptions
android.app.ActivityThread
android.app.ActivityThread$1
android.app.ActivityThread$2
android.app.ActivityThread$ActivityClientRecord
android.app.ActivityThread$ActivityConfigChangeData
android.app.ActivityThread$AppBindData
android.app.ActivityThread$ApplicationThread
android.app.ActivityThread$BindServiceData
android.app.ActivityThread$ContextCleanupInfo
android.app.ActivityThread$CreateServiceData
android.app.ActivityThread$DropBoxReporter
android.app.ActivityThread$EventLoggingReporter
android.app.ActivityThread$GcIdler
android.app.ActivityThread$H
...

On peut constater que ces chemins de nom de classe complets sont les quatre composants principaux Activity, Fragment et les contrôles communs TextView que nous utilisons dans le développement d'applications.

Ainsi, lorsque notre application est en cours d'exécution, la bibliothèque de polices, les ressources et les classes système proviennent d'ici, car Zygote a été préchargé à l'avance au démarrage.

L'avantage du préchargement de ces ressources dans Zygote est que chaque application n'a pas besoin de charger ces ressources, mais utilise ces classes et ressources qui ont été préchargées à l'avance et peuvent être utilisées directement.

Il convient de noter que, étant donné que preoload() est appelé dans le processus principal et prend plus de temps, si vous souhaitez optimiser la vitesse de démarrage du système, vous pouvez également commencer avec cette méthode.

Créer une boucle ZygoteServer en attente du processus enfant fork

Nous devons d’abord comprendre, qu’est-ce que fork ? Fork peut être compris comme une copie, de sorte que le processus fork de Zygote copie en fait un processus en tant que processus enfant basé sur Zygote. Le processus enfant possède les ressources que Zygote a traitées.

L'une des responsabilités de Zygote est d'être responsable de la création des processus enfants fork. Par exemple, si vous souhaitez recevoir une notification AMS via une communication socket pour créer un processus d'application, AMS est le client à ce moment-là et Zygote est le serveur. recevez des messages socket, vous devez créer un serveur ServerSocket et attendre en boucle.

class ZygoteServer {

    private LocalServerSocket mServerSocket;

    void registerServerSocket(String socketName) {
        if (mServerSocket == null) {
 			...
            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                // 创建 ServerSocket
                mServerSocket = new LocalServerSocket(fd);
            } catch (IOException ex) {
				...
            }
        }
    }

    void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
		...
		
		// 循环等待 socket 消息通知 fork 进程
        while (true) {
 			...
            try {
            	// 没有消息休眠等待
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                ...
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                ...
                boolean done = peers.get(i).runOnce(this);
                ...
            }
        }
    }
}

ZygoteConnection.java

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
	...

	// fork 子进程
    pid = Zygote.forkAndSpecialize(...);
    ...
}

Zygote.java

public static int forkAndSpecialize(...) {
	...
	int pid = nativeForkAndSpecialize(...);
	...
	return pid;
}

native private static int nativeForkAndSpecialize(...);

frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(...) {
	...
	return ForkAndSpecializeCommon(...);
}

static pid_t ForkAndSpecializeCommon(...) {
	...
	// 调用 Linux 的 fork() 创建进程
	pid_t pid = fork();
	...
}

Il peut être analysé à partir du code source que ZygoteServer crée réellement un ServerSocket. Dans ZygoteInit, runSelectLoop() est appelé pendant que le serveur attend que la communication du socket client informe le processus fork. Le processus fork est transféré à la couche native et appelle finalement la fonction standard Linux fork().

Créer un processus SystemServer

Le processus SystemServer sera créé dans ZygoteInit. SystemServer est le premier processus enfant créé par Zygote :

ZygoteInit.java

private static boolean startSystemServer(...) {
	...
	pid = Zygote.forkSystemServer(...);
	...
	
	return true;
}

Comment fork a été présenté dans la section précédente. forkSystemServer() crée uniquement le processus SystemServer, alors quand SystemServer s'exécute-t-il ? Analysez ensuite le code source ci-dessous :

ZygoteInit.java

private static boolean startSystemServer(...) {
	...
	pid = Zygote.forkSystemServer(...);
	...
	if (pid == 0) {
		...
		handleSystemServerProcess(parsedArgs);
	}
	
	return true;
}

private static void handleSystemServerProcess(...) {
	...
	ZygoteInit.zygoteInit(...);
}

public static final void zygoteInit(...) {
	...
	// 初始化运行环境
	RuntimeInit.commonInit(); 
	// 打开 Binder 驱动,初始化 Binder
	ZygoteInit.nativeZygoteInit(); 
	// 反射调用 main() 入口函数
	RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

RuntimeInit.java

protected static void applicationInit(...) {
	...
	// Remaining arguments are passed to the start class's static main
	invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
    Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        ...
    }

    Method m;
    try {
    	// 反射调用 main() 方法
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
    	...
    }
    ...
}

La création du processus SystemServer est finalement effectuée via la fonction de réflexion main().

A quoi sert SystemServer ? Continuez à analyser le code source de SystemServer.

SystemServer.java

public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
	...
	// system_server 进程启动服务管理类
	mSystemServiceManager = new SystemServiceManager(mSystemContext);	
	
	try {
		// 启动引导服务
		startBootstrapServices();
		// 启动核心服务
		startCoreServices();
		// 启动其他服务
		startOtherServices();
		...
	}
	...
}

private void startBootstrapServices() {
	...
	// 启动 AMS 服务,在高版本是 ATMS
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    mActivityManagerService.setInstaller(installer);	
    ...
    // 启动 PMS
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
	...
}

private void startCoreServices() {
	// 为了能让 SystemServiceManager 统一的方式管理服务,通过 SystemService 代理这些服务
	mSystemServiceManager.startService(DropBoxManagerService.class);

	mSystemServiceManager.startService(BatteryService.class);

    mSystemServiceManager.startService(UsageStatsService.class);
    mActivityManagerService.setUsageStatsManager(
            LocalServices.getService(UsageStatsManagerInternal.class));

	mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
}

private void startOtherServices() {
	...
	// 各种系统信息的管理,例如字体、系统设置、开发者选项等
	mActivityManagerService.installSystemProviders();
	...
}

SystemServer est en fait l'entrée pour démarrer le service de démarrage, le service principal et d'autres services.On peut constater que ces services ne sont pas créés via le fork de Zygote, mais directement créés par new, donc ces services sont tous dans le processus system_server.

Étant donné que de nombreux services sont démarrés, SystemServer a créé SystemServiceManager pour gérer ces services.

Afin d'être traités de manière unifiée, ces services sont fournis à SystemServiceManager pour une gestion via l'agent SystemService.

Chaque processus de la couche application doit utiliser ces services, alors comment ces processus obtiennent-ils ces services ?

Prenons comme exemple le service de batterie BatteryService :

public abstract class SystemService {
	...
	public abstract void onStart();
	...

    protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated, int dumpPriority) {
        // 将服务注册到 ServiceManager
        ServiceManager.addService(name, service, allowIsolated, dumpPriority);
    }
}

// 服务通过 SystemService 代理
public final class BatteryService extends SystemService {
    @Override
    public void onStart() {
    	...
		// 注册服务到 ServiceManager
        mBinderService = new BinderService();
        publishBinderService("battery", mBinderService);
    	...
    }
}

public final class SystemServer {
    
    private void startCoreServices() {
    	...
		mSystemServiceManager.startService(BatteryService.class);
		...
	}	
}

public class SystemServiceManager {
    @SuppressWarnings("unchecked")
    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            ...
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (...) {
            	...
            }    
            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

    public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + service.getClass().getName()
                    + ": onStart threw an exception", ex);
        }
        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
    }
}

On peut voir qu'une fois ces services unifiés et gérés par SystemServiceManager, les services seront finalement enregistrés auprès de ServiceManager. ServiceManager enregistre la liste Binder où la clé est le nom du service et la valeur est le service. Le processus de la couche application peut facilement obtenir le Classeur pour communiquer en fonction du nom du service. .

ServiceManager est un processus indépendant. Comme Zygote, c'est également un processus indépendant démarré lors de l'exécution du script init.rc.

Passons brièvement en revue ce que fait SystemServer :

  • Créer SystemServiceManager pour une gestion unifiée des services

  • Démarrez divers services tels que AMS, PMS, etc.

  • Enregistrez le service démarré sur ServiceManager

L'organigramme de SystemServer est le suivant :

Le classeur est initialisé lorsque le processus enfant démarre

Le processus enfant fork de Zygote initialisera Binder lors du démarrage du processus enfant, par exemple en ouvrant le pilote Binder. Chaque processus a son propre Binder. L'appel spécifique consiste à appeler nativeZygoteInit() avant que la fonction main() du processus enfant ne soit exécutée :

ZygoteInit.java

public static final void zygoteInit(...) {
	... 
	// 创建 Binder
	ZygoteInit.nativeZygoteInit(); 
	...
}

private static final native void nativeZygoteInit();

frameworks/base/core/jni/AndroidRuntime.cpp

static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}

int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
{
    const JNINativeMethod methods[] = {
        { "nativeZygoteInit", "()V",
            (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
    };
    return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
        methods, NELEM(methods));
}

frameworks/base/cmds/app_process/app_main.cpp

virtual void onZygoteInit()
{
	// 打开 Binder 驱动
    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    // 启动 Binder 线程池
    proc->startThreadPool();
}

frameworks/native/libs/binder/ProcessState.cpp

// Binder 通信数据大小 1M-8k
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
// 最大 Binder 线程数量 15
#define DEFAULT_MAX_BINDER_THREADS 15

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

static int open_driver(const char *driver)
{
	// 打开 Binder 驱动
    int fd = open(driver, O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
          ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
                vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
            close(fd);
            fd = -1;
        }
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
    }
    return fd;
}

ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver)) // 打开 binder 驱动
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
    	// mmap 内存映射
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

Dans ce code, nous pouvons en apprendre beaucoup sur Binder :

  • Chaque processus créé par Zygote initialisera son propre Binder
  • Binder possède un pool de threads et est multithread. Étant donné que le processus A peut avoir plusieurs processus communiquant avec lui, la conception est concurrente.
  • La taille maximale des données pour la communication Binder est de 1 Mo à 8 Ko.
  • Le nombre maximum de fils Binder est de 15

AMS utilise socket pour notifier le processus de fork de Zygote et suit ce processus.

Résumer

Nous sommes partis du processus d'initialisation du démarrage du système et l'avons analysé jusqu'à la création du processus Zygote et la création du processus SystemServer, et avons trié grossièrement le processus global.

Le processus init est le deuxième processus démarré après le démarrage du noyau système. Ce processus se déroule dans l'espace utilisateur. Une fois le processus d'initialisation démarré, il analysera init.rc et exécutera le script pour démarrer le processus Zygote et le processus ServiceManager.

Parce que Zygote est démarré par le processus d'initialisation, il se trouve également dans l'espace utilisateur. Le traitement métier de Zygote couvre la couche native et la couche Java.

Choses que Zygote gère au niveau de la couche native :

  • startVm() crée une machine virtuelle
  • startReg() enregistre dynamiquement Java pour appeler jni natif
  • La réflexion appelle main() de ZygoteInit

Choses que Zygote gère au niveau de la couche Java :

  • Précharger les ressources système, telles que les classes système, les ressources, les bibliothèques système partagées, etc.
  • La création de ZygoteServer est en fait un ServerSocket qui attend d'être averti du processus enfant fork.
  • Créer un processus SystemServer

Le processus global de Zygote de la création au démarrage est le suivant :

Le premier processus enfant du fork de Zygote est SystemServer.

Choses que SystemServer gère :

  • Créer SystemServiceManager pour une gestion unifiée des services
  • Démarrez divers services tels que AMS, PMS, etc.
  • Enregistrez le service démarré sur ServiceManager

L'organigramme de SystemServer est le suivant :

Il explique également brièvement le processus d'initialisation de Binder lorsque Zygote crée un processus enfant. L'initialisation de Binder est traitée avant que le processus ne termine l'exécution de la méthode main() de l'entrée principale. Grâce à une simple analyse de processus, nous pouvons apprendre quelques informations sur Binder :

  • Chaque processus créé par Zygote initialisera son propre Binder
  • Binder possède un pool de threads et est multithread. Étant donné que le processus A peut avoir plusieurs processus communiquant avec lui, la conception est concurrente.
  • La taille maximale des données pour la communication Binder est de 1 Mo à 8 Ko.
  • Le nombre maximum de fils Binder est de 15

Le processus global de démarrage du système est le suivant :

Problème commun

1. Quel est le procédé le plus original du processus Zygote (ou l'origine du Zygote) ? [Le nom initial du processus Zygote]

Zygote était à l'origine app_process, qui a été démarré au démarrage du processus d'initialisation, et a été modifié en Zygote dans app_main.c.

2. Zygote est-il dans l'espace noyau ou dans l'espace utilisateur ?

Étant donné que le processus d'initialisation est créé dans l'espace utilisateur et que Zygote est créé et démarré par le processus d'initialisation, Zygote se trouve dans l'espace utilisateur.

3. Lorsque le processus d'application est démarré, pourquoi est-il dérivé de Zygote au lieu d'être dérivé du processus d'initialisation ?

Zygote fait beaucoup de choses de la création au démarrage, comme créer des machines virtuelles, enregistrer jni, précharger des ressources, etc. Le processus fork est en fait un processus de copie. S'il n'est pas dans le processus fork de Zygote, alors le processus d'application nouvellement créé devra recommencer le processus ci-dessus. Une fois que vous avez dérivé le processus enfant de Zygote et que le processus d'application est créé et exécuté, vous pouvez utiliser directement les ressources pertinentes sans autre traitement.

Les principales tâches du processus d'initialisation sont de monter des fichiers (identifier divers fichiers, ce qui équivaut à analyser le disque dur), d'analyser init.rc et de traiter les scripts (démarrer Zygote, les processus ServiceManager, etc.).

4. Pourquoi Zygote utilise-t-il la communication socket au lieu de Binder ?

Il existe actuellement deux théories sur Internet : l'une est que cela provoquerait un blocage, et l'autre est que cela provoquerait des erreurs de lecture et d'écriture.

(1) Zygote utilisant un classeur pour communiquer entraînera un blocage

Supposons que Zygote utilise Binder pour communiquer. Étant donné que Binder prend en charge le multithreading, il existe un problème de concurrence. La solution au problème de concurrence est le verrouillage. Si la branche de processus s'exécute dans une situation multithread, Binder attend le verrouillage. sous le mécanisme de verrouillage.

Pourquoi une impasse se produit-elle ? Nous pouvons utiliser un scénario pour analyser.

Supposons qu'AMS utilise la communication Binder pour informer Zygote de créer un processus d'application. Afin de garantir qu'aucun problème de concurrence ne se produise, la communication entre AMS et Zygote sera verrouillée. Le Binder dont AMS a besoin pour communiquer avec Zygote appartient à Zygote (le Binder obtenu doit être le Binder de la partie communicante).Lorsque Zygote bifurque le processus, il copiera également l'état de verrouillage du Binder en attente. Alors, qui déverrouillera le Binder du processus enfant s'il est verrouillé ? Si le processus enfant n'est pas déverrouillé, un blocage se produira.

Analysons-le à partir du principe du fork.

Dans la zone mémoire, la mémoire du mutex de variable statique sera copiée dans le processus enfant, et même s'il y a plusieurs threads dans le processus parent, ils ne seront pas hérités dans le processus enfant. Ces deux caractéristiques de fork provoquent une impasse. c'est la raison.

  • doit() dans le thread est exécuté en premier
  • Lorsque doit est exécuté, la variable mutex mutex sera verrouillée.
  • Le contenu de la variable mutex sera copié inchangé dans le processus enfant qui sort du fork (avant cela, le contenu de la variable mutex a été réécrit dans un état verrouillé par le thread)
  • Lorsque le processus enfant appelle à nouveau Doit, il constatera que le mutex a été verrouillé lorsqu'il verrouille le mutex, il attend donc que le processus qui possède le mutex le libère (personne ne possède réellement le verrou mutex). Le thread Doit libérera le
    sien mutex avant la fin de l'exécution, mais le mutex et le mutex dans le processus enfant sont déjà deux mémoires, donc même si le verrou mutex est libéré, cela n'aura aucun impact sur le mutex dans le processus enfant, ce qui finira par conduire à la mort.

(2) Zygote utilisant un classeur pour communiquer provoquera des erreurs de lecture et d'écriture

La raison fondamentale est que lorsque vous souhaitez créer une nouvelle communication ProcessState pour Binder, vous devez utiliser mmap pour demander un morceau de mémoire afin de le fournir au noyau pour l'échange de données.

S'il est directement dupliqué, lorsque le processus enfant communique avec le classeur, le noyau continuera à utiliser l'adresse demandée par le processus parent pour écrire des données, et à ce moment le processus enfant COW (Copy on Write) sera déclenché , provoquant le remappage de l'espace d'adressage. Le processus enfant tente également d'accéder à l'adresse précédemment mappée par le processus parent, ce qui provoquera des erreurs de segment SIGSEGV et SEGV_MAPERR.

Vous pouvez écrire une démo vous-même pour essayer de fournir une interface JNI pour appeler fork. Lorsque pid == 0, continuez à utiliser l'objet binder obtenu par le processus parent pour effectuer des appels de binder, et vous obtiendrez cette erreur de segmentation.

5. Quelle est la relation entre ServiceManager et SystemServiceManager ?

ServiceManager et SystemServiceManager ne sont pas liés.

ServiceManager est un processus indépendant. Comme Zygote, il est démarré en exécutant le script init.rc. Les services démarrés dans SystemServer seront éventuellement enregistrés auprès de ServiceManager pour être utilisés par la couche supérieure.

SystemServiceManager est une classe de gestion créée dans SystemServer pour gérer les services de démarrage dans le processus system_server.

Principe Android PMS (PackageManagerService)

Qu'est-ce que le SPM

PMS (PackageManagerService) est un service de système de gestion de packages fourni par Android. Il est utilisé pour gérer toutes les informations sur les packages, y compris l'installation, la désinstallation, les mises à jour et l'analyse d'AndroidManifest.xml. Dans des circonstances normales, nous ne séparerons pas le PMS pour l'expliquer séparément, car la chose la plus importante du PMS est de fournir des services à l'AMS (ActivityManagerService).

Avez-vous déjà réfléchi à la raison pour laquelle nos téléphones sont si lents à démarrer ? En effet, PMS traitera l'analyse apk pendant cette période de démarrage du téléphone. Au moins 70 % du temps de démarrage est consacré à l'analyse PMS, c'est donc l'une des raisons pour lesquelles le téléphone démarre lentement.

D'un point de vue analytique, on peut comprendre que PMS enregistre les données nécessaires à une fourniture ultérieure à AMS. Il s'agit d'un cache qui enregistre les données d'application.

Le rôle de AndroidManifest.xml

Lorsque le téléphone est allumé, le système analysera deux répertoires après le démarrage de PMS, à savoir le répertoire /data/app où est stocké l'apk installé par l'utilisateur et le répertoire /system/app où se trouve l'apk installé par le système. stocké.

Comme mentionné tout à l'heure, PMS est destiné à servir AMS, alors quelles données PMS doit-il fournir ? Pourquoi avez-vous besoin d’AndroidManifest.xml ?

Nous savons tous qu'AndroidManifest.xml définit les quatre principaux composants, autorisations et autres informations de l'apk. Il s'agit d'un fichier de définition. La chose la plus importante pour que PMS analyse l'apk est d'analyser les fichiers apk dans les répertoires /data/app et /system/app, de rechercher le fichier AndroidManifest.xml dans le package apk, puis d'analyser les informations AndroidManifest.xml et de les enregistrer dans la mémoire système, donc lorsque AMS a besoin de données d'application, il peut trouver PMS et obtenir rapidement des informations pertinentes de la mémoire.

S'il n'y a pas de AndroidManifest.xml, l'analyse PMS consiste à enregistrer toutes les informations du fichier de classe dans chaque apk. Cette quantité de données est énorme, et l'analyse sera très lente, et la vitesse de démarrage du téléphone mobile sera encore plus lente.

Processus d'analyse de l'apk PMS

Processus de démarrage du PMS

Tous les services de base du système Android seront démarrés via SystemServer, et PMS ne fait pas exception. SystemServer commencera à fonctionner lorsque le téléphone sera allumé.

SystemServer.java

public static void main(String[] args) {
	new SystemServer().run();
}

private void run() {
	...
	try {
		...
		startBootstrapServices();
		startCoreServices();
		startOtherServices();
		...
	}
	...
}

private void startBootstrapServices() {
	...
	// 启动 AMS 
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    mActivityManagerService.setInstaller(installer);
    ...
    // 启动 PMS
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    mFirstBoot = mPackageManagerService.isFirstBoot();
    mPackageManager = mSystemContext.getPackageManager();   
    ... 
}

PackageManagerService.java

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    // Self-check for initial settings.
    PackageManagerServiceCompilerMapping.checkProperties();

	// 创建自己的实例
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    m.enableSystemUserPackages();
    // 将 PMS 添加到 ServiceManager,AMS 找 PMS 拿数据时就是通过 ServiceManager 找到 PMS
    ServiceManager.addService("package", m); 
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
    return m;
}

Lorsque SystemServer est démarré par Zygote et que la méthode main() est appelée, la méthode run() de SystemServer est exécutée pour démarrer certains services de base. Par exemple, AMS est démarré en premier, puis PMS est démarré. AMS et PMS sont ajoutés à ServiceManager. , et ServiceManager gère ces services. .

ServiceManager ne fournit que les méthodes addService() et getService(). Lorsque le processus d'application doit obtenir le service système correspondant, il obtiendra l'agent Binder du service correspondant via ServiceManager et utilisera la communication Binder pour obtenir les données :

Par exemple, lors de l'appel de getPackageManager() dans Application, Activité, etc. :

ContextWrapper.java

@Override
public PackageManager getPackageManager() {
	// mBase 是 ContextImpl
    return mBase.getPackageManager();
}

ContextImpl.java

@Override
public PackageManager getPackageManager() {
    if (mPackageManager != null) {
        return mPackageManager;
    }

    IPackageManager pm = ActivityThread.getPackageManager();
    if (pm != null) {
        // Doesn't matter if we make more than one instance.
        return (mPackageManager = new ApplicationPackageManager(this, pm));
    }

    return null;
}

ActivityThread.java

public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
        return sPackageManager;
    }
    // 通过 ServiceManager 拿到 PMS
    IBinder b = ServiceManager.getService("package");
    //Slog.v("PackageManager", "default service binder = " + b);
    sPackageManager = IPackageManager.Stub.asInterface(b); // binder 通信
    //Slog.v("PackageManager", "default service = " + sPackageManager);
    return sPackageManager;
}

Processus apk d'analyse PMS

Une compréhension simple du flux de traitement PMS est que lorsque le téléphone est allumé, il analysera les deux répertoires /data/app et /system/app, analysera le fichier AndroidManifest.xml des fichiers apk dans ces deux répertoires, générera le résumé. informations de l'application et enregistrez-les en tant que Java Bean dans la mémoire. .

PackageManagerService.java

// data/app 目录
private static final File sAppInstallDir =
        new File(Environment.getDataDirectory(), "app");

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
	...
	// /system/app 目录
	final File systemAppDir = new File(Environment.getRootDirectory(), "app");
	// 扫描 /system/app 目录下的 apk 文件
    scanDirTracedLI(systemAppDir,
            mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM_DIR,
            scanFlags
            | SCAN_AS_SYSTEM,
            0);	
	...
	// 扫描 /data/app 目录下的 apk 文件
	scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
	...
}

private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
    try {
        scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
	final File[] files = scanDir.listFiles();
	...
    try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
             mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
             mParallelPackageParserCallback)) {
         // Submit files for parsing in parallel
         int fileCount = 0;
         for (File file : files) {
         	 // 判断是否是 .apk 后缀的文件
             final boolean isPackage = (isApkFile(file) || file.isDirectory())
                     && !PackageInstallerService.isStageName(file.getName());
             if (!isPackage) {
                 // Ignore entries which are not packages
                 continue;
             }
             // 添加到子线程交给 PackageParser 解析 apk 文件
             parallelPackageParser.submit(file, parseFlags);
             fileCount++;
         }
         ...
     }
}

ParallelPackageParser.java

public void submit(File scanFile, int parseFlags) {
    mService.submit(() -> {
        ParseResult pr = new ParseResult();
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
        try {
            PackageParser pp = new PackageParser();
            pp.setSeparateProcesses(mSeparateProcesses);
            pp.setOnlyCoreApps(mOnlyCore);
            pp.setDisplayMetrics(mMetrics);
            pp.setCacheDir(mCacheDir);
            pp.setCallback(mPackageParserCallback);
            pr.scanFile = scanFile; // 传入待解析的 apk 文件
            // 交给 packageParser 解析 apk
            pr.pkg = parsePackage(pp, scanFile, parseFlags);
        } catch (Throwable e) {
            pr.throwable = e;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
        ...
    });
}

protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
        int parseFlags) throws PackageParser.PackageParserException {
    return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}

PackageParser.java

public static final String APK_FILE_EXTENSION = ".apk";

public static final boolean isApkFile(File file) {
	return isApkPath(file.getName());
}

public static boolean isApkPath(String path) {
	return path.endsWith(APK_FILE_EXTENSION);
}

Comme le montre le code source, PMS analyse en fait les apks dans les deux répertoires /data/app/ et /system/app/, et détermine si les fichiers du répertoire sont apk. Il détermine également simplement si le suffixe du fichier est ​​.apk. Ensuite, commencez à analyser l'apk via PackageParser.

Il convient de noter que les méthodes d'analyse dans différentes versions du code source du système sont également différentes. Dans les versions 6.0, 7.0 et 8.0, la méthode de démarrage de l'analyse est toujours l'analyse directe, mais dans la version 10.0, un pool de threads est utilisé pour mettre dans un thread enfant pour l'analyse, ce qui accélère le processus.Amélioration de la vitesse de démarrage du téléphone.

Analyse du code source de la classe PackageParser

D'après l'analyse ci-dessus, l'analyse de l'apk est finalement confiée à PackageParser. Continuez à voir comment il est analysé :

PackageParser.java

public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
     // 如果有缓存,直接返回解析后的信息
     Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
     if (parsed != null) {
         return parsed;
     }        
     ...
	 // apk 文件不是目录,所以会走的 parseMonolithicPackage()
     if (packageFile.isDirectory()) {
         parsed = parseClusterPackage(packageFile, flags);
     } else {
         parsed = parseMonolithicPackage(packageFile, flags);
     }
     ...
}

public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
	...
    final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
    try {
    	// 解析 apk
        final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
        pkg.setCodePath(apkFile.getCanonicalPath());
        pkg.setUse32bitAbi(lite.use32bitAbi);
        return pkg;
    } catch (IOException e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to get path: " + apkFile, e);
    } finally {
        IoUtils.closeQuietly(assetLoader);
    }
}

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        throws PackageParserException {
    final String apkPath = apkFile.getAbsolutePath();    
	...
	// 开始 dom 解析 AndroidManifest.xml
	XmlResourceParser parser = null;
    try {
        final int cookie = assets.findCookieForPath(apkPath);
        ...
        // ANDROID_MANIFEST_FILENAME 就是 AndroidManifest.xml
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        ...
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
        ...
        return pkg;

    } catch (PackageParserException e) {
        throw e;
    } catch (Exception e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to read manifest from " + apkPath, e);
    } finally {
        IoUtils.closeQuietly(parser);
    }
}

private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
     final String splitName;
     final String pkgName;

     try {
         Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
         pkgName = packageSplit.first; // 拿到包名
         splitName = packageSplit.second;

         ...
     } 
     ...
     // 后续的流程就是将 xml 解析的信息如权限、四大组件等信息存到 Package
     final Package pkg = new Package(pkgName);
	 ...
     return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}

public final static class Package implements Parcelable {
	// 包名
	public String packageName;
	...
	// 申请的权限
    public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
    public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
    // 四大组件
    public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
    public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
    public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
    public final ArrayList<Service> services = new ArrayList<Service>(0);
    ...
}

Le code source ci-dessus est en fait facile à comprendre. Il s'agit d'abord d'obtenir le fichier AndroidManifest.xml en fonction du chemin du fichier apk transmis, puis de commencer à analyser le fichier XML via dom et de stocker différentes informations de données de balise dans différents champs du package. classe, telles que les informations d'autorisation, les informations sur quatre composants volumineux, etc., sont analysées et stockées en mémoire pour faciliter la recherche ultérieure de l'AMS par le PMS pour obtenir des données.

À partir de la version 9.0, la mise en cache des résultats analysés sera activée par défaut. S'il existe un cache, les informations sur les résultats analysés seront renvoyées directement. Sinon, le fichier AndroidManifest.xml de chaque fichier apk sera analysé :

ParallelPackageParser.java

protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
        int parseFlags) throws PackageParser.PackageParserException {
    // 开启缓存
    return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}

PackageParser.java

public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    // 如果有缓存,直接返回
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
        return parsed;
    }
    ...
}

Ce qui précède est le processus d'analyse apk du PMS. En termes simples, les informations de données à utiliser par AMS sont analysées et stockées en mémoire à l'avance, afin que des informations telles que l'activité puissent être rapidement localisées.

Lorsque nous téléchargeons et installons des applications depuis l'App Store ou utilisons adb install, nous passons également par le processus d'analyse ci-dessus.

résumé

Résumons brièvement le processus d'analyse de l'apk PMS :

  • Le système de téléphonie mobile démarre, Zygote démarre SystemServer, SystemServer démarre AMS, PMS et s'enregistre auprès de ServiceManager
  • PMS analyse tous les fichiers apk dans les répertoires /data/app/ et /system/app/, obtient le fichier AndroidManifest.xml de chaque fichier apk et effectue une analyse dom
  • Analysez AndroidManifest.xml et convertissez les autorisations, quatre composants principaux et d'autres informations de données en Java Beans et enregistrez-les en mémoire.
  • Lorsqu'AMS a besoin d'obtenir des informations sur les données apk, il obtient l'agent Binder de PMS via ServiceManager et l'obtient via la communication Binder.

Savez-vous à quoi sert le processus d'analyse PMS ?

Après avoir compris le processus d'analyse de l'apk par PMS, nous pouvons implémenter la fonction de chargement dynamique basée sur le principe du hook, utiliser PackageParser pour analyser manuellement un fichier apk téléchargé depuis le réseau, puis l'ajouter à la mémoire de PMS par réflexion pour réaliser la dynamique. fonction de chargement.

La démo suivante implémente une fonction de chargement dynamique simple, ajoutant la diffusion d'un fichier apk externe au cache PMS.

Préparez d'abord la diffusion qui doit être ajoutée dynamiquement, qui est placée dans le fichier apk externe hook-debug.apk :

public class HookReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("HookReceiver", "hook receiver receive message");
        // 给宿主广播发消息
        Intent sendIntent = new Intent();
        sendIntent.setAction("com.example.demo.main");
        context.sendBroadcast(sendIntent);
    }
}

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hook">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Demo" >
        <!-- 清单文件也需要添加广播注册,PackageParser 动态加载时需要使用 -->
        <receiver android:name=".HookReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.demo.hook" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Après avoir empaqueté l'apk externe, pour faciliter la démonstration, la démo consiste à importer l'apk dans le répertoire de cache. Vient ensuite l'analyse dynamique :

public class HookPackageParser {

    public void parse(Context context, File apkFile) throws Exception {
        Class<?> packageParserClazz = Class.forName("android.content.pm.PackageParser");
        Method parsePackageMethod = packageParserClazz.getDeclaredMethod("parsePackage", File.class, int.class);
        parsePackageMethod.setAccessible(true);
        Object packageParserObj = packageParserClazz.newInstance();

        // 调用 PackageParser.parsePackage() 获取到解析后的 Package
        Object packageObj = parsePackageMethod.invoke(packageParserObj, apkFile, PackageManager.GET_RECEIVERS);

        // 获取 receivers 成员变量
        Field receiversField = packageObj.getClass().getDeclaredField("receivers");
        List receivers = (List) receiversField.get(packageObj);

        DexClassLoader dexClassLoader = new DexClassLoader(
                apkFile.getAbsolutePath(),
                context.getDir("plugin", Context.MODE_PRIVATE).getAbsolutePath(),
                null,
                context.getClassLoader());

        Class<?> componentClazz = Class.forName("android.content.pm.PackageParser$Component");
        Field intentsField = componentClazz.getDeclaredField("intents");
        for (Object receiverObj : receivers) {
            String name = (String) receiverObj.getClass().getField("className").get(receiverObj);
            try {
                BroadcastReceiver hookReceiver = (BroadcastReceiver) dexClassLoader.loadClass(name).newInstance();
                List<? extends IntentFilter> filters = (List<? extends IntentFilter>) intentsField.get(receiverObj);
                for (IntentFilter filter : filters) {
                    context.registerReceiver(hookReceiver, filter);
                }
            } catch (Exception e) {
                // ignore
            }
        }
    }
}

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        checkPermission(this);

		// 注册一个用于接收 hook 广播发送的消息验证是否动态装载了外部 apk 的广播
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.demo.main");
        registerReceiver(new MainReceiver(), filter);
    }

    private boolean checkPermission(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            activity.requestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, 1);

        }
        return false;
    }

    // 先 hook 外部 apk 的 HookReceiver
    public void hookReceiver(View view) {
        HookPackageParser packageParser = new HookPackageParser();
        File directory = getCacheDir();
        String path = directory.getAbsolutePath() + "/hook-debug.apk";
        File file = new File(path);
        if (!file.exists()) {
            throw new RuntimeException("hook apk no exist");
        }

        try {
            packageParser.parse(this, file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // hook 后尝试发送广播看是否生效
    public void sendBroadcast(View view) {
        Intent intent = new Intent();
        intent.setAction("com.example.demo.hook");
        sendBroadcast(intent);
    }

    private static class MainReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i("MainReceiver", "receive hook receiver message");
        }
    }
}

PackageParser doit être obtenu par réflexion, puis la réflexion appelle son parsePackage() pour transmettre le chemin apk pour terminer l'analyse afin d'obtenir l'objet Package, puis refléter les activités, les fournisseurs, les récepteurs et les variables de services de PMS, et ajouter le les données que nous y avons analysées, et ceci est réalisé. Chargement dynamique.

Principe Android AMS (Activity Manager Service)

Qu’est-ce que l’AMS

AMS (ActivityManagerService) est principalement responsable du démarrage, de la commutation et de la planification des quatre composants principaux du système, ainsi que de la gestion et de la planification des processus d'application. Normalement, nous ne séparerons pas AMS pour l'expliquer séparément, car AMS doit obtenir des informations via PMS (PackageManagerService).

Les nœuds suivants seront entrecoupés de contenu lié au PMS (PackageManagerService) lors de l'explication d'AMS.

Que se passerait-il sans PMS et AMS ?

À partir des principes d'Android PMS, nous pouvons apprendre que PMS exécutera le fichier AndroidManifest.xml qui analyse tous les apk lorsque le téléphone est allumé, stocke chaque information apk dans la mémoire et fournit une interface de requête. On peut considérer que PMS agit en tant que cache d'informations sur les packages.

L'une des responsabilités d'AMS est de gérer et de planifier une activité. Lorsque vous devez démarrer une activité, vous trouverez d'abord PMS pour interroger les informations d'activité à sauter, puis traiter une série d'opérations ultérieures.

Nous pouvons réfléchir à une question : pourquoi Android fournit-il AMS et PMS ? Que se passerait-il sans AMS et PMS ?

Supposons que vous souhaitiez démarrer l’activité d’un certain processus. Dans un bref résumé, vous passerez par cinq étapes :

  • Parcourez le répertoire data/app pour obtenir tous les fichiers apk de ce répertoire
  • Décompressez tous les apk et obtenez AndroidManifest.xml
  • DOM analyse AndroidManifest.xml, analyse la balise Activity, génère la classe de données correspondante et la stocke en mémoire.
  • Recherchez les informations d'activité à extraire de la mémoire, obtenez le nom complet de la classe de l'activité et construisez l'objet par réflexion.
  • Exécuter le cycle de vie de l'activité en séquence

Si cela est fait à chaque fois que le processus est planifié, vous pouvez imaginer que l'efficacité sera très faible.

Afin de localiser rapidement l'activité à sauter, les trois premières étapes seront transmises à PMS. PMS l'analysera et l'enregistrera dans la mémoire à l'avance lors du démarrage. De cette façon, lorsqu'AMS doit accéder à une activité, il peut être directement récupéré à partir du PMS. En extrayant les informations des données stockées dans la mémoire, l'opération peut être complétée plus rapidement.

Une fois qu'AMS a obtenu les informations nécessaires pour créer une activité à partir de PMS et l'a créée, il devra prendre en compte le cycle de vie de l'activité. L'activité ne peut pas être détruite dès sa sortie, AMS doit donc gérer le cycle de vie de l'activité créée. . Chaque application et processus possède son propre ActivityThread, AMS a donc également besoin d'un centre de cache pour gérer le cycle de vie de l'activité. ActivityThread joue ce rôle (plus précisément, la variable mActivities sous ActivityThread).

ActivityThread.java

final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	...
	Activity activity = null;
	try {
		// 创建 Activity
		java.lang.ClassLoader cl = appContext.getClassLoader();
		activity = mInstrumentation.newActivity(
			cl, component.getClassName(), r.intent);
		...
	} catch (Exception e) {
		...
	}
	...
	try {
		...
		if (activity != null) {
			...
 			r.activity = activity; // ActivityClientRecord 记录 Activity
		}
		mActivities.put(r.token, r); // 将创建的 Activity 存储到 map
	} catch (SuperNotCalledException e) {
		...
	}
	...
	return activity;
}

public static final class ActivityClientRecord {
	...
	Activity activity; // 持有 Activity
	...
}

Vous pouvez voir qu'il existe une variable membre de mActivities dans le code source d'ActivityThread et que ActivityClientRecord contient une référence à Activity. La gestion des mActivités sera utilisée dans les méthodes de traitement du cycle de vie ultérieures telles que performXxxActivity().

Processus de démarrage de l'application

Sous Android, qu'il s'agisse du démarrage d'une application ou du démarrage d'une activité au sein de l'application, la méthode startActivity() est appelée. De manière générale, cela passera par trois étapes :

  • Dites à AMS de démarrer une activité spécifiée
  • AMS trouve les informations d'activité à démarrer à partir de PMS
  • Démarrer l'activité spécifiée

Ensuite, nous analysons l'ensemble du processus de startActivity() via le code source (API 28).

Activity.java

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

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

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
           	@Nullable Bundle options) {
	if (mParent == null) {
		...
		// 委托给 Instrumentation
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        ....
	}
	...
}

Lorsque nous appellerons startActivity(), nous appellerons éventuellement startActivityForResult(), et la couche supérieure de l'application déléguera le service principal Android à Instrumentation. Instrumentation est spécifiquement responsable du traitement de toutes les activités liées à l'application et à l'activité, c'est-à-dire que la communication avec AMS sera confiée à Instrumentation.

Instrumentation.java

public ActivityResult execStartActivity(
          Context who, IBinder contextThread, IBinder token, Activity target,
          Intent intent, int requestCode, Bundle options) {
	...
	try {
		...
		// 找到 AMS 告知要启动 Activity
        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);	
        ...            	
	}
	...
}

L'instrumentation indiquera à AMS de démarrer une activité.

Il convient de noter que le traitement du code source des différentes versions du système est également différent ici. Avant la version 9.0, AMS était utilisé et après la version 9.0, ATMS (ActivityTaskManagerService) était utilisé :

public ActivityResult execStartActivity(
          Context who, IBinder contextThread, IBinder token, Activity target,
          Intent intent, int requestCode, Bundle options) {
	...
	try {
		...
		// 9.0 之后会通过 ATMS 告知要启动 Activity
        int result = ActivityTaskManager.getService().startActivity(whoThread,
        		 who.getBasePackageName(), who.getAttributionTag(), intent,
        		 intent.resolveTypeIfNeeded(tho.getContentResolver()), token,
        		 target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);	
        ...            	
	}
	...
}

Le code source actuel est analysé sur la base de l'API 28, nous revenons donc à l'analyse du code source de l'API 28.

ActivityManager.getService().startActivity() Ce code peut être divisé en deux parties : ActivityManager.getService() et startActivity().

Regardons d'abord ActivityManager.getService() :

ActivityManager.java

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

private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
            	// 通过 ServiceManager 获取 AMS 服务
            	// IActivityManager 是 binder 代理
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am; 
            }
        };

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;
		}
	}
}

ActivityManager.getService() obtiendra IActivityManager, qui est un objet proxy de classeur. Le code montre que lorsque l'objet ActivityManager.getService() est utilisé pour appeler la méthode, une communication entre processus a réellement lieu. L'objet proxy du classeur communique avec AMS et le processus dans lequel la communication se produit est system_server. . En même temps, c’est aussi un bon point d’accroche.

À ce stade, la première étape consistant à demander à AMS de démarrer l’activité est terminée.

Continuons à analyser le code source.

ActivityManagerService.java

@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) {
    // 有多个 startActivityAsUser() 重载方法,为了方便查看省略了多个方法调用  
    return startActivityAsUser(...); 
}

public final int startActivityAsUser(...) {
	...
	// mActivityStartController.obtainStarter 返回 ActivityStarter 对象
    return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setProfilerInfo(profilerInfo)
            .setActivityOptions(bOptions)
            .setMayWait(userId)
            .execute();
}

Utilisez AMS pour appeler la méthode startActivity(), principalement pour trouver mActivityStartController.obtainStarter() pour obtenir l'objet ActivityStarter. Il est évident que le mode constructeur est utilisé pour configurer les paramètres pertinents, en se concentrant sur la méthodeexecute().

Selon les étapes que nous avons définies au début, la deuxième étape consiste pour AMS à trouver PMS pour obtenir des informations relatives à l'activité de saut. Alors, comment AMS communique-t-il avec PMS ?

ActivityStarter.java

int execute() {
	try {
		if (mRequest.mayWait) {
			return startActivityMayWait(...);
		} else {
			return startActivity(...);
		}
	} finally {
		...
	}
}

private int startActivityMayWait(...) {
	...
	// AMS 和 PMS 产生关联,从 PMS 获取信息
	ResolveInfo rInfo = mSupervisor.resolveIntent(...);
	...
	return res;
}

ActivityStackSupervisor.java

ResolveInfo resolveIntent(...) {
	// mService 是 AMS
	synchronized (mService) {
		...
		try {
			// mService.getPackageManagerInternalLocked() 返回 PackageManagerInternalImpl
			return mService.getPackageManagerInternalLocked().resolveIntent(...);
		} finally {
			....
		}
	}
	...
}

ActivityManagerService.java

PackageManagerInternal getPackageManagerInternalLocked() {
    if (mPackageManagerInt == null) {
        mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
    }
    return mPackageManagerInt;
}

PackageManagerService.java

// PackageManagerInternalImpl 的作用是,它作为内部类能拿到外部类的引用
// 所以可以充当 AMS 和 PMS 的桥梁,让 AMS 能获取到 PMS
private class PackageManagerInternalImpl extends PackageManagerInternal {
	...
	
    @Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType,
            int flags, int userId, boolean resolveForStart, int filterCallingUid) {
        // 作为桥梁外部调用相关方法时都转发给 PMS
        // ResolveInfo 持有 ActivityInfo、serviceInfo 等信息
        return resolveIntentInternal(
                intent, resolvedType, flags, userId, resolveForStart, filterCallingUid);
    }
      
    ...
}

ResolveInfo.java

public class ResolveInfo implements Parcelable {
	public ActivityInfo activityInfo;
	public ServiceInfo serviceInfo;
	public ProviderInfo providerInfo;
	...
}

La communication entre AMS et PMS obtient l'objet d'empaquetage ResolveInfo, qui peut stocker ActivityInfo, ServiceInfo et d'autres informations. La raison pour ne pas renvoyer directement ActivityInfo devrait être que le même ensemble de logique peut être réutilisé lors du démarrage d'autres composants tels que Service, car le les méthodes d'acquisition sont fondamentalement les mêmes.

La communication entre AMS et PMS n'est pas une communication directe, mais via la classe interne PackageManagerInternalImpl de PMS comme pont. La classe interne contient une référence à la classe externe, donc PackageManagerInternalImpl peut accéder directement à PMS. Cette approche peut non seulement implémenter des fonctions, mais également réduire le couplage entre AMS et PMS et limiter l'accès aux API publiques.

Maintenant que vous avez terminé la deuxième étape et obtenu les informations sur l'activité à démarrer à partir du PMS, vous pouvez démarrer la troisième étape pour démarrer l'activité.

ActivityStarter.java

private int startActivityMayWait(...) {
	...
	// AMS 和 PMS 产生关联,从 PMS 获取信息
	ResolveInfo rInfo = mSupervisor.resolveIntent(...);
	...
	// 从封装的 ResolveInfo 获取到要跳转的 ActivityInfo 信息
	ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
	...
	// 开始启动 Activity
	int res = startActivity(...);
	...
	return res;
}

private int startActivity(...) {
	int result = START_CANCELED;
	try {
		...
        result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                 startFlags, doResume, options, inTask, outActivity);
	} finally {
		...	
	}
	...
}

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
         IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
         int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
         ActivityRecord[] outActivity) {
	...
	mSuperVisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
			mOptions);
	...
}

ActivityStackSupervisor.java

boolean resumeFocusedStackTopActivityLocked(
        ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
   if (!readyToResume()) {
        return false;
    }

    if (targetStack != null && isFocusedStack(targetStack)) {
        return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
    }

    final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
    if (r == null || !r.isState(RESUMED)) {
        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
    } else if (r.isState(RESUMED)) {
        // Kick off any lingering app transitions form the MoveTaskToFront operation.
        mFocusedStack.executeAppTransition(targetOptions);
    }

    return false;		
}

ActivityStack.java

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
	...
	try {
		...
		result = resumeTopActivityInnerLocked(prev, options);
		...
	}
}

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
	...
	mStackSupervisor.startSpecificActivityLocked(next, true, false);
	...
}

void startSpecificActivityLocked(ActivityRecord r,
        boolean andResume, boolean checkConfig) {
	...
	// 先判断要启动的 Activity 进程是否已经存在
	if (app != null && app.thread != null) {
		try {
			...
			// 进程已经存在,启动 Activity
			realStartActivityLocked(r, app, andResume, checkConfig);
			return;
		} catch (RemoteException e) {
			...
		}
	}
	
	// 如果进程不存在,AMS 通知 zygote 启动进程,最终反射调用 ActivityThread.main()
	// 有多个 startProcessLocked() 重载方法,为了方便查看省略了多个方法调用
    mService.startProcessLocked(...);	
}

ActivityManagerService.java

private boolean startProcessLocked(...) {
	...
	// 其中一个 startProcessLocked() 提供了要启动的进程为 ActivityThread
	final String entryPoint = "android.app.ActivityThread";
	... // 省略其他 startProcessLocked() 调用
	if (mConstants.FLAG_PROCESS_START_ASYNC) {
		...
		// 传入要启动的进程
		final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint, ...)
	}
	...
}

private ProcessStartResult startProcess(String entryPoint, ...) {
	try {
		...
		startResult = Process.start(entryPoint, ...);
		...
	} finally {
		...
	}
}

Process.java

public static final ProcessStartResult start(final String processClass, ...) {
	return zygoteProcess.start(processClass, ...);
}

D'après l'analyse du code source ci-dessus, avant de démarrer l'Activité, il faut en fait la subdiviser en deux processus :

  • Si le processus de l'activité à démarrer n'a pas été créé, AMS en informera le processus de fork de Zygote et appellera éventuellement la méthode main() d'ActivityThread de manière réfléchie, puis passera par la création d'activité ultérieure et les processus de cycle de vie ultérieurs.

  • Le processus a été créé, realStartActivityLocked() crée une activité et les processus de cycle de vie ultérieurs

Le démarrage d'une activité implique le cycle de vie. Puisque AMS est un service qui gère et planifie l'activité, il doit être lié à l'activité démarrée. Comment AMS gère-t-il le cycle de vie de l’activité ?

ActivityThread.java

final ApplicationThread mAppThread = new ApplicationThread();

public static void main(String[] args) {
	...
	ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    ...
}

private void attach(boolean system, long startSeq) {
	...
	if (!system) {
		...
		final IActivityManager mgr = ActivityManager.getService();
		try {
			// 将 ApplicationThread 给到 AMS 作为句柄管理
			mgr.attachApplication(mAppThread, startSeq);
		} catch (RemoteException ex) {
			...
		}
		...
	}
	...
}

private class ApplicationThread extends IApplicationThread.Stub {
	// AMS 下发管理四大组件,将处理转发给变量名为 mH 的 Handler
	...
	
	@Override
	public void scheduleTransaction(ClientTransaction transaction) {
		ActivityThread.this.scheduleTransaction(transaction);
	}
}

class H extends Handler {
	// Activity 生命周期等消息的处理
	...
}

Dans la méthode main() d'ActivityThread, vous pouvez voir à partir du code source qu'un ApplicationThread est créé, puis cet objet est donné à AMS. Le cycle de vie de l'activité utilise en fait ApplicationThread comme handle et le donne à AMS. AMS peut utiliser ce handle pour émettre et gérer le cycle de vie de l'activité ; le même AMS gère également les quatre composants principaux et les autres traitements du processus via ApplicationThread. Dans ApplicationThread, le handler transmet le traitement piloté par message.

Avant de démarrer l'activité, vous devez d'abord commencer à créer l'application. AMS obtient ApplicationThread et appelle attachApplication() :

ActivityManagerService.java

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
                
    @Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            ...
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            ...
        }
    }

    @GuardedBy("this")
    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
		...
		if (app.isolatedEntryPoint != null) {
			...
		} else if (app.instr != null) {
		   // 通过 ApplicationThread 和 App 进程通信
           thread.bindApplication(processName, appInfo, providers,
                   app.instr.mClass,
                   profilerInfo, app.instr.mArguments,
                   app.instr.mWatcher,
                   app.instr.mUiAutomationConnection, testMode,
                   mBinderTransactionTrackingEnabled, enableTrackAllocation,
                   isRestrictedBackupMode || !normalMode, app.persistent,
                   new Configuration(getGlobalConfiguration()), app.compat,
                   getCommonServicesLocked(app.isolated),
                   mCoreSettingsObserver.getCoreSettingsLocked(),
                   buildSerial, isAutofillCompatEnabled);			
		} else {
			...	
		}
		...

        if (normalMode) {
            try {
            	// 创建完 Application 后走 Activity 生命周期流程
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }
	}    
}

ActivityThread.java

private class ApplicationThread extends IApplicationThread.Stub {

    public final void bindApplication(...) {
		...
		// 在 ActivityThread 也是获取的 AMS 数据创建 application
		AppBindData data = new AppBindData();
        data.processName = processName;
        data.appInfo = appInfo;
        data.providers = providers;
        data.instrumentationName = instrumentationName;
        data.instrumentationArgs = instrumentationArgs;
        data.instrumentationWatcher = instrumentationWatcher;
        data.instrumentationUiAutomationConnection = instrumentationUiConnection;
        data.debugMode = debugMode;
        data.enableBinderTracking = enableBinderTracking;
        data.trackAllocation = trackAllocation;
        data.restrictedBackupMode = isRestrictedBackupMode;
        data.persistent = persistent;
        data.config = config;
        data.compatInfo = compatInfo;
        data.initProfilerInfo = profilerInfo;
        data.buildSerial = buildSerial;
        data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
        sendMessage(H.BIND_APPLICATION, data); // 消息驱动发消息给到 Handler
	}
}
	
class H extends Handler {
	public static final int BIND_APPLICATION        = 110;
	...
	
	public void handleMessage(Message msg) {
		switch (mssg.what) {
			case BIND_APPLICATION:
			AppBindData data = (AppBindData) msg.obj;
			handleBindApplication(data);
			break;
			...
		}
	}	
}
	
private void handleBindApplication(AppBindData data) {
	...
	// 反射创建 Instrumentation 负责管理 application 和 activity 相关所有活动处理
	try {
		final ClassLoader cl = instrContext.getClassLoader();
		mInstrumentation = (Instrumentation)
			cl.loadClass(data.instrumentationName.getClassName()).newInstance();
	} catch (Exception e) {
		...
	}
	...
	try {
		// info 是 LoadedApk,反射创建 application 
		app = data.info.makeApplication(data.restrictedBackupMode, null);
		...
	}
	...
} 

public final class LoadedApk {

    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
		...
		// 创建 application
		Application app = null;
		try {
			ClassLoader cl = getClassLoader();
			ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
			app = mActivityThread.mInstrumentation.newApplication(
				cl, appClass, appContext);
		} catch (Exception e) {
			...
		}
		...
		if (instrumentation != null) {
			try {
				// 调用 application.onCreate()
				instrumentation.callApplicationOnCreate(app);
			} catch (Exception e) {
				...	
			}
		}
		...
	}	
}

attachApplication() fait en réalité trois choses :

  • Créer une instrumentation, responsable de la gestion de tous les traitements d'activité liés à l'application et à l'activité
  • Créer une application
  • Créer une activité et démarrer le processus du cycle de vie

Nous avons mentionné ci-dessus que le processus de création et de cycle de vie ultérieur de l'activité démarre à partir de la méthode realStartActivityLocked(). Continuons à analyser le processus de création d’Activité.

ActivityManagerService.java

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    final ActivityStackSupervisor mStackSupervisor;    
        
    @Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            ...
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            ...
        }
    }

    @GuardedBy("this")
    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
		...
		if (mStackSupervisor.attachApplicationLocked(app)) {
		}
		...
	}    
}

ActivityStackSupervisor.java

public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
        RecentTasks.Callbacks {
        
   	boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
		...
		if (realStartActivityLocked(activity, app, top == activity) {
		}
		...
	}	

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
		...
		// app.thread 是 ApplicationThread
		// Create activity launch transaction.
		final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
			r.appToken);
        clientTransaction.addCallback(LaunchActivityItem.obtain(...));	
        
        final ActivityLifecycleItem lifecycleItem;
        if (andResume) {
			lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
		} else {
			lifecycleItem = PauseActivityItem.obtain();
		}  
		// 执行完 LaunchActivityItem 事务后要处在的生命周期状态   				
		clientTransaction.setLifecycleStateRequest(lifecycleItem);
		
		// mService 是 AMS
		// Schedule transaction.
		mService.getLifecycleManager().scheduleTransaction(clientTransaction);
		...
	}	
}

ClientTransaction est une classe de gestion des transactions client, qui représente différents cycles de vie d'activité à travers différents états LaunchActivityItem, ResumeActivityItem, PauseActivityItem, etc., et est gérée état par état.

Il est mentionné ci-dessus qu'AMS gère le cycle de vie de l'activité via le handle ApplicationThread. Lors de la création de ClientTransaction.obtain(), vous pouvez également voir que le traitement du cycle de vie émis par ApplicationThread est transmis :

ClientTransaction.java

public class ClientTransaction implements Parcelable, ObjectPoolItem {
    private IApplicationThread mClient;

	// 记录事务结束后要处在的生命周期状态,后面会用到
    public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
        mLifecycleStateRequest = stateRequest;
    }

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

    public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
        ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
        if (instance == null) {
            instance = new ClientTransaction();
        }
        instance.mClient = client; // 存储的 ApplicationThread
        instance.mActivityToken = activityToken;

        return instance;
    }	
}

Continuez à analyser ce que fait mService.getLifecycleManager().scheduleTransaction(clientTransaction) :

ActivityManagerService.java

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
	
	ClientLifecycleManager getLifecycleManager() { return mLifecycleManager; }
}

class ClientLifecycleManager {
	
	void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
		final IApplicationThread client = transaction.getClient();
		transaction.schedule();
		...
	}
}

ClientTransaction.java

public class ClientTransaction implements Parcelable, ObjectPoolItem {
    private IApplicationThread mClient;

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this); // 拿着 ApplicationThread 下发通知
    }	
}

ActivityThread.java

private class ApplicationThread extends IApplicationThread.Stub {
    @Override
    public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    	// 在 ActivityThread 是找不到 scheduleTransaction 方法的
    	// 而是在 ActivityThread 的父类 ClientTransactionHandler 调用
        ActivityThread.this.scheduleTransaction(transaction);
    }	
}

AMS prend le handle ApplicationThread et demande à ActivityThread de créer une activité. Dans le code source d'ActivityThread, vous constaterez que le code ActivityThread.this.scheduleTransaction() est introuvable. En fait, il est placé dans la classe parent ClientTransactionHandler héritée par Fil d'activité.

ClientTransactionHandler.java

public abstract class ClientTransactionHandler {
	
	void scheduleTransaction(ClientTransaction transaction) {
		transaction.preExecute(this);
		// 通过 Handler 发了一条消息
		sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
	}
}

public final class ActivityThread extends ClientTransactionHandler {
	// TransactionExecutor的构造传入的 ActivityThread
    private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);	
	
	class H extends Handler {
		public static final int EXECUTE_TRANSACTION = 159;

		public void handleMessage(Message msg) {
			switch (msg.what) {
				case EXECUTE_TRANSACTION:
				final ClientTransaction transaction = (ClientTransaction) msg.obj;
				mTransactionExecutor.execute(transaction);
				break;
			}
		}
	}
}

TransactionExecutor.java

public class TransactionExecutor {
	// ActivityThread
	private ClientTransactionHandler mTransactionHandler;

    public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
        mTransactionHandler = clientTransactionHandler;
    }
	
	public void execute(ClientTransaction transaction) {
		...
		
		executeCallbacks(transaction); 

        executeLifecycleState(transaction);
		...	
	}

    public void executeCallbacks(ClientTransaction transaction) {
    	final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
		...
		final int size = callbacks.size();
		for (int i = 0; i < size; ++i) {
			final ClientTransactionItem item = callbacks.get(i);
			...
			// 执行事务,这里是设置了 LaunchActivityItem
			item.execute(mTransactionHandler, token, mPendingActions);
			...
		}
	}

	private void executeLifecycleState(ClientTransaction transaction) {
		final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
		...
		// 执行完事务后处在的生命周期状态,这里是 ResumeActivityItem
		lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
	}
}

Une fois que le gestionnaire a reçu le message, il démarre le traitement de la transaction et le TransactionExecutor est responsable du traitement de la transaction. Cela dépend principalement de deux fonctions :

  • executeCallbacks() : exécute des transactions spécifiques, telles que LaunchActivityItem
  • executeLifecycleState() : dans quel état se trouver après l'exécution de la transaction, tel que ResumeActivityItem, PauseActivityItem
LaunchActivityItem.java

public class LaunchActivityItem extends ClientTransactionItem {
	
	@Override
	public void execute(ClientTransactionHandler client, IBinder token,
		PendingTransactionActions pendingActions) {
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client);
        // 最终到 ActivityThread 开始创建 Activity
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);		
	}
}

ActivityThread.java

public final class ActivityThread extends ClientTransactionHandler {
	
	@Override
	public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
		...
		final Activity a = performLaunchActivity(r, customIntent);
		...
	}

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
		...
		// 通过 Instrumentation 反射创建 Activity
		ContextImpl appContext = createBaseContextForActivity(r);
		Activity activity = null;
		try {
			ClassLoader cl = appContext.getClassLoader();
			activity = mInstrumentation.newActivity(
				cl, component.getClassName(), r.intent);
			...
		}
		...
		// 调用 Activity 生命周期 onCreate()
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }		
	}
}

Un bref résumé de ce que fait realStartActivityLocked() :

ClientTransaction a été créé pour gérer la transaction d'exécution du cycle de vie.Il peut être considéré comme la classe d'exécution d'AMS qui gère spécifiquement le cycle de vie de l'activité. Différents cycles de vie sont représentés par différents états, le cycle de vie de onCreate() représenté par LaunchActivityItem et le cycle de vie de onResume() représenté par ResumeActivityItem. L'exécution spécifique de la transaction sera finalement envoyée au ActivityLifecycleItem correspondant, et les rappels seront être exécuté par ces classes d'état.Cycle de vie de l'activité.

À ce stade, l’analyse du processus de démarrage de l’application se termine ici.

Résumez le processus de démarrage de l'application globale :

  • Lorsque startActivity() est appelé, il ira en fait à l'instrumentation, qui communique avec AMS.
  • L'instrumentation trouvera le ServiceManager pour obtenir l'AMS (obtient en fait l'agent de liaison) et appellera startActivity(). Avant la version 9.0, vous bénéficiez de l'AMS, et après la version 9.0, vous obtenez l'ATMS.
  • AMS trouve PMS et obtient les informations sur l'activité démarrée
  • Déterminez ensuite si le processus de l'activité qui doit être démarrée existe déjà. S'il n'existe pas, AMS informe le processus fork de Zygote via le socket, puis appelle le main() de ActivityThread de manière réfléchie pour créer l'instrumentation, l'application et l'activité et suivre le processus du cycle de vie.
  • Si le processus de l'activité qui doit être démarrée existe déjà, créez l'activité et suivez le processus du cycle de vie

Le processus spécifique est le suivant :

le hook démarre une interface qui n'est pas enregistrée dans AndroidManifest.xml

Le code source d'AMS a été analysé, alors quels sont les scénarios d'application du code source d'AMS ?

Nous rencontrons souvent un scénario en entreprise. Si l'utilisateur n'est pas connecté, il passera à l'interface de connexion, et s'il est connecté, il passera à d'autres interfaces. Par conséquent, il y aura une écriture similaire à celle-ci :

if (isLogin) {
	startActivity(new Intent(context, LoginActivity.class);
} else {
	startActivity(new Intent(context, OtherActivity.class);
}

Lorsqu'un projet comporte un grand nombre de zones où il est nécessaire de déterminer le login puis de passer à l'interface, s'il y a des changements métier ultérieurs, comme la modification de l'interface de jump login, cette méthode de codage en dur est très intrusive, nécessite un un large éventail de modifications et n'est pas propice à la maintenance.

Peut-être direz-vous : je peux utiliser une intention implicite pour définir des actions dans AndroidManifest.xml, puis utiliser des classes constantes pour fournir une gestion unifiée des actions :

AndroidManifest.xml

<manifest>
	<application>
		<activity name="LoginActivity">
			<intent-filter>
				<action name="android.intent.action.login" />
			</intent-filter>
		</activity>	
	</application>
</manifest>

public class Constants {
	public static final String ACTION_LOGIN = "android.intent.action.login";
}

if (isLogin) {
	startActivity(new Intent(context, Constants.ACTION_LOGIN);
} else {
	startActivity(new Intent(context, OtherActivity.class);
}

Ensuite, j'ajouterai une autre condition : je souhaite démarrer l'interface de connexion normalement sans l'enregistrer dans AndroidManifest.xml. Est-ce possible ?

Cela n'est pas possible dans des circonstances normales, mais une fois familiarisés avec le code source d'AMS et de PMS, nous pouvons utiliser des hooks pour contourner la détection du système et démarrer une activité normalement.

Pour faire simple, le hook consiste à contourner le traitement du système et à obtenir le même effet de différentes manières.Cependant, ce processus nécessite toujours certaines informations créées par le système pour nous aider à terminer le traitement.

Mais il y a généralement trois prérequis pour utiliser les hooks :

  • Trouver le point d'accroche approprié : En Java, il s'agit généralement d'une variable membre statique ou d'une méthode membre. Les variables membres non statiques ne peuvent pas être accrochées car le point d'accroche sera obtenu par réflexion.
  • Compatibilité du hook : Hook utilisera l'API du code source du système, mais différentes versions du code source auront des points de hook et des méthodes de hook différents.
  • Familier avec les principes du code source : s'assurer que les processus système peuvent être correctement raccordés dans des conditions normales

Selon le cas mentionné au début où chaque saut nécessite une authentification, nous essayons d'utiliser des hooks pour écrire uniquement des sauts métier spécifiques dans le code, et passer automatiquement à l'interface de connexion sans nous connecter, sinon passer à l'interface métier spécifique. La démo sera implémentée sur le code source de l'API 28.

Il y a deux difficultés majeures pour obtenir cet effet :

  • Comment contourner la vérification lorsque vous n'êtes pas dans l'interface d'enregistrement AndroidManifest.xml ?
  • Après avoir contourné le contrôle, comment puis-je démarrer cette activité normalement ?

Pas dans l'interface d'enregistrement AndroidManifest.xml, il a été mentionné au début de l'article que l'analyse principale d'AMS est indissociable de PMS, car AMS demandera à PMS d'obtenir des informations sur l'activité pendant le processus de démarrage de l'activité, donc ce problème est plus spécifiquement comment le contourner.Inspection du PMS.

Pour obtenir cet effet, le hook peut intercepter startActivity(), remplacer les informations d'intention transportant l'activité cible et ajouter des informations supplémentaires à l'intention tout en conservant les données d'origine. De cette façon, le processus peut être exécuté normalement sous l’inspection du PMS.

Alors, où sont les crochets appropriés ? L'instrumentation a un processus pour communiquer avec AMS. Nous devons obtenir l'agent liant d'AMS. Ici, nous republions le code source :

ActivityManager.java

// 已经创建好的 IActivityManager,更具体说要拿到 mInstance,是一个很好的 hook 点
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

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; 
            }
        };

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;
		}
	}
}

Le hook ne crée pas complètement les informations par lui-même, mais utilise l'API du système ou les informations créées à l'avance. mInstance, qui est le IActivityManager, est l'objet que nous voulons utiliser. Nous devons intercepter son traitement lors de l'appel de startActivity() et remplace notre Intent. , qui nécessite l’utilisation d’agents dynamiques. le code s'affiche comme ci-dessous :

public class Hooker {

    public void hook() throws Exception {
        hookAms();
    }

    public void hookAms() throws Exception {
        if (proxyActivity == null) {
            throw new NullPointerException("proxyActivity is null");
        }

        Class ActivityManagerClz = Class.forName("android.app.ActivityManager");
        Field IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");
        IActivityManagerSingletonField.setAccessible(true);
        // 获取到 ActivityManager 的 IActivityManagerSingleton 内部静态成员变量
        Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);

        Class SingletonClz = Class.forName("android.util.Singleton");
        Field mInstanceField = SingletonClz.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        // 拿到 ActivityManagerService 的 binder 代理
        // 做这一步是为了绕过 AMS 的鉴权,因为要设置的 LoginActivity 也是没有在 AndroidManifest.xml 注册,也就是要绕过 PMS
        Object IActivityManagerObj = mInstanceField.get(IActivityManagerSingletonObj);

        Class IActivityManagerClz = Class.forName("android.app.IActivityManager");
        Object proxyIActivityManager = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[] {IActivityManagerClz}, new AmsInvocationHandler(IActivityManagerObj));
        // 将 IActivityManagerSingleton 的 mInstance 成员替换为我们自己的代理对象
        // 将 mInstance 的调用都跑我们的代理
        mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);
    }

    private class AmsInvocationHandler implements InvocationHandler {
        private final Object iActivityManagerObject;

        public AmsInvocationHandler(Object iActivityManagerObject) {
            this.iActivityManagerObject = iActivityManagerObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 系统使用 mInstance 的调用都会走到代理
            if ("startActivity".contains(method.getName())) {
                Intent intent = null;
                int index = 0;
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if (arg instanceof Intent) {
                        intent = (Intent) args[i];
                        index = i;
                        break;
                    }
                }
                if (intent != null) {
                    Intent proxyIntent = new Intent();
                    ComponentName componentName = new ComponentName(context, proxyActivity);
                    proxyIntent.setComponent(componentName);
                    proxyIntent.putExtra("oldIntent", intent);
                    args[index] = proxyIntent; // 替换真实意图
                }
            }
            return method.invoke(iActivityManagerObject, args);
        }
    }
}

Le problème du contournement du PMS a été résolu. Vient ensuite la deuxième question : Comment démarrer l'activité normalement ?

Le processus de création et de cycle de vie d'Activity est tous terminé sous le pilote de message Handler. Le code source spécifique se trouve dans la variable membre mH d'ActivityThread :

ActivityThread.java

final H mH = new H();

class H extends Handler {
	public static final int EXECUTE_TRANSACTION = 159;

	public void handleMessage(Message msg) {
		switch (msg.what) {
			case EXECUTE_TRANSACTION:
			final ClientTransaction transaction = (ClientTransaction) msg.obj;
			mTransactionExecutor.execute(transaction);
			break;
		}
	}
}

Malheureusement, le message handleMessage() ne peut pas être intercepté dans ActivityThread.

En fait, Handler nous a déjà fourni des points d’accroche :

Handler.java

final Callback mCallback;

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
        	// 如果 mCallback 不为空,先处理 mCallback 的 handleMessage()
        	// 如果 mCallback 的 handleMessage() 返回 false,调用兜底的 handleMessage()
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Nous pouvons configurer notre propre mCallback, afin que le message puisse être intercepté et traité à l'avance avant l'appel de handleMessage(). Le message qui doit être intercepté retournera vrai et ne sera pas livré. Sinon, il retournera faux et procéder normalement sans affecter le processus de démarrage.

Le code complet est le suivant :

public class Hooker {
    private final Context context;
    private Class<?> proxyActivity;

    public Hooker(@NonNull Context context) {
        this.context = context;
    }

    public void setProxyActivity(@NonNull Class<?> proxyActivity) {
        this.proxyActivity = proxyActivity;
    }

    public void hook() throws Exception {
        hookAms();
        hookSystemHandler();
    }

    public void hookAms() throws Exception {
        if (proxyActivity == null) {
            throw new NullPointerException("proxyActivity is null");
        }

        Class ActivityManagerClz = Class.forName("android.app.ActivityManager");
        Field IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");
        IActivityManagerSingletonField.setAccessible(true);
        // 获取到 ActivityManager 的 IActivityManagerSingleton 内部静态成员变量
        Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);

        Class SingletonClz = Class.forName("android.util.Singleton");
        Field mInstanceField = SingletonClz.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        // 拿到 ActivityManagerService 的 binder 代理
        // 做这一步是为了绕过 AMS 的鉴权,因为要设置的 LoginActivity 也是没有在 AndroidManifest.xml 注册,也就是要绕过 PMS
        Object IActivityManagerObj = mInstanceField.get(IActivityManagerSingletonObj);

        Class IActivityManagerClz = Class.forName("android.app.IActivityManager");
        Object proxyIActivityManager = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[] {IActivityManagerClz}, new AmsInvocationHandler(IActivityManagerObj));
        // 将 IActivityManagerSingleton 的 mInstance 成员替换为我们自己的代理对象
        // 将 mInstance 的调用都跑我们的代理
        mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);
    }

    public void hookSystemHandler() throws Exception {
        Class ActivityThreadClz = Class.forName("android.app.ActivityThread");
        Field field = ActivityThreadClz.getDeclaredField("sCurrentActivityThread");
        field.setAccessible(true);
        Object ActivityThreadObj = field.get(null);

        Field mHField = ActivityThreadClz.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mHObj = (Handler) mHField.get(ActivityThreadObj);

        Field mCallbackField = Handler.class.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        ProxyHandlerCallback proxyCallback = new ProxyHandlerCallback();
        mCallbackField.set(mHObj, proxyCallback);
    }

    private class ProxyHandlerCallback implements Handler.Callback {
		public static final int EXECUTE_TRANSACTION = 159;
		
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == EXECUTE_TRANSACTION) {
                try {
                    Class<?> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction");
                    if (!ClientTransactionClz.isInstance(msg.obj)) {
                        return false;
                    }

                    Class<?> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem");

                    Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");
                    mActivityCallbacksField.setAccessible(true);
                    Object mActivityCallbacksObj = mActivityCallbacksField.get(msg.obj);
                    List list = (List) mActivityCallbacksObj;
                    if (list.size() == 0) {
                        return false;
                    }
                    
                    Object LaunchActivityItemObj = list.get(0);
                    if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) {
                        return false;
                    }

                    Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent");
                    mIntentField.setAccessible(true);
                    Intent mIntent = (Intent) mIntentField.get(LaunchActivityItemObj);
                    Intent realIntent = mIntent.getParcelableExtra("oldIntent");
                    if (realIntent != null) {
                        SharedPreferences sp = context.getSharedPreferences("name", MODE_PRIVATE);
                        boolean isLogin = sp.getBoolean("isLogin", false);
                        if (isLogin) {
                            mIntent.setComponent(realIntent.getComponent());
                        } else {
                            ComponentName componentName = new ComponentName(context, LoginActivity.class);
                            mIntent.putExtra("extraIntent", realIntent.getComponent().getClassName()); // 提供给登陆界面登陆成功后跳转到哪个目标页面
                            mIntent.setComponent(componentName);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return false; // 返回 false 不影响系统的执行
        }
    }

    private class AmsInvocationHandler implements InvocationHandler {
        private final Object iActivityManagerObject;

        public AmsInvocationHandler(Object iActivityManagerObject) {
            this.iActivityManagerObject = iActivityManagerObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 系统使用 mInstance 的调用都会走到代理
            if ("startActivity".contains(method.getName())) {
                Intent intent = null;
                int index = 0;
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if (arg instanceof Intent) {
                        intent = (Intent) args[i];
                        index = i;
                        break;
                    }
                }
                if (intent != null) {
                    Intent proxyIntent = new Intent();
                    ComponentName componentName = new ComponentName(context, proxyActivity);
                    proxyIntent.setComponent(componentName);
                    proxyIntent.putExtra("oldIntent", intent);
                    args[index] = proxyIntent; // 替换真实意图
                }
            }
            return method.invoke(iActivityManagerObject, args);
        }
    }
}

Cela complétera notre effet. Écrivons une démo pour tester :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.demo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Demo">
        <activity android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- 没有注册 LoginActivity -->
        <activity android:name=".Page1Activity" />
        <activity android:name=".Page2Activity" />
    </application>
</manifest>

public class MainActivity extends AppCompatActivity {
    private Hooker hooker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        hooker = new Hooker(this);
    }

    public void logout(View view) {
        SharedPreferences sp = getSharedPreferences("name", MODE_PRIVATE);
        sp.edit().putBoolean("isLogin", false).apply();
    }

    public void startPage1(View view) {
        hooker.setProxyActivity(Page1Activity.class);
        try {
            hooker.hook();
        } catch (Exception e) {
            e.printStackTrace();
        }
        startActivity(new Intent(this, Page1Activity.class));
    }

    public void startPage2(View view) {
        hooker.setProxyActivity(Page2Activity.class);
        try {
            hooker.hook();
        } catch (Exception e) {
            e.printStackTrace();
        }
        startActivity(new Intent(this, Page2Activity.class));
    }
}

public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }

    public void login(View view) throws ClassNotFoundException {
        SharedPreferences sp = getSharedPreferences("name", MODE_PRIVATE);
        sp.edit().putBoolean("isLogin", true).apply();

        String className = getIntent().getStringExtra("extraIntent");
        if (!TextUtils.isEmpty(className)) {
            startActivity(new Intent(this, Class.forName(className)));

            finish();
        }
    }
}

Je suppose que tu aimes

Origine blog.csdn.net/qq_32907491/article/details/132796412
conseillé
Classement