Process class startViaZygote method, the final step calls the zygoteSendArgsAndGetResult, this function is returned as openZygoteSocketIfNeeded ZygoteState objects, And many of the arguments in the ArrayList are passed in as arguments.

frameworks/base/core/java/android/os/Process.java

public class Process {...private static ProcessStartResult startViaZygote(final String processClass,
                                  final String niceName,
                                  final int uid, final int gid,
                                  final int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
            ArrayList<String> argsForZygote = new ArrayList<String>();

            // --runtime-args, --setuid=, --setgid=,
            // and --setgroups= must go first
            argsForZygote.add("--runtime-args");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
            if((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) ! =0) {
                argsForZygote.add("--enable-jni-logging");
            }
            if((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) ! =0) {
                argsForZygote.add("--enable-safemode");
            }
            if((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) ! =0) {
                argsForZygote.add("--enable-debugger");
            }
            if((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) ! =0) {
                argsForZygote.add("--enable-checkjni");
            }
            if((debugFlags & Zygote.DEBUG_ENABLE_JIT) ! =0) {
                argsForZygote.add("--enable-jit");
            }
            if((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) ! =0) {
                argsForZygote.add("--generate-debug-info");
            }
            if((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) ! =0) {
                argsForZygote.add("--enable-assert");
            }
            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
                argsForZygote.add("--mount-external-default");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
                argsForZygote.add("--mount-external-read");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
                argsForZygote.add("--mount-external-write");
            }
            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);

            //TODO optionally enable debuger
            //argsForZygote.add("--enable-debugger");

            // --setgroups is a comma-separated list
            if(gids ! =null && gids.length > 0) {
                StringBuilder sb = new StringBuilder();
                sb.append("--setgroups=");

                int sz = gids.length;
                for (int i = 0; i < sz; i++) {
                    if(i ! =0) {
                        sb.append(', ');
                    }
                    sb.append(gids[i]);
                }

                argsForZygote.add(sb.toString());
            }

            if(niceName ! =null) {
                argsForZygote.add("--nice-name=" + niceName);
            }

            if(seInfo ! =null) {
                argsForZygote.add("--seinfo=" + seInfo);
            }

            if(instructionSet ! =null) {
                argsForZygote.add("--instruction-set=" + instructionSet);
            }

            if(appDataDir ! =null) {
                argsForZygote.add("--app-data-dir=" + appDataDir);
            }

            argsForZygote.add(processClass);

            if(extraArgs ! =null) {
                for(String arg : extraArgs) { argsForZygote.add(arg); }}returnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); }}... }Copy the code

ZygoteSendArgsAndGetResult function to the zygote process to send a list of parameters, the process starts a child and returns the new child process pid.

frameworks/base/core/java/android/os/Process.java

public class Process {...private static ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList
       
         args)
       
            throws ZygoteStartFailedEx {
        try {
            // If a newline character is found, an exception is thrown
            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                if (args.get(i).indexOf('\n') > =0) {
                    throw new ZygoteStartFailedEx("embedded newlines not allowed"); }}/ / see com. Android. Internal. OS. ZygoteInit. ReadArgumentList ()
            // Write the Writer with cache used by the socket
            final BufferedWriter writer = zygoteState.writer;
            // Read the used input stream from the socket
            final DataInputStream inputStream = zygoteState.inputStream;
            // Total number of parameters to write
            writer.write(Integer.toString(args.size()));
            writer.newLine();
            // Write each parameter
            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                writer.write(arg);
                writer.newLine();
            }

            writer.flush();

            // Should there be a timeout on this?
            ProcessStartResult result = new ProcessStartResult();

            // Always read the entire result from the input stream to avoid leaving bytes in the stream for future processes to stumble upon.
            result.pid = inputStream.readInt();
            result.usingWrapper = inputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw newZygoteStartFailedEx(ex); }}... }Copy the code

The necessary parameters to start the process have been written to the socket, and the Zygote process receives the message sent from the socket for processing. ZygoteInit readArgumentList is a method for getting arguments. The ZygoteInit class does not have the readentList method at all. The thread is broken. You can guess that the socket server is in the Zygote process.

