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 >=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