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