The opening

This article uses Android – 11.0.0_R25 as the basic analysis

Last article Android source analysis – init process, we analyzed the Android first user process init process startup process and after the daemon service

The init process starts many services, such as Zygote, ServiceManager, MediaServer, SurfaceFlinger, etc. Zygote process starts analysis

introduce

Zygote means Zygote, and it has two main functions, one is to start SystemServer, and the other is to incubate and start the App

Start the service

We will already know the init process from init. Rc file parsing and start the service, where the zygote is defined, the init. The first few lines of the rc one import, an import/system/etc/init/hw/init. ${ro. Zygote}. The rc

We can find several corresponding files in the same directory as init.rc: Init. Zygote32_64. Rc init. Zygote32. Rc init. Zygote64_32. Rc init. Zygote64. Rc, specific import which files related to specific hardware, 64 mobile phones so popular now, Let’s take init.zygote64.rc as our target

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    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
Copy the code

App_process64 — Xzygote /system/bin –zygote –start-system-server

Zygote is started from frameworks/base/ CMDS /app_process/app_main.cpp

int main(int argc, char* const argv[])
{...// Create an AppRuntime that inherits from AndroidRuntime and overrides some callback methods
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    // When starting the service, the parameter passed in contains the file path
    // We don't need this parameter, just subtract the number and move the pointerargc--; argv++; .// Process parameters, where only one -xzygote parameter is added
    int i;
    for (i = 0; i < argc; i++) {
        ...
        if (argv[i][0] != The '-') {
            break;
        }
        if (argv[i][1] = =The '-' && argv[i][2] = =0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i])); . }// Parse runtime arguments. Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    // Skip the /system/bin argument, which is not currently in use
    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") = =0) {
            // There are --zygote parameters
            zygote = true;
            //ZYGOTE_NICE_NAME is zygote64 in 64-bit mode and zygote in 32-bit mode
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") = =0) {
            // There is a -start-system-server parameter
            startSystemServer = true;
        } else if (strcmp(arg, "--application") = =0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=".12) = =0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--".2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if(! className.isEmpty()) {
        // This branch will not enter Zygote mode. }else {
        // We're in zygote mode.
        // Create a Dalvik cache directory
        maybeCreateDalvikCache(a);// Add startup parameters
        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        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.
        // There are no other parameters in Zygote mode
        for (; i < argc; ++i) {
            args.add(String8(argv[i])); }}if(! niceName.isEmpty()) {
        // Set the program name and process name
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) {
        // Execute AndroidRuntime::start method
        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(a);LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); }}Copy the code

AndroidRuntime::start = AndroidRuntime::start = AndroidRuntime::start = AndroidRuntime::start The second parameter passes some options (start-system-server and ABi-list), and the third parameter passes true, which means additional JVM parameters need to be added when starting the virtual machine

AndroidRuntime::start

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{...static const String8 startSystemServer("start-system-server");
    // Whether this is the primary zygote, meaning the zygote which will fork system server.
    // Two Zygotes will be started on 64_32-bit compatible devices, one named Zygote and the other named Zygote_secondary
    bool primary_zygote = false;

    // With the start-system-server option, Zygote is the primary Zygote
    for (size_t i = 0; i < options.size(a); ++i) {if (options[i] == startSystemServer) {
            primary_zygote = true;
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); }}// Check and configure some environment variables./* start the virtual machine */
    / / load libart. So
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    
    JNIEnv* env;
    / / start the JVM
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) ! =0) {
        return;
    }
    // Callback the method overridden in AppRuntime
    onVmCreated(env);

    /* * Register android functions. */
    // Register the Android JNI function
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    // Create a Java layer String array to hold the parameters
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass ! =NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray ! =NULL);
    / / the first parameter is the name of the class. Com android. Internal. OS. ZygoteInit
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr ! =NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    // The remaining parameters are start-system-server and ABi-list respectively
    for (size_t i = 0; i < options.size(a); ++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. */
    // Replace the "." in Java class names with "/", which is the class name rule in JNI
    char* slashClassName = toSlashClassName(className ! =NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        // Get the main method in ZygoteInit. The argument is String and the return value is void
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            // Execute ZygoteInit's main method
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

            // The following code will not execute unless the JVM hangs. }}... }Copy the code

First check if the option carries the start-system-server parameter, if so, treat it as the primary Zygote, and then start the JVM

Start the JVM

JniInvocation

Use JniInvocation initialization Android virtual machine environment ART, it is the path to the libnativehelper/include_platform/nativehelper/JniInvocation. H, we look at how it is

Let’s first look at its constructor

/* libnativehelper/include_platform/nativehelper/JniInvocation.h */
class JniInvocation final {
  public:
    JniInvocation() {
        impl_ = JniInvocationCreate(a); } ~JniInvocation() {
        JniInvocationDestroy(impl_); }... }Copy the code

A JniInvocationImpl instance object is created by calling the JniInvocationCreate method

JniInvocationImpl* JniInvocationCreate(a) {
    return new JniInvocationImpl(a); }Copy the code

The JniInvocation::Init method is then called

bool Init(const char* library) {
    return JniInvocationInit(impl_, library) ! =0;
}

int JniInvocationInit(JniInvocationImpl* instance, const char* library) {
    return instance->Init(library) ? 1 : 0;
}
Copy the code

As you can see, JniInvocation is actually a proxy class, the internal implementation is to JniInvocationImpl, path for libnativehelper/JniInvocation. CPP

bool JniInvocationImpl::Init(const char* library) {...// All non-debug is libart.so
  library = GetLibrary(library, buffer);
  // Load libart.so
  handle_ = OpenLibrary(library);
  if (handle_ == NULL) {
    // If loading libart.so fails, return false
    if (strcmp(library, kLibraryFallback) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library, GetError().c_str());
      return false; }...// If loading another library fails, try backloading the libart.so library
    library = kLibraryFallback;
    handle_ = OpenLibrary(library);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, GetError().c_str());
      return false; }}// Get three JVM-related function addresses from the libart.so library
  if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}
