ZygoteInit.java revision 599c918d9794b51992de85b42befa0c71d9ec07f
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.content.res.ColorStateList;
23import android.graphics.drawable.Drawable;
24import android.net.LocalServerSocket;
25import android.os.Debug;
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;
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                    }
314                }
315
316                if (missingClasses != null &&
317                        "1".equals(SystemProperties.get("persist.service.adb.enable"))) {
318                    throw new IllegalStateException(
319                            "Missing class(es) for preloading, update preloaded-classes ["
320                            + missingClasses + "]");
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        /*
511         * Pass the remaining arguments to SystemServer.
512         * "--nice-name=system_server com.android.server.SystemServer"
513         */
514        RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
515        /* should never reach here */
516    }
517
518    /**
519     * Prepare the arguments and fork for the system server process.
520     */
521    private static boolean startSystemServer()
522            throws MethodAndArgsCaller, RuntimeException {
523        /* Hardcoded command line to start the system server */
524        String args[] = {
525            "--setuid=1000",
526            "--setgid=1000",
527            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
528            "--capabilities=121715744,121715744",
529            "--runtime-init",
530            "--nice-name=system_server",
531            "com.android.server.SystemServer",
532        };
533        ZygoteConnection.Arguments parsedArgs = null;
534
535        int pid;
536
537        try {
538            parsedArgs = new ZygoteConnection.Arguments(args);
539
540            /*
541             * Enable debugging of the system process if *either* the command line flags
542             * indicate it should be debuggable or the ro.debuggable system property
543             * is set to "1"
544             */
545            int debugFlags = parsedArgs.debugFlags;
546            if ("1".equals(SystemProperties.get("ro.debuggable")))
547                debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
548
549            /* Request to fork the system server process */
550            pid = Zygote.forkSystemServer(
551                    parsedArgs.uid, parsedArgs.gid,
552                    parsedArgs.gids, debugFlags, null);
553        } catch (IllegalArgumentException ex) {
554            throw new RuntimeException(ex);
555        }
556
557        /* For child process */
558        if (pid == 0) {
559            handleSystemServerProcess(parsedArgs);
560        }
561
562        return true;
563    }
564
565    public static void main(String argv[]) {
566        try {
567            registerZygoteSocket();
568            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
569                SystemClock.uptimeMillis());
570            preloadClasses();
571            //cacheRegisterMaps();
572            preloadResources();
573            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
574                SystemClock.uptimeMillis());
575
576            // Do an initial gc to clean up after startup
577            gc();
578
579            // If requested, start system server directly from Zygote
580            if (argv.length != 2) {
581                throw new RuntimeException(
582                        "ZygoteInit.main expects two arguments");
583            }
584
585            if (argv[1].equals("true")) {
586                startSystemServer();
587            }
588
589            Log.i(TAG, "Accepting command socket connections");
590
591            if (ZYGOTE_FORK_MODE) {
592                runForkMode();
593            } else {
594                runSelectLoopMode();
595            }
596
597            closeServerSocket();
598        } catch (MethodAndArgsCaller caller) {
599            caller.run();
600        } catch (RuntimeException ex) {
601            Log.e(TAG, "Zygote died with exception", ex);
602            closeServerSocket();
603            throw ex;
604        }
605    }
606
607    /**
608     * Runs the zygote in accept-and-fork mode. In this mode, each peer
609     * gets its own zygote spawner process. This code is retained for
610     * reference only.
611     *
612     * @throws MethodAndArgsCaller in a child process when a main() should
613     * be executed.
614     */
615    private static void runForkMode() throws MethodAndArgsCaller {
616        while (true) {
617            ZygoteConnection peer = acceptCommandPeer();
618
619            int pid;
620
621            pid = Zygote.fork();
622
623            if (pid == 0) {
624                // The child process should handle the peer requests
625
626                // The child does not accept any more connections
627                try {
628                    sServerSocket.close();
629                } catch (IOException ex) {
630                    Log.e(TAG, "Zygote Child: error closing sockets", ex);
631                } finally {
632                    sServerSocket = null;
633                }
634
635                peer.run();
636                break;
637            } else if (pid > 0) {
638                peer.closeSocket();
639            } else {
640                throw new RuntimeException("Error invoking fork()");
641            }
642        }
643    }
644
645    /**
646     * Runs the zygote process's select loop. Accepts new connections as
647     * they happen, and reads commands from connections one spawn-request's
648     * worth at a time.
649     *
650     * @throws MethodAndArgsCaller in a child process when a main() should
651     * be executed.
652     */
653    private static void runSelectLoopMode() throws MethodAndArgsCaller {
654        ArrayList<FileDescriptor> fds = new ArrayList();
655        ArrayList<ZygoteConnection> peers = new ArrayList();
656        FileDescriptor[] fdArray = new FileDescriptor[4];
657
658        fds.add(sServerSocket.getFileDescriptor());
659        peers.add(null);
660
661        int loopCount = GC_LOOP_COUNT;
662        while (true) {
663            int index;
664
665            /*
666             * Call gc() before we block in select().
667             * It's work that has to be done anyway, and it's better
668             * to avoid making every child do it.  It will also
669             * madvise() any free memory as a side-effect.
670             *
671             * Don't call it every time, because walking the entire
672             * heap is a lot of overhead to free a few hundred bytes.
673             */
674            if (loopCount <= 0) {
675                gc();
676                loopCount = GC_LOOP_COUNT;
677            } else {
678                loopCount--;
679            }
680
681
682            try {
683                fdArray = fds.toArray(fdArray);
684                index = selectReadable(fdArray);
685            } catch (IOException ex) {
686                throw new RuntimeException("Error in select()", ex);
687            }
688
689            if (index < 0) {
690                throw new RuntimeException("Error in select()");
691            } else if (index == 0) {
692                ZygoteConnection newPeer = acceptCommandPeer();
693                peers.add(newPeer);
694                fds.add(newPeer.getFileDesciptor());
695            } else {
696                boolean done;
697                done = peers.get(index).runOnce();
698
699                if (done) {
700                    peers.remove(index);
701                    fds.remove(index);
702                }
703            }
704        }
705    }
706
707    /**
708     * The Linux syscall "setreuid()"
709     * @param ruid real uid
710     * @param euid effective uid
711     * @return 0 on success, non-zero errno on fail
712     */
713    static native int setreuid(int ruid, int euid);
714
715    /**
716     * The Linux syscall "setregid()"
717     * @param rgid real gid
718     * @param egid effective gid
719     * @return 0 on success, non-zero errno on fail
720     */
721    static native int setregid(int rgid, int egid);
722
723    /**
724     * Invokes the linux syscall "setpgid"
725     *
726     * @param pid pid to change
727     * @param pgid new process group of pid
728     * @return 0 on success or non-zero errno on fail
729     */
730    static native int setpgid(int pid, int pgid);
731
732    /**
733     * Invokes the linux syscall "getpgid"
734     *
735     * @param pid pid to query
736     * @return pgid of pid in question
737     * @throws IOException on error
738     */
739    static native int getpgid(int pid) throws IOException;
740
741    /**
742     * Invokes the syscall dup2() to copy the specified descriptors into
743     * stdin, stdout, and stderr. The existing stdio descriptors will be
744     * closed and errors during close will be ignored. The specified
745     * descriptors will also remain open at their original descriptor numbers,
746     * so the caller may want to close the original descriptors.
747     *
748     * @param in new stdin
749     * @param out new stdout
750     * @param err new stderr
751     * @throws IOException
752     */
753    static native void reopenStdio(FileDescriptor in,
754            FileDescriptor out, FileDescriptor err) throws IOException;
755
756    /**
757     * Calls close() on a file descriptor
758     *
759     * @param fd descriptor to close
760     * @throws IOException
761     */
762    static native void closeDescriptor(FileDescriptor fd)
763            throws IOException;
764
765    /**
766     * Toggles the close-on-exec flag for the specified file descriptor.
767     *
768     * @param fd non-null; file descriptor
769     * @param flag desired close-on-exec flag state
770     * @throws IOException
771     */
772    static native void setCloseOnExec(FileDescriptor fd, boolean flag)
773            throws IOException;
774
775    /**
776     * Retrieves the permitted capability set from another process.
777     *
778     * @param pid &gt;=0 process ID or 0 for this process
779     * @throws IOException on error
780     */
781    static native long capgetPermitted(int pid)
782            throws IOException;
783
784    /**
785     * Sets the permitted and effective capability sets of this process.
786     *
787     * @param permittedCapabilities permitted set
788     * @param effectiveCapabilities effective set
789     * @throws IOException on error
790     */
791    static native void setCapabilities(
792            long permittedCapabilities,
793            long effectiveCapabilities) throws IOException;
794
795    /**
796     * Invokes select() on the provider array of file descriptors (selecting
797     * for readability only). Array elements of null are ignored.
798     *
799     * @param fds non-null; array of readable file descriptors
800     * @return index of descriptor that is now readable or -1 for empty array.
801     * @throws IOException if an error occurs
802     */
803    static native int selectReadable(FileDescriptor[] fds) throws IOException;
804
805    /**
806     * Creates a file descriptor from an int fd.
807     *
808     * @param fd integer OS file descriptor
809     * @return non-null; FileDescriptor instance
810     * @throws IOException if fd is invalid
811     */
812    static native FileDescriptor createFileDescriptor(int fd)
813            throws IOException;
814
815    /**
816     * Class not instantiable.
817     */
818    private ZygoteInit() {
819    }
820
821    /**
822     * Helper exception class which holds a method and arguments and
823     * can call them. This is used as part of a trampoline to get rid of
824     * the initial process setup stack frames.
825     */
826    public static class MethodAndArgsCaller extends Exception
827            implements Runnable {
828        /** method to call */
829        private final Method mMethod;
830
831        /** argument array */
832        private final String[] mArgs;
833
834        public MethodAndArgsCaller(Method method, String[] args) {
835            mMethod = method;
836            mArgs = args;
837        }
838
839        public void run() {
840            try {
841                mMethod.invoke(null, new Object[] { mArgs });
842            } catch (IllegalAccessException ex) {
843                throw new RuntimeException(ex);
844            } catch (InvocationTargetException ex) {
845                Throwable cause = ex.getCause();
846                if (cause instanceof RuntimeException) {
847                    throw (RuntimeException) cause;
848                } else if (cause instanceof Error) {
849                    throw (Error) cause;
850                }
851                throw new RuntimeException(ex);
852            }
853        }
854    }
855}
856