Android Zygote Startup Process Source Resolution

Keywords: socket Android Java Linux

The Zygote process is Android and Java The founder of the world.stay android In the system, all application processes and SysteemServer processes come from the Zygote process fork.Its importance can be seen here.Although the Zygote process is equivalent to the root process of the Android system, in fact it is also the root process of the Android system. Linux The init process of the system started.The sequence of processes is:

init process -> Zygote process -> SystemServer process -> Application process

The Zygote process is started by the init process, the SystemServer process and the application process by the Zygote process.Based on 6.0 source code, this paper mainly analyses the start process of Zygote process.The init process calls ZygoteInit#main() when it starts the Zygote process.With this as the starting point, analyze step by step.

Source location: frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

Process overview

ZygoteInit#main();

public static void main(String argv[]) {
    try {
        // Set DDMS Available
        RuntimeInit.enableDdms();
        // Initialize Startup Parameters
        boolean startSystemServer = false;
        String socketName = "zygote";
        String abiList = null;
        for (int i = 1; i < argv.length; i++) {
            if ("start-system-server".equals(argv[i])) {
                startSystemServer = 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]);
            }
        }
        // Register socket
        registerZygoteSocket(socketName);
        // Preload various resources
        preload();
        ...
        if (startSystemServer) {
            // Start SystemServer process
            startSystemServer(abiList, socketName);
        }
        // Monitor socket s and start new applications.--Later on
        runSelectLoop(abiList);
        closeServerSocket();
    } catch (MethodAndArgsCaller caller) {
        // Calling SystemServer#main() through reflection--later
        caller.run();
    } catch (RuntimeException ex) {
        closeServerSocket();
        throw ex;
    }
}

The above is a general process, which is explained step by step according to the source code.After setting up the DDMS available, initialize various parameters, then register the Socket for the Zygote process, preload various resources, but these are not the focus!Students, focus on startSystemServer (abiList, socketName) (hand on blackboard)!The following is a simple paste of the source code for registerZygoteSocket(socketName) and preload(). Uninterested students can skip the following two sections of code directly.

ZygoteInit#registerZygoteSocket()

private static void registerZygoteSocket(String socketName) {
    if (sServerSocket == null) {
        int fileDesc;
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        ...
        FileDescriptor fd = new FileDescriptor();
        fd.setInt$(fileDesc);
        // Create socket s using fd instead of IP and port
        sServerSocket = new LocalServerSocket(fd);
        ...
    }
}

ZygoteInit#registerZygoteSocket()

static void preload() {
    preloadClasses(); // Loading various required class files
    preloadResources(); // Load resource file
    preloadOpenGL(); // Initialize OpenGL
    preloadSharedLibraries(); // Loading System Libraries
    preloadTextResources(); //Load text resources
    WebViewFactory.prepareWebViewInZygote(); // Initialize WebView
}

Start SystemServer process

Follow up startSystemServer()

private static boolean startSystemServer(String abiList, String socketName) throws MethodAndArgsCaller, RuntimeException {
    long capabilities = posixCapabilitiesAsBits(
        OsConstants.CAP_BLOCK_SUSPEND,
        OsConstants.CAP_KILL,
        ...
    );
    /* Hardcoded command line to start the system server */
    String args[] = {
        "--setuid=1000",
        "--setgid=1000",
        "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
        "--capabilities=" + capabilities + "," + capabilities,
        "--nice-name=system_server",
        "--runtime-args",
        "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;

    int pid;

    try {
        parsedArgs = new ZygoteConnection.Arguments(args);
        // Open System Debugging Properties
        ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
        ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        // Request fork SystemServer process
        pid = Zygote.forkSystemServer(
                parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids,
                parsedArgs.debugFlags,
                null,
                parsedArgs.permittedCapabilities,
                parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }

    // A pid of 0 represents a subprocess, the SystemServer process, from which the SystemServer process and the Zygote process diverge
    if (pid == 0) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }

        handleSystemServerProcess(parsedArgs);
    }

    return true;
}

The previous large section constructs parameters and goes directly into the code block in the try.First, a ZygoteConnection.Arguments is constructed from the args array, then the Zygote#forkSyatemServer() method fork is called based on various parameters of the parsedArgs object to produce the first subprocess, the SystemServer process.Finally, SystemServer#main() is called by executing the handleSystemServerProcess reflection.As you can see, the main purpose of this code is to fork out the SystemServer process.The details of the reflection call are not yet visible here, and will be analyzed below.

First, look at what you did when you constructed the ZygoteConnection.Arguments object, with particular attention to the values of several parameters in Zygote#forkSystemServer().

Source location: frameworks/base/core/java/com/android/internal/os/ZygoteConnection$Arguments.java

    Arguments(String args[]) throws IllegalArgumentException {
        parseArgs(args);
    }

    private void parseArgs(String args[]) throws IllegalArgumentException {
        int curArg = 0;
        boolean seenRuntimeArgs = false;

        for ( /* curArg */ ; curArg < args.length; curArg++) {
            String arg = args[curArg];

            if (arg.equals("--")) {
                curArg++;
                break;
            } else if (arg.startsWith("--setuid=")) {
                if (uidSpecified) {
                    throw new IllegalArgumentException("Duplicate arg specified");
                }
                uidSpecified = true;
                uid = Integer.parseInt(arg.substring(arg.indexOf('=') + 1));
            } else if (arg.startsWith("--setgid=")) {
                if (gidSpecified) {
                gidSpecified = true;
                gid = Integer.parseInt(arg.substring(arg.indexOf('=') + 1));
            } else if (arg.startsWith("--target-sdk-version=")) {
                targetSdkVersionSpecified = true;
                targetSdkVersion = Integer.parseInt(arg.substring(arg.indexOf('=') + 1));
            } 
            ...
              else if (arg.equals("--runtime-args")) {
                seenRuntimeArgs = true;
            } else if (arg.startsWith("--capabilities=")) {
                capabilitiesSpecified = true;
                String capString = arg.substring(arg.indexOf('=')+1);
                String[] capStrings = capString.split(",", 2);
                if (capStrings.length == 1) {
                    effectiveCapabilities = Long.decode(capStrings[0]);
                    permittedCapabilities = effectiveCapabilities;
                } else {
                    permittedCapabilities = Long.decode(capStrings[0]);
                    effectiveCapabilities = Long.decode(capStrings[1]);
                }
            } else if (arg.startsWith("--setgroups=")) {
                String[] params = arg.substring(arg.indexOf('=') + 1).split(",");
                gids = new int[params.length];
                for (int i = params.length - 1; i >= 0 ; i--) {
                    gids[i] = Integer.parseInt(params[i]);
                }
            } else if (arg.startsWith("--nice-name=")) {
                niceName = arg.substring(arg.indexOf('=') + 1);
            } else {
                break;
            }
        }

        // Save parameters that are not parsed
        remainingArgs = new String[args.length - curArg];
        System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);    
    }

Comparing the incoming args array, you can see that parsedArgs.uid=1000, parsedArgs.gid=1000, parsedArgs.gids={"1001","1002",..."3007"}, parsedArgs.gid=1000, parsedArgs.niceName=system_server, parsedArgs.seenRuntimeArgs=true.If it ends halfway, save the unresolved parameters to the remainingArgs array.(
Once you have the Arguments object, you begin requesting the creation of the SystemServer process.

Source location: frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

ZygoteInit#handleSystemServerProcess()

  private static void handleSystemServerProcess(
        ZygoteConnection.Arguments parsedArgs)
        throws ZygoteInit.MethodAndArgsCaller {

    closeServerSocket();

    if (parsedArgs.niceName != null) {
        Process.setArgV0(parsedArgs.niceName);
    }
    ...
    // Default to null
    if (parsedArgs.invokeWith != null) {
        ...
    } else {
        ...
        RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    }

    /* should never reach here */
}

A child process created by Zygote has the Socket object of the Zygote process by default, and the child process can't use it, so call closeServerSocket() to close it first.When parsedArgs.niceName=system_server, call Process.setArgV0() here to set the process name: system_server.Since the parsedArgs.invokeWith property defaults to null, RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl) is finally called to further start SystemServer, where the parameter parsedArgs.remainingArgs is the array of unresolved objects stored above.

Source location: frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

RuntimeInit#zygoteInit()

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
    // Redirect Log Output
    redirectLogStreams();
    //Initialize run environment
    commonInit();
    //Start Binder Thread Pool
    nativeZygoteInit();
    //Call Program Entry Function
    applicationInit(targetSdkVersion, argv, classLoader);
}

RuntimeInit#applicationInit()

private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    // Initialize virtual machine environment
    VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
    VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
    final Arguments args;
    try {
        args = new Arguments(argv);
    } catch (IllegalArgumentException ex) {
        return;
    }

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

RuntimeInit#invokeStaticMain()

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    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 {
        // Get main method
        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);
    }
    // Judgment modifier
    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException("Main method is not public and static on " + className);
    }

    throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

The className passed in here is com.android.server.SystemServer, gets the main method, and then determines that the modifier must be static and must be of type public.Nothing is more interesting than throwing a MethodAndArgsCaller exception after doing all this.It takes a lot of hard work to initialize, a variety of ways to invoke, and finally you throw me an exception!!Don't worry, this exception was caught in the Zygote#main() method.This is done to clear the call stack of the application process creation process.

 public static void main(String argv[]) {
    try {
        ...
        startSystemServer(abiList, socketName);
        ...
    } catch (MethodAndArgsCaller caller) {
        caller.run();
    }
}

Follow up on MethodAndArgsCaller#run(), and it feels like something big is going to happen!!

  public void run() {
        try {
            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 new RuntimeException(ex);
        }
    }

I said something big was going to happen!I said something big was going to happen!!I said something big is going to happen!!!You can see here that com.android.server.SystemServer#main(String[] args) is called through reflection.At this point, the Zygote process for ks out the SystemServer process and successfully calls SystemServer#main().

Now the SystemServer process is created, and the main method is called.Is this the end of the Zygote process's mission?As we said above, all application processes and SystemServer processes come from the Zygote process fork.Now that the SystemServer process is over, what about the application process?

Let's go back to ZygoteInit#main()

public static void main(String argv[]) {
      ...
      startSystemServer(abiList, socketName);
      runSelectLoop(abiList);
      closeServerSocket();
}

All the previous code in the main method seems to have nothing to do with the application process, and the last line closes the socket, so it appears that the settings related to the application process are all in runSelectLoop() and are being followed up.

Listen for Socket s, Start Application Processes

ZygoteInit#runSelectLoop(),ZygoteInit#acceptCommandPeer()

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    FileDescriptor[] fdArray = new FileDescriptor[4];
    fds.add(sServerSocket.getFileDescriptor());
    peers.add(null);
    int loopCount = GC_LOOP_COUNT;
    while (true) {
        int index;
        if (loopCount <= 0) {
            gc();
            loopCount = GC_LOOP_COUNT;
        } else {
            loopCount--;
        }

        try {
            fdArray = fds.toArray(fdArray);
            index = selectReadable(fdArray);
        } catch (IOException ex) {
            throw new RuntimeException("Error in select()", ex);
        }

        if (index < 0) {
            throw new RuntimeException("Error in select()");
        } else if (index == 0) {
            ZygoteConnection newPeer = acceptCommandPeer(abiList);
            peers.add(newPeer);
            fds.add(newPeer.getFileDescriptor());
        } else {
            boolean done;
            done = peers.get(index).runOnce();

            if (done) {
                peers.remove(index);
                fds.remove(index);
            }
        }
    }
}

private static ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        return new ZygoteConnection(sServerSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException("IOException during accept()", ex);
    }
}

There's a dead loop here, listening to socket s all the time, then calling ZygoteConnection#runOnce(), feeling the truth out of the function name runOne and following up.

Source location: frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

ZygoteConnection#runOnce()

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    String args[];
    args = readArgumentList();
    parsedArgs = new Arguments(args);
     try {
        ...
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                parsedArgs.appDataDir);
    } catch (ErrnoException ex) {
        logAndPrintError(newStderr, "Exception creating pipe", ex);
    } catch (IllegalArgumentException ex) {
        logAndPrintError(newStderr, "Invalid zygote arguments", ex);
    } catch (ZygoteSecurityException ex) {
        logAndPrintError(newStderr,
                "Zygote security policy prevents request: ", ex);
    }

    try {
        if (pid == 0) {
            // in child
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

            // should never get here, the child is expected to either
            // throw ZygoteInit.MethodAndArgsCaller or exec().
            return true;
        } else {
            // in parent...pid of < 0 means failure
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

Similar to starting a SystemServer process.Zygote#forkAndSpecialize() is called here to create the application process, and the parameter parsedArgs is read out on a row of socket s.See ZygoteConnection#readArgumentList()

private String[] readArgumentList() throws IOException {
    int argc;
    try {
        String s = mSocketReader.readLine();
        if (s == null) {
            return null;
        }
        argc = Integer.parseInt(s);
    } catch (NumberFormatException ex) {
        throw new IOException("invalid wire format");
    }
    if (argc > MAX_ZYGOTE_ARGC) {
        throw new IOException("max arg count exceeded");
    }
    String[] result = new String[argc];
    for (int i = 0; i < argc; i++) {
        result[i] = mSocketReader.readLine();
        if (result[i] == null) {
            // We got an unexpected EOF.
            throw new IOException("truncated request");
        }
    }
    return result;
}

Since you haven't seen the source code for sending the Socket message yet, here's a bold guess: it should be uid, gid, niceName, and so on.

After reading the various parameters through the Socket, call ZygoteConnection#handleChildProc(), which calls the application's entry method once the application process has been created.Follow-up.

private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {
    // Close Socket connections copied from the Zygote process  
    closeSocket();
    ZygoteInit.closeServerSocket();
    if (parsedArgs.niceName != null) {
        Process.setArgV0(parsedArgs.niceName);
    }
    ...
    RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, null /* classLoader */);
}

Finally, RuntimeInit#zygoteInit() is called, followed by a process similar to that of System Server startup.Interested students look at it by themselves.

Summarize the Zygote startup process:

  1. Initialize DDMS

  2. Register Socket for Zygote Process

  3. Loading resources such as class, resource, OpenGL, WebView

  4. fork Out SystemServer Process

  5. Start SystemServer process

  6. Calling runSelectLoop() keeps listening for Socket information

  7. Receive the Create Application Socket message and call ZygoteConnection#runOnce().Call Zygote#forkAndSpecialize() in runOnce() to create an application process

  8. Start application process

Posted by mraiur on Sat, 22 Jun 2019 09:44:42 -0700