Copy the code

Loading libart. So libraries

Let’s start with the GetLibrary method

static const char* kLibraryFallback = "libart.so";

const char* JniInvocationImpl::GetLibrary(const char* library,
                                          char* buffer,
                                          bool (*is_debuggable)(),
                                          int (*get_library_system_property)(char* buffer)) {
#ifdef __ANDROID__
  const char* default_library;

  if (!is_debuggable()) {
    library = kLibraryFallback;
    default_library = kLibraryFallback;
  } else{... }#else.const char* default_library = kLibraryFallback;
#endif
  if (library == NULL) {
    library = default_library;
  }

  return library;
}
Copy the code

As you can see, libart.so is returned directly in debug mode or when the library parameter is NULL

The OpenLibrary method uses the dlopen function to load the libart.so library

void* OpenLibrary(const char* filename) {...const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
  return dlopen(filename, kDlopenFlags); . }Copy the code

dlopen

Void *dlopen(const char *filename, int flags);

Documents: man7.org/linux/man-p…

This is a Linux function that loads a dynamically linked library and returns a handle on successful loading

RTLD_NOW means calculate library dependencies immediately, RTLD_NODELETE means do not unload the library during DLCLOSE so that the static global variables of the object are not reinitialized when the library is reloaded. This flag is used to ensure that libart.so is not unmapped when closed. Because even after the JNI_DeleteJavaVM call, some threads may not have finished exiting, which could result in a segment error if the library is uninstalled

Find the function address from the libart.so library

The FindSymbol function is then called to find the function address

#define FUNC_POINTER void*

bool JniInvocationImpl::FindSymbol(FUNC_POINTER* pointer, const char* symbol) {
  // Get the function address
  *pointer = GetSymbol(handle_, symbol);
  // Unmount libart.so library
  if (*pointer == NULL) {
    ALOGE("Failed to find symbol %s: %s\n", symbol, GetError().c_str());
    CloseLibrary(handle_);
    handle_ = NULL;
    return false;
  }
  return true;
}

FUNC_POINTER GetSymbol(void* handle, const char* symbol) {...return dlsym(handle, symbol); . }Copy the code

dlsym

Void * DLSYm (void * RESTRICT Handle, const char * Restrict symbol);

Documents: man7.org/linux/man-p…

Also a Linux function used to get the address of a function from a loaded dynamic link library

The first argument passed in is the handle returned when the library was loaded earlier, and the second argument is the function name

conclusion

To recap, JniInvocationImpl::Init loads the libart.so library and gets three Pointers from it:

  • JNI_GetDefaultJavaVMInitArgs: Obtains default vm initialization parameters
  • JNI_CreateJavaVM: Creates aVM instance
  • JNI_GetCreatedJavaVMs: Obtains the created VM instance

These functions are defined in jni.h and will be used later when we create the JVM

AndroidRuntime::startVm

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{ JavaVMInitArgs initArgs; .// A bunch of JVM options are configured
    
    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray(a); initArgs.nOptions = mOptions.size(a); initArgs.ignoreUnrecognized = JNI_FALSE;// Create and initialize the JVM
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return - 1;
    }

    return 0;
}
Copy the code

The JNI_CreateJavaVM method is the same as the libart.so method. The source path is art/runtime/jni/java_vm_ext.cc

There is an onVmCreated callback later, but nothing is done in Zygote mode

Register JNI functions

The startReg function is then called to register the Android JNI function

