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