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