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