Android application startup involves process startup, and we know that the Linux kernel implements process replication through fork, vfork, and Clone system calls.

Fork is a heavyweight call because it creates a full copy of the parent process and then executes as a child process. To reduce the amount of work associated with this call, Linux uses a copy-on-write technique.

Vfork is similar to fork, but does not create a copy of the parent’s data. Instead, parent and child processes share data. Since fork uses a copy-on-write technique, Vfork no longer has an advantage in terms of speed, so avoid it.

Clone creates threads that provide precise control over sharing and replication between parent and child processes.

When an Android application starts creating a new process, the Linux system calls fork.

Back to the ActivityManagerService startProcessLocked method, the static method start of the Process class is called to start the new Process.

For a preview of the sequence diagram, the sequence diagram is actually drawn after analysis.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback {...private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {...try{...if (entryPoint == null) entryPoint = "android.app.ActivityThread"; . Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs); . }catch(RuntimeException e) { ...... }}... }Copy the code

The start function does:

Start a new process.

If processes are enabled, a new process is created and processClass’s static main() function is executed there. When this function returns, the process continues to run.

If the process is not enabled, a new thread is created in the caller’s process and main() of processClass is called there.

If it is not an empty string, the niceName parameter is a custom name to be supplied to the process, rather than using processClass. This allows you to easily create identifiable processes, even if you use the same basic processClass to start them.

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

public class Process {...public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex); }}... }Copy the code

Start just goes through and actually calls the startViaZygote method. In startViaZygote method method, into the organization in an ArrayList list, the basic meaning of each parameter is self-explanatory, last call zygoteSendArgsAndGetResult function. Call zygoteSendArgsAndGetResult function before you call the openZygoteSocketIfNeeded function. Both functions are known by their names.

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

