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