/* * Register android native functions with the VM. */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{...JavaCreateThreadEtc (); // Set the Native thread creation function to create threads via javaCreateThreadEtc
    // Attach the created thread to the JVM so that it can execute both C/C ++ code and Java code
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");

    // Create a local reference stack frame
    env->PushLocalFrame(200);

    // Register jNI functions
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return - 1;
    }
    // Remove the current frame from the stack, freeing all local references in it
    env->PopLocalFrame(NULL);

    return 0;
}
Copy the code

We hook the Native thread creation function first, then the thread creation will call the javaCreateThreadEtc function we set up, attach the created thread to the JVM, so that it can execute both C/C ++ code and Java code. We’ll analyze this later when we see the Android thread being created

PushLocalFrame and PopLocalFrame are a pair of functions that manage local references to JNI

The PopLocalFrame function calls PopLocalFrame to remove the frame from the stack and release all local references

Next we look at the register_jni_procs function

struct RegJNIRec {
    int (*mProc)(JNIEnv*);
};
    
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {...return - 1; }}return 0;
}
Copy the code

Very simple, just loop through all the functions in the gRegJNI array

#define REG_JNI(name)      { name }

static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_os_RuntimeInit),
        REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
        REG_JNI(register_android_os_SystemClock),
        REG_JNI(register_android_util_EventLog),
        REG_JNI(register_android_util_Log),
        ...
};
Copy the code

GRegJNI array stores many Java classes to register JNI function functions, later if you read the source code to see the Android native method can come here to find its corresponding c++ implementation

Register_com_android_internal_os_RuntimeInit register_com_android_internal_os_RuntimeInit register_com_android_internal_os_RuntimeInit

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
    const JNINativeMethod methods[] = {
            {"nativeFinishInit"."()V",
             (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
            {"nativeSetExitWithoutCleanup"."(Z)V",
             (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
    };
    return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
        methods, NELEM(methods));
}
Copy the code

Create a JNINativeMethod structure, the first member is the Java method name, the second member is the Java signature of the corresponding method, the third member is the Java method corresponding to the function pointer to the native function, and then call the jniRegisterNativeMethods function

int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
    const JNINativeMethod* gMethods, int numMethods)
{
    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

    ALOGV("Registering %s's %d native methods...", className, numMethods);

    scoped_local_ref<jclass> c(env, findClass(env, className));
    ALOG_ALWAYS_FATAL_IF(c.get() = =NULL."Native registration unable to find class '%s'; aborting...",
                         className);

    int result = e->RegisterNatives(c.get(), gMethods, numMethods);
    ALOG_ALWAYS_FATAL_IF(result < 0."RegisterNatives failed for '%s'; aborting...",
                         className);

    return 0;
}
Copy the code

This function first obtains a JClass object by Java class name, and then calls JNIEnv::RegisterNatives function. This function is defined in jni.h and implemented in libart.so library. It will be used when registering native methods dynamically, so I won’t go further here

Enter the JAVA world

With the JVM started and JNI functions registered, it’s time to enter the JAVA world

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{...// Create a Java layer String array to hold the parameters
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass ! =NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray ! =NULL);
    / / the first parameter is the name of the class. Com android. Internal. OS. ZygoteInit
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr ! =NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    // The remaining parameters are start-system-server and ABi-list respectively
    for (size_t i = 0; i < options.size(a); ++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. */
    // Replace the "." in Java class names with "/", which is the class name rule in JNI
    char* slashClassName = toSlashClassName(className ! =NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        // Get the main method in ZygoteInit. The argument is String and the return value is void
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            // Execute ZygoteInit's main method
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

            // The following code will not execute unless the JVM hangs. }}... }Copy the code

Here don’t understand the knowledge of himself to mend the JNI, anyhow is invoked the com. Android. Internal. OS. Static method main ZygoteInit class, with com. Android. Internal. OS. ZygoteInit, Start-system-server and ABi-list are used as parameters

ZygoteInit

ZygoteInit class source code path for frameworks/base/core/Java/com/android/internal/OS/ZygoteInit. Java

Let’s start analyzing its main method

public static void main(String argv[]) {
    ZygoteServer zygoteServer = null;
    
    // Mark zygote start. This ensures that thread creation will throw
    // an error.
    // Mark zygote to start, no thread creation allowed (zygote must be single thread)
    ZygoteHooks.startZygoteNoThreadCreation();
    
    // Zygote goes into its own process group.
    // Set the process group ID
    try {
        Os.setpgid(0.0);
    } catch (ErrnoException ex) {
        throw new RuntimeException("Failed to setpgid (0, 0)", ex);
    }

    Runnable caller;
    try{...// Configure parameters
        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])) {
                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)) {
                zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
            } else {
                throw new RuntimeException("Unknown command line argument: "+ argv[i]); }}//public static final String PRIMARY_SOCKET_NAME = "zygote";
        final booleanisPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME); .if(! enableLazyPreload) { .../ / preloadpreload(bootTimingsTraceLog); . }...// Invoke Java layer garbage collectiongcAndFinalize(); .// Callback the onZygoteInit function in AppRRuntime
        Zygote.initNativeState(isPrimaryZygote);

        // Remove the thread creation restriction (soon to fork, child process must be able to create thread)
        ZygoteHooks.stopZygoteNoThreadCreation();

        / / create a socket
        zygoteServer = new ZygoteServer(isPrimaryZygote);

        / / start SystemServer
        if (startSystemServer) {
            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) {
                r.run();
                return;
            }
        }

        Log.i(TAG, "Accepting command socket connections");

        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        // Execute an endless loop to listen on the socket, responsible for receiving events, start App
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        if(zygoteServer ! =null) { zygoteServer.closeServerSocket(); }}// We're in the child process and have exited the select loop. Proceed to execute the
    // command.
    // After receiving AMS request to start App, fork out a child process to process App start
    if(caller ! =null) { caller.run(); }}Copy the code