First, the sequence diagram:

Let’s analyze the main function to find the answer. Here’s what the ZygoteInit class does:

It is the startup class for the Zygote process. Preinitialize some classes, then wait for commands on UNIX domain sockets. Based on these commands, subprocesses that inherit the initial state of the VIRTUAL machine are derived.

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

public class ZygoteInit {...public static void main(String argv[]) {
        try{... 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]); }}if (abiList == null) {
                throw new RuntimeException("No ABI list supplied."); } registerZygoteSocket(socketName); . Log.i(TAG,"Accepting command socket connections");
            runSelectLoop(abiList);

            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throwex; }}... }Copy the code

On my device, abiList= “armeabi-v7a,armeabi”, socketName= “zygote”. The registerZygoteSocket function implements the registration of sockets. The static getenv method of the System class is called to find the value corresponding to the ANDROID_SOCKET_zygote string as the key, then the literal value of the string is converted to an integer value, and then the FileDescriptor object is created. Set the integer value to the FileDescriptor object using the setInt$method. Finally, the LocalServerSocket object is created by passing the file descriptor object FD as an input parameter to the LocalServerSocket constructor.

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

public class ZygoteInit {...private static void registerZygoteSocket(String socketName) {
        if (sServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
            try {
                String env = System.getenv(fullSocketName);
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
            }

            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                sServerSocket = new LocalServerSocket(fd);
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex); }}}... }Copy the code

In the LocalServerSocket constructor, the LocalSocketImpl object is created and its LISTEN method is called, Finally, the LocalSocketImpl impl getSockAddress method is used to obtain the LocalSocketAddress object, which is assigned to the LocalServerSocket class member variable localAddress.

frameworks/base/core/java/android/net/LocalServerSocket.java

public class LocalServerSocket {
    private final LocalSocketImpl impl;
    private finalLocalSocketAddress localAddress; .public LocalServerSocket(FileDescriptor fd) throws IOException
    {
        impl = newLocalSocketImpl(fd); impl.listen(LISTEN_BACKLOG); localAddress = impl.getSockAddress(); }... }Copy the code

The LISTEN method internally calls the JNI method listen_native.

frameworks/base/core/java/android/net/LocalSocketImpl.java

class LocalSocketImpl
{...private native void listen_native(FileDescriptor fd, int backlog)
            throws IOException; .protected void listen(int backlog) throws IOException
    {
        if (fd == null) {
            throw new IOException("socket not created"); } listen_native(fd, backlog); }... }Copy the code

Go to the native implementation of listen_native JNI method. First from Java objects to work out the file descriptor fd plastic values, this is to use jniGetFDFromFileDescriptor method. The listen function is then called to listen for client socket connections.

frameworks/base/core/jni/android_net_LocalSocketImpl.cpp

