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