First call ZygoteHooks. StartZygoteNoThreadCreation () create a thread, Zygote must ensure that the single thread, the mechanism and fork, the fork function will only copies the current thread to the child, at the same time, the fork will lock is copied to the child, If a thread holds the lock before the fork, but the thread is not copied into the child process, the lock is held permanently, resulting in a deadlock

android.system.Os

We have a look at what is Os, according to the import. We know it’s fully qualified class called android system. The Os and its source code path for libcore/luni/SRC/main/Java/android/system/Os Java

.import libcore.io.Libcore;

public final class Os {
    private Os(a) {}...public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }... }Copy the code

It’s full of static proxy methods of this form, which are actually executed by calling libcore. OS, and we’ll trace it with the setpgid method

Libcore in Libcore luni/SRC/main/Java/Libcore/IO/Libcore. Java, OS is one of the static variables

public final class Libcore {
    private Libcore(a) {}/**
     * Direct access to syscalls. Code should strongly prefer using {@link #os}
     * unless it has a strong reason to bypass the helpful checks/guards that it
     * provides.
     */
    public static final Os rawOs = new Linux();

    /**
     * Access to syscalls with helpful checks/guards.
     * For read access only; the only supported way to update this field is via
     * {@link #compareAndSetOs}.
     */
    @UnsupportedAppUsage
    public static volatile Os os = new BlockGuardOs(rawOs);

    public static Os getOs(a) {
        returnos; }... }Copy the code

The OS type is BlockGuardOs, instantiated with the Linux-type constant rawOs as the constructor parameter, which is inherited from the ForwardingOs

public class ForwardingOs implements Os {
    @UnsupportedAppUsage
    private final Os os;

    @UnsupportedAppUsage
    @libcore.api.CorePlatformApi
    protected ForwardingOs(Os os) {
        this.os = Objects.requireNonNull(os); }...public void setpgid(int pid, int pgid) throws ErrnoException { os.setpgid(pid, pgid); }... }Copy the code

As you can see, this is another proxy class that actually calls the methods of Linux classes directly. BlockGuardOs does some callback listening on some methods, and also calls the methods of Linux classes directly

public final class Linux implements Os {
    Linux() { }

    ...
    public native int getpgid(int pid); . }Copy the code

There are basically JNI call native method, the corresponding c + + source code path for libcore/luni/SRC/main/native/libcore_io_Linux CPP, the following is a function of registered JNI function

#define NATIVE_METHOD(className, functionName, signature)                \
  MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
  
#define MAKE_JNI_NATIVE_METHOD(name, signature, function)                      \
  _NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function)
  
#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \
  MAKE_CHECKED_JNI_NATIVE_METHOD(kind, name, sig, fn)
  
#define MAKE_CHECKED_JNI_NATIVE_METHOD(native_kind, name_, signature_, fn) \
  ([]() {                                                                \
    using namespace nativehelper::detail;  /* NOLINT(google-build-using-namespace) */ \
    static_assert(                                                       \
        MatchJniDescriptorWithFunctionType<native_kind,                  \
                                           decltype(fn),                 \
                                           fn,                           \
                                           sizeof(signature_)>(signature_),\
        "JNI signature doesn't match C++ function type.");               \
    /* Suppress implicit cast warnings by explicitly casting. */         \
    return JNINativeMethod {                                             \
        const_cast<decltype(JNINativeMethod::name)>(name_),              \
        const_cast<decltype(JNINativeMethod::signature)>(signature_),    \
        reinterpret_cast<void*>(&(fn))};                                 \
  })()

static JNINativeMethod gMethods[] = {
    ...
    NATIVE_METHOD(Linux, setpgid, "(II)V"),... };void register_libcore_io_Linux(JNIEnv* env) {...jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods));
}
Copy the code

It can be seen that the format of Java layer method corresponding to native method is Linux_ method name. We can find the function corresponding to setpgid method by this rule

static void Linux_setpgid(JNIEnv* env, jobject, jint pid, int pgid) {
    throwIfMinusOne(env, "setpgid".TEMP_FAILURE_RETRY(setpgid(pid, pgid)));
}
Copy the code