/* private native void listen_native(int fd, int backlog) throws IOException; * /
static void
socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, jint backlog)
{
    int ret;
    int fd;

    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    if (env->ExceptionCheck()) {
        return;
    }

    ret = listen(fd, backlog);

    if (ret < 0) {
        jniThrowIOException(env, errno);
        return; }}Copy the code

The listen method is defined in the sys/socket.h header file.

Sockfd —- binds the socket file descriptor to the address.

Backlog —- server load, indicating the number of outstanding requests to be queued by the system process.

int listen(int sockfd, int backlog);
Copy the code

Now go back to the next call to the runSelectLoop function in the main method of the ZygoteInit class. The runSelectLoop function runs the poll loop of the Zygote process. Accept new connections and read commands from the connections, one request at a time. The poll method is described in detail below. The second argument passed by the poll function is -1, which blocks until one of the FDS arrays reaches the ready state or a signal is captured. POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN: POLLIN If the event occurred on a connected socket descriptor, the client sent a message to the server. If the message processing is complete, the connected socket descriptor is removed from the poll listener list.

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

public class ZygoteInit {...private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            for (int i = 0; i < pollFds.length; ++i) {
                pollFds[i] = new StructPollfd();
                pollFds[i].fd = fds.get(i);
                pollFds[i].events = (short) POLLIN;
            }
            try {
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce();
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

    ......
}
Copy the code

I. Poll function analysis

Let’s start with the poll function. You can see from the previous section that calling the Os class static method poll is ultimately delegated to the Posix class poll method implementation.

libcore/luni/src/main/java/libcore/io/Posix.java

public final class Posix implements Os {...public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException; . }Copy the code

Let’s look at the Native implementation. Focus on the poll method defined in the header file poll.h.

libcore/luni/src/main/native/libcore_io_Posix.cpp

static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {...int rc;
    while (true) {... rc =poll(fds.get(), count, timeoutMs);
        if (rc >= 0|| errno ! = EINTR) {break; }... }...return rc;
}
Copy the code

Select and poll work principle:

  1. Select mainly uses polling to implement ready FD processing;
  2. Poll and SELECT are basically the same, except that poll does not limit the number of FDS.



    The following is a prototype of the function defined in poll.h.
Int poll(struct pollfd FDS [], nfds_t NFDS, int timeout);Copy the code

FDS —- An array of struct pollfd structure types that lists the file descriptors we need to poll(), defined as follows:

typedef struct pollfd {
        int fd;           /* The file descriptor that needs to be detected or selected */
        short events;     /* Events of interest on file descriptor fd */
        short revents;    /* The actual event currently occurring on file descriptor fd */
} pollfd_t;
Copy the code

The Events and Revents fields in the PollFD structure are masks. The caller initializes events to specify the events that need to be checked for descriptor fd. When poll() returns, Revents are set to represent the event that actually happened on the file descriptor.

A bitmask Input in events Return to the revents describe
POLLIN POLLRDNORM POLLRDBAND POLLPRI POLLRDHUB ● ● ● ● ● ● ● ● ● ● Read non-high priority data equivalent to POLLIN read priority data (not available in Linux) Read high priority data Peer socket closed
POLLOUT POLLWRNORM POLLWRBAND Low low low Low low low Common data writable is the same as POLLOUT priority data writable
POLLERR POLLHUP POLLNVAL Low low low An error has occurred hanging up file descriptor is not open
POLLMSG Not available in Linux

The following table lists the masks in the Events and Revents fields:

NFDS —- specifies the number of elements in FDS, and nfds_t is an unsigned integer

Timeout —- Determines blocking behavior, as follows:

-1: blocks until one of the FDS arrays reaches the ready state or a signal is captured

0: does not block and returns immediately

> 0: blocking time

Like select(), the precision of timeout is affected by the granularity of the software clock and rounded up if it is not an integer multiple of the time granularity.

The return value

> 0: total number of socket descriptors in array FDS ready for read, write, or error states;

== 0: no socket descriptor in array FDS is ready to read, write, or fail; The poll times out

-1: the poll function call fails and the global variable errno is automatically set to one of the following values:

EBADF: Invalid file descriptor specified in one or more structures;

EFAULT: the FDS pointer points to an address outside the address space of the process.

EINTR: a signal is generated before the requested event and the call can be re-initiated;

EINVAL: the NFDS parameter exceeds the PLIMIT_NOFILE value;

ENOMEM: Insufficient free memory to complete the request.

AcceptCommandPeer function analysis

The acceptCommandPeer function first calls the server socket to accept the connection from the client, which actually returns a LocalSocket object, and then creates a ZygoteConnection object.

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

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

SServerSocket is actually a LocalServerSocket object, and calling its Accept method is actually calling the Accept method of LocalSocketImpl. Finally, a LocalSocket object is returned that encapsulates a LocalSocketImpl object inside.

frameworks/base/core/java/android/net/LocalServerSocket.java

public class LocalServerSocket {
    private final LocalSocketImpl impl;
    private finalLocalSocketAddress localAddress; .public LocalSocket accept(a) throws IOException
    {
        LocalSocketImpl acceptedImpl = new LocalSocketImpl();

        impl.accept (acceptedImpl);

        return newLocalSocket(acceptedImpl, LocalSocket.SOCKET_UNKNOWN); }... }Copy the code