public class Process {.../** * Start a new process through the zygote mechanism. * *@paramProcessClass the name of the class to run its static main() method *@paramNiceName Specifies the name of the beautiful process that appears in the ps command@paramUid A POSIX UID. New processes should use setuid() *@paramGid A POSIX GID. The new process should use setgid() *@paramGids null also works; The new process should set group() to a list of supplementary group ids *@paramDebugFlags Additional flags *@paramTargetSdkVersion targetSdkVersion of the application *@paramSeInfo null is also available, SELinux information for the new process *@paramAbi processes should use ABI *@paramInstructionSet null is also available for the instructionSet to use *@paramAppDataDir NULL is also available for the application's data directory *@paramExtraArgs is the extra parameter * for the Zygote process@returnObject * that describes the result of trying to start the process@throwsZygoteStartFailedEx If the process fails to start for any reason */
    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

Let’s analyze openZygoteSocketIfNeeded first. It attempts to open the socket to the Zygote process. If it is, do nothing. Possible blocking and retry.

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

public class Process {...public static final String ZYGOTE_SOCKET = "zygote"; ./** * The state of the connection to the main zygote. * /
    static ZygoteState primaryZygoteState;
    /** * The state of the connection with the second zygote. * /
    staticZygoteState secondaryZygoteState; .private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
            try {
                primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); }}if (primaryZygoteState.matches(abi)) {
            return primaryZygoteState;
        }

        // Does not match the main zygote, try the second one
        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
            try {
            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); }}if (secondaryZygoteState.matches(abi)) {
            return secondaryZygoteState;
        }

        throw new ZygoteStartFailedEx("Unsupported zygote ABI: "+ abi); }... }Copy the code

The next step is to analyze the static method connect of the ZygoteState class. The ZygoteState class represents the state of communication with the Zygote process. If the argument passed to the connect method is ZYGOTE_SOCKET (which is the string “zygote”), this represents the remote address to connect to. This method first creates a LocalSocket object and then calls its connect method. After the connection is successful, the input and output streams of the socket are obtained and the input stream is encapsulated as a DataInputStream object. The output stream is then encapsulated as a BufferedWriter object. You can then retrieve messages from the Zygote process via the DataInputStream object and send messages to the Zygote process via the BufferedWriter object.

public class Process {...public static class ZygoteState {
        final LocalSocket socket;
        final DataInputStream inputStream;
        final BufferedWriter writer;
        final List<String> abiList;

        boolean mClosed;
        
        private ZygoteState(LocalSocket socket, DataInputStream inputStream, BufferedWriter writer, List
       
         abiList)
        {
            this.socket = socket;
            this.inputStream = inputStream;
            this.writer = writer;
            this.abiList = abiList;
        }

        public static ZygoteState connect(String socketAddress) throws IOException {
            DataInputStream zygoteInputStream = null;
            BufferedWriter zygoteWriter = null;
            final LocalSocket zygoteSocket = new LocalSocket();

            try {
                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
                        LocalSocketAddress.Namespace.RESERVED));

                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());

                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                        zygoteSocket.getOutputStream()), 256);
            } catch (IOException ex) {
                try {
                    zygoteSocket.close();
                } catch (IOException ignore) {
                }

                throw ex;
            }

            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
            Log.i("Zygote"."Process: zygote socket opened, supported ABIS: " + abiListString);

            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                    Arrays.asList(abiListString.split(",")));
        }

        boolean matches(String abi) {
            return abiList.contains(abi);
        }

        public void close(a) {
            try {
                socket.close();
            } catch (IOException ex) {
                Log.e(LOG_TAG,"I/O exception on routine close", ex);
            }

            mClosed = true;
        }

        boolean isClosed(a) {
            returnmClosed; }}... }Copy the code

Continue analyzing the connect method of the LocalSocket object. The LocalSocket class creates a (non-server) socket in the UNIX domain name space.

Connect method, with an input of type LocalSocketAddress, that throws IOException if already connected. The implCreateIfNeeded function creates the LocalSocketImpl object when needed. Next, call the Connect method of the LocalSocketImpl object.

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

public class LocalSocket implements Closeable {
    private finalLocalSocketImpl impl; ./** * connect this socket to the endpoint. Can only be called on instances that have not yet been connected. * *@paramEndpoint endpoint address *@throwsIOException If the socket is invalid or the address does not exist */
    public void connect(LocalSocketAddress endpoint) throws IOException {
        synchronized (this) {
            if (isConnected) {
                throw new IOException("already connected");
            }

            implCreateIfNeeded();
            impl.connect(endpoint, 0);
            isConnected = true;
            isBound = true; }}... }Copy the code

LocalSocketImpl classes for android.net.LocalSocket and android.net.LocalServerSocket socket implementation. Only AF_LOCAL sockets are supported.

Based on the call stack above, timeout is passed equal to 0. In LocalSocketImpl’s connect method, first check whether the file descriptor is empty, then throw IOException, and then call the internal native function connectLocal.

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

class LocalSocketImpl
{...private native void connectLocal(FileDescriptor fd, String name,
            int namespace) throws IOException; ./** Note: Timeout is currently ignored */
    protected void connect(LocalSocketAddress address, int timeout)
                        throws IOException
    {        
        if (fd == null) {
            throw new IOException("socket not created"); } connectLocal(fd, address.getName(), address.getNamespace().getId()); }... }Copy the code

Through the JNI interface, the LocalSocketImpl connectLocal method is finally implemented by the socket_CONNECt_local local method. The socket_connect_local method first fetches the file descriptor fd and then calls the socket_local_client_connect method.

frameworks/base/core/jni/android_net_LocalSocketImpl.cpp

static void
socket_connect_local(JNIEnv *env, jobject object, jobject fileDescriptor, jstring name, jint namespaceId)
{
    int ret;
    int fd;

    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

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

    ScopedUtfChars nameUtf8(env, name);

    ret = socket_local_client_connect(
                fd,
                nameUtf8.c_str(),
                namespaceId,
                SOCK_STREAM);

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

The socket_local_client_connect method is defined in the header file cutils/sockets. H and implemented in socket_local_client.c. The socket_make_sockadDR_un method is first called to build the sockaddr_un structure, and then connect is called to make the actual connection. The connect method is defined in the sys/socket.h header, which is a standard Linux system call method.

system/core/libcutils/socket_local_client.c

/** * returns the same fd when connected to a peer named "name" on a fd or -1 on an error. * /
int socket_local_client_connect(int fd, const char *name, int namespaceId, 
        int type UNUSED)
{
    struct sockaddr_un addr;
    socklen_t alen;
    int err;

    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);

    if (err < 0) {
        goto error;
    }

    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
        goto error;
    }

    return fd;

error:
    return - 1;
}
Copy the code

What type of socket is it? This needs to start with socket creation. The implCreateIfNeeded function in the LocalSocket class, which creates the LocalSocketImpl object when needed, has the answer.

The create method of the LocalSocketImpl object is actually called in the implCreateIfNeeded method. Note that its input parameter, sockType, is ultimately assigned to SOCKET_STREAM, which creates the stream socket. SOCK_STREAM indicates that TCP is used.

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

public class LocalSocket implements Closeable {
    private finalLocalSocketImpl impl; .private final intsockType; .public static final int SOCKET_STREAM = 2; ./** * Create an AF_LOCAL/UNIX domain stream socket. * /
    public LocalSocket(a) {
        this(SOCKET_STREAM);
    }

    /** * Create an AF_LOCAL/UNIX domain stream socket with the given socket type */
    public LocalSocket(int sockType) {
        this(new LocalSocketImpl(), sockType);
        isBound = false;
        isConnected = false; }.../*package*/ LocalSocket(LocalSocketImpl impl, int sockType) {
        this.impl = impl;
        this.sockType = sockType;
        this.isConnected = false;
        this.isBound = false; }...private void implCreateIfNeeded(a) throws IOException {
        if(! implCreated) {synchronized (this) {
                if(! implCreated) {try {
                        impl.create(sockType);
                    } finally {
                        implCreated = true;
                    }
                }
            }
        }
    }
    ......
}
Copy the code

Next, examine the create method of the LocalSocketImpl class. The create method creates a socket in the underlying operating system and then calls the Os static method Socket to create the socket. Here we focus on AF_UNIX.

UNIX (AF_UNIX) domains allow communication between applications on the same host. (POSIx.1g uses the name AF_LOCAL as a synonym for AF_UNIX)

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

class LocalSocketImpl
{...public void create (int sockType) throws IOException {
        if (fd == null) {
            int osType;
            switch (sockType) {
                case LocalSocket.SOCKET_DGRAM:
                    osType = OsConstants.SOCK_DGRAM;
                    break;
                case LocalSocket.SOCKET_STREAM:
                    osType = OsConstants.SOCK_STREAM;
                    break;
                case LocalSocket.SOCKET_SEQPACKET:
                    osType = OsConstants.SOCK_SEQPACKET;
                    break;
                default:
                    throw new IllegalStateException("unknown sockType");
            }
            try {
                fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
                mFdCreatedInternally = true;
            } catch(ErrnoException e) { e.rethrowAsIOException(); }}}... }Copy the code

The static method socket of the Os class takes three arguments OsConstants.AF_UNIX, OsConstants.SOCK_STREAM, and 0. This method just does a relay and ends up calling the OS socket method, a static member of the Libcore class.

libcore/luni/src/main/java/android/system/Os.java

public final class Os {.../** * See socket(2). */
  public static FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { returnLibcore.os.socket(domain, type, protocol); }... }Copy the code

The Libcore class implementation is very simple, first making the constructor private and then defining a member variable OS, which is actually a BlockGuardOs object that implements the OS interface.

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

package libcore.io;

public final class Libcore {
    private Libcore(a) {}public static Os os = new BlockGuardOs(new Posix());
}
Copy the code

The following is an analysis of the construction process of BlockGuardOs objects. In addition, BlockGuardOs does not inherit directly from the Os interface, indicating that the ForwardingOs class may implement the Os interface.

We see that the Socket method is overridden in the BlockGuardOs class. This calls the Posix object passed to the BlockGuardOs object when the Libcore class initializes the static member OS.

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

public class BlockGuardOs extends ForwardingOs {
    public BlockGuardOs(Os os) {
        super(os); }...@Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {
        returntagSocket(os.socket(domain, type, protocol)); }... }Copy the code

Checking the ForwardingOs source confirms our suspicions that it implements the Os interface.

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

public class ForwardingOs implements Os {
    protected final Os os;

