ZygoteInit.java revision 84e001a9f514a047816f27db6ce836966efa481e
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import static android.system.OsConstants.S_IRWXG;
20import static android.system.OsConstants.S_IRWXO;
21
22import android.content.res.Resources;
23import android.content.res.TypedArray;
24import android.net.LocalServerSocket;
25import android.opengl.EGL14;
26import android.os.Build;
27import android.os.Debug;
28import android.os.Process;
29import android.os.SystemClock;
30import android.os.SystemProperties;
31import android.os.Trace;
32import android.system.ErrnoException;
33import android.system.Os;
34import android.system.OsConstants;
35import android.util.EventLog;
36import android.util.Log;
37import android.util.Slog;
38import android.webkit.WebViewFactory;
39
40import dalvik.system.DexFile;
41import dalvik.system.PathClassLoader;
42import dalvik.system.VMRuntime;
43
44import libcore.io.IoUtils;
45
46import java.io.BufferedReader;
47import java.io.FileDescriptor;
48import java.io.IOException;
49import java.io.InputStream;
50import java.io.InputStreamReader;
51import java.lang.reflect.InvocationTargetException;
52import java.lang.reflect.Method;
53import java.lang.reflect.Modifier;
54import java.util.ArrayList;
55
56/**
57 * Startup class for the zygote process.
58 *
59 * Pre-initializes some classes, and then waits for commands on a UNIX domain
60 * socket. Based on these commands, forks off child processes that inherit
61 * the initial state of the VM.
62 *
63 * Please see {@link ZygoteConnection.Arguments} for documentation on the
64 * client protocol.
65 *
66 * @hide
67 */
68public class ZygoteInit {
69    private static final String TAG = "Zygote";
70
71    private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
72
73    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
74
75    private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
76    private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
77
78    /** when preloading, GC after allocating this many bytes */
79    private static final int PRELOAD_GC_THRESHOLD = 50000;
80
81    private static final String ABI_LIST_ARG = "--abi-list=";
82
83    private static final String SOCKET_NAME_ARG = "--socket-name=";
84
85    private static LocalServerSocket sServerSocket;
86
87    /**
88     * Used to pre-load resources.  We hold a global reference on it so it
89     * never gets destroyed.
90     */
91    private static Resources mResources;
92
93    /**
94     * The number of times that the main Zygote loop
95     * should run before calling gc() again.
96     */
97    static final int GC_LOOP_COUNT = 10;
98
99    /**
100     * The name of a resource file that contains classes to preload.
101     */
102    private static final String PRELOADED_CLASSES = "preloaded-classes";
103
104    /** Controls whether we should preload resources during zygote init. */
105    private static final boolean PRELOAD_RESOURCES = true;
106
107    /**
108     * Invokes a static "main(argv[]) method on class "className".
109     * Converts various failing exceptions into RuntimeExceptions, with
110     * the assumption that they will then cause the VM instance to exit.
111     *
112     * @param loader class loader to use
113     * @param className Fully-qualified class name
114     * @param argv Argument vector for main()
115     */
116    static void invokeStaticMain(ClassLoader loader,
117            String className, String[] argv)
118            throws ZygoteInit.MethodAndArgsCaller {
119        Class<?> cl;
120
121        try {
122            cl = loader.loadClass(className);
123        } catch (ClassNotFoundException ex) {
124            throw new RuntimeException(
125                    "Missing class when invoking static main " + className,
126                    ex);
127        }
128
129        Method m;
130        try {
131            m = cl.getMethod("main", new Class[] { String[].class });
132        } catch (NoSuchMethodException ex) {
133            throw new RuntimeException(
134                    "Missing static main on " + className, ex);
135        } catch (SecurityException ex) {
136            throw new RuntimeException(
137                    "Problem getting static main on " + className, ex);
138        }
139
140        int modifiers = m.getModifiers();
141        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
142            throw new RuntimeException(
143                    "Main method is not public and static on " + className);
144        }
145
146        /*
147         * This throw gets caught in ZygoteInit.main(), which responds
148         * by invoking the exception's run() method. This arrangement
149         * clears up all the stack frames that were required in setting
150         * up the process.
151         */
152        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
153    }
154
155    /**
156     * Registers a server socket for zygote command connections
157     *
158     * @throws RuntimeException when open fails
159     */
160    private static void registerZygoteSocket(String socketName) {
161        if (sServerSocket == null) {
162            int fileDesc;
163            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
164            try {
165                String env = System.getenv(fullSocketName);
166                fileDesc = Integer.parseInt(env);
167            } catch (RuntimeException ex) {
168                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
169            }
170
171            try {
172                sServerSocket = new LocalServerSocket(
173                        createFileDescriptor(fileDesc));
174            } catch (IOException ex) {
175                throw new RuntimeException(
176                        "Error binding to local socket '" + fileDesc + "'", ex);
177            }
178        }
179    }
180
181    /**
182     * Waits for and accepts a single command connection. Throws
183     * RuntimeException on failure.
184     */
185    private static ZygoteConnection acceptCommandPeer(String abiList) {
186        try {
187            return new ZygoteConnection(sServerSocket.accept(), abiList);
188        } catch (IOException ex) {
189            throw new RuntimeException(
190                    "IOException during accept()", ex);
191        }
192    }
193
194    /**
195     * Close and clean up zygote sockets. Called on shutdown and on the
196     * child's exit path.
197     */
198    static void closeServerSocket() {
199        try {
200            if (sServerSocket != null) {
201                FileDescriptor fd = sServerSocket.getFileDescriptor();
202                sServerSocket.close();
203                if (fd != null) {
204                    Os.close(fd);
205                }
206            }
207        } catch (IOException ex) {
208            Log.e(TAG, "Zygote:  error closing sockets", ex);
209        } catch (ErrnoException ex) {
210            Log.e(TAG, "Zygote:  error closing descriptor", ex);
211        }
212
213        sServerSocket = null;
214    }
215
216    /**
217     * Return the server socket's underlying file descriptor, so that
218     * ZygoteConnection can pass it to the native code for proper
219     * closure after a child process is forked off.
220     */
221
222    static FileDescriptor getServerSocketFileDescriptor() {
223        return sServerSocket.getFileDescriptor();
224    }
225
226    private static final int UNPRIVILEGED_UID = 9999;
227    private static final int UNPRIVILEGED_GID = 9999;
228
229    private static final int ROOT_UID = 0;
230    private static final int ROOT_GID = 0;
231
232    /**
233     * Sets effective user ID.
234     */
235    private static void setEffectiveUser(int uid) {
236        int errno = setreuid(ROOT_UID, uid);
237        if (errno != 0) {
238            Log.e(TAG, "setreuid() failed. errno: " + errno);
239        }
240    }
241
242    /**
243     * Sets effective group ID.
244     */
245    private static void setEffectiveGroup(int gid) {
246        int errno = setregid(ROOT_GID, gid);
247        if (errno != 0) {
248            Log.e(TAG, "setregid() failed. errno: " + errno);
249        }
250    }
251
252    static void preload() {
253        Log.d(TAG, "begin preload");
254        preloadClasses();
255        preloadResources();
256        preloadOpenGL();
257        preloadSharedLibraries();
258        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
259        // for memory sharing purposes.
260        WebViewFactory.prepareWebViewInZygote();
261        Log.d(TAG, "end preload");
262    }
263
264    private static void preloadSharedLibraries() {
265        Log.i(TAG, "Preloading shared libraries...");
266        System.loadLibrary("android");
267        System.loadLibrary("compiler_rt");
268        System.loadLibrary("jnigraphics");
269    }
270
271    private static void preloadOpenGL() {
272        if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
273            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
274        }
275    }
276
277    /**
278     * Performs Zygote process initialization. Loads and initializes
279     * commonly used classes.
280     *
281     * Most classes only cause a few hundred bytes to be allocated, but
282     * a few will allocate a dozen Kbytes (in one case, 500+K).
283     */
284    private static void preloadClasses() {
285        final VMRuntime runtime = VMRuntime.getRuntime();
286
287        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
288                PRELOADED_CLASSES);
289        if (is == null) {
290            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
291        } else {
292            Log.i(TAG, "Preloading classes...");
293            long startTime = SystemClock.uptimeMillis();
294
295            // Drop root perms while running static initializers.
296            setEffectiveGroup(UNPRIVILEGED_GID);
297            setEffectiveUser(UNPRIVILEGED_UID);
298
299            // Alter the target heap utilization.  With explicit GCs this
300            // is not likely to have any effect.
301            float defaultUtilization = runtime.getTargetHeapUtilization();
302            runtime.setTargetHeapUtilization(0.8f);
303
304            // Start with a clean slate.
305            System.gc();
306            runtime.runFinalizationSync();
307            Debug.startAllocCounting();
308
309            try {
310                BufferedReader br
311                    = new BufferedReader(new InputStreamReader(is), 256);
312
313                int count = 0;
314                String line;
315                while ((line = br.readLine()) != null) {
316                    // Skip comments and blank lines.
317                    line = line.trim();
318                    if (line.startsWith("#") || line.equals("")) {
319                        continue;
320                    }
321
322                    try {
323                        if (false) {
324                            Log.v(TAG, "Preloading " + line + "...");
325                        }
326                        Class.forName(line);
327                        if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
328                            if (false) {
329                                Log.v(TAG,
330                                    " GC at " + Debug.getGlobalAllocSize());
331                            }
332                            System.gc();
333                            runtime.runFinalizationSync();
334                            Debug.resetGlobalAllocSize();
335                        }
336                        count++;
337                    } catch (ClassNotFoundException e) {
338                        Log.w(TAG, "Class not found for preloading: " + line);
339                    } catch (UnsatisfiedLinkError e) {
340                        Log.w(TAG, "Problem preloading " + line + ": " + e);
341                    } catch (Throwable t) {
342                        Log.e(TAG, "Error preloading " + line + ".", t);
343                        if (t instanceof Error) {
344                            throw (Error) t;
345                        }
346                        if (t instanceof RuntimeException) {
347                            throw (RuntimeException) t;
348                        }
349                        throw new RuntimeException(t);
350                    }
351                }
352
353                Log.i(TAG, "...preloaded " + count + " classes in "
354                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
355            } catch (IOException e) {
356                Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
357            } finally {
358                IoUtils.closeQuietly(is);
359                // Restore default.
360                runtime.setTargetHeapUtilization(defaultUtilization);
361
362                // Fill in dex caches with classes, fields, and methods brought in by preloading.
363                runtime.preloadDexCaches();
364
365                Debug.stopAllocCounting();
366
367                // Bring back root. We'll need it later.
368                setEffectiveUser(ROOT_UID);
369                setEffectiveGroup(ROOT_GID);
370            }
371        }
372    }
373
374    /**
375     * Load in commonly used resources, so they can be shared across
376     * processes.
377     *
378     * These tend to be a few Kbytes, but are frequently in the 20-40K
379     * range, and occasionally even larger.
380     */
381    private static void preloadResources() {
382        final VMRuntime runtime = VMRuntime.getRuntime();
383
384        Debug.startAllocCounting();
385        try {
386            System.gc();
387            runtime.runFinalizationSync();
388            mResources = Resources.getSystem();
389            mResources.startPreloading();
390            if (PRELOAD_RESOURCES) {
391                Log.i(TAG, "Preloading resources...");
392
393                long startTime = SystemClock.uptimeMillis();
394                TypedArray ar = mResources.obtainTypedArray(
395                        com.android.internal.R.array.preloaded_drawables);
396                int N = preloadDrawables(runtime, ar);
397                ar.recycle();
398                Log.i(TAG, "...preloaded " + N + " resources in "
399                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
400
401                startTime = SystemClock.uptimeMillis();
402                ar = mResources.obtainTypedArray(
403                        com.android.internal.R.array.preloaded_color_state_lists);
404                N = preloadColorStateLists(runtime, ar);
405                ar.recycle();
406                Log.i(TAG, "...preloaded " + N + " resources in "
407                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
408            }
409            mResources.finishPreloading();
410        } catch (RuntimeException e) {
411            Log.w(TAG, "Failure preloading resources", e);
412        } finally {
413            Debug.stopAllocCounting();
414        }
415    }
416
417    private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
418        int N = ar.length();
419        for (int i=0; i<N; i++) {
420            if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
421                if (false) {
422                    Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
423                }
424                System.gc();
425                runtime.runFinalizationSync();
426                Debug.resetGlobalAllocSize();
427            }
428            int id = ar.getResourceId(i, 0);
429            if (false) {
430                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
431            }
432            if (id != 0) {
433                if (mResources.getColorStateList(id) == null) {
434                    throw new IllegalArgumentException(
435                            "Unable to find preloaded color resource #0x"
436                            + Integer.toHexString(id)
437                            + " (" + ar.getString(i) + ")");
438                }
439            }
440        }
441        return N;
442    }
443
444
445    private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
446        int N = ar.length();
447        for (int i=0; i<N; i++) {
448            if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
449                if (false) {
450                    Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
451                }
452                System.gc();
453                runtime.runFinalizationSync();
454                Debug.resetGlobalAllocSize();
455            }
456            int id = ar.getResourceId(i, 0);
457            if (false) {
458                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
459            }
460            if (id != 0) {
461                if (mResources.getDrawable(id, null) == null) {
462                    throw new IllegalArgumentException(
463                            "Unable to find preloaded drawable resource #0x"
464                            + Integer.toHexString(id)
465                            + " (" + ar.getString(i) + ")");
466                }
467            }
468        }
469        return N;
470    }
471
472    /**
473     * Runs several special GCs to try to clean up a few generations of
474     * softly- and final-reachable objects, along with any other garbage.
475     * This is only useful just before a fork().
476     */
477    /*package*/ static void gc() {
478        final VMRuntime runtime = VMRuntime.getRuntime();
479
480        /* runFinalizationSync() lets finalizers be called in Zygote,
481         * which doesn't have a HeapWorker thread.
482         */
483        System.gc();
484        runtime.runFinalizationSync();
485        System.gc();
486        runtime.runFinalizationSync();
487        System.gc();
488        runtime.runFinalizationSync();
489    }
490
491    /**
492     * Finish remaining work for the newly forked system server process.
493     */
494    private static void handleSystemServerProcess(
495            ZygoteConnection.Arguments parsedArgs)
496            throws ZygoteInit.MethodAndArgsCaller {
497
498        closeServerSocket();
499
500        // set umask to 0077 so new files and directories will default to owner-only permissions.
501        Os.umask(S_IRWXG | S_IRWXO);
502
503        if (parsedArgs.niceName != null) {
504            Process.setArgV0(parsedArgs.niceName);
505        }
506
507        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
508        if (systemServerClasspath != null) {
509            performSystemServerDexOpt(systemServerClasspath);
510        }
511
512        if (parsedArgs.invokeWith != null) {
513            String[] args = parsedArgs.remainingArgs;
514            // If we have a non-null system server class path, we'll have to duplicate the
515            // existing arguments and append the classpath to it. ART will handle the classpath
516            // correctly when we exec a new process.
517            if (systemServerClasspath != null) {
518                String[] amendedArgs = new String[args.length + 2];
519                amendedArgs[0] = "-cp";
520                amendedArgs[1] = systemServerClasspath;
521                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
522            }
523
524            WrapperInit.execApplication(parsedArgs.invokeWith,
525                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
526                    null, args);
527        } else {
528            ClassLoader cl = null;
529            if (systemServerClasspath != null) {
530                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
531                Thread.currentThread().setContextClassLoader(cl);
532            }
533
534            /*
535             * Pass the remaining arguments to SystemServer.
536             */
537            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
538        }
539
540        /* should never reach here */
541    }
542
543    /**
544     * Performs dex-opt on the elements of {@code classPath}, if needed. We
545     * choose the instruction set of the current runtime.
546     */
547    private static void performSystemServerDexOpt(String classPath) {
548        final String[] classPathElements = classPath.split(":");
549        final InstallerConnection installer = new InstallerConnection();
550        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
551
552        try {
553            for (String classPathElement : classPathElements) {
554                final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
555                        false /* defer */);
556                if (dexopt == DexFile.DEXOPT_NEEDED) {
557                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
558                } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
559                    installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
560                }
561            }
562        } catch (IOException ioe) {
563            throw new RuntimeException("Error starting system_server", ioe);
564        } finally {
565            installer.disconnect();
566        }
567    }
568
569    /**
570     * Prepare the arguments and fork for the system server process.
571     */
572    private static boolean startSystemServer(String abiList, String socketName)
573            throws MethodAndArgsCaller, RuntimeException {
574        long capabilities = posixCapabilitiesAsBits(
575            OsConstants.CAP_BLOCK_SUSPEND,
576            OsConstants.CAP_KILL,
577            OsConstants.CAP_NET_ADMIN,
578            OsConstants.CAP_NET_BIND_SERVICE,
579            OsConstants.CAP_NET_BROADCAST,
580            OsConstants.CAP_NET_RAW,
581            OsConstants.CAP_SYS_MODULE,
582            OsConstants.CAP_SYS_NICE,
583            OsConstants.CAP_SYS_RESOURCE,
584            OsConstants.CAP_SYS_TIME,
585            OsConstants.CAP_SYS_TTY_CONFIG
586        );
587        /* Hardcoded command line to start the system server */
588        String args[] = {
589            "--setuid=1000",
590            "--setgid=1000",
591            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
592            "--capabilities=" + capabilities + "," + capabilities,
593            "--runtime-init",
594            "--nice-name=system_server",
595            "com.android.server.SystemServer",
596        };
597        ZygoteConnection.Arguments parsedArgs = null;
598
599        int pid;
600
601        try {
602            parsedArgs = new ZygoteConnection.Arguments(args);
603            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
604            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
605
606            /* Request to fork the system server process */
607            pid = Zygote.forkSystemServer(
608                    parsedArgs.uid, parsedArgs.gid,
609                    parsedArgs.gids,
610                    parsedArgs.debugFlags,
611                    null,
612                    parsedArgs.permittedCapabilities,
613                    parsedArgs.effectiveCapabilities);
614        } catch (IllegalArgumentException ex) {
615            throw new RuntimeException(ex);
616        }
617
618        /* For child process */
619        if (pid == 0) {
620            if (hasSecondZygote(abiList)) {
621                waitForSecondaryZygote(socketName);
622            }
623
624            handleSystemServerProcess(parsedArgs);
625        }
626
627        return true;
628    }
629
630    /**
631     * Gets the bit array representation of the provided list of POSIX capabilities.
632     */
633    private static long posixCapabilitiesAsBits(int... capabilities) {
634        long result = 0;
635        for (int capability : capabilities) {
636            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
637                throw new IllegalArgumentException(String.valueOf(capability));
638            }
639            result |= (1L << capability);
640        }
641        return result;
642    }
643
644    public static void main(String argv[]) {
645        try {
646            // Start profiling the zygote initialization.
647            SamplingProfilerIntegration.start();
648
649            boolean startSystemServer = false;
650            String socketName = "zygote";
651            String abiList = null;
652            for (int i = 1; i < argv.length; i++) {
653                if ("start-system-server".equals(argv[i])) {
654                    startSystemServer = true;
655                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
656                    abiList = argv[i].substring(ABI_LIST_ARG.length());
657                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
658                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
659                } else {
660                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
661                }
662            }
663
664            if (abiList == null) {
665                throw new RuntimeException("No ABI list supplied.");
666            }
667
668            registerZygoteSocket(socketName);
669            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
670                SystemClock.uptimeMillis());
671            preload();
672            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
673                SystemClock.uptimeMillis());
674
675            // Finish profiling the zygote initialization.
676            SamplingProfilerIntegration.writeZygoteSnapshot();
677
678            // Do an initial gc to clean up after startup
679            gc();
680
681            // Disable tracing so that forked processes do not inherit stale tracing tags from
682            // Zygote.
683            Trace.setTracingEnabled(false);
684
685            if (startSystemServer) {
686                startSystemServer(abiList, socketName);
687            }
688
689            Log.i(TAG, "Accepting command socket connections");
690            runSelectLoop(abiList);
691
692            closeServerSocket();
693        } catch (MethodAndArgsCaller caller) {
694            caller.run();
695        } catch (RuntimeException ex) {
696            Log.e(TAG, "Zygote died with exception", ex);
697            closeServerSocket();
698            throw ex;
699        }
700    }
701
702    /**
703     * Return {@code true} if this device configuration has another zygote.
704     *
705     * We determine this by comparing the device ABI list with this zygotes
706     * list. If this zygote supports all ABIs this device supports, there won't
707     * be another zygote.
708     */
709    private static boolean hasSecondZygote(String abiList) {
710        return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
711    }
712
713    private static void waitForSecondaryZygote(String socketName) {
714        String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
715                Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
716        while (true) {
717            try {
718                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
719                zs.close();
720                break;
721            } catch (IOException ioe) {
722                Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
723            }
724
725            try {
726                Thread.sleep(1000);
727            } catch (InterruptedException ie) {
728            }
729        }
730    }
731
732    /**
733     * Runs the zygote process's select loop. Accepts new connections as
734     * they happen, and reads commands from connections one spawn-request's
735     * worth at a time.
736     *
737     * @throws MethodAndArgsCaller in a child process when a main() should
738     * be executed.
739     */
740    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
741        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
742        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
743        FileDescriptor[] fdArray = new FileDescriptor[4];
744
745        fds.add(sServerSocket.getFileDescriptor());
746        peers.add(null);
747
748        int loopCount = GC_LOOP_COUNT;
749        while (true) {
750            int index;
751
752            /*
753             * Call gc() before we block in select().
754             * It's work that has to be done anyway, and it's better
755             * to avoid making every child do it.  It will also
756             * madvise() any free memory as a side-effect.
757             *
758             * Don't call it every time, because walking the entire
759             * heap is a lot of overhead to free a few hundred bytes.
760             */
761            if (loopCount <= 0) {
762                gc();
763                loopCount = GC_LOOP_COUNT;
764            } else {
765                loopCount--;
766            }
767
768
769            try {
770                fdArray = fds.toArray(fdArray);
771                index = selectReadable(fdArray);
772            } catch (IOException ex) {
773                throw new RuntimeException("Error in select()", ex);
774            }
775
776            if (index < 0) {
777                throw new RuntimeException("Error in select()");
778            } else if (index == 0) {
779                ZygoteConnection newPeer = acceptCommandPeer(abiList);
780                peers.add(newPeer);
781                fds.add(newPeer.getFileDescriptor());
782            } else {
783                boolean done;
784                done = peers.get(index).runOnce();
785
786                if (done) {
787                    peers.remove(index);
788                    fds.remove(index);
789                }
790            }
791        }
792    }
793
794    /**
795     * The Linux syscall "setreuid()"
796     * @param ruid real uid
797     * @param euid effective uid
798     * @return 0 on success, non-zero errno on fail
799     */
800    static native int setreuid(int ruid, int euid);
801
802    /**
803     * The Linux syscall "setregid()"
804     * @param rgid real gid
805     * @param egid effective gid
806     * @return 0 on success, non-zero errno on fail
807     */
808    static native int setregid(int rgid, int egid);
809
810    /**
811     * Invokes the linux syscall "setpgid"
812     *
813     * @param pid pid to change
814     * @param pgid new process group of pid
815     * @return 0 on success or non-zero errno on fail
816     */
817    static native int setpgid(int pid, int pgid);
818
819    /**
820     * Invokes the linux syscall "getpgid"
821     *
822     * @param pid pid to query
823     * @return pgid of pid in question
824     * @throws IOException on error
825     */
826    static native int getpgid(int pid) throws IOException;
827
828    /**
829     * Invokes the syscall dup2() to copy the specified descriptors into
830     * stdin, stdout, and stderr. The existing stdio descriptors will be
831     * closed and errors during close will be ignored. The specified
832     * descriptors will also remain open at their original descriptor numbers,
833     * so the caller may want to close the original descriptors.
834     *
835     * @param in new stdin
836     * @param out new stdout
837     * @param err new stderr
838     * @throws IOException
839     */
840    static native void reopenStdio(FileDescriptor in,
841            FileDescriptor out, FileDescriptor err) throws IOException;
842
843    /**
844     * Toggles the close-on-exec flag for the specified file descriptor.
845     *
846     * @param fd non-null; file descriptor
847     * @param flag desired close-on-exec flag state
848     * @throws IOException
849     */
850    static native void setCloseOnExec(FileDescriptor fd, boolean flag)
851            throws IOException;
852
853    /**
854     * Invokes select() on the provider array of file descriptors (selecting
855     * for readability only). Array elements of null are ignored.
856     *
857     * @param fds non-null; array of readable file descriptors
858     * @return index of descriptor that is now readable or -1 for empty array.
859     * @throws IOException if an error occurs
860     */
861    static native int selectReadable(FileDescriptor[] fds) throws IOException;
862
863    /**
864     * Creates a file descriptor from an int fd.
865     *
866     * @param fd integer OS file descriptor
867     * @return non-null; FileDescriptor instance
868     * @throws IOException if fd is invalid
869     */
870    static native FileDescriptor createFileDescriptor(int fd)
871            throws IOException;
872
873    /**
874     * Class not instantiable.
875     */
876    private ZygoteInit() {
877    }
878
879    /**
880     * Helper exception class which holds a method and arguments and
881     * can call them. This is used as part of a trampoline to get rid of
882     * the initial process setup stack frames.
883     */
884    public static class MethodAndArgsCaller extends Exception
885            implements Runnable {
886        /** method to call */
887        private final Method mMethod;
888
889        /** argument array */
890        private final String[] mArgs;
891
892        public MethodAndArgsCaller(Method method, String[] args) {
893            mMethod = method;
894            mArgs = args;
895        }
896
897        public void run() {
898            try {
899                mMethod.invoke(null, new Object[] { mArgs });
900            } catch (IllegalAccessException ex) {
901                throw new RuntimeException(ex);
902            } catch (InvocationTargetException ex) {
903                Throwable cause = ex.getCause();
904                if (cause instanceof RuntimeException) {
905                    throw (RuntimeException) cause;
906                } else if (cause instanceof Error) {
907                    throw (Error) cause;
908                }
909                throw new RuntimeException(ex);
910            }
911        }
912    }
913}
914