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