The accept method implementation in the LocalSocketImpl class ends up calling the overloaded version of jNI implementation.

frameworks/base/core/java/android/net/LocalSocketImpl.java

class LocalSocketImpl
{...privateFileDescriptor fd; .private native FileDescriptor accept
            (FileDescriptor fd, LocalSocketImpl s) throws IOException; .protected void accept(LocalSocketImpl s) throws IOException
    {
        if (fd == null) {
            throw new IOException("socket not created");
        }

        s.fd = accept(fd, s);
        s.mFdCreatedInternally = true; }... }Copy the code

The accept method is defined in the sys/socket.h header file.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Copy the code

The accept system call is primarily used for connection-based socket types, such as SOCK_STREAM and SOCK_SEQPACKET. It extracts the first connection request in the waiting connection queue for a listening socket, creates a new socket, and returns a file descriptor pointing to that socket.

Sockfd —- uses the socket descriptor set up by the system call socket, binds to a local address (usually the server socket) by bind, and listens for connections through LISTEN;

Addr —- a pointer to a struct sockaddr that is filled in with the address of the communication layer server peer socket (usually the client address). The exact format of the returned address addr is determined by the socket address class (such as TCP or UDP). If addr is NULL and no valid address is entered, in this case addrlen is not used and should be set to NULL.

Addrlen —- a value result argument that the calling function must initialize to contain the size of the structure pointed to by ADDR and return with the actual value of the peer address (typically the server address).

frameworks/base/core/jni/android_net_LocalSocketImpl.cpp

/* private native FileDescriptor ** accept (FileDescriptor fd, LocalSocketImpl s) ** throws IOException; * /
static jobject
socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s)
{
    union {
        struct sockaddr address;
        struct sockaddr_un un_address;
    } sa;

    int ret;
    int retFD;
    int fd;
    socklen_t addrlen;

    if (s == NULL) {
        jniThrowNullPointerException(env, NULL);
        return NULL;
    }

    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    if (env->ExceptionCheck()) {
        return NULL;
    }

    do {
        addrlen = sizeof(sa);
        ret = accept(fd, &(sa.address), &addrlen);
    } while (ret < 0 && errno == EINTR);

    if (ret < 0) {
        jniThrowIOException(env, errno);
        return NULL;
    }

    retFD = ret;

    return jniCreateFileDescriptor(env, retFD);
}
Copy the code

In the ZygoteConnection constructor, the input and output streams of the socket are taken from LocalSocket and the connection timeout is set to 1000 ms. This allows you to interact with the client.

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

class ZygoteConnection {
    private static final String TAG = "Zygote";

    private static final int CONNECTION_TIMEOUT_MILLIS = 1000;

    private final LocalSocket mSocket;
    private final DataOutputStream mSocketOutStream;
    private final BufferedReader mSocketReader;
    private final Credentials peer;
    private final String abiList;

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

        mSocketOutStream
                = new DataOutputStream(socket.getOutputStream());

        mSocketReader = new BufferedReader(
                new InputStreamReader(socket.getInputStream()), 256);

        mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);

        try {
            peer = mSocket.getPeerCredentials();
        } catch (IOException ex) {
            Log.e(TAG, "Cannot read peer credentials", ex);
            throwex; }}... }Copy the code

3. RunOnce function analysis

Finally, what does the runOnce function do? RunOnce is defined in the ZygoteConnection class. First call readentList to get the start process arguments from the client. Then check permissions. Then call forkAndSpecialize to create a new process, return PID equals 0, and start calling handleChildProc to handle the child process. Return PID > 0 and start calling handleParentProc to process the parent process.

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

class ZygoteConnection {...boolean runOnce(a) throws ZygoteInit.MethodAndArgsCaller {

        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;

        try {
            args = readArgumentList();
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            Log.w(TAG, "IOException on command socket " + ex.getMessage());
            closeSocket();
            return true;
        }

        if (args == null) {
            // EOF reached.
            closeSocket();
            return true;
        }

        /** the stderr of the most recent request, if avail */
        PrintStream newStderr = null;

        if(descriptors ! =null && descriptors.length >= 3) {
            newStderr = new PrintStream(
                    new FileOutputStream(descriptors[2]));
        }

        int pid = -1;
        FileDescriptor childPipeFd = null;
        FileDescriptor serverPipeFd = null;

        try {
            parsedArgs = new Arguments(args);

            if (parsedArgs.abiListQuery) {
                return handleAbiListQuery();
            }

            if(parsedArgs.permittedCapabilities ! =0|| parsedArgs.effectiveCapabilities ! =0) {
                throw new ZygoteSecurityException("Client may not specify capabilities: " +
                        "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
                        ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
            }

            applyUidSecurityPolicy(parsedArgs, peer);
            applyInvokeWithSecurityPolicy(parsedArgs, peer);

            applyDebuggerSystemProperty(parsedArgs);
            applyInvokeWithSystemProperty(parsedArgs);

            int[][] rlimits = null;

            if(parsedArgs.rlimits ! =null) {
                rlimits = parsedArgs.rlimits.toArray(intArray2d);
            }

            if(parsedArgs.invokeWith ! =null) {
                FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
                childPipeFd = pipeFds[1];
                serverPipeFd = pipeFds[0];
                Os.fcntlInt(childPipeFd, F_SETFD, 0);
            }

            /** * In order to avoid leaking descriptors to the Zygote child, * the native code must close the two Zygote socket descriptors * in the child process before it switches from Zygote-root to * the UID and privileges of the application being launched. * * In order to avoid "bad file descriptor" errors when the * two LocalSocket objects are closed, the Posix file * descriptors are released via a dup2() call which closes * the socket and substitutes an open descriptor  to /dev/null. */

            int [] fdsToClose = { -1, -1 };

            FileDescriptor fd = mSocket.getFileDescriptor();

            if(fd ! =null) {
                fdsToClose[0] = fd.getInt$();
            }

            fd = ZygoteInit.getServerSocketFileDescriptor();

            if(fd ! =null) {
                fdsToClose[1] = fd.getInt$();
            }

            fd = null;

            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;
                returnhandleParentProc(pid, descriptors, serverPipeFd, parsedArgs); }}finally{ IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}... }Copy the code

