• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.os;
18 
19 import static libcore.io.OsConstants.S_IRWXG;
20 import static libcore.io.OsConstants.S_IRWXO;
21 
22 import android.content.res.Resources;
23 import android.content.res.TypedArray;
24 import android.net.LocalServerSocket;
25 import android.os.Debug;
26 import android.os.Process;
27 import android.os.SystemClock;
28 import android.os.Trace;
29 import android.util.EventLog;
30 import android.util.Log;
31 
32 import dalvik.system.VMRuntime;
33 import dalvik.system.Zygote;
34 
35 import libcore.io.IoUtils;
36 import libcore.io.Libcore;
37 
38 import java.io.BufferedReader;
39 import java.io.FileDescriptor;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.InputStreamReader;
43 import java.lang.reflect.InvocationTargetException;
44 import java.lang.reflect.Method;
45 import java.lang.reflect.Modifier;
46 import java.util.ArrayList;
47 
48 /**
49  * Startup class for the zygote process.
50  *
51  * Pre-initializes some classes, and then waits for commands on a UNIX domain
52  * socket. Based on these commands, forks off child processes that inherit
53  * the initial state of the VM.
54  *
55  * Please see {@link ZygoteConnection.Arguments} for documentation on the
56  * client protocol.
57  *
58  * @hide
59  */
60 public class ZygoteInit {
61 
62     private static final String TAG = "Zygote";
63 
64     private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
65 
66     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
67     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
68 
69     /** when preloading, GC after allocating this many bytes */
70     private static final int PRELOAD_GC_THRESHOLD = 50000;
71 
72     public static final String USAGE_STRING =
73             " <\"start-system-server\"|\"\" for startSystemServer>";
74 
75     private static LocalServerSocket sServerSocket;
76 
77     /**
78      * Used to pre-load resources.  We hold a global reference on it so it
79      * never gets destroyed.
80      */
81     private static Resources mResources;
82 
83     /**
84      * The number of times that the main Zygote loop
85      * should run before calling gc() again.
86      */
87     static final int GC_LOOP_COUNT = 10;
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      */
invokeStaticMain(ClassLoader loader, String className, String[] argv)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      */
registerZygoteSocket()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      */
acceptCommandPeer()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      */
closeServerSocket()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      */
setEffectiveUser(int uid)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      */
setEffectiveGroup(int gid)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 
preload()226     static void preload() {
227         preloadClasses();
228         preloadResources();
229     }
230 
231     /**
232      * Performs Zygote process initialization. Loads and initializes
233      * commonly used classes.
234      *
235      * Most classes only cause a few hundred bytes to be allocated, but
236      * a few will allocate a dozen Kbytes (in one case, 500+K).
237      */
preloadClasses()238     private static void preloadClasses() {
239         final VMRuntime runtime = VMRuntime.getRuntime();
240 
241         InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
242                 PRELOADED_CLASSES);
243         if (is == null) {
244             Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
245         } else {
246             Log.i(TAG, "Preloading classes...");
247             long startTime = SystemClock.uptimeMillis();
248 
249             // Drop root perms while running static initializers.
250             setEffectiveGroup(UNPRIVILEGED_GID);
251             setEffectiveUser(UNPRIVILEGED_UID);
252 
253             // Alter the target heap utilization.  With explicit GCs this
254             // is not likely to have any effect.
255             float defaultUtilization = runtime.getTargetHeapUtilization();
256             runtime.setTargetHeapUtilization(0.8f);
257 
258             // Start with a clean slate.
259             System.gc();
260             runtime.runFinalizationSync();
261             Debug.startAllocCounting();
262 
263             try {
264                 BufferedReader br
265                     = new BufferedReader(new InputStreamReader(is), 256);
266 
267                 int count = 0;
268                 String line;
269                 while ((line = br.readLine()) != null) {
270                     // Skip comments and blank lines.
271                     line = line.trim();
272                     if (line.startsWith("#") || line.equals("")) {
273                         continue;
274                     }
275 
276                     try {
277                         if (false) {
278                             Log.v(TAG, "Preloading " + line + "...");
279                         }
280                         Class.forName(line);
281                         if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
282                             if (false) {
283                                 Log.v(TAG,
284                                     " GC at " + Debug.getGlobalAllocSize());
285                             }
286                             System.gc();
287                             runtime.runFinalizationSync();
288                             Debug.resetGlobalAllocSize();
289                         }
290                         count++;
291                     } catch (ClassNotFoundException e) {
292                         Log.w(TAG, "Class not found for preloading: " + line);
293                     } catch (Throwable t) {
294                         Log.e(TAG, "Error preloading " + line + ".", t);
295                         if (t instanceof Error) {
296                             throw (Error) t;
297                         }
298                         if (t instanceof RuntimeException) {
299                             throw (RuntimeException) t;
300                         }
301                         throw new RuntimeException(t);
302                     }
303                 }
304 
305                 Log.i(TAG, "...preloaded " + count + " classes in "
306                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
307             } catch (IOException e) {
308                 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
309             } finally {
310                 IoUtils.closeQuietly(is);
311                 // Restore default.
312                 runtime.setTargetHeapUtilization(defaultUtilization);
313 
314                 Debug.stopAllocCounting();
315 
316                 // Bring back root. We'll need it later.
317                 setEffectiveUser(ROOT_UID);
318                 setEffectiveGroup(ROOT_GID);
319             }
320         }
321     }
322 
323     /**
324      * Load in commonly used resources, so they can be shared across
325      * processes.
326      *
327      * These tend to be a few Kbytes, but are frequently in the 20-40K
328      * range, and occasionally even larger.
329      */
preloadResources()330     private static void preloadResources() {
331         final VMRuntime runtime = VMRuntime.getRuntime();
332 
333         Debug.startAllocCounting();
334         try {
335             System.gc();
336             runtime.runFinalizationSync();
337             mResources = Resources.getSystem();
338             mResources.startPreloading();
339             if (PRELOAD_RESOURCES) {
340                 Log.i(TAG, "Preloading resources...");
341 
342                 long startTime = SystemClock.uptimeMillis();
343                 TypedArray ar = mResources.obtainTypedArray(
344                         com.android.internal.R.array.preloaded_drawables);
345                 int N = preloadDrawables(runtime, ar);
346                 ar.recycle();
347                 Log.i(TAG, "...preloaded " + N + " resources in "
348                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
349 
350                 startTime = SystemClock.uptimeMillis();
351                 ar = mResources.obtainTypedArray(
352                         com.android.internal.R.array.preloaded_color_state_lists);
353                 N = preloadColorStateLists(runtime, ar);
354                 ar.recycle();
355                 Log.i(TAG, "...preloaded " + N + " resources in "
356                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
357             }
358             mResources.finishPreloading();
359         } catch (RuntimeException e) {
360             Log.w(TAG, "Failure preloading resources", e);
361         } finally {
362             Debug.stopAllocCounting();
363         }
364     }
365 
preloadColorStateLists(VMRuntime runtime, TypedArray ar)366     private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
367         int N = ar.length();
368         for (int i=0; i<N; i++) {
369             if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
370                 if (false) {
371                     Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
372                 }
373                 System.gc();
374                 runtime.runFinalizationSync();
375                 Debug.resetGlobalAllocSize();
376             }
377             int id = ar.getResourceId(i, 0);
378             if (false) {
379                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
380             }
381             if (id != 0) {
382                 if (mResources.getColorStateList(id) == null) {
383                     throw new IllegalArgumentException(
384                             "Unable to find preloaded color resource #0x"
385                             + Integer.toHexString(id)
386                             + " (" + ar.getString(i) + ")");
387                 }
388             }
389         }
390         return N;
391     }
392 
393 
preloadDrawables(VMRuntime runtime, TypedArray ar)394     private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
395         int N = ar.length();
396         for (int i=0; i<N; i++) {
397             if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
398                 if (false) {
399                     Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
400                 }
401                 System.gc();
402                 runtime.runFinalizationSync();
403                 Debug.resetGlobalAllocSize();
404             }
405             int id = ar.getResourceId(i, 0);
406             if (false) {
407                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
408             }
409             if (id != 0) {
410                 if (mResources.getDrawable(id) == null) {
411                     throw new IllegalArgumentException(
412                             "Unable to find preloaded drawable resource #0x"
413                             + Integer.toHexString(id)
414                             + " (" + ar.getString(i) + ")");
415                 }
416             }
417         }
418         return N;
419     }
420 
421     /**
422      * Runs several special GCs to try to clean up a few generations of
423      * softly- and final-reachable objects, along with any other garbage.
424      * This is only useful just before a fork().
425      */
gc()426     /*package*/ static void gc() {
427         final VMRuntime runtime = VMRuntime.getRuntime();
428 
429         /* runFinalizationSync() lets finalizers be called in Zygote,
430          * which doesn't have a HeapWorker thread.
431          */
432         System.gc();
433         runtime.runFinalizationSync();
434         System.gc();
435         runtime.runFinalizationSync();
436         System.gc();
437         runtime.runFinalizationSync();
438     }
439 
440     /**
441      * Finish remaining work for the newly forked system server process.
442      */
handleSystemServerProcess( ZygoteConnection.Arguments parsedArgs)443     private static void handleSystemServerProcess(
444             ZygoteConnection.Arguments parsedArgs)
445             throws ZygoteInit.MethodAndArgsCaller {
446 
447         closeServerSocket();
448 
449         // set umask to 0077 so new files and directories will default to owner-only permissions.
450         Libcore.os.umask(S_IRWXG | S_IRWXO);
451 
452         if (parsedArgs.niceName != null) {
453             Process.setArgV0(parsedArgs.niceName);
454         }
455 
456         if (parsedArgs.invokeWith != null) {
457             WrapperInit.execApplication(parsedArgs.invokeWith,
458                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
459                     null, parsedArgs.remainingArgs);
460         } else {
461             /*
462              * Pass the remaining arguments to SystemServer.
463              */
464             RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
465         }
466 
467         /* should never reach here */
468     }
469 
470     /**
471      * Prepare the arguments and fork for the system server process.
472      */
startSystemServer()473     private static boolean startSystemServer()
474             throws MethodAndArgsCaller, RuntimeException {
475         /* Hardcoded command line to start the system server */
476         String args[] = {
477             "--setuid=1000",
478             "--setgid=1000",
479             "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006,3007",
480             "--capabilities=130104352,130104352",
481             "--runtime-init",
482             "--nice-name=system_server",
483             "com.android.server.SystemServer",
484         };
485         ZygoteConnection.Arguments parsedArgs = null;
486 
487         int pid;
488 
489         try {
490             parsedArgs = new ZygoteConnection.Arguments(args);
491             ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
492             ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
493 
494             /* Request to fork the system server process */
495             pid = Zygote.forkSystemServer(
496                     parsedArgs.uid, parsedArgs.gid,
497                     parsedArgs.gids,
498                     parsedArgs.debugFlags,
499                     null,
500                     parsedArgs.permittedCapabilities,
501                     parsedArgs.effectiveCapabilities);
502         } catch (IllegalArgumentException ex) {
503             throw new RuntimeException(ex);
504         }
505 
506         /* For child process */
507         if (pid == 0) {
508             handleSystemServerProcess(parsedArgs);
509         }
510 
511         return true;
512     }
513 
main(String argv[])514     public static void main(String argv[]) {
515         try {
516             // Start profiling the zygote initialization.
517             SamplingProfilerIntegration.start();
518 
519             registerZygoteSocket();
520             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
521                 SystemClock.uptimeMillis());
522             preload();
523             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
524                 SystemClock.uptimeMillis());
525 
526             // Finish profiling the zygote initialization.
527             SamplingProfilerIntegration.writeZygoteSnapshot();
528 
529             // Do an initial gc to clean up after startup
530             gc();
531 
532             // Disable tracing so that forked processes do not inherit stale tracing tags from
533             // Zygote.
534             Trace.setTracingEnabled(false);
535 
536             // If requested, start system server directly from Zygote
537             if (argv.length != 2) {
538                 throw new RuntimeException(argv[0] + USAGE_STRING);
539             }
540 
541             if (argv[1].equals("start-system-server")) {
542                 startSystemServer();
543             } else if (!argv[1].equals("")) {
544                 throw new RuntimeException(argv[0] + USAGE_STRING);
545             }
546 
547             Log.i(TAG, "Accepting command socket connections");
548 
549             runSelectLoop();
550 
551             closeServerSocket();
552         } catch (MethodAndArgsCaller caller) {
553             caller.run();
554         } catch (RuntimeException ex) {
555             Log.e(TAG, "Zygote died with exception", ex);
556             closeServerSocket();
557             throw ex;
558         }
559     }
560 
561     /**
562      * Runs the zygote process's select loop. Accepts new connections as
563      * they happen, and reads commands from connections one spawn-request's
564      * worth at a time.
565      *
566      * @throws MethodAndArgsCaller in a child process when a main() should
567      * be executed.
568      */
runSelectLoop()569     private static void runSelectLoop() throws MethodAndArgsCaller {
570         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
571         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
572         FileDescriptor[] fdArray = new FileDescriptor[4];
573 
574         fds.add(sServerSocket.getFileDescriptor());
575         peers.add(null);
576 
577         int loopCount = GC_LOOP_COUNT;
578         while (true) {
579             int index;
580 
581             /*
582              * Call gc() before we block in select().
583              * It's work that has to be done anyway, and it's better
584              * to avoid making every child do it.  It will also
585              * madvise() any free memory as a side-effect.
586              *
587              * Don't call it every time, because walking the entire
588              * heap is a lot of overhead to free a few hundred bytes.
589              */
590             if (loopCount <= 0) {
591                 gc();
592                 loopCount = GC_LOOP_COUNT;
593             } else {
594                 loopCount--;
595             }
596 
597 
598             try {
599                 fdArray = fds.toArray(fdArray);
600                 index = selectReadable(fdArray);
601             } catch (IOException ex) {
602                 throw new RuntimeException("Error in select()", ex);
603             }
604 
605             if (index < 0) {
606                 throw new RuntimeException("Error in select()");
607             } else if (index == 0) {
608                 ZygoteConnection newPeer = acceptCommandPeer();
609                 peers.add(newPeer);
610                 fds.add(newPeer.getFileDesciptor());
611             } else {
612                 boolean done;
613                 done = peers.get(index).runOnce();
614 
615                 if (done) {
616                     peers.remove(index);
617                     fds.remove(index);
618                 }
619             }
620         }
621     }
622 
623     /**
624      * The Linux syscall "setreuid()"
625      * @param ruid real uid
626      * @param euid effective uid
627      * @return 0 on success, non-zero errno on fail
628      */
setreuid(int ruid, int euid)629     static native int setreuid(int ruid, int euid);
630 
631     /**
632      * The Linux syscall "setregid()"
633      * @param rgid real gid
634      * @param egid effective gid
635      * @return 0 on success, non-zero errno on fail
636      */
setregid(int rgid, int egid)637     static native int setregid(int rgid, int egid);
638 
639     /**
640      * Invokes the linux syscall "setpgid"
641      *
642      * @param pid pid to change
643      * @param pgid new process group of pid
644      * @return 0 on success or non-zero errno on fail
645      */
setpgid(int pid, int pgid)646     static native int setpgid(int pid, int pgid);
647 
648     /**
649      * Invokes the linux syscall "getpgid"
650      *
651      * @param pid pid to query
652      * @return pgid of pid in question
653      * @throws IOException on error
654      */
getpgid(int pid)655     static native int getpgid(int pid) throws IOException;
656 
657     /**
658      * Invokes the syscall dup2() to copy the specified descriptors into
659      * stdin, stdout, and stderr. The existing stdio descriptors will be
660      * closed and errors during close will be ignored. The specified
661      * descriptors will also remain open at their original descriptor numbers,
662      * so the caller may want to close the original descriptors.
663      *
664      * @param in new stdin
665      * @param out new stdout
666      * @param err new stderr
667      * @throws IOException
668      */
reopenStdio(FileDescriptor in, FileDescriptor out, FileDescriptor err)669     static native void reopenStdio(FileDescriptor in,
670             FileDescriptor out, FileDescriptor err) throws IOException;
671 
672     /**
673      * Toggles the close-on-exec flag for the specified file descriptor.
674      *
675      * @param fd non-null; file descriptor
676      * @param flag desired close-on-exec flag state
677      * @throws IOException
678      */
setCloseOnExec(FileDescriptor fd, boolean flag)679     static native void setCloseOnExec(FileDescriptor fd, boolean flag)
680             throws IOException;
681 
682     /**
683      * Retrieves the permitted capability set from another process.
684      *
685      * @param pid &gt;=0 process ID or 0 for this process
686      * @throws IOException on error
687      */
capgetPermitted(int pid)688     static native long capgetPermitted(int pid)
689             throws IOException;
690 
691     /**
692      * Invokes select() on the provider array of file descriptors (selecting
693      * for readability only). Array elements of null are ignored.
694      *
695      * @param fds non-null; array of readable file descriptors
696      * @return index of descriptor that is now readable or -1 for empty array.
697      * @throws IOException if an error occurs
698      */
selectReadable(FileDescriptor[] fds)699     static native int selectReadable(FileDescriptor[] fds) throws IOException;
700 
701     /**
702      * Creates a file descriptor from an int fd.
703      *
704      * @param fd integer OS file descriptor
705      * @return non-null; FileDescriptor instance
706      * @throws IOException if fd is invalid
707      */
createFileDescriptor(int fd)708     static native FileDescriptor createFileDescriptor(int fd)
709             throws IOException;
710 
711     /**
712      * Class not instantiable.
713      */
ZygoteInit()714     private ZygoteInit() {
715     }
716 
717     /**
718      * Helper exception class which holds a method and arguments and
719      * can call them. This is used as part of a trampoline to get rid of
720      * the initial process setup stack frames.
721      */
722     public static class MethodAndArgsCaller extends Exception
723             implements Runnable {
724         /** method to call */
725         private final Method mMethod;
726 
727         /** argument array */
728         private final String[] mArgs;
729 
MethodAndArgsCaller(Method method, String[] args)730         public MethodAndArgsCaller(Method method, String[] args) {
731             mMethod = method;
732             mArgs = args;
733         }
734 
run()735         public void run() {
736             try {
737                 mMethod.invoke(null, new Object[] { mArgs });
738             } catch (IllegalAccessException ex) {
739                 throw new RuntimeException(ex);
740             } catch (InvocationTargetException ex) {
741                 Throwable cause = ex.getCause();
742                 if (cause instanceof RuntimeException) {
743                     throw (RuntimeException) cause;
744                 } else if (cause instanceof Error) {
745                     throw (Error) cause;
746                 }
747                 throw new RuntimeException(ex);
748             }
749         }
750     }
751 }
752