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