You can see that the Linux system layer function is called directly

conclusion

In summary, the purpose of the Android.system. Os class is to make it easy for the Java layer to call Linux system methods

preload

This is followed by some parameter configuration and a call to preload

static void preload(TimingsTraceLog bootTimingsTraceLog) {...// Preload Java classespreloadClasses(); .// Load three JAR files
    / * / system/framework/android hidl. The base - V1.0 - Java. Jar * /
    / * / system/framework/android hidl. The manager - V1.0 - Java. Jar * /
    /* /system/framework/android.test.base.jar */cacheNonBootClasspathClassLoaders(); .// Preloads system resourcespreloadResources(); .// Preload the hardware abstraction layer?nativePreloadAppProcessHALs(); .// Preload OpengLmaybePreloadGraphicsDriver(); .// Preload the dynamic library
    preloadSharedLibraries();
    //TextView preloads Font
    preloadTextResources();
    // Preload WebviewChromiumWebViewFactory.prepareWebViewInZygote(); . }Copy the code

preloadClasses

Let’s look at the preloading of Java classes

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

private static void preloadClasses(a) {
    final VMRuntime runtime = VMRuntime.getRuntime();

    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; }...// The Java class loader loads classes
            Class.forName(line, true.null); count++; . } Log.i(TAG,"... preloaded " + count + " classes in "
                + (SystemClock.uptimeMillis() - startTime) + "ms.");
    } catch (IOException e) {
        Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
    } finally{... }}Copy the code

The main code is to read /system/etc/preloaded classes and load them using the classloader via class.forname. Before compiling path for frameworks/base/config/preloaded – classes

Why is preloading required

One of the things Zygote does is incubate apps. How does that happen? We know that the parent process can share resources with the parent process after the fork. Since every time we start an App, we need to use the virtual machine, load some necessary classes such as views, etc. Why not load these in the parent process, and the child process can use them directly? This is why the Zygote process is preloaded

Start a binder thread pool

InitNativeState (isPrimaryZygote). This method calls the native method nativeInitNativeState. This method is registered in AndroidRuntime, and implemented in AndroidRuntime,

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

As we’ve seen before, we’re doing onZygoteInit, a subclass of AndroidRuntime called AppRuntime

virtual void onZygoteInit(a)
{
    sp<ProcessState> proc = ProcessState::self(a);ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool(a); }Copy the code

The Binder thread pool is started with this function, but Binder details will be discussed later

Start the SystemServer

The first Zygote process to hatch was SystemServer. How did it hatch and what did SystemServer do after hatching

ZygoteServer

A constructor

We know that our apps are hatched from Zygote, and the App starts with the startActivity method of ActivityManagerService. How does AMS communicate with Zygote

Let’s start with ZygoteServer’s constructor

ZygoteServer(boolean isPrimaryZygote) {
    ...
    if(isPrimaryZygote) { mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME); . }else{... }... }Copy the code

There are some things that are related to the USAP mechanism, but it’s turned off by default in AOSP. We’ll talk about the USAP mechanism later. Just focus on mZygoteSocket for now. It is by calling the Zygote. CreateManagedSocketFromInitSocket assignment

static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
    int fileDesc;
    //ANDROID_SOCKET_zygote
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

    try {
        // Get the file descriptor
        String env = System.getenv(fullSocketName);
        fileDesc = Integer.parseInt(env);
    } catch (RuntimeException ex) {
        throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
    }

    try {
        FileDescriptor fd = new FileDescriptor();
        fd.setInt$(fileDesc);
        return new LocalServerSocket(fd);
    } catch (IOException ex) {
        throw new RuntimeException(
            "Error building socket from file descriptor: "+ fileDesc, ex); }}Copy the code

Is very simple, a fd is obtained from the system property, and then instantiate LocalServerSocket, path for frameworks/base/core/Java/android/net/LocalServerSocket. Java

public LocalServerSocket(FileDescriptor fd) throws IOException
{
    impl = new LocalSocketImpl(fd);
    impl.listen(LISTEN_BACKLOG);
    localAddress = impl.getSockAddress();
}
Copy the code

A LocalSocketImpl is created internally, and then a listen method declaration is called to start listening for the FD. Linux’s LISTEN function is called internally

runSelectLoop

