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