Previous Article:

# Android 10 Startup Analysis of servicemanager part 2

app_main

Zygote starts the Zygote service in an Action Trigger (late init). Zygote starts the Zygote service in Action Trigger (late init).

Zygote service start source is located in the entrance/frameworks/base/CMDS/app_process/app_main CPP, we’ll start with the main method of this file parsing.

int main(int argc, Char * const argv[]) {// Declare the runtime of the AppRuntime class, the runtime of the skia graphics engine AppRuntime initialized in the constructor of the AppRuntime class (argv[0], computeArgBlockSize(argc, argv)); . bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { zygote = true; // For 64-bit systems nice_name is zygote64; For a 32-bit system, zygote niceName = ZYGOTE_NICE_NAME. } else if (STRCMP (arg, "--start-system-server") == 0) {if (STRCMP (arg, "--start-system-server") == 0) { } else if (STRCMP (arg, "--application") == 0) { } else if (STRNCMP (arg, "--nice-name=", 12) == 0) {if (STRNCMP (arg, "--nice-name=", 12) == 0) { } else if (strncmp(arg, "--", 2) ! = 0) { className.setTo(arg); break; } else { --i; break; }}... }Copy the code

As you can see, app_main is divided into Zygote mode and application mode according to the difference of parameters passed in at startup.

Zygote starts with the following parameters: zygote64_32.rc

-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
Copy the code

Let’s move on:

Vector<String8> args; if (! className.isEmpty()) { // We're not in zygote mode, the only argument we need to pass // to RuntimeInit is the application argument. // // The Remainder of args get passed to startup class main(). Make // copies of them before we overwrite them with the process name. args.add(application ? String8("application") : String8("tool")); runtime.setClassNameAndArgs(className, argc - i, argv + i); if (! LOG_NDEBUG) { String8 restOfArgs; char* const* argv_new = argv + i; int argc_new = argc - i; for (int k = 0; k < argc_new; ++k) { restOfArgs.append("""); restOfArgs.append(argv_new[k]); restOfArgs.append("" "); } ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string()); }} else {// We're in zygote mode. If (startSystemServer) {// Append arg args.add(String8("start-system-serve argr")); } char prop[PROP_VALUE_MAX]; if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) { LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.", ABI_LIST_PROPERTY); return 11; } String8 abiFlag("--abi-list="); abiFlag.append(prop); args.add(abiFlag); // In zygote mode, pass all remaining arguments to the zygote // main() method. for (; i < argc; ++i) { args.add(String8(argv[i])); } } if (! niceName.isEmpty()) { runtime.setArgv0(niceName.string(), true /* setProcName */); } the if (zygote) {/ / branch into the runtime. Start (" com. Android. Internal. OS. ZygoteInit ", args, zygote); } else if (className) { runtime.start("com.android.internal.os.RuntimeInit", args, zygote); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); }Copy the code

With the startup arguments passed in, the code continues from the else branch of the if statement into Zygote mode. As for the application mode, we’ll ignore it for now and explain it when we analyze the startup process of the app.

The above code will last through the runtime. Start (” com. Android. Internal. OS. ZygoteInit “, args, zygote); Statement that transfers control to the AppRuntime class to continue execution.

Continue with the start function for AppRuntime:

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... Jni_invocation jni_Invocation; // Invocation invocation jni_Invocation. jni_invocation.Init(NULL); JNIEnv* env; if (startVm(&mJavaVM, &env, zygote) ! = 0) { return; } onVmCreated(env); // Register JNI method if (startReg(env) < 0) {ALOGE("Unable to register all Android natives\n"); return; } /* * We want to call main() with a String array with arguments in it. * At present we have two arguments, the class name and an option string. * Create an array to hold them. */ jclass stringClass; jobjectArray strArray; jstring classNameStr; / / equivalent to the strArray [0] = "com. Android. Internal. OS. ZygoteInit" stringClass = env - > FindClass (" Java/lang/String "); assert(stringClass ! = NULL); strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); assert(strArray ! = NULL); classNameStr = env->NewStringUTF(className); assert(classNameStr ! = NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); / / strArray [1] = "start - system - server"; / / strArray [2] = "-- abi - list = XXX"; For (size_t I = 0; i < options.size(); ++i) { jstring optionsStr = env->NewStringUTF(options.itemAt(i).string()); assert(optionsStr ! = NULL); env->SetObjectArrayElement(strArray, i + 1, optionsStr); } /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ / / will "com. Android. Internal. OS. ZygoteInit" into "com/android/internal/OS/ZygoteInit char * slashClassName = toSlashClassName(className ! = NULL ? className : ""); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); JmethodID startMeth = env->GetStaticMethodID(startClass); "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", Env ->CallStaticVoidMethod(startClass, startMeth,); /* keep going */} else {// call zygoteinit.main () env->CallStaticVoidMethod(startMeth, startMeth, strArray); #if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env); #endif } } free(slashClassName); ALOGD("Shutting down VM\n"); if (mJavaVM->DetachCurrentThread() != JNI_OK) ALOGW("Warning: unable to detach main thread\n"); if (mJavaVM->DestroyJavaVM() != 0) ALOGW("Warning: VM did not shut down cleanly\n"); }Copy the code

The start() function does three things: it calls startVm to start the vm, calls startReg to register the JNI method, and starts the Zygote process using JNI.

ZygoteInit

With the above analysis, the code goes to the main method in zygoteinit.java to continue execution. From here, the Zygote process is actually started. We from/frameworks/base/core/Java/com/android/internal/OS/ZygoteInit. Java this file to continue to look down.

Public static void main(String argv[]) {//ZygoteServer ZygoteServer = null; Start, the / / tag zygote invoked ZygoteHooks Jni methods, to ensure that no other threads run in the current ZygoteHooks. StartZygoteNoThreadCreation (); // Set pid to 0, Zygote enter its own process group try {os.setpgid (0, 0); } catch (ErrnoException ex) {throw new RuntimeException("Failed to setpgid(0,0)", ex); } Runnable caller; try { ... // Enable the Dalvik Debug Monitor Service (DDMS) runtimeinit.enableddms (); // Parse the argument passed to app_main.cpp -start () Boolean startSystemServer = false; String zygoteSocketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; for (int i = 1; i < argv.length; I ++) {if ("start-system-server".equals(argv[I])) {if ("start-system-server".equals(argv[I])) { StartSystemServer = true; } else if ("--enable-lazy-preload". Equals (argv[I])) { enable-lazy-preload 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)) {//SOCKET_NAME_ARG zygote or zygote_secondary Zyoget64_32.rc zygoteSocketName = argv[I]. Substring (socket_name_arg.length ()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); Zygote_secondary Final Boolean isPrimaryZygote = // Create the socket based on the incoming socket name zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME); if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, We will preload things prior to our first fork. // enableLazyPreload is false, preload if (! enableLazyPreload) { bootTimingsTraceLog.traceBegin("ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); Preload (bootTimingsTraceLog); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload } else { Zygote.resetNicePriority(); } // Do an initial gc to clean up after startup bootTimingsTraceLog.traceBegin("PostZygoteInitGC"); gcAndFinalize(); bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC bootTimingsTraceLog.traceEnd(); // ZygoteInit // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false, 0); Zygote.initNativeState(isPrimaryZygote); ZygoteHooks.stopZygoteNoThreadCreation(); // Call the ZygoteServer constructor to create the socket Server, which creates two sockets based on the arguments passed in: /dev/socket/zygote and /dev/socket/zygote_secondary zygoteServer = new zygoteServer (isPrimaryZygote); If (startSystemServer) {// Fork the system Server process Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r ! = null} in the // child (system_server) process. if (r ! = null) {// Start SystemServer r.run(); return; } } Log.i(TAG, "Accepting command socket connections"); / / ZygoteServer into an infinite loop, processing requests caller = ZygoteServer. RunSelectLoop (abiList); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); throw ex; } finally { if (zygoteServer ! = null) {4 zygoteServer.closeServerSocket(); } } // We're in the child process and have exited the select loop. Proceed to execute the // command. if (caller ! = null) { caller.run(); }}Copy the code

The main method does the following:

  1. Load resources and classes for the process.
  2. Create a Socket Server based on the incoming socket name.
  3. Fork SystemServer process.

preload

Since the preload method is the resource and class responsible for loading the process, what resources and classes are loaded, and where are those resources located?

Let’s look at what the preload method does:

static void preload(TimingsTraceLog bootTimingsTraceLog) { beginPreload(); // preloadClasses(); cacheNonBootClasspathClassLoaders(); // preloadResources(); / / load the HAL nativePreloadAppProcessHALs related content (); / / loading graphics driver maybePreloadGraphicsDriver (); // Load android, Compiler_rt, Jnigraphics library preloadSharedLibraries(); PreloadTextResources (); // To initialize the webView; WebViewFactory.prepareWebViewInZygote(); endPreload(); warmUpJcaProviders(); sPreloadComplete = true; }Copy the code

preloadClasses

private static void preloadClasses() { final VMRuntime runtime = VMRuntime.getRuntime(); // the path to preload classes is /system /etc/preload-classes InputStream is; try { is = new FileInputStream(PRELOADED_CLASSES); } catch (FileNotFoundException e) { Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + "."); return; }... try { BufferedReader br = new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE); int count = 0; String line; while ((line = br.readLine()) ! = null) { // Skip comments and blank lines. line = line.trim(); if (line.startsWith("#") || line.equals("")) { continue; } Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line); Try {// Initialize Class. ForName (line, true, null) with Class. count++; } catch (ClassNotFoundException e) { Log.w(TAG, "Class not found for preloading: " + line); } catch (UnsatisfiedLinkError e) { Log.w(TAG, "Problem preloading " + line + ": " + e); } catch (Throwable t) { Log.e(TAG, "Error preloading " + line + ".", t); if (t instanceof Error) { throw (Error) t; } if (t instanceof RuntimeException) { throw (RuntimeException) t; } throw new RuntimeException(t); } Trace.traceEnd(Trace.TRACE_TAG_DALVIK); } } catch (IOException e) { Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e); } finally { ... }}Copy the code

As you can see, the preloadClasses method reads the contents of the /system/etc/ preload-classes file and initializes the Class with class.forname. /system/etc/preloaded-classes

Because it’s too much, I’ll just take some screenshots to show you what classes are loaded.

From the load list, we can see that many of the familiar classes are, in fact, Java classes that we might use when our application runs.

preloadResources

private static void preloadResources() { final VMRuntime runtime = VMRuntime.getRuntime(); try { mResources = Resources.getSystem(); mResources.startPreloading(); if (PRELOAD_RESOURCES) { Log.i(TAG, "Preloading resources..." ); long startTime = SystemClock.uptimeMillis(); . / / loading com. Android. Internal state Richard armitage rray. Preloaded_drawables image resources in TypedArray ar = mResources. ObtainTypedArray ( com.android.internal.R.array.preloaded_drawables); int N = preloadDrawables(ar); ar.recycle(); Log.i(TAG, "... preloaded " + N + " resources in " + (SystemClock.uptimeMillis() - startTime) + "ms."); startTime = SystemClock.uptimeMillis(); . / / loading com. Android. Internal state Richard armitage rray. The color of preloaded_color_state_lists resources ar = mResources. ObtainTypedArray ( com.android.internal.R.array.preloaded_color_state_lists); N = preloadColorStateLists(ar); ar.recycle(); Log.i(TAG, "... preloaded " + N + " resources in " + (SystemClock.uptimeMillis() - startTime) + "ms."); if (mResources.getBoolean( com.android.internal.R.bool.config_freeformWindowManagement)) { startTime = SystemClock.uptimeMillis(); . / / loading com. Android. Internal state Richard armitage rray. Pictures of preloaded_freeform_multi_window_drawables resources ar = mResources. ObtainTypedArray ( com.android.internal.R.array.preloaded_freeform_multi_window_drawables); N = preloadDrawables(ar); ar.recycle(); Log.i(TAG, "... preloaded " + N + " resource in " + (SystemClock.uptimeMillis() - startTime) + "ms."); } } mResources.finishPreloading(); } catch (RuntimeException e) { Log.w(TAG, "Failure preloading resources", e); }}Copy the code

As you can see from the above code, preloadResources loads specific image resources and color resources. Where exactly are the paths of these resources?

Com. Android. Internal. State Richard armitage rray. Preloaded_drawables paths in the/frameworks/base/core/res/res/values/arrays. XML, other resource path can be similar to find. You can go to this path to see what the included resource files look like.

preloadSharedLibraries

private static void preloadSharedLibraries() { Log.i(TAG, "Preloading shared libraries..." ); System.loadLibrary("android"); System.loadLibrary("compiler_rt"); System.loadLibrary("jnigraphics"); }Copy the code

Run the preloadSharedLibraries command to load the libandroid.so, libcompiler_rt.so and libjnigraphecs.so libraries in the /system/lib directory.


Why does Android preload resources in Zygote, and what are the benefits?

This question is left to the reader to think about for himself and will not be answered here.

forkSystemServer

private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) { ... Args [] = {"--setuid=1000", "--setgid=1000", "- setgroups = 1001100 2100 3100 4100 5100 6100 7100 8100 9101 0101 8102 1102 3," + "1024103 2106 5300 1300 2300 3300 6300 7300 9301 0" and "-" capabilities = "+ +", "+" capabilities "capabilities, "--nice-name=system_server", "--runtime-args", "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT, "com.android.server.SystemServer", }; ZygoteArguments parsedArgs = null; int pid; ParsedArgs = new ZygoteArguments(args); parsedArgs = new ZygoteArguments(args); Zygote.applyDebuggerSystemProperty(parsedArgs); Zygote.applyInvokeWithSystemProperty(parsedArgs); boolean profileSystemServer = SystemProperties.getBoolean( "dalvik.vm.profilesystemserver", false); if (profileSystemServer) { parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER; } //fork the systemServer child, Fork = Zygote. ForkSystemServer (Parsedargs.muid, com_android_internal_os_Zygote. CPP) parsedArgs.mGid, parsedArgs.mGids, parsedArgs.mRuntimeFlags, null, parsedArgs.mPermittedCapabilities, parsedArgs.mEffectiveCapabilities); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } if (pid == 0) {//pid == 0, If (hasSecondZygote(abiList)) {// If (hasSecondZygote(abiList)) {waitForSecondaryZygote(socketName); } / / fork will copy the socket, the system server. Need to take the initiative to close zygoteServer closeServerSocket (); / / loading system server related logic return handleSystemServerProcess (parsedArgs); } return null; }Copy the code

A Zygote forkSystemServer method just fork of the child, and handleSystemServerProcess method constructs a Runnable object, create a child thread for launching SystemServer logic.

private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) { Os.umask(S_IRWXG | S_IRWXO); if (parsedArgs.mNiceName ! = null) {// Nicename is system_server process.setargv0 (parsedargs.mnicename); }... if (parsedArgs.mInvokeWith ! = null) { String[] args = parsedArgs.mRemainingArgs; // If we have a non-null system server class path, we'll have to duplicate the // existing arguments and append the classpath to it. ART will handle the classpath // correctly when we exec a new process. if (systemServerClasspath ! = null) { String[] amendedArgs = new String[args.length + 2]; amendedArgs[0] = "-cp"; amendedArgs[1] = systemServerClasspath; System.arraycopy(args, 0, amendedArgs, 2, args.length); args = amendedArgs; } WrapperInit.execApplication(parsedArgs.mInvokeWith, parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion, VMRuntime.getCurrentInstructionSet(), null, args); throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else {/ / parsedArgs mInvokeWith is null, will enter the branch createSystemServerClassLoader (); ClassLoader cl = sCachedSystemServerClassLoader; if (cl ! = null) { Thread.currentThread().setContextClassLoader(cl); } /* * Pass the remaining arguments to SystemServer. */ return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion, parsedArgs.mRemainingArgs, cl); } /* should never reach here */ }Copy the code

Zygoteinit.zygoteinit:

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) { ... RuntimeInit.commonInit(); / / / / android_internal_os_ZygoteInit_nativePreloadAppProcessHALs registered two jni function //android_internal_os_ZygoteInit_nativePreloadGraphicsDriver ZygoteInit.nativeZygoteInit(); return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); }Copy the code

RuntimeInit. ApplicationInit:

protected static Runnable applicationInit(int targetSdkVersion, String[] argv, This this) {/ / true representative application exits. Don't call AppRuntime onExit (), or you will call before exit nativeSetExitWithoutCleanup (true); / / set the virtual machine memory utilization parameter value of 0.75 VMRuntime. GetRuntime (). SetTargetHeapUtilization (0.75 f); VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args = new Arguments(argv); // Remaining arguments are passed to the start class's static main return findStaticMain(args.startClass, args.startArgs, classLoader); }Copy the code

Continue to see findStaticMain:

protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<? > cl; Try {/ / the className here for com. Android. Server SystemServer cl = Class. Class.forname (className, true, this); } catch (ClassNotFoundException ex) { throw new RuntimeException( "Missing class when invoking static main " + className, ex); } Method m; try { m = cl.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException ex) { throw new RuntimeException( "Missing static main on " + className, ex); } catch (SecurityException ex) { throw new RuntimeException( "Problem getting static main on " + className, ex); } int modifiers = m.getModifiers(); if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { throw new RuntimeException( "Main method is not public  and static on " + className); } /* * This throw gets caught in ZygoteInit.main(), which responds * by invoking the exception's run() method. This arrangement * clears up all the stack frames that were required in setting * up the process. */ return new MethodAndArgsCaller(m, argv); }Copy the code

Here by reflecting the com. Android. Server. The main method of SystemServer class, and passed to MethodAndArgsCaller is used to construct a Runnable. As long as the Runnable is executed, will start to call com. Android. Server SystemServer main method in the class.

At this point, Zygote logic has been all executed, Android started into the SystemServer stage.

Finally, we summarize Zygote’s business logic with a flow chart:

sequenceDiagram participant app_main; participant AppRuntime; participant ZygoteInit; participant ZygoteInit; app_main->> AppRuntime : Enter Zygote mode AppRuntime ->> AppRuntime: Create and start Dalvik AppRuntime ->> AppRuntime: register Jni method AppRuntime ->> ZygoteInit: ZygoteInit ->> ZygoteInit: Zygote creates socket Server ZygoteInit ->> ZygoteInit: ZygoteInit ->> ZygoteInit ->> ZygoteInit ->> ZygoteInit ->> ZygoteInit

Next post:

# Android 10 Startup Analysis SystemServer part 4