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