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.POLLIN;
20import static android.system.OsConstants.S_IRWXG;
21import static android.system.OsConstants.S_IRWXO;
22
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.net.LocalServerSocket;
26import android.opengl.EGL14;
27import android.os.Process;
28import android.os.SystemClock;
29import android.os.SystemProperties;
30import android.os.Trace;
31import android.system.ErrnoException;
32import android.system.Os;
33import android.system.OsConstants;
34import android.system.StructPollfd;
35import android.text.Hyphenator;
36import android.util.EventLog;
37import android.util.Log;
38import android.webkit.WebViewFactory;
39
40import dalvik.system.DexFile;
41import dalvik.system.PathClassLoader;
42import dalvik.system.VMRuntime;
43
44import libcore.io.IoUtils;
45
46import java.io.BufferedReader;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileNotFoundException;
50import java.io.IOException;
51import java.io.InputStream;
52import java.io.InputStreamReader;
53import java.lang.reflect.InvocationTargetException;
54import java.lang.reflect.Method;
55import java.util.ArrayList;
56
57/**
58 * Startup class for the zygote process.
59 *
60 * Pre-initializes some classes, and then waits for commands on a UNIX domain
61 * socket. Based on these commands, forks off child processes that inherit
62 * the initial state of the VM.
63 *
64 * Please see {@link ZygoteConnection.Arguments} for documentation on the
65 * client protocol.
66 *
67 * @hide
68 */
69public class ZygoteInit {
70    private static final String TAG = "Zygote";
71
72    private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
73
74    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
75
76    private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
77    private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
78
79    /** when preloading, GC after allocating this many bytes */
80    private static final int PRELOAD_GC_THRESHOLD = 50000;
81
82    private static final String ABI_LIST_ARG = "--abi-list=";
83
84    private static final String SOCKET_NAME_ARG = "--socket-name=";
85
86    private static LocalServerSocket sServerSocket;
87
88    /**
89     * Used to pre-load resources.  We hold a global reference on it so it
90     * never gets destroyed.
91     */
92    private static Resources mResources;
93
94    /**
95     * The path of a file that contains classes to preload.
96     */
97    private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
98
99    /** Controls whether we should preload resources during zygote init. */
100    private static final boolean PRELOAD_RESOURCES = true;
101
102    /**
103     * Registers a server socket for zygote command connections
104     *
105     * @throws RuntimeException when open fails
106     */
107    private static void registerZygoteSocket(String socketName) {
108        if (sServerSocket == null) {
109            int fileDesc;
110            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
111            try {
112                String env = System.getenv(fullSocketName);
113                fileDesc = Integer.parseInt(env);
114            } catch (RuntimeException ex) {
115                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
116            }
117
118            try {
119                FileDescriptor fd = new FileDescriptor();
120                fd.setInt$(fileDesc);
121                sServerSocket = new LocalServerSocket(fd);
122            } catch (IOException ex) {
123                throw new RuntimeException(
124                        "Error binding to local socket '" + fileDesc + "'", ex);
125            }
126        }
127    }
128
129    /**
130     * Waits for and accepts a single command connection. Throws
131     * RuntimeException on failure.
132     */
133    private static ZygoteConnection acceptCommandPeer(String abiList) {
134        try {
135            return new ZygoteConnection(sServerSocket.accept(), abiList);
136        } catch (IOException ex) {
137            throw new RuntimeException(
138                    "IOException during accept()", ex);
139        }
140    }
141
142    /**
143     * Close and clean up zygote sockets. Called on shutdown and on the
144     * child's exit path.
145     */
146    static void closeServerSocket() {
147        try {
148            if (sServerSocket != null) {
149                FileDescriptor fd = sServerSocket.getFileDescriptor();
150                sServerSocket.close();
151                if (fd != null) {
152                    Os.close(fd);
153                }
154            }
155        } catch (IOException ex) {
156            Log.e(TAG, "Zygote:  error closing sockets", ex);
157        } catch (ErrnoException ex) {
158            Log.e(TAG, "Zygote:  error closing descriptor", ex);
159        }
160
161        sServerSocket = null;
162    }
163
164    /**
165     * Return the server socket's underlying file descriptor, so that
166     * ZygoteConnection can pass it to the native code for proper
167     * closure after a child process is forked off.
168     */
169
170    static FileDescriptor getServerSocketFileDescriptor() {
171        return sServerSocket.getFileDescriptor();
172    }
173
174    private static final int UNPRIVILEGED_UID = 9999;
175    private static final int UNPRIVILEGED_GID = 9999;
176
177    private static final int ROOT_UID = 0;
178    private static final int ROOT_GID = 0;
179
180    static void preload() {
181        Log.d(TAG, "begin preload");
182        preloadClasses();
183        preloadResources();
184        preloadOpenGL();
185        preloadSharedLibraries();
186        preloadTextResources();
187        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
188        // for memory sharing purposes.
189        WebViewFactory.prepareWebViewInZygote();
190        Log.d(TAG, "end preload");
191    }
192
193    private static void preloadSharedLibraries() {
194        Log.i(TAG, "Preloading shared libraries...");
195        System.loadLibrary("android");
196        System.loadLibrary("compiler_rt");
197        System.loadLibrary("jnigraphics");
198    }
199
200    private static void preloadOpenGL() {
201        if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
202            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
203        }
204    }
205
206    private static void preloadTextResources() {
207        Hyphenator.init();
208    }
209
210    /**
211     * Performs Zygote process initialization. Loads and initializes
212     * commonly used classes.
213     *
214     * Most classes only cause a few hundred bytes to be allocated, but
215     * a few will allocate a dozen Kbytes (in one case, 500+K).
216     */
217    private static void preloadClasses() {
218        final VMRuntime runtime = VMRuntime.getRuntime();
219
220        InputStream is;
221        try {
222            is = new FileInputStream(PRELOADED_CLASSES);
223        } catch (FileNotFoundException e) {
224            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
225            return;
226        }
227
228        Log.i(TAG, "Preloading classes...");
229        long startTime = SystemClock.uptimeMillis();
230
231        // Drop root perms while running static initializers.
232        final int reuid = Os.getuid();
233        final int regid = Os.getgid();
234
235        // We need to drop root perms only if we're already root. In the case of "wrapped"
236        // processes (see WrapperInit), this function is called from an unprivileged uid
237        // and gid.
238        boolean droppedPriviliges = false;
239        if (reuid == ROOT_UID && regid == ROOT_GID) {
240            try {
241                Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
242                Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
243            } catch (ErrnoException ex) {
244                throw new RuntimeException("Failed to drop root", ex);
245            }
246
247            droppedPriviliges = true;
248        }
249
250        // Alter the target heap utilization.  With explicit GCs this
251        // is not likely to have any effect.
252        float defaultUtilization = runtime.getTargetHeapUtilization();
253        runtime.setTargetHeapUtilization(0.8f);
254
255        try {
256            BufferedReader br
257                = new BufferedReader(new InputStreamReader(is), 256);
258
259            int count = 0;
260            String line;
261            while ((line = br.readLine()) != null) {
262                // Skip comments and blank lines.
263                line = line.trim();
264                if (line.startsWith("#") || line.equals("")) {
265                    continue;
266                }
267
268                try {
269                    if (false) {
270                        Log.v(TAG, "Preloading " + line + "...");
271                    }
272                    // Load and explicitly initialize the given class. Use
273                    // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
274                    // (to derive the caller's class-loader). Use true to force initialization, and
275                    // null for the boot classpath class-loader (could as well cache the
276                    // class-loader of this class in a variable).
277                    Class.forName(line, true, null);
278                    count++;
279                } catch (ClassNotFoundException e) {
280                    Log.w(TAG, "Class not found for preloading: " + line);
281                } catch (UnsatisfiedLinkError e) {
282                    Log.w(TAG, "Problem preloading " + line + ": " + e);
283                } catch (Throwable t) {
284                    Log.e(TAG, "Error preloading " + line + ".", t);
285                    if (t instanceof Error) {
286                        throw (Error) t;
287                    }
288                    if (t instanceof RuntimeException) {
289                        throw (RuntimeException) t;
290                    }
291                    throw new RuntimeException(t);
292                }
293            }
294
295            Log.i(TAG, "...preloaded " + count + " classes in "
296                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
297        } catch (IOException e) {
298            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
299        } finally {
300            IoUtils.closeQuietly(is);
301            // Restore default.
302            runtime.setTargetHeapUtilization(defaultUtilization);
303
304            // Fill in dex caches with classes, fields, and methods brought in by preloading.
305            runtime.preloadDexCaches();
306
307            // Bring back root. We'll need it later if we're in the zygote.
308            if (droppedPriviliges) {
309                try {
310                    Os.setreuid(ROOT_UID, ROOT_UID);
311                    Os.setregid(ROOT_GID, ROOT_GID);
312                } catch (ErrnoException ex) {
313                    throw new RuntimeException("Failed to restore root", ex);
314                }
315            }
316        }
317    }
318
319    /**
320     * Load in commonly used resources, so they can be shared across
321     * processes.
322     *
323     * These tend to be a few Kbytes, but are frequently in the 20-40K
324     * range, and occasionally even larger.
325     */
326    private static void preloadResources() {
327        final VMRuntime runtime = VMRuntime.getRuntime();
328
329        try {
330            mResources = Resources.getSystem();
331            mResources.startPreloading();
332            if (PRELOAD_RESOURCES) {
333                Log.i(TAG, "Preloading resources...");
334
335                long startTime = SystemClock.uptimeMillis();
336                TypedArray ar = mResources.obtainTypedArray(
337                        com.android.internal.R.array.preloaded_drawables);
338                int N = preloadDrawables(runtime, ar);
339                ar.recycle();
340                Log.i(TAG, "...preloaded " + N + " resources in "
341                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
342
343                startTime = SystemClock.uptimeMillis();
344                ar = mResources.obtainTypedArray(
345                        com.android.internal.R.array.preloaded_color_state_lists);
346                N = preloadColorStateLists(runtime, ar);
347                ar.recycle();
348                Log.i(TAG, "...preloaded " + N + " resources in "
349                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
350            }
351            mResources.finishPreloading();
352        } catch (RuntimeException e) {
353            Log.w(TAG, "Failure preloading resources", e);
354        }
355    }
356
357    private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
358        int N = ar.length();
359        for (int i=0; i<N; i++) {
360            int id = ar.getResourceId(i, 0);
361            if (false) {
362                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
363            }
364            if (id != 0) {
365                if (mResources.getColorStateList(id, null) == null) {
366                    throw new IllegalArgumentException(
367                            "Unable to find preloaded color resource #0x"
368                            + Integer.toHexString(id)
369                            + " (" + ar.getString(i) + ")");
370                }
371            }
372        }
373        return N;
374    }
375
376
377    private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
378        int N = ar.length();
379        for (int i=0; i<N; i++) {
380            int id = ar.getResourceId(i, 0);
381            if (false) {
382                Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
383            }
384            if (id != 0) {
385                if (mResources.getDrawable(id, null) == null) {
386                    throw new IllegalArgumentException(
387                            "Unable to find preloaded drawable resource #0x"
388                            + Integer.toHexString(id)
389                            + " (" + ar.getString(i) + ")");
390                }
391            }
392        }
393        return N;
394    }
395
396    /**
397     * Runs several special GCs to try to clean up a few generations of
398     * softly- and final-reachable objects, along with any other garbage.
399     * This is only useful just before a fork().
400     */
401    /*package*/ static void gcAndFinalize() {
402        final VMRuntime runtime = VMRuntime.getRuntime();
403
404        /* runFinalizationSync() lets finalizers be called in Zygote,
405         * which doesn't have a HeapWorker thread.
406         */
407        System.gc();
408        runtime.runFinalizationSync();
409        System.gc();
410    }
411
412    /**
413     * Finish remaining work for the newly forked system server process.
414     */
415    private static void handleSystemServerProcess(
416            ZygoteConnection.Arguments parsedArgs)
417            throws ZygoteInit.MethodAndArgsCaller {
418
419        closeServerSocket();
420
421        // set umask to 0077 so new files and directories will default to owner-only permissions.
422        Os.umask(S_IRWXG | S_IRWXO);
423
424        if (parsedArgs.niceName != null) {
425            Process.setArgV0(parsedArgs.niceName);
426        }
427
428        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
429        if (systemServerClasspath != null) {
430            performSystemServerDexOpt(systemServerClasspath);
431        }
432
433        if (parsedArgs.invokeWith != null) {
434            String[] args = parsedArgs.remainingArgs;
435            // If we have a non-null system server class path, we'll have to duplicate the
436            // existing arguments and append the classpath to it. ART will handle the classpath
437            // correctly when we exec a new process.
438            if (systemServerClasspath != null) {
439                String[] amendedArgs = new String[args.length + 2];
440                amendedArgs[0] = "-cp";
441                amendedArgs[1] = systemServerClasspath;
442                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
443            }
444
445            WrapperInit.execApplication(parsedArgs.invokeWith,
446                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
447                    VMRuntime.getCurrentInstructionSet(), null, args);
448        } else {
449            ClassLoader cl = null;
450            if (systemServerClasspath != null) {
451                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
452                Thread.currentThread().setContextClassLoader(cl);
453            }
454
455            /*
456             * Pass the remaining arguments to SystemServer.
457             */
458            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
459        }
460
461        /* should never reach here */
462    }
463
464    /**
465     * Performs dex-opt on the elements of {@code classPath}, if needed. We
466     * choose the instruction set of the current runtime.
467     */
468    private static void performSystemServerDexOpt(String classPath) {
469        final String[] classPathElements = classPath.split(":");
470        final InstallerConnection installer = new InstallerConnection();
471        installer.waitForConnection();
472        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
473
474        try {
475            for (String classPathElement : classPathElements) {
476                final int dexoptNeeded = DexFile.getDexOptNeeded(
477                        classPathElement, "*", instructionSet, false /* defer */);
478                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
479                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
480                            instructionSet, dexoptNeeded, false /* boot complete */);
481                }
482            }
483        } catch (IOException ioe) {
484            throw new RuntimeException("Error starting system_server", ioe);
485        } finally {
486            installer.disconnect();
487        }
488    }
489
490    /**
491     * Prepare the arguments and fork for the system server process.
492     */
493    private static boolean startSystemServer(String abiList, String socketName)
494            throws MethodAndArgsCaller, RuntimeException {
495        long capabilities = posixCapabilitiesAsBits(
496            OsConstants.CAP_BLOCK_SUSPEND,
497            OsConstants.CAP_KILL,
498            OsConstants.CAP_NET_ADMIN,
499            OsConstants.CAP_NET_BIND_SERVICE,
500            OsConstants.CAP_NET_BROADCAST,
501            OsConstants.CAP_NET_RAW,
502            OsConstants.CAP_SYS_MODULE,
503            OsConstants.CAP_SYS_NICE,
504            OsConstants.CAP_SYS_RESOURCE,
505            OsConstants.CAP_SYS_TIME,
506            OsConstants.CAP_SYS_TTY_CONFIG
507        );
508        /* Hardcoded command line to start the system server */
509        String args[] = {
510            "--setuid=1000",
511            "--setgid=1000",
512            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
513            "--capabilities=" + capabilities + "," + capabilities,
514            "--nice-name=system_server",
515            "--runtime-args",
516            "com.android.server.SystemServer",
517        };
518        ZygoteConnection.Arguments parsedArgs = null;
519
520        int pid;
521
522        try {
523            parsedArgs = new ZygoteConnection.Arguments(args);
524            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
525            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
526
527            /* Request to fork the system server process */
528            pid = Zygote.forkSystemServer(
529                    parsedArgs.uid, parsedArgs.gid,
530                    parsedArgs.gids,
531                    parsedArgs.debugFlags,
532                    null,
533                    parsedArgs.permittedCapabilities,
534                    parsedArgs.effectiveCapabilities);
535        } catch (IllegalArgumentException ex) {
536            throw new RuntimeException(ex);
537        }
538
539        /* For child process */
540        if (pid == 0) {
541            if (hasSecondZygote(abiList)) {
542                waitForSecondaryZygote(socketName);
543            }
544
545            handleSystemServerProcess(parsedArgs);
546        }
547
548        return true;
549    }
550
551    /**
552     * Gets the bit array representation of the provided list of POSIX capabilities.
553     */
554    private static long posixCapabilitiesAsBits(int... capabilities) {
555        long result = 0;
556        for (int capability : capabilities) {
557            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
558                throw new IllegalArgumentException(String.valueOf(capability));
559            }
560            result |= (1L << capability);
561        }
562        return result;
563    }
564
565    public static void main(String argv[]) {
566        try {
567            RuntimeInit.enableDdms();
568            // Start profiling the zygote initialization.
569            SamplingProfilerIntegration.start();
570
571            boolean startSystemServer = false;
572            String socketName = "zygote";
573            String abiList = null;
574            for (int i = 1; i < argv.length; i++) {
575                if ("start-system-server".equals(argv[i])) {
576                    startSystemServer = true;
577                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
578                    abiList = argv[i].substring(ABI_LIST_ARG.length());
579                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
580                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
581                } else {
582                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
583                }
584            }
585
586            if (abiList == null) {
587                throw new RuntimeException("No ABI list supplied.");
588            }
589
590            registerZygoteSocket(socketName);
591            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
592                SystemClock.uptimeMillis());
593            preload();
594            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
595                SystemClock.uptimeMillis());
596
597            // Finish profiling the zygote initialization.
598            SamplingProfilerIntegration.writeZygoteSnapshot();
599
600            // Do an initial gc to clean up after startup
601            gcAndFinalize();
602
603            // Disable tracing so that forked processes do not inherit stale tracing tags from
604            // Zygote.
605            Trace.setTracingEnabled(false);
606
607            if (startSystemServer) {
608                startSystemServer(abiList, socketName);
609            }
610
611            Log.i(TAG, "Accepting command socket connections");
612            runSelectLoop(abiList);
613
614            closeServerSocket();
615        } catch (MethodAndArgsCaller caller) {
616            caller.run();
617        } catch (RuntimeException ex) {
618            Log.e(TAG, "Zygote died with exception", ex);
619            closeServerSocket();
620            throw ex;
621        }
622    }
623
624    /**
625     * Return {@code true} if this device configuration has another zygote.
626     *
627     * We determine this by comparing the device ABI list with this zygotes
628     * list. If this zygote supports all ABIs this device supports, there won't
629     * be another zygote.
630     */
631    private static boolean hasSecondZygote(String abiList) {
632        return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
633    }
634
635    private static void waitForSecondaryZygote(String socketName) {
636        String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
637                Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
638        while (true) {
639            try {
640                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
641                zs.close();
642                break;
643            } catch (IOException ioe) {
644                Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
645            }
646
647            try {
648                Thread.sleep(1000);
649            } catch (InterruptedException ie) {
650            }
651        }
652    }
653
654    /**
655     * Runs the zygote process's select loop. Accepts new connections as
656     * they happen, and reads commands from connections one spawn-request's
657     * worth at a time.
658     *
659     * @throws MethodAndArgsCaller in a child process when a main() should
660     * be executed.
661     */
662    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
663        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
664        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
665
666        fds.add(sServerSocket.getFileDescriptor());
667        peers.add(null);
668
669        while (true) {
670            StructPollfd[] pollFds = new StructPollfd[fds.size()];
671            for (int i = 0; i < pollFds.length; ++i) {
672                pollFds[i] = new StructPollfd();
673                pollFds[i].fd = fds.get(i);
674                pollFds[i].events = (short) POLLIN;
675            }
676            try {
677                Os.poll(pollFds, -1);
678            } catch (ErrnoException ex) {
679                throw new RuntimeException("poll failed", ex);
680            }
681            for (int i = pollFds.length - 1; i >= 0; --i) {
682                if ((pollFds[i].revents & POLLIN) == 0) {
683                    continue;
684                }
685                if (i == 0) {
686                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
687                    peers.add(newPeer);
688                    fds.add(newPeer.getFileDesciptor());
689                } else {
690                    boolean done = peers.get(i).runOnce();
691                    if (done) {
692                        peers.remove(i);
693                        fds.remove(i);
694                    }
695                }
696            }
697        }
698    }
699
700    /**
701     * Class not instantiable.
702     */
703    private ZygoteInit() {
704    }
705
706    /**
707     * Helper exception class which holds a method and arguments and
708     * can call them. This is used as part of a trampoline to get rid of
709     * the initial process setup stack frames.
710     */
711    public static class MethodAndArgsCaller extends Exception
712            implements Runnable {
713        /** method to call */
714        private final Method mMethod;
715
716        /** argument array */
717        private final String[] mArgs;
718
719        public MethodAndArgsCaller(Method method, String[] args) {
720            mMethod = method;
721            mArgs = args;
722        }
723
724        public void run() {
725            try {
726                mMethod.invoke(null, new Object[] { mArgs });
727            } catch (IllegalAccessException ex) {
728                throw new RuntimeException(ex);
729            } catch (InvocationTargetException ex) {
730                Throwable cause = ex.getCause();
731                if (cause instanceof RuntimeException) {
732                    throw (RuntimeException) cause;
733                } else if (cause instanceof Error) {
734                    throw (Error) cause;
735                }
736                throw new RuntimeException(ex);
737            }
738        }
739    }
740}
741