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