ForkAndSpecialize simply passes parameters to the nativeForkAndSpecialize method, which is a JNI function.

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

public final class Zygote {...public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
          String instructionSet, String appDataDir) {
        VM_HOOKS.preFork();
        int pid = nativeForkAndSpecialize(
                  uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                  instructionSet, appDataDir);
        // Enable tracing as soon as possible for the child process.
        if (pid == 0) {
            Trace.setTracingEnabled(true);

            // Note that this event ends at the end of handleChildProc,
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
        }
        VM_HOOKS.postForkCommon();
        return pid;
    }

    native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
          String instructionSet, String appDataDir); . }Copy the code

The Native implementation is defined in com_Android_internal_OS_zygote.cpp. Nativeforkandspecializecommon calls the ForkAndSpecializeCommon function.

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 debug_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {
    // Grant CAP_WAKE_ALARM to the Bluetooth process.
    jlong capabilities = 0;
    if (uid == AID_BLUETOOTH) {
        capabilities |= (1LL << CAP_WAKE_ALARM);
    }

    return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
            rlimits, capabilities, capabilities, mount_external, se_info,
            se_name, false, fdsToClose, instructionSet, appDataDir);
}
Copy the code

The ForkAndSpecializeCommon function sees the familiar fork system call, which returns twice, once to the child and once to the parent.

frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                     jint debug_flags, jobjectArray javaRlimits,
                                     jlong permittedCapabilities, jlong effectiveCapabilities,
                                     jint mount_external,
                                     jstring java_se_info, jstring java_se_name,
                                     bool is_system_server, jintArray fdsToClose,
                                     jstring instructionSet, jstring dataDir) {...pid_tpid = fork(); . }Copy the code

One of the wonders of the fork call is that it is called only once and can return twice. It may have three different return values:

1. In the parent process, fork returns the process ID of the newly created child process;