Then let’s look at the runSelectLoop method called in ZygoteInit

Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
    ArrayList<ZygoteConnection> peers = new ArrayList<>();

    // Add server socket fd to the list header.
    socketFDs.add(mZygoteSocket.getFileDescriptor());
    peers.add(null); .while (true) {... StructPollfd[] pollFDs; . pollFDs =new StructPollfd[socketFDs.size()];
        
        int pollIndex = 0;
        for (FileDescriptor socketFD : socketFDs) {
            pollFDs[pollIndex] = new StructPollfd();
            pollFDs[pollIndex].fd = socketFD;
            pollFDs[pollIndex].events = (short) POLLIN; ++pollIndex; }...// The above paragraphs are all related to the USAP mechanism

        int pollReturnValue;
        try {
            // Wait for events on the file descriptor
            pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }

        if (pollReturnValue == 0) { // No event was received (timed out), waiting for the event again from the beginning of the loop. }else{...while (--pollIndex >= 0) {
                // No data to read, skip
                if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                    continue;
                }

                if (pollIndex == 0) {
                    //pollIndex == 0 indicates that the fd is the fd of the ZygoteServer socket

                    // Set up a socket connection
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    // Add the client socket fd to the list
                    socketFDs.add(newPeer.getFileDescriptor());

                } else if (pollIndex < usapPoolEventFDIndex) {
                    If no USAP mechanism is used, the pollIndex < usapPoolEventFDIndex condition is valid
                    // The socket is a client socket

                    try {
                        // Internal fork returns a Runnable to be executed for subsequent child tasks
                        ZygoteConnection connection = peers.get(pollIndex);
                        final Runnable command = connection.processOneCommand(this);

                        // After fork, this variable is set to true in the child process
                        if (mIsForkChild) { // In the child process
                            if (command == null) {
                                throw new IllegalStateException("command == null");
                            }
                            // Return the Runnable from ZygoteInit
                            return command;
                        } else { // In the parent process
                            if(command ! =null) {
                                throw new IllegalStateException("command ! = null");
                            }

                            // Close the socket and clear the list
                            if(connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(pollIndex); socketFDs.remove(pollIndex); }}}catch (Exception e) {
                        ...
                    } finally {
                        mIsForkChild = false; }}else{...// This branch cannot be reached without USAP enabled}}... }... }}Copy the code

Add server socket fd to the header of the list, which is easy to determine whether the event is from a client or a server socker. A NULL is also added to the list of peers as a counterpart to socketFDs

The Zygote process never exits the loop, except when a child is forked and returned

poll

To understand what follows, we need to look at the poll function

Poll is a function in the character device driver in Linux that waits for an event on a file descriptor

Struct pollfd * FDS, nfds_t NFDS, int timeout);

Documents: man7.org/linux/man-p…

The first parameter is a pointer to the PollFD structure

struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};
Copy the code
  • Fd, needless to say, is the file descriptor
  • Event means what events are concerned about,POLLINStands for readable,POLLOUTStands for writable and so on
  • Revents are notified by the kernel, and when the function returns, it sets the event that actually occurred in the corresponding FD, such as when the FD has a readable event, settingPOLLIN

The second parameter, NFDS, represents the number of Fd’s, the size of the PollFD array

The third parameter indicates the timeout period

The return value:

  • Greater than 0: indicates that fd events are generated. The value is the number of FDS that generate events
  • 0: indicates timeout
  • Less than 0: an error occurs

StructPollfd & pollfd

Now that we know what poll does, let’s move on to the runSelectLoop method

The infinite loop first creates an array of StructPollfd objects, which in turn create StructPollfd objects based on socketFDs, and make their events pollin-readable

StructPollfd corresponds to pollfd in C to make it easier for the Java layer to call Linux’s poll function

StructPollfd path for libcore/luni/SRC/main/Java/android/system/StructPollfd. Java

public final class StructPollfd {
    public FileDescriptor fd;
    public short events;
    public short revents;
    publicObject userData; . }Copy the code

call

The OS.poll method is then called

About Os analysis above, we know that he calls the JNI functions, native function naming format for Linux_ function name, we went to libcore luni/SRC/main/native/libcore_io_Linux. Look for the CPP

static jint Linux_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {...// Convert the Java object StructPollfd array to the C struct Pollfd array
    int rc;
    while (true) {... rc = poll(fds.get(), count, timeoutMs);if (rc >= 0|| errno ! = EINTR) {break; }... }if (rc == -1) {
        throwErrnoException(env, "poll");
        return -1; }...// Set the revents value of the Java object StructPollfd
    return rc;
}
Copy the code

A Linux poll function is called, and revents is written into the StructPollfd object

PollFDs revents () ¶ runSelectLoop () ¶ If poll returns -1, a Java exception will be raised. If poll returns 0, no event will be generated. If not, skip

Establish a connection

PollFDs pollFDs pollFDs pollFDs pollFDs pollFDs pollFDs pollFDs pollFDs pollFDs pollFDs At this point we call the acceptCommandPeer method to establish a new connection

private ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        return createNewConnection(mZygoteSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException(
                "IOException during accept()", ex); }}Copy the code

The mzygotesocket.accept method is called

public LocalSocket accept(a) throws IOException
{
    LocalSocketImpl acceptedImpl = new LocalSocketImpl();

    impl.accept(acceptedImpl);

    return LocalSocket.createLocalSocketForAccept(acceptedImpl);
}
Copy the code