    public ForwardingOs(Os os) {
        this.os = os; }...public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { returnos.socket(domain, type, protocol); }... }Copy the code

The Os interface defines a series of socket-related functions, including socket functions.

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

public interface Os {...public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; . }Copy the code

After the above analysis, we know that the final socket implementation is delegated to the SOCKET method of Posix class, which is a JNI method.

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

public final class Posix implements Os {
    Posix() { }
    ......
    public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; . }Copy the code

Let’s analyze the native implementation of Posix class socket methods. ThrowIfMinusOne is a helper function that throws a Java exception through Native when the socket(domain, Type, protocol) call returns -1.

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

static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {
    if (domain == AF_PACKET) {
        protocol = htons(protocol);  // Packet sockets specify the protocol in host byte order.
    }
    int fd = throwIfMinusOne(env, "socket".TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
    returnfd ! =- 1 ? jniCreateFileDescriptor(env, fd) : NULL;
}
Copy the code

Socket (domain, type, protocol) is defined in the <sys/socket.h> header file.

int socket (int domain, int type, int protocol);
Copy the code

Domain —- Communication features. Each domain has its own Address representation format, starting with AF, indicating the Address family.



Type —- Indicates the socket type to further determine the communication characteristics.



Protocol —- Selects the default protocol for a given domain and socket type. When multiple protocols are supported for the same domain and socket type, you can select a specific protocol by using this field. The default value is 0.



In Process startViaZygote method, the above Process openZygoteSocketIfNeeded function is analyzed, and the next section, continue to analyze zygoteSendArgsAndGetResult function, through its function name can you guess its role.