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