Create an instance of LocalSocketImpl(Client Socket) and call the Accept method of LocalSocketImpl(Zygote Socket)

protected void accept(LocalSocketImpl s) throws IOException {
    if (fd == null) {
        throw new IOException("socket not created");
    }

    try {
        s.fd = Os.accept(fd, null /* address */);
        s.mFdCreatedInternally = true;
    } catch (ErrnoException e) {
        throwe.rethrowAsIOException(); }}Copy the code

Call Linux’s Accept function, accept the connection, and return a new client socket fd. Set the fd variable in LocalSocketImpl to this fd. Then call LocalSocket. CreateLocalSocketForAccept LocalSocketImpl packing into LocalSocket

static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) {
    return createConnectedLocalSocket(impl, SOCKET_UNKNOWN);
}

private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) {
    LocalSocket socket = new LocalSocket(impl, sockType);
    socket.isConnected = true;
    socket.isBound = true;
    socket.implCreated = true;
    return socket;
}
Copy the code

A ZygoteConnection wrapper socket connection is then created using this LocalSocket

protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
        throws IOException {
    return new ZygoteConnection(socket, abiList);
}
Copy the code

So what does the constructor do

ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
    mSocket = socket;
    this.abiList = abiList;

    mSocketOutStream = new DataOutputStream(socket.getOutputStream());
    mSocketReader =
            new BufferedReader(
                    newInputStreamReader(socket.getInputStream()), Zygote.SOCKET_BUFFER_SIZE); . isEof =false;
}
Copy the code

Open the client socket I/O stream, ready to read and write data

This connection and fd are then added to peers and socketFDs, respectively

Run the client socket command

PollFDs array in the second cycle was included in the new connection is established the client socket, then call Os. The poll, can get to the client socket readable events, call connection at this time. ProcessOneCommand method

Runnable processOneCommand(ZygoteServer zygoteServer) {
    String[] args;

    try {
        // Read the parameters from the client socket
        args = Zygote.readArgumentList(mSocketReader);
    } catch (IOException ex) {
        throw new IllegalStateException("IOException on command socket", ex); }...int pid;
    FileDescriptor childPipeFd = null;
    FileDescriptor serverPipeFd = null;

    // Parse the parameters
    ZygoteArguments parsedArgs = newZygoteArguments(args); .// a series of parameter verification work

    // Create a child process
    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
            parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
            parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
            parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
            parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);

    try {
        if (pid == 0) { // In the child process
            // Set mIsForkChild = true
            zygoteServer.setForkChild();
            // Close zygote socket copied from fork in child process
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;

            return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
        } else { // In the parent process
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            handleParentProc(pid, serverPipeFd);
            return null; }}finally{ IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}Copy the code

Start the APP Process

The Zygote. Forkandwte method is used to fork the child process after reading the parameters from the client socket and verifying them

static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
        int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
        int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
        boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
        boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
    // Stop other threads
    ZygoteHooks.preFork();

    / / fork process
    intpid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs, bindMountAppStorageDirs); .// Resume other threads
    ZygoteHooks.postForkCommon();
    return pid;
}
Copy the code

The Zygote process starts four threads:

  • HeapTaskDaemon
  • ReferenceQueueDaemon
  • FinalizerDaemon
  • FinalizerWatchdogDaemon

We have also analyzed the effect of multiple threads on forking, so we will stop the other threads and wait for the fork to be restarted

Then execute nativeForkAndSpecialize native function, path of frameworks/base/core/jni/com_android_internal_os_Zygote. CPP

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) {...pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);

    if (pid == 0) {
      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                       capabilities, capabilities,
                       mount_external, se_info, nice_name, false,
                       is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
                       is_top_app == JNI_TRUE, pkg_data_info_list,
                       whitelisted_data_info_list,
                       mount_data_dirs == JNI_TRUE,
                       mount_storage_dirs == JNI_TRUE);
    }
    return pid;
}
Copy the code

ForkCommon is called, and SpecializeCommon is called in the child process

