FileDocCategorySizeDatePackage
ZygoteInit.javaAPI DocAndroid 5.1 API32461Thu Mar 12 22:22:10 GMT 2015com.android.internal.os

ZygoteInit

public class ZygoteInit extends Object
Startup class for the zygote process. Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these commands, forks off child processes that inherit the initial state of the VM. Please see {@link ZygoteConnection.Arguments} for documentation on the client protocol.
hide

Fields Summary
private static final String
TAG
private static final String
PROPERTY_DISABLE_OPENGL_PRELOADING
private static final String
ANDROID_SOCKET_PREFIX
private static final int
LOG_BOOT_PROGRESS_PRELOAD_START
private static final int
LOG_BOOT_PROGRESS_PRELOAD_END
private static final int
PRELOAD_GC_THRESHOLD
when preloading, GC after allocating this many bytes
private static final String
ABI_LIST_ARG
private static final String
SOCKET_NAME_ARG
private static android.net.LocalServerSocket
sServerSocket
private static android.content.res.Resources
mResources
Used to pre-load resources. We hold a global reference on it so it never gets destroyed.
static final int
GC_LOOP_COUNT
The number of times that the main Zygote loop should run before calling gc() again.
private static final String
PRELOADED_CLASSES
The path of a file that contains classes to preload.
private static final boolean
PRELOAD_RESOURCES
Controls whether we should preload resources during zygote init.
private static final int
UNPRIVILEGED_UID
private static final int
UNPRIVILEGED_GID
private static final int
ROOT_UID
private static final int
ROOT_GID
Constructors Summary
private ZygoteInit()
Class not instantiable.

    
Methods Summary
private static ZygoteConnectionacceptCommandPeer(java.lang.String abiList)
Waits for and accepts a single command connection. Throws RuntimeException on failure.

        try {
            return new ZygoteConnection(sServerSocket.accept(), abiList);
        } catch (IOException ex) {
            throw new RuntimeException(
                    "IOException during accept()", ex);
        }
    
static voidcloseServerSocket()
Close and clean up zygote sockets. Called on shutdown and on the child's exit path.

        try {
            if (sServerSocket != null) {
                FileDescriptor fd = sServerSocket.getFileDescriptor();
                sServerSocket.close();
                if (fd != null) {
                    Os.close(fd);
                }
            }
        } catch (IOException ex) {
            Log.e(TAG, "Zygote:  error closing sockets", ex);
        } catch (ErrnoException ex) {
            Log.e(TAG, "Zygote:  error closing descriptor", ex);
        }

        sServerSocket = null;
    
static native java.io.FileDescriptorcreateFileDescriptor(int fd)
Creates a file descriptor from an int fd.

param
fd integer OS file descriptor
return
non-null; FileDescriptor instance
throws
IOException if fd is invalid

static voidgc()
Runs several special GCs to try to clean up a few generations of softly- and final-reachable objects, along with any other garbage. This is only useful just before a fork().

        final VMRuntime runtime = VMRuntime.getRuntime();

        /* runFinalizationSync() lets finalizers be called in Zygote,
         * which doesn't have a HeapWorker thread.
         */
        System.gc();
        runtime.runFinalizationSync();
        System.gc();
        runtime.runFinalizationSync();
        System.gc();
        runtime.runFinalizationSync();
    
static java.io.FileDescriptorgetServerSocketFileDescriptor()
Return the server socket's underlying file descriptor, so that ZygoteConnection can pass it to the native code for proper closure after a child process is forked off.

        return sServerSocket.getFileDescriptor();
    
static native intgetpgid(int pid)
Invokes the linux syscall "getpgid"

param
pid pid to query
return
pgid of pid in question
throws
IOException on error

private static voidhandleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
Finish remaining work for the newly forked system server process.


        closeServerSocket();

        // set umask to 0077 so new files and directories will default to owner-only permissions.
        Os.umask(S_IRWXG | S_IRWXO);

        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName);
        }

        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
        if (systemServerClasspath != null) {
            performSystemServerDexOpt(systemServerClasspath);
        }

        if (parsedArgs.invokeWith != null) {
            String[] args = parsedArgs.remainingArgs;
            // If we have a non-null system server class path, we'll have to duplicate the
            // existing arguments and append the classpath to it. ART will handle the classpath
            // correctly when we exec a new process.
            if (systemServerClasspath != null) {
                String[] amendedArgs = new String[args.length + 2];
                amendedArgs[0] = "-cp";
                amendedArgs[1] = systemServerClasspath;
                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
            }

            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    null, args);
        } else {
            ClassLoader cl = null;
            if (systemServerClasspath != null) {
                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
                Thread.currentThread().setContextClassLoader(cl);
            }

            /*
             * Pass the remaining arguments to SystemServer.
             */
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
        }

        /* should never reach here */
    
private static booleanhasSecondZygote(java.lang.String abiList)
Return {@code true} if this device configuration has another zygote. We determine this by comparing the device ABI list with this zygotes list. If this zygote supports all ABIs this device supports, there won't be another zygote.

        return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
    
static voidinvokeStaticMain(java.lang.ClassLoader loader, java.lang.String className, java.lang.String[] argv)
Invokes a static "main(argv[]) method on class "className". Converts various failing exceptions into RuntimeExceptions, with the assumption that they will then cause the VM instance to exit.

param
loader class loader to use
param
className Fully-qualified class name
param
argv Argument vector for main()


                                                     
       
               
              
        Class<?> cl;

        try {
            cl = loader.loadClass(className);
        } 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 new ZygoteInit.MethodAndArgsCaller(m, argv);
    
public static voidmain(java.lang.String[] argv)

        try {
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();

            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]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }

            registerZygoteSocket(socketName);
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                SystemClock.uptimeMillis());
            preload();
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());

            // Finish profiling the zygote initialization.
            SamplingProfilerIntegration.writeZygoteSnapshot();

            // Do an initial gc to clean up after startup
            gc();

            // Disable tracing so that forked processes do not inherit stale tracing tags from
            // Zygote.
            Trace.setTracingEnabled(false);

            if (startSystemServer) {
                startSystemServer(abiList, 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();
            throw ex;
        }
    
private static voidperformSystemServerDexOpt(java.lang.String classPath)
Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction set of the current runtime.

        final String[] classPathElements = classPath.split(":");
        final InstallerConnection installer = new InstallerConnection();
        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();

        try {
            for (String classPathElement : classPathElements) {
                final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
                        false /* defer */);
                if (dexopt == DexFile.DEXOPT_NEEDED) {
                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
                } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
                    installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
                }
            }
        } catch (IOException ioe) {
            throw new RuntimeException("Error starting system_server", ioe);
        } finally {
            installer.disconnect();
        }
    
private static longposixCapabilitiesAsBits(int capabilities)
Gets the bit array representation of the provided list of POSIX capabilities.

        long result = 0;
        for (int capability : capabilities) {
            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
                throw new IllegalArgumentException(String.valueOf(capability));
            }
            result |= (1L << capability);
        }
        return result;
    
static voidpreload()

        Log.d(TAG, "begin preload");
        preloadClasses();
        preloadResources();
        preloadOpenGL();
        preloadSharedLibraries();
        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
        // for memory sharing purposes.
        WebViewFactory.prepareWebViewInZygote();
        Log.d(TAG, "end preload");
    
private static voidpreloadClasses()
Performs Zygote process initialization. Loads and initializes commonly used classes. Most classes only cause a few hundred bytes to be allocated, but a few will allocate a dozen Kbytes (in one case, 500+K).

        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;
        }

        Log.i(TAG, "Preloading classes...");
        long startTime = SystemClock.uptimeMillis();

        // Drop root perms while running static initializers.
        setEffectiveGroup(UNPRIVILEGED_GID);
        setEffectiveUser(UNPRIVILEGED_UID);

        // Alter the target heap utilization.  With explicit GCs this
        // is not likely to have any effect.
        float defaultUtilization = runtime.getTargetHeapUtilization();
        runtime.setTargetHeapUtilization(0.8f);

        // Start with a clean slate.
        System.gc();
        runtime.runFinalizationSync();
        Debug.startAllocCounting();

        try {
            BufferedReader br
                = new BufferedReader(new InputStreamReader(is), 256);

            int count = 0;
            String line;
            while ((line = br.readLine()) != null) {
                // Skip comments and blank lines.
                line = line.trim();
                if (line.startsWith("#") || line.equals("")) {
                    continue;
                }

                try {
                    if (false) {
                        Log.v(TAG, "Preloading " + line + "...");
                    }
                    Class.forName(line);
                    if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
                        if (false) {
                            Log.v(TAG,
                                " GC at " + Debug.getGlobalAllocSize());
                        }
                        System.gc();
                        runtime.runFinalizationSync();
                        Debug.resetGlobalAllocSize();
                    }
                    count++;
                } catch (ClassNotFoundException e) {
                    Log.w(TAG, "Class not found for preloading: " + line);
                } catch (UnsatisfiedLinkError e) {
                    Log.w(TAG, "Problem preloading " + line + ": " + e);
                } catch (Throwable t) {
                    Log.e(TAG, "Error preloading " + line + ".", t);
                    if (t instanceof Error) {
                        throw (Error) t;
                    }
                    if (t instanceof RuntimeException) {
                        throw (RuntimeException) t;
                    }
                    throw new RuntimeException(t);
                }
            }

            Log.i(TAG, "...preloaded " + count + " classes in "
                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
        } catch (IOException e) {
            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
        } finally {
            IoUtils.closeQuietly(is);
            // Restore default.
            runtime.setTargetHeapUtilization(defaultUtilization);

            // Fill in dex caches with classes, fields, and methods brought in by preloading.
            runtime.preloadDexCaches();

            Debug.stopAllocCounting();

            // Bring back root. We'll need it later.
            setEffectiveUser(ROOT_UID);
            setEffectiveGroup(ROOT_GID);
        }
    
private static intpreloadColorStateLists(dalvik.system.VMRuntime runtime, android.content.res.TypedArray ar)

        int N = ar.length();
        for (int i=0; i<N; i++) {
            if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
                if (false) {
                    Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
                }
                System.gc();
                runtime.runFinalizationSync();
                Debug.resetGlobalAllocSize();
            }
            int id = ar.getResourceId(i, 0);
            if (false) {
                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
            }
            if (id != 0) {
                if (mResources.getColorStateList(id) == null) {
                    throw new IllegalArgumentException(
                            "Unable to find preloaded color resource #0x"
                            + Integer.toHexString(id)
                            + " (" + ar.getString(i) + ")");
                }
            }
        }
        return N;
    
private static intpreloadDrawables(dalvik.system.VMRuntime runtime, android.content.res.TypedArray ar)

        int N = ar.length();
        for (int i=0; i<N; i++) {
            if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
                if (false) {
                    Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
                }
                System.gc();
                runtime.runFinalizationSync();
                Debug.resetGlobalAllocSize();
            }
            int id = ar.getResourceId(i, 0);
            if (false) {
                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
            }
            if (id != 0) {
                if (mResources.getDrawable(id, null) == null) {
                    throw new IllegalArgumentException(
                            "Unable to find preloaded drawable resource #0x"
                            + Integer.toHexString(id)
                            + " (" + ar.getString(i) + ")");
                }
            }
        }
        return N;
    
private static voidpreloadOpenGL()

        if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        }
    
private static voidpreloadResources()
Load in commonly used resources, so they can be shared across processes. These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even larger.

        final VMRuntime runtime = VMRuntime.getRuntime();

        Debug.startAllocCounting();
        try {
            System.gc();
            runtime.runFinalizationSync();
            mResources = Resources.getSystem();
            mResources.startPreloading();
            if (PRELOAD_RESOURCES) {
                Log.i(TAG, "Preloading resources...");

                long startTime = SystemClock.uptimeMillis();
                TypedArray ar = mResources.obtainTypedArray(
                        com.android.internal.R.array.preloaded_drawables);
                int N = preloadDrawables(runtime, ar);
                ar.recycle();
                Log.i(TAG, "...preloaded " + N + " resources in "
                        + (SystemClock.uptimeMillis()-startTime) + "ms.");

                startTime = SystemClock.uptimeMillis();
                ar = mResources.obtainTypedArray(
                        com.android.internal.R.array.preloaded_color_state_lists);
                N = preloadColorStateLists(runtime, ar);
                ar.recycle();
                Log.i(TAG, "...preloaded " + N + " resources in "
                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
            }
            mResources.finishPreloading();
        } catch (RuntimeException e) {
            Log.w(TAG, "Failure preloading resources", e);
        } finally {
            Debug.stopAllocCounting();
        }
    
private static voidpreloadSharedLibraries()

        Log.i(TAG, "Preloading shared libraries...");
        System.loadLibrary("android");
        System.loadLibrary("compiler_rt");
        System.loadLibrary("jnigraphics");
    
private static voidregisterZygoteSocket(java.lang.String socketName)
Registers a server socket for zygote command connections

throws
RuntimeException when open fails

        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 {
                sServerSocket = new LocalServerSocket(
                        createFileDescriptor(fileDesc));
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    
static native voidreopenStdio(java.io.FileDescriptor in, java.io.FileDescriptor out, java.io.FileDescriptor err)
Invokes the syscall dup2() to copy the specified descriptors into stdin, stdout, and stderr. The existing stdio descriptors will be closed and errors during close will be ignored. The specified descriptors will also remain open at their original descriptor numbers, so the caller may want to close the original descriptors.

param
in new stdin
param
out new stdout
param
err new stderr
throws
IOException

private static voidrunSelectLoop(java.lang.String abiList)
Runs the zygote process's select loop. Accepts new connections as they happen, and reads commands from connections one spawn-request's worth at a time.

throws
MethodAndArgsCaller in a child process when a main() should be executed.

        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;

            /*
             * Call gc() before we block in select().
             * It's work that has to be done anyway, and it's better
             * to avoid making every child do it.  It will also
             * madvise() any free memory as a side-effect.
             *
             * Don't call it every time, because walking the entire
             * heap is a lot of overhead to free a few hundred bytes.
             */
            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);
                }
            }
        }
    
static native intselectReadable(java.io.FileDescriptor[] fds)
Invokes select() on the provider array of file descriptors (selecting for readability only). Array elements of null are ignored.

param
fds non-null; array of readable file descriptors
return
index of descriptor that is now readable or -1 for empty array.
throws
IOException if an error occurs

static native voidsetCloseOnExec(java.io.FileDescriptor fd, boolean flag)
Toggles the close-on-exec flag for the specified file descriptor.

param
fd non-null; file descriptor
param
flag desired close-on-exec flag state
throws
IOException

private static voidsetEffectiveGroup(int gid)
Sets effective group ID.

        int errno = setregid(ROOT_GID, gid);
        if (errno != 0) {
            Log.e(TAG, "setregid() failed. errno: " + errno);
        }
    
private static voidsetEffectiveUser(int uid)
Sets effective user ID.


             
         
        int errno = setreuid(ROOT_UID, uid);
        if (errno != 0) {
            Log.e(TAG, "setreuid() failed. errno: " + errno);
        }
    
static native intsetpgid(int pid, int pgid)
Invokes the linux syscall "setpgid"

param
pid pid to change
param
pgid new process group of pid
return
0 on success or non-zero errno on fail

static native intsetregid(int rgid, int egid)
The Linux syscall "setregid()"

param
rgid real gid
param
egid effective gid
return
0 on success, non-zero errno on fail

static native intsetreuid(int ruid, int euid)
The Linux syscall "setreuid()"

param
ruid real uid
param
euid effective uid
return
0 on success, non-zero errno on fail

private static booleanstartSystemServer(java.lang.String abiList, java.lang.String socketName)
Prepare the arguments and fork for the system server process.

        long capabilities = posixCapabilitiesAsBits(
            OsConstants.CAP_BLOCK_SUSPEND,
            OsConstants.CAP_KILL,
            OsConstants.CAP_NET_ADMIN,
            OsConstants.CAP_NET_BIND_SERVICE,
            OsConstants.CAP_NET_BROADCAST,
            OsConstants.CAP_NET_RAW,
            OsConstants.CAP_SYS_MODULE,
            OsConstants.CAP_SYS_NICE,
            OsConstants.CAP_SYS_RESOURCE,
            OsConstants.CAP_SYS_TIME,
            OsConstants.CAP_SYS_TTY_CONFIG
        );
        /* 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,1032,3001,3002,3003,3006,3007",
            "--capabilities=" + capabilities + "," + capabilities,
            "--runtime-init",
            "--nice-name=system_server",
            "com.android.server.SystemServer",
        };
        ZygoteConnection.Arguments parsedArgs = null;

        int pid;

        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

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

        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            handleSystemServerProcess(parsedArgs);
        }

        return true;
    
private static voidwaitForSecondaryZygote(java.lang.String socketName)

        String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
                Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
        while (true) {
            try {
                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
                zs.close();
                break;
            } catch (IOException ioe) {
                Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException ie) {
            }
        }