2. In the child process, fork returns 0;

3. If an error occurs, fork returns a negative value.

After the fork function completes, if the new process is created successfully, there are two processes, one child and one parent. In the child process, fork returns 0, and in the parent process, fork returns the process ID of the newly created child. We can use the value returned by fork to determine whether the current process is a child or a parent.

Finally, we will analyze the handleChildProc function. Since the –invoke-with parameter is not passed to the client, we will focus on zygoteInit, the static method of the RuntimeInit class.

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

class ZygoteConnection {...private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        /** * By the time we get here, the native code has closed two actual Zygote socket connections and replaced /dev/null in their place. The LocalSocket object still needs to be closed properly. * /

        closeSocket();
        ZygoteInit.closeServerSocket();

        if(descriptors ! =null) {
            try {
                Os.dup2(descriptors[0], STDIN_FILENO);
                Os.dup2(descriptors[1], STDOUT_FILENO);
                Os.dup2(descriptors[2], STDERR_FILENO);

                for (FileDescriptor fd: descriptors) {
                    IoUtils.closeQuietly(fd);
                }
                newStderr = System.err;
            } catch (ErrnoException ex) {
                Log.e(TAG, "Error reopening stdio", ex); }}if(parsedArgs.niceName ! =null) {
            Process.setArgV0(parsedArgs.niceName);
        }

        // End of the postFork event.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        if(parsedArgs.invokeWith ! =null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(),
                    pipeFd, parsedArgs.remainingArgs);
        } else {
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, null /* classLoader */); }}... }Copy the code

The RuntimeInit class zygoteInit static method calls four methods in turn to do some work, redirecting system. out and system. err to the Android log, general initialization, As well as native initialization, the last function is the main function called when the application is launched via the Wrapper process.

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

public class RuntimeInit {...public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit"); redirectLogStreams(); commonInit(); nativeZygoteInit(); applicationInit(targetSdkVersion, argv, classLoader); }... }Copy the code

The invokeStaticMain method is eventually called in the applicationInit method.

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

public class RuntimeInit {...private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {...final Arguments args;
        try {
            args = new Arguments(argv);
        } catch (IllegalArgumentException ex) {
            Slog.e(TAG, ex.getMessage());
            // let the process exit
            return; }... invokeStaticMain(args.startClass, args.startArgs, classLoader); }... }Copy the code

The invokeStaticMain Method gets the corresponding Class and Method by reflection, and then throws an exception of type MethodAndArgsCaller that calls ZygoteInit’s internal static Class. This exception inherits the Runnable interface. Eventually this exception is caught by zygoteinit.main (), which responds by calling the exception’s run() method.

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

public class RuntimeInit {...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 {
            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. */
        throw newZygoteInit.MethodAndArgsCaller(m, argv); }... }Copy the code

We see that the MethodAndArgsCaller run Method calls the Method represented by the Method object passed in the previous step. This method is actually an android. App. ActivityThread static main method.

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

    public static class MethodAndArgsCaller extends Exception
            implements Runnable {
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

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

        public void run(a) {
            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 newRuntimeException(ex); }}}Copy the code

The MethodAndArgsCaller exception is caught in the main method of the ZygoteInit class and its run method is called. So far the process is all straightened out! The reason for this, combined with the comments, is that this arrangement removes all stack frames required to request the setup process.

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

public class ZygoteInit {...public static void main(String argv[]) {
        try{... }catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch(RuntimeException ex) { ...... }}... }Copy the code

Finally, the readArgumentList method is nowhere to be found in the ZygoteInit class. It is found in the runOnce method. It is very simple.

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

class ZygoteConnection {...private String[] readArgumentList()
            throws IOException {

        int argc;

        try {
            String s = mSocketReader.readLine();

            if (s == null) {
                // EOF reached.
                return null;
            }
            argc = Integer.parseInt(s);
        } catch (NumberFormatException ex) {
            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
            throw new IOException("invalid wire format");
        }

        // See bug 1092107: large argc can be used for a DOS attack
        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"); }}returnresult; }... }Copy the code