static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
                        const std::vector<int>& fds_to_close,
                        const std::vector<int>& fds_to_ignore,
                        bool is_priority_fork) {
  // Sets the child signal handler function
  SetSignalHandlers(a); .// block SIGCHLD signal before fork
  BlockSignal(SIGCHLD, fail_fn); .The fork / / execution
  pid_tpid = fork(); .// Restore SIGCHLD signal
  UnblockSignal(SIGCHLD, fail_fn);

  return pid;
}
Copy the code
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
                             jint runtime_flags, jobjectArray rlimits,
                             jlong permitted_capabilities, jlong effective_capabilities,
                             jint mount_external, jstring managed_se_info,
                             jstring managed_nice_name, bool is_system_server,
                             bool is_child_zygote, jstring managed_instruction_set,
                             jstring managed_app_data_dir, bool is_top_app,
                             jobjectArray pkg_data_info_list,
                             jobjectArray whitelisted_data_info_list,
                             bool mount_data_dirs, bool mount_storage_dirs) {
  const char* process_name = is_system_server ? "system_server" : "zygote"; .// Create a process group
  if(! is_system_server &&getuid() = =0) {
    const int rc = createProcessGroup(uid, getpid());
    if (rc == -EROFS) {
      ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
    } else if(rc ! =0) {
      ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0.strerror(-rc)); }}/ / set the GroupId
  SetGids(env, gids, is_child_zygote, fail_fn);
  // Set the resource Limit
  SetRLimits(env, rlimits, fail_fn); .// Set the scheduling policy
  SetSchedulerPolicy(fail_fn, is_top_app); .// Set the thread name
  if (nice_name.has_value()) {
    SetThreadName(nice_name.value());
  } else if (is_system_server) {
    SetThreadName("system_server");
  }

  // SIGCHLD signals are no longer processed in the child process
  UnsetChldSignalHandler(a); .if (is_child_zygote) {
      initUnsolSocketToSystemServer(a); }/ / call the Zygote. CallPostForkChildHooks method
  env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
                            is_system_server, is_child_zygote, managed_instruction_set);

  // Set the default process priority
  setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);

  if (env->ExceptionCheck()) {
    fail_fn("Error calling post fork hooks."); }}Copy the code

After the child process is created, ZygoteConnection returns handleChildProc in the child process and NULL in the parent process

If ZygoteServer is a child and command is not null, return common to ZygoteInit. If ZygoteInit is a parent, continue the socket poll loop

After zygoteinit. runSelectLoop, this Runnable is executed if the return value caller (corresponding to command in ZygoteServer) is not null

So what does handleChildProc do

private Runnable handleChildProc(ZygoteArguments parsedArgs,
        FileDescriptor pipeFd, boolean isZygote) {
    // Close the client socket that initiates the App request in the child process
    closeSocket();
    // Set the process nameZygote.setAppProcessName(parsedArgs, TAG); .if(parsedArgs.mInvokeWith ! =null) {
        // Related to process memory leak or overflow?
        WrapperInit.execApplication(parsedArgs.mInvokeWith,
                parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
                VMRuntime.getCurrentInstructionSet(),
                pipeFd, parsedArgs.mRemainingArgs);

        // Should not get here.
        throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
    } else {
        if(! isZygote) {// Execute this method based on arguments
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, null /* classLoader */);
        } else {
            return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, null /* classLoader */); }}}Copy the code

Perform ZygoteInit. ZygoteInit

public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {...// Redirect Log
    RuntimeInit.redirectLogStreams();
    // General initialization
    RuntimeInit.commonInit();
    // As mentioned earlier, enable binder thread pools
    ZygoteInit.nativeZygoteInit();
    return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
            classLoader);
}
Copy the code

RuntimeInit path for frameworks/base/core/Java/com/android/internal/OS/RuntimeInit. Java, to perform general initialization

protected static final void commonInit(a) {
    // Set the default thread exception handler
    LoggingHandler loggingHandler = new LoggingHandler();
    RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
    Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));

    // Set the time zone
    RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));

    // Reset the Log configuration
    LogManager.getLogManager().reset();
    new AndroidConfig();

    // Set the NETWORK UA information
    String userAgent = getDefaultUserAgent();
    System.setProperty("http.agent", userAgent);

    // Initialize network traffic statisticsNetworkManagementSocketTagger.install(); . initialized =true;
}
Copy the code

Then execute RuntimeInit applicationInit

protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    // If the application calls system.exit (), the process is terminated immediately without running any hook functions
    nativeSetExitWithoutCleanup(true);
    // Set vm parameters
    VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
    VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
    // Parse the parameters
    final Arguments args = newArguments(argv); .// Find the main method in startClass
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}
Copy the code

The startClass here for android. App. ActivityThread

protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<? > cl;try {
        cl = Class.forName(className, true, classLoader);
    } 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

Using reflection in Java, I found the corresponding main method in ActivityThread and used it to create a Runnable object MethodAndArgsCaller

static class MethodAndArgsCaller implements Runnable {
    private final Method mMethod;
    private final String[] mArgs;

    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }

    public void run(a) {
        try {
            // Execute the activityThread. main method
            mMethod.invoke(null.new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            Throwable cause = ex.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            }
            throw newRuntimeException(ex); }}}Copy the code

As mentioned earlier, after zygoteinit. runSelectLoop, if the return caller is not null, the Runnable is executed, i.e. the run method for MethodAndArgsCaller, and the reflection calls activityThread. main

The end of the

At this point, Zygote process part of the analysis of the end, behind the fork out of the App process section is very rough, behind the App to start the piece, I will comb through the logic here, add up