ZygoteInit.java revision 55fe944f987bcbdea8bbec7ea411684f69623da4
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.POLLIN;
20import static android.system.OsConstants.S_IRWXG;
21import static android.system.OsConstants.S_IRWXO;
22
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.net.LocalServerSocket;
26import android.opengl.EGL14;
27import android.os.Build;
28import android.os.Debug;
29import android.os.Process;
30import android.os.SystemClock;
31import android.os.SystemProperties;
32import android.os.Trace;
33import android.system.ErrnoException;
34import android.system.Os;
35import android.system.OsConstants;
36import android.system.StructPollfd;
37import android.util.EventLog;
38import android.util.Log;
39import android.util.Slog;
40import android.webkit.WebViewFactory;
41
42import dalvik.system.DexFile;
43import dalvik.system.PathClassLoader;
44import dalvik.system.VMRuntime;
45
46import libcore.io.IoUtils;
47
48import java.io.BufferedReader;
49import java.io.FileDescriptor;
50import java.io.FileInputStream;
51import java.io.FileNotFoundException;
52import java.io.IOException;
53import java.io.InputStream;
54import java.io.InputStreamReader;
55import java.lang.reflect.InvocationTargetException;
56import java.lang.reflect.Method;
57import java.lang.reflect.Modifier;
58import java.util.ArrayList;
59
60/**
61 * Startup class for the zygote process.
62 *
63 * Pre-initializes some classes, and then waits for commands on a UNIX domain
64 * socket. Based on these commands, forks off child processes that inherit
65 * the initial state of the VM.
66 *
67 * Please see {@link ZygoteConnection.Arguments} for documentation on the
68 * client protocol.
69 *
70 * @hide
71 */
72public class ZygoteInit {
73    private static final String TAG = "Zygote";
74
75    private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
76
77    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
78
79    private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
80    private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
81
82    /** when preloading, GC after allocating this many bytes */
83    private static final int PRELOAD_GC_THRESHOLD = 50000;
84
85    private static final String ABI_LIST_ARG = "--abi-list=";
86
87    private static final String SOCKET_NAME_ARG = "--socket-name=";
88
89    private static LocalServerSocket sServerSocket;
90
91    /**
92     * Used to pre-load resources.  We hold a global reference on it so it
93     * never gets destroyed.
94     */
95    private static Resources mResources;
96
97    /**
98     * The path of a file that contains classes to preload.
99     */
100    private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
101
102    /** Controls whether we should preload resources during zygote init. */
103    private static final boolean PRELOAD_RESOURCES = true;
104
105    /**
106     * Registers a server socket for zygote command connections
107     *
108     * @throws RuntimeException when open fails
109     */
110    private static void registerZygoteSocket(String socketName) {
111        if (sServerSocket == null) {
112            int fileDesc;
113            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
114            try {
115                String env = System.getenv(fullSocketName);
116                fileDesc = Integer.parseInt(env);
117            } catch (RuntimeException ex) {
118                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
119            }
120
121            try {
122                FileDescriptor fd = new FileDescriptor();
123                fd.setInt$(fileDesc);
124                sServerSocket = new LocalServerSocket(fd);
125            } catch (IOException ex) {
126                throw new RuntimeException(
127                        "Error binding to local socket '" + fileDesc + "'", ex);
128            }
129        }
130    }
131
132    /**
133     * Waits for and accepts a single command connection. Throws
134     * RuntimeException on failure.
135     */
136    private static ZygoteConnection acceptCommandPeer(String abiList) {
137        try {
138            return new ZygoteConnection(sServerSocket.accept(), abiList);
139        } catch (IOException ex) {
140            throw new RuntimeException(
141                    "IOException during accept()", ex);
142        }
143    }
144
145    /**
146     * Close and clean up zygote sockets. Called on shutdown and on the
147     * child's exit path.
148     */
149    static void closeServerSocket() {
150        try {
151            if (sServerSocket != null) {
152                FileDescriptor fd = sServerSocket.getFileDescriptor();
153                sServerSocket.close();
154                if (fd != null) {
155                    Os.close(fd);
156                }
157            }
158        } catch (IOException ex) {
159            Log.e(TAG, "Zygote:  error closing sockets", ex);
160        } catch (ErrnoException ex) {
161            Log.e(TAG, "Zygote:  error closing descriptor", ex);
162        }
163
164        sServerSocket = null;
165    }
166
167    /**
168     * Return the server socket's underlying file descriptor, so that
169     * ZygoteConnection can pass it to the native code for proper
170     * closure after a child process is forked off.
171     */
172
173    static FileDescriptor getServerSocketFileDescriptor() {
174        return sServerSocket.getFileDescriptor();
175    }
176
177    private static final int UNPRIVILEGED_UID = 9999;
178    private static final int UNPRIVILEGED_GID = 9999;
179
180    private static final int ROOT_UID = 0;
181    private static final int ROOT_GID = 0;
182
183    static void preload() {
184        Log.d(TAG, "begin preload");
185        Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
186        preloadClasses();
187        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
188        Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
189        preloadResources();
190        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
191        Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
192        preloadOpenGL();
193        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
194        preloadSharedLibraries();
195        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
196        // for memory sharing purposes.
197        WebViewFactory.prepareWebViewInZygote();
198        Log.d(TAG, "end preload");
199    }
200
201    private static void preloadSharedLibraries() {
202        Log.i(TAG, "Preloading shared libraries...");
203        System.loadLibrary("android");
204        System.loadLibrary("compiler_rt");
205        System.loadLibrary("jnigraphics");
206    }
207
208    private static void preloadOpenGL() {
209        if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
210            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
211        }
212    }
213
214    /**
215     * Performs Zygote process initialization. Loads and initializes
216     * commonly used classes.
217     *
218     * Most classes only cause a few hundred bytes to be allocated, but
219     * a few will allocate a dozen Kbytes (in one case, 500+K).
220     */
221    private static void preloadClasses() {
222        final VMRuntime runtime = VMRuntime.getRuntime();
223
224        InputStream is;
225        try {
226            is = new FileInputStream(PRELOADED_CLASSES);
227        } catch (FileNotFoundException e) {
228            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
229            return;
230        }
231
232        Log.i(TAG, "Preloading classes...");
233        long startTime = SystemClock.uptimeMillis();
234
235        // Drop root perms while running static initializers.
236        final int reuid = Os.getuid();
237        final int regid = Os.getgid();
238
239        // We need to drop root perms only if we're already root. In the case of "wrapped"
240        // processes (see WrapperInit), this function is called from an unprivileged uid
241        // and gid.
242        boolean droppedPriviliges = false;
243        if (reuid == ROOT_UID && regid == ROOT_GID) {
244            try {
245                Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
246                Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
247            } catch (ErrnoException ex) {
248                throw new RuntimeException("Failed to drop root", ex);
249            }
250
251            droppedPriviliges = true;
252        }
253
254        // Alter the target heap utilization.  With explicit GCs this
255        // is not likely to have any effect.
256        float defaultUtilization = runtime.getTargetHeapUtilization();
257        runtime.setTargetHeapUtilization(0.8f);
258
259        try {
260            BufferedReader br
261                = new BufferedReader(new InputStreamReader(is), 256);
262
263            int count = 0;
264            String line;
265            while ((line = br.readLine()) != null) {
266                // Skip comments and blank lines.
267                line = line.trim();
268                if (line.startsWith("#") || line.equals("")) {
269                    continue;
270                }
271
272                Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClass " + line);
273                try {
274                    if (false) {
275                        Log.v(TAG, "Preloading " + line + "...");
276                    }
277                    // Load and explicitly initialize the given class. Use
278                    // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
279                    // (to derive the caller's class-loader). Use true to force initialization, and
280                    // null for the boot classpath class-loader (could as well cache the
281                    // class-loader of this class in a variable).
282                    Class.forName(line, true, null);
283                    count++;
284                } catch (ClassNotFoundException e) {
285                    Log.w(TAG, "Class not found for preloading: " + line);
286                } catch (UnsatisfiedLinkError e) {
287                    Log.w(TAG, "Problem preloading " + line + ": " + e);
288                } catch (Throwable t) {
289                    Log.e(TAG, "Error preloading " + line + ".", t);
290                    if (t instanceof Error) {
291                        throw (Error) t;
292                    }
293                    if (t instanceof RuntimeException) {
294                        throw (RuntimeException) t;
295                    }
296                    throw new RuntimeException(t);
297                }
298                Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
299            }
300
301            Log.i(TAG, "...preloaded " + count + " classes in "
302                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
303        } catch (IOException e) {
304            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
305        } finally {
306            IoUtils.closeQuietly(is);
307            // Restore default.
308            runtime.setTargetHeapUtilization(defaultUtilization);
309
310            // Fill in dex caches with classes, fields, and methods brought in by preloading.
311            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches");
312            runtime.preloadDexCaches();
313            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
314
315            // Bring back root. We'll need it later if we're in the zygote.
316            if (droppedPriviliges) {
317                try {
318                    Os.setreuid(ROOT_UID, ROOT_UID);
319                    Os.setregid(ROOT_GID, ROOT_GID);
320                } catch (ErrnoException ex) {
321                    throw new RuntimeException("Failed to restore root", ex);
322                }
323            }
324        }
325    }
326
327    /**
328     * Load in commonly used resources, so they can be shared across
329     * processes.
330     *
331     * These tend to be a few Kbytes, but are frequently in the 20-40K
332     * range, and occasionally even larger.
333     */
334    private static void preloadResources() {
335        final VMRuntime runtime = VMRuntime.getRuntime();
336
337        try {
338            mResources = Resources.getSystem();
339            mResources.startPreloading();
340            if (PRELOAD_RESOURCES) {
341                Log.i(TAG, "Preloading resources...");
342
343                long startTime = SystemClock.uptimeMillis();
344                TypedArray ar = mResources.obtainTypedArray(
345                        com.android.internal.R.array.preloaded_drawables);
346                int N = preloadDrawables(runtime, ar);
347                ar.recycle();
348                Log.i(TAG, "...preloaded " + N + " resources in "
349                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
350
351                startTime = SystemClock.uptimeMillis();
352                ar = mResources.obtainTypedArray(
353                        com.android.internal.R.array.preloaded_color_state_lists);
354                N = preloadColorStateLists(runtime, ar);
355                ar.recycle();
356                Log.i(TAG, "...preloaded " + N + " resources in "
357                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
358            }
359            mResources.finishPreloading();
360        } catch (RuntimeException e) {
361            Log.w(TAG, "Failure preloading resources", e);
362        }
363    }
364
365    private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
366        int N = ar.length();
367        for (int i=0; i<N; i++) {
368            int id = ar.getResourceId(i, 0);
369            if (false) {
370                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
371            }
372            if (id != 0) {
373                if (mResources.getColorStateList(id) == null) {
374                    throw new IllegalArgumentException(
375                            "Unable to find preloaded color resource #0x"
376                            + Integer.toHexString(id)
377                            + " (" + ar.getString(i) + ")");
378                }
379            }
380        }
381        return N;
382    }
383
384
385    private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
386        int N = ar.length();
387        for (int i=0; i<N; i++) {
388            int id = ar.getResourceId(i, 0);
389            if (false) {
390                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
391            }
392            if (id != 0) {
393                if (mResources.getDrawable(id, null) == null) {
394                    throw new IllegalArgumentException(
395                            "Unable to find preloaded drawable resource #0x"
396                            + Integer.toHexString(id)
397                            + " (" + ar.getString(i) + ")");
398                }
399            }
400        }
401        return N;
402    }
403
404    /**
405     * Runs several special GCs to try to clean up a few generations of
406     * softly- and final-reachable objects, along with any other garbage.
407     * This is only useful just before a fork().
408     */
409    /*package*/ static void gcAndFinalize() {
410        final VMRuntime runtime = VMRuntime.getRuntime();
411
412        /* runFinalizationSync() lets finalizers be called in Zygote,
413         * which doesn't have a HeapWorker thread.
414         */
415        System.gc();
416        runtime.runFinalizationSync();
417        System.gc();
418    }
419
420    /**
421     * Finish remaining work for the newly forked system server process.
422     */
423    private static void handleSystemServerProcess(
424            ZygoteConnection.Arguments parsedArgs)
425            throws ZygoteInit.MethodAndArgsCaller {
426
427        closeServerSocket();
428
429        // set umask to 0077 so new files and directories will default to owner-only permissions.
430        Os.umask(S_IRWXG | S_IRWXO);
431
432        if (parsedArgs.niceName != null) {
433            Process.setArgV0(parsedArgs.niceName);
434        }
435
436        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
437        if (systemServerClasspath != null) {
438            performSystemServerDexOpt(systemServerClasspath);
439        }
440
441        if (parsedArgs.invokeWith != null) {
442            String[] args = parsedArgs.remainingArgs;
443            // If we have a non-null system server class path, we'll have to duplicate the
444            // existing arguments and append the classpath to it. ART will handle the classpath
445            // correctly when we exec a new process.
446            if (systemServerClasspath != null) {
447                String[] amendedArgs = new String[args.length + 2];
448                amendedArgs[0] = "-cp";
449                amendedArgs[1] = systemServerClasspath;
450                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
451            }
452
453            WrapperInit.execApplication(parsedArgs.invokeWith,
454                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
455                    VMRuntime.getCurrentInstructionSet(), null, args);
456        } else {
457            ClassLoader cl = null;
458            if (systemServerClasspath != null) {
459                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
460                Thread.currentThread().setContextClassLoader(cl);
461            }
462
463            /*
464             * Pass the remaining arguments to SystemServer.
465             */
466            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
467        }
468
469        /* should never reach here */
470    }
471
472    /**
473     * Performs dex-opt on the elements of {@code classPath}, if needed. We
474     * choose the instruction set of the current runtime.
475     */
476    private static void performSystemServerDexOpt(String classPath) {
477        final String[] classPathElements = classPath.split(":");
478        final InstallerConnection installer = new InstallerConnection();
479        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
480
481        try {
482            for (String classPathElement : classPathElements) {
483                final int dexoptNeeded = DexFile.getDexOptNeeded(
484                        classPathElement, "*", instructionSet, false /* defer */);
485                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
486                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
487                            instructionSet, dexoptNeeded, false /* boot complete */);
488                }
489            }
490        } catch (IOException ioe) {
491            throw new RuntimeException("Error starting system_server", ioe);
492        } finally {
493            installer.disconnect();
494        }
495    }
496
497    /**
498     * Prepare the arguments and fork for the system server process.
499     */
500    private static boolean startSystemServer(String abiList, String socketName)
501            throws MethodAndArgsCaller, RuntimeException {
502        long capabilities = posixCapabilitiesAsBits(
503            OsConstants.CAP_BLOCK_SUSPEND,
504            OsConstants.CAP_KILL,
505            OsConstants.CAP_NET_ADMIN,
506            OsConstants.CAP_NET_BIND_SERVICE,
507            OsConstants.CAP_NET_BROADCAST,
508            OsConstants.CAP_NET_RAW,
509            OsConstants.CAP_SYS_MODULE,
510            OsConstants.CAP_SYS_NICE,
511            OsConstants.CAP_SYS_RESOURCE,
512            OsConstants.CAP_SYS_TIME,
513            OsConstants.CAP_SYS_TTY_CONFIG
514        );
515        /* Hardcoded command line to start the system server */
516        String args[] = {
517            "--setuid=1000",
518            "--setgid=1000",
519            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
520            "--capabilities=" + capabilities + "," + capabilities,
521            "--nice-name=system_server",
522            "--runtime-args",
523            "com.android.server.SystemServer",
524        };
525        ZygoteConnection.Arguments parsedArgs = null;
526
527        int pid;
528
529        try {
530            parsedArgs = new ZygoteConnection.Arguments(args);
531            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
532            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
533
534            /* Request to fork the system server process */
535            pid = Zygote.forkSystemServer(
536                    parsedArgs.uid, parsedArgs.gid,
537                    parsedArgs.gids,
538                    parsedArgs.debugFlags,
539                    null,
540                    parsedArgs.permittedCapabilities,
541                    parsedArgs.effectiveCapabilities);
542        } catch (IllegalArgumentException ex) {
543            throw new RuntimeException(ex);
544        }
545
546        /* For child process */
547        if (pid == 0) {
548            if (hasSecondZygote(abiList)) {
549                waitForSecondaryZygote(socketName);
550            }
551
552            handleSystemServerProcess(parsedArgs);
553        }
554
555        return true;
556    }
557
558    /**
559     * Gets the bit array representation of the provided list of POSIX capabilities.
560     */
561    private static long posixCapabilitiesAsBits(int... capabilities) {
562        long result = 0;
563        for (int capability : capabilities) {
564            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
565                throw new IllegalArgumentException(String.valueOf(capability));
566            }
567            result |= (1L << capability);
568        }
569        return result;
570    }
571
572    public static void main(String argv[]) {
573        try {
574            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
575            RuntimeInit.enableDdms();
576            // Start profiling the zygote initialization.
577            SamplingProfilerIntegration.start();
578
579            boolean startSystemServer = false;
580            String socketName = "zygote";
581            String abiList = null;
582            for (int i = 1; i < argv.length; i++) {
583                if ("start-system-server".equals(argv[i])) {
584                    startSystemServer = true;
585                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
586                    abiList = argv[i].substring(ABI_LIST_ARG.length());
587                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
588                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
589                } else {
590                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
591                }
592            }
593
594            if (abiList == null) {
595                throw new RuntimeException("No ABI list supplied.");
596            }
597
598            registerZygoteSocket(socketName);
599            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");
600            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
601                SystemClock.uptimeMillis());
602            preload();
603            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
604                SystemClock.uptimeMillis());
605            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
606
607            // Finish profiling the zygote initialization.
608            SamplingProfilerIntegration.writeZygoteSnapshot();
609
610            // Do an initial gc to clean up after startup
611            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC");
612            gcAndFinalize();
613            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
614
615            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
616
617            // Disable tracing so that forked processes do not inherit stale tracing tags from
618            // Zygote.
619            Trace.setTracingEnabled(false);
620
621            if (startSystemServer) {
622                startSystemServer(abiList, socketName);
623            }
624
625            Log.i(TAG, "Accepting command socket connections");
626            runSelectLoop(abiList);
627
628            closeServerSocket();
629        } catch (MethodAndArgsCaller caller) {
630            caller.run();
631        } catch (RuntimeException ex) {
632            Log.e(TAG, "Zygote died with exception", ex);
633            closeServerSocket();
634            throw ex;
635        }
636    }
637
638    /**
639     * Return {@code true} if this device configuration has another zygote.
640     *
641     * We determine this by comparing the device ABI list with this zygotes
642     * list. If this zygote supports all ABIs this device supports, there won't
643     * be another zygote.
644     */
645    private static boolean hasSecondZygote(String abiList) {
646        return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
647    }
648
649    private static void waitForSecondaryZygote(String socketName) {
650        String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
651                Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
652        while (true) {
653            try {
654                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
655                zs.close();
656                break;
657            } catch (IOException ioe) {
658                Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
659            }
660
661            try {
662                Thread.sleep(1000);
663            } catch (InterruptedException ie) {
664            }
665        }
666    }
667
668    /**
669     * Runs the zygote process's select loop. Accepts new connections as
670     * they happen, and reads commands from connections one spawn-request's
671     * worth at a time.
672     *
673     * @throws MethodAndArgsCaller in a child process when a main() should
674     * be executed.
675     */
676    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
677        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
678        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
679
680        fds.add(sServerSocket.getFileDescriptor());
681        peers.add(null);
682
683        while (true) {
684            StructPollfd[] pollFds = new StructPollfd[fds.size()];
685            for (int i = 0; i < pollFds.length; ++i) {
686                pollFds[i] = new StructPollfd();
687                pollFds[i].fd = fds.get(i);
688                pollFds[i].events = (short) POLLIN;
689            }
690            try {
691                Os.poll(pollFds, -1);
692            } catch (ErrnoException ex) {
693                throw new RuntimeException("poll failed", ex);
694            }
695            for (int i = pollFds.length - 1; i >= 0; --i) {
696                if ((pollFds[i].revents & POLLIN) == 0) {
697                    continue;
698                }
699                if (i == 0) {
700                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
701                    peers.add(newPeer);
702                    fds.add(newPeer.getFileDesciptor());
703                } else {
704                    boolean done = peers.get(i).runOnce();
705                    if (done) {
706                        peers.remove(i);
707                        fds.remove(i);
708                    }
709                }
710            }
711        }
712    }
713
714    /**
715     * Class not instantiable.
716     */
717    private ZygoteInit() {
718    }
719
720    /**
721     * Helper exception class which holds a method and arguments and
722     * can call them. This is used as part of a trampoline to get rid of
723     * the initial process setup stack frames.
724     */
725    public static class MethodAndArgsCaller extends Exception
726            implements Runnable {
727        /** method to call */
728        private final Method mMethod;
729
730        /** argument array */
731        private final String[] mArgs;
732
733        public MethodAndArgsCaller(Method method, String[] args) {
734            mMethod = method;
735            mArgs = args;
736        }
737
738        public void run() {
739            try {
740                mMethod.invoke(null, new Object[] { mArgs });
741            } catch (IllegalAccessException ex) {
742                throw new RuntimeException(ex);
743            } catch (InvocationTargetException ex) {
744                Throwable cause = ex.getCause();
745                if (cause instanceof RuntimeException) {
746                    throw (RuntimeException) cause;
747                } else if (cause instanceof Error) {
748                    throw (Error) cause;
749                }
750                throw new RuntimeException(ex);
751            }
752        }
753    }
754}
755