1 /* 2 * Copyright 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.commands.monkey; 18 19 import android.app.ActivityManager; 20 import android.app.IActivityController; 21 import android.app.IActivityManager; 22 import android.content.ComponentName; 23 import android.content.Intent; 24 import android.content.pm.IPackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.os.Build; 27 import android.os.Debug; 28 import android.os.Environment; 29 import android.os.Process; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.os.StrictMode; 33 import android.os.SystemClock; 34 import android.os.UserHandle; 35 import android.view.IWindowManager; 36 import android.view.Surface; 37 38 import java.io.BufferedReader; 39 import java.io.BufferedWriter; 40 import java.io.File; 41 import java.io.FileReader; 42 import java.io.FileWriter; 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.io.InputStreamReader; 46 import java.io.Writer; 47 import java.util.Arrays; 48 import java.util.ArrayList; 49 import java.util.HashSet; 50 import java.util.Iterator; 51 import java.util.List; 52 import java.util.Random; 53 import java.util.Set; 54 55 /** 56 * Application that injects random key events and other actions into the system. 57 */ 58 public class Monkey { 59 60 /** 61 * Monkey Debugging/Dev Support 62 * <p> 63 * All values should be zero when checking in. 64 */ 65 private final static int DEBUG_ALLOW_ANY_STARTS = 0; 66 67 private final static int DEBUG_ALLOW_ANY_RESTARTS = 0; 68 69 private IActivityManager mAm; 70 71 private IWindowManager mWm; 72 73 private IPackageManager mPm; 74 75 /** Command line arguments */ 76 private String[] mArgs; 77 78 /** Current argument being parsed */ 79 private int mNextArg; 80 81 /** Data of current argument */ 82 private String mCurArgData; 83 84 /** Running in verbose output mode? 1= verbose, 2=very verbose */ 85 private int mVerbose; 86 87 /** Ignore any application crashes while running? */ 88 private boolean mIgnoreCrashes; 89 90 /** Ignore any not responding timeouts while running? */ 91 private boolean mIgnoreTimeouts; 92 93 /** Ignore security exceptions when launching activities */ 94 /** (The activity launch still fails, but we keep pluggin' away) */ 95 private boolean mIgnoreSecurityExceptions; 96 97 /** Monitor /data/tombstones and stop the monkey if new files appear. */ 98 private boolean mMonitorNativeCrashes; 99 100 /** Ignore any native crashes while running? */ 101 private boolean mIgnoreNativeCrashes; 102 103 /** Send no events. Use with long throttle-time to watch user operations */ 104 private boolean mSendNoEvents; 105 106 /** This is set when we would like to abort the running of the monkey. */ 107 private boolean mAbort; 108 109 /** 110 * Count each event as a cycle. Set to false for scripts so that each time 111 * through the script increments the count. 112 */ 113 private boolean mCountEvents = true; 114 115 /** 116 * This is set by the ActivityController thread to request collection of ANR 117 * trace files 118 */ 119 private boolean mRequestAnrTraces = false; 120 121 /** 122 * This is set by the ActivityController thread to request a 123 * "dumpsys meminfo" 124 */ 125 private boolean mRequestDumpsysMemInfo = false; 126 127 /** 128 * This is set by the ActivityController thread to request a 129 * bugreport after ANR 130 */ 131 private boolean mRequestAnrBugreport = false; 132 133 /** 134 * This is set by the ActivityController thread to request a 135 * bugreport after a system watchdog report 136 */ 137 private boolean mRequestWatchdogBugreport = false; 138 139 /** 140 * Synchronization for the ActivityController callback to block 141 * until we are done handling the reporting of the watchdog error. 142 */ 143 private boolean mWatchdogWaiting = false; 144 145 /** 146 * This is set by the ActivityController thread to request a 147 * bugreport after java application crash 148 */ 149 private boolean mRequestAppCrashBugreport = false; 150 151 /**Request the bugreport based on the mBugreportFrequency. */ 152 private boolean mGetPeriodicBugreport = false; 153 154 /** 155 * Request the bugreport based on the mBugreportFrequency. 156 */ 157 private boolean mRequestPeriodicBugreport = false; 158 159 /** Bugreport frequency. */ 160 private long mBugreportFrequency = 10; 161 162 /** Failure process name */ 163 private String mReportProcessName; 164 165 /** 166 * This is set by the ActivityController thread to request a "procrank" 167 */ 168 private boolean mRequestProcRank = false; 169 170 /** Kill the process after a timeout or crash. */ 171 private boolean mKillProcessAfterError; 172 173 /** Generate hprof reports before/after monkey runs */ 174 private boolean mGenerateHprof; 175 176 /** If set, only match error if this text appears in the description text. */ 177 private String mMatchDescription; 178 179 /** Package blacklist file. */ 180 private String mPkgBlacklistFile; 181 182 /** Package whitelist file. */ 183 private String mPkgWhitelistFile; 184 185 /** Categories we are allowed to launch **/ 186 private ArrayList<String> mMainCategories = new ArrayList<String>(); 187 188 /** Applications we can switch to. */ 189 private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>(); 190 191 /** The delay between event inputs **/ 192 long mThrottle = 0; 193 194 /** Whether to randomize each throttle (0-mThrottle ms) inserted between events. */ 195 boolean mRandomizeThrottle = false; 196 197 /** The number of iterations **/ 198 int mCount = 1000; 199 200 /** The random number seed **/ 201 long mSeed = 0; 202 203 /** The random number generator **/ 204 Random mRandom = null; 205 206 /** Dropped-event statistics **/ 207 long mDroppedKeyEvents = 0; 208 209 long mDroppedPointerEvents = 0; 210 211 long mDroppedTrackballEvents = 0; 212 213 long mDroppedFlipEvents = 0; 214 215 long mDroppedRotationEvents = 0; 216 217 /** The delay between user actions. This is for the scripted monkey. **/ 218 long mProfileWaitTime = 5000; 219 220 /** Device idle time. This is for the scripted monkey. **/ 221 long mDeviceSleepTime = 30000; 222 223 boolean mRandomizeScript = false; 224 225 boolean mScriptLog = false; 226 227 /** Capture bugreprot whenever there is a crash. **/ 228 private boolean mRequestBugreport = false; 229 230 /** a filename to the setup script (if any) */ 231 private String mSetupFileName = null; 232 233 /** filenames of the script (if any) */ 234 private ArrayList<String> mScriptFileNames = new ArrayList<String>(); 235 236 /** a TCP port to listen on for remote commands. */ 237 private int mServerPort = -1; 238 239 private static final File TOMBSTONES_PATH = new File("/data/tombstones"); 240 241 private static final String TOMBSTONE_PREFIX = "tombstone_"; 242 243 private HashSet<Long> mTombstones = null; 244 245 float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT]; 246 247 MonkeyEventSource mEventSource; 248 249 private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor(); 250 251 private boolean mPermissionTargetSystem = false; 252 253 // information on the current activity. 254 public static Intent currentIntent; 255 256 public static String currentPackage; 257 258 /** 259 * Monitor operations happening in the system. 260 */ 261 private class ActivityController extends IActivityController.Stub { activityStarting(Intent intent, String pkg)262 public boolean activityStarting(Intent intent, String pkg) { 263 final boolean allow = isActivityStartingAllowed(intent, pkg); 264 if (mVerbose > 0) { 265 // StrictMode's disk checks end up catching this on 266 // userdebug/eng builds due to PrintStream going to a 267 // FileOutputStream in the end (perhaps only when 268 // redirected to a file?) So we allow disk writes 269 // around this region for the monkey to minimize 270 // harmless dropbox uploads from monkeys. 271 StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); 272 Logger.out.println(" // " + (allow ? "Allowing" : "Rejecting") + " start of " 273 + intent + " in package " + pkg); 274 StrictMode.setThreadPolicy(savedPolicy); 275 } 276 currentPackage = pkg; 277 currentIntent = intent; 278 return allow; 279 } 280 isActivityStartingAllowed(Intent intent, String pkg)281 private boolean isActivityStartingAllowed(Intent intent, String pkg) { 282 if (MonkeyUtils.getPackageFilter().checkEnteringPackage(pkg)) { 283 return true; 284 } 285 if (DEBUG_ALLOW_ANY_STARTS != 0) { 286 return true; 287 } 288 // In case the activity is launching home and the default launcher 289 // package is disabled, allow anyway to prevent ANR (see b/38121026) 290 final Set<String> categories = intent.getCategories(); 291 if (intent.getAction() == Intent.ACTION_MAIN 292 && categories != null 293 && categories.contains(Intent.CATEGORY_HOME)) { 294 try { 295 final ResolveInfo resolveInfo = 296 mPm.resolveIntent(intent, intent.getType(), 0, UserHandle.myUserId()); 297 final String launcherPackage = resolveInfo.activityInfo.packageName; 298 if (pkg.equals(launcherPackage)) { 299 return true; 300 } 301 } catch (RemoteException e) { 302 Logger.err.println("** Failed talking with package manager!"); 303 return false; 304 } 305 } 306 return false; 307 } 308 activityResuming(String pkg)309 public boolean activityResuming(String pkg) { 310 StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); 311 Logger.out.println(" // activityResuming(" + pkg + ")"); 312 boolean allow = MonkeyUtils.getPackageFilter().checkEnteringPackage(pkg) 313 || (DEBUG_ALLOW_ANY_RESTARTS != 0); 314 if (!allow) { 315 if (mVerbose > 0) { 316 Logger.out.println(" // " + (allow ? "Allowing" : "Rejecting") 317 + " resume of package " + pkg); 318 } 319 } 320 currentPackage = pkg; 321 StrictMode.setThreadPolicy(savedPolicy); 322 return allow; 323 } 324 appCrashed(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace)325 public boolean appCrashed(String processName, int pid, 326 String shortMsg, String longMsg, 327 long timeMillis, String stackTrace) { 328 StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); 329 Logger.err.println("// CRASH: " + processName + " (pid " + pid + ")"); 330 Logger.err.println("// Short Msg: " + shortMsg); 331 Logger.err.println("// Long Msg: " + longMsg); 332 Logger.err.println("// Build Label: " + Build.FINGERPRINT); 333 Logger.err.println("// Build Changelist: " + Build.VERSION.INCREMENTAL); 334 Logger.err.println("// Build Time: " + Build.TIME); 335 Logger.err.println("// " + stackTrace.replace("\n", "\n// ")); 336 StrictMode.setThreadPolicy(savedPolicy); 337 338 if (mMatchDescription == null 339 || shortMsg.contains(mMatchDescription) 340 || longMsg.contains(mMatchDescription) 341 || stackTrace.contains(mMatchDescription)) { 342 if (!mIgnoreCrashes || mRequestBugreport) { 343 synchronized (Monkey.this) { 344 if (!mIgnoreCrashes) { 345 mAbort = true; 346 } 347 if (mRequestBugreport){ 348 mRequestAppCrashBugreport = true; 349 mReportProcessName = processName; 350 } 351 } 352 return !mKillProcessAfterError; 353 } 354 } 355 return false; 356 } 357 appEarlyNotResponding(String processName, int pid, String annotation)358 public int appEarlyNotResponding(String processName, int pid, String annotation) { 359 return 0; 360 } 361 appNotResponding(String processName, int pid, String processStats)362 public int appNotResponding(String processName, int pid, String processStats) { 363 StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); 364 Logger.err.println("// NOT RESPONDING: " + processName + " (pid " + pid + ")"); 365 Logger.err.println(processStats); 366 StrictMode.setThreadPolicy(savedPolicy); 367 368 if (mMatchDescription == null || processStats.contains(mMatchDescription)) { 369 synchronized (Monkey.this) { 370 mRequestAnrTraces = true; 371 mRequestDumpsysMemInfo = true; 372 mRequestProcRank = true; 373 if (mRequestBugreport) { 374 mRequestAnrBugreport = true; 375 mReportProcessName = processName; 376 } 377 } 378 if (!mIgnoreTimeouts) { 379 synchronized (Monkey.this) { 380 mAbort = true; 381 } 382 } 383 } 384 385 return (mKillProcessAfterError) ? -1 : 1; 386 } 387 systemNotResponding(String message)388 public int systemNotResponding(String message) { 389 StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); 390 Logger.err.println("// WATCHDOG: " + message); 391 StrictMode.setThreadPolicy(savedPolicy); 392 393 synchronized (Monkey.this) { 394 if (mMatchDescription == null || message.contains(mMatchDescription)) { 395 if (!mIgnoreCrashes) { 396 mAbort = true; 397 } 398 if (mRequestBugreport) { 399 mRequestWatchdogBugreport = true; 400 } 401 } 402 mWatchdogWaiting = true; 403 } 404 synchronized (Monkey.this) { 405 while (mWatchdogWaiting) { 406 try { 407 Monkey.this.wait(); 408 } catch (InterruptedException e) { 409 } 410 } 411 } 412 return (mKillProcessAfterError) ? -1 : 1; 413 } 414 } 415 416 /** 417 * Run the procrank tool to insert system status information into the debug 418 * report. 419 */ reportProcRank()420 private void reportProcRank() { 421 commandLineReport("procrank", "procrank"); 422 } 423 424 /** 425 * Dump the most recent ANR trace. Wait about 5 seconds first, to let the 426 * asynchronous report writing complete. 427 */ reportAnrTraces()428 private void reportAnrTraces() { 429 try { 430 Thread.sleep(5 * 1000); 431 } catch (InterruptedException e) { 432 } 433 434 // The /data/anr directory might have multiple files, dump the most 435 // recent of those files. 436 File[] recentTraces = new File("/data/anr/").listFiles(); 437 if (recentTraces != null) { 438 File mostRecent = null; 439 long mostRecentMtime = 0; 440 for (File trace : recentTraces) { 441 final long mtime = trace.lastModified(); 442 if (mtime > mostRecentMtime) { 443 mostRecentMtime = mtime; 444 mostRecent = trace; 445 } 446 } 447 448 if (mostRecent != null) { 449 commandLineReport("anr traces", "cat " + mostRecent.getAbsolutePath()); 450 } 451 } 452 } 453 454 /** 455 * Run "dumpsys meminfo" 456 * <p> 457 * NOTE: You cannot perform a dumpsys call from the ActivityController 458 * callback, as it will deadlock. This should only be called from the main 459 * loop of the monkey. 460 */ reportDumpsysMemInfo()461 private void reportDumpsysMemInfo() { 462 commandLineReport("meminfo", "dumpsys meminfo"); 463 } 464 465 /** 466 * Print report from a single command line. 467 * <p> 468 * TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both 469 * streams (might be important for some command lines) 470 * 471 * @param reportName Simple tag that will print before the report and in 472 * various annotations. 473 * @param command Command line to execute. 474 */ commandLineReport(String reportName, String command)475 private void commandLineReport(String reportName, String command) { 476 Logger.err.println(reportName + ":"); 477 Runtime rt = Runtime.getRuntime(); 478 Writer logOutput = null; 479 480 try { 481 // Process must be fully qualified here because android.os.Process 482 // is used elsewhere 483 java.lang.Process p = Runtime.getRuntime().exec(command); 484 485 if (mRequestBugreport) { 486 logOutput = 487 new BufferedWriter(new FileWriter(new File(Environment 488 .getLegacyExternalStorageDirectory(), reportName), true)); 489 } 490 // pipe everything from process stdout -> System.err 491 InputStream inStream = p.getInputStream(); 492 InputStreamReader inReader = new InputStreamReader(inStream); 493 BufferedReader inBuffer = new BufferedReader(inReader); 494 String s; 495 while ((s = inBuffer.readLine()) != null) { 496 if (mRequestBugreport) { 497 try { 498 // When no space left on the device the write will 499 // occurs an I/O exception, so we needed to catch it 500 // and continue to read the data of the sync pipe to 501 // aviod the bugreport hang forever. 502 logOutput.write(s); 503 logOutput.write("\n"); 504 } catch (IOException e) { 505 while(inBuffer.readLine() != null) {} 506 Logger.err.println(e.toString()); 507 break; 508 } 509 } else { 510 Logger.err.println(s); 511 } 512 } 513 514 int status = p.waitFor(); 515 Logger.err.println("// " + reportName + " status was " + status); 516 517 if (logOutput != null) { 518 logOutput.close(); 519 } 520 } catch (Exception e) { 521 Logger.err.println("// Exception from " + reportName + ":"); 522 Logger.err.println(e.toString()); 523 } 524 } 525 526 // Write the numbe of iteration to the log writeScriptLog(int count)527 private void writeScriptLog(int count) { 528 // TO DO: Add the script file name to the log. 529 try { 530 Writer output = new BufferedWriter(new FileWriter(new File( 531 Environment.getLegacyExternalStorageDirectory(), "scriptlog.txt"), true)); 532 output.write("iteration: " + count + " time: " 533 + MonkeyUtils.toCalendarTime(System.currentTimeMillis()) + "\n"); 534 output.close(); 535 } catch (IOException e) { 536 Logger.err.println(e.toString()); 537 } 538 } 539 540 // Write the bugreport to the sdcard. getBugreport(String reportName)541 private void getBugreport(String reportName) { 542 reportName += MonkeyUtils.toCalendarTime(System.currentTimeMillis()); 543 String bugreportName = reportName.replaceAll("[ ,:]", "_"); 544 commandLineReport(bugreportName + ".txt", "bugreport"); 545 } 546 547 /** 548 * Command-line entry point. 549 * 550 * @param args The command-line arguments 551 */ main(String[] args)552 public static void main(String[] args) { 553 // Set the process name showing in "ps" or "top" 554 Process.setArgV0("com.android.commands.monkey"); 555 556 Logger.err.println("args: " + Arrays.toString(args)); 557 int resultCode = (new Monkey()).run(args); 558 System.exit(resultCode); 559 } 560 561 /** 562 * Run the command! 563 * 564 * @param args The command-line arguments 565 * @return Returns a posix-style result code. 0 for no error. 566 */ run(String[] args)567 private int run(String[] args) { 568 // Super-early debugger wait 569 for (String s : args) { 570 if ("--wait-dbg".equals(s)) { 571 Debug.waitForDebugger(); 572 } 573 } 574 575 // Default values for some command-line options 576 mVerbose = 0; 577 mCount = 1000; 578 mSeed = 0; 579 mThrottle = 0; 580 581 // prepare for command-line processing 582 mArgs = args; 583 for (String a: args) { 584 Logger.err.println(" arg: \"" + a + "\""); 585 } 586 mNextArg = 0; 587 588 // set a positive value, indicating none of the factors is provided yet 589 for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { 590 mFactors[i] = 1.0f; 591 } 592 593 if (!processOptions()) { 594 return -1; 595 } 596 597 if (!loadPackageLists()) { 598 return -1; 599 } 600 601 // now set up additional data in preparation for launch 602 if (mMainCategories.size() == 0) { 603 mMainCategories.add(Intent.CATEGORY_LAUNCHER); 604 mMainCategories.add(Intent.CATEGORY_MONKEY); 605 } 606 607 if (mSeed == 0) { 608 mSeed = System.currentTimeMillis() + System.identityHashCode(this); 609 } 610 611 if (mVerbose > 0) { 612 Logger.out.println(":Monkey: seed=" + mSeed + " count=" + mCount); 613 MonkeyUtils.getPackageFilter().dump(); 614 if (mMainCategories.size() != 0) { 615 Iterator<String> it = mMainCategories.iterator(); 616 while (it.hasNext()) { 617 Logger.out.println(":IncludeCategory: " + it.next()); 618 } 619 } 620 } 621 622 if (!checkInternalConfiguration()) { 623 return -2; 624 } 625 626 if (!getSystemInterfaces()) { 627 return -3; 628 } 629 630 if (!getMainApps()) { 631 return -4; 632 } 633 634 mRandom = new Random(mSeed); 635 636 if (mScriptFileNames != null && mScriptFileNames.size() == 1) { 637 // script mode, ignore other options 638 mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle, 639 mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime); 640 mEventSource.setVerbose(mVerbose); 641 642 mCountEvents = false; 643 } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) { 644 if (mSetupFileName != null) { 645 mEventSource = new MonkeySourceRandomScript(mSetupFileName, 646 mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom, 647 mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); 648 mCount++; 649 } else { 650 mEventSource = new MonkeySourceRandomScript(mScriptFileNames, 651 mThrottle, mRandomizeThrottle, mRandom, 652 mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); 653 } 654 mEventSource.setVerbose(mVerbose); 655 mCountEvents = false; 656 } else if (mServerPort != -1) { 657 try { 658 mEventSource = new MonkeySourceNetwork(mServerPort); 659 } catch (IOException e) { 660 Logger.out.println("Error binding to network socket."); 661 return -5; 662 } 663 mCount = Integer.MAX_VALUE; 664 } else { 665 // random source by default 666 if (mVerbose >= 2) { // check seeding performance 667 Logger.out.println("// Seeded: " + mSeed); 668 } 669 mEventSource = new MonkeySourceRandom(mRandom, mMainApps, 670 mThrottle, mRandomizeThrottle, mPermissionTargetSystem); 671 mEventSource.setVerbose(mVerbose); 672 // set any of the factors that has been set 673 for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { 674 if (mFactors[i] <= 0.0f) { 675 ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]); 676 } 677 } 678 679 // in random mode, we start with a random activity 680 ((MonkeySourceRandom) mEventSource).generateActivity(); 681 } 682 683 // validate source generator 684 if (!mEventSource.validate()) { 685 return -5; 686 } 687 688 // If we're profiling, do it immediately before/after the main monkey 689 // loop 690 if (mGenerateHprof) { 691 signalPersistentProcesses(); 692 } 693 694 mNetworkMonitor.start(); 695 int crashedAtCycle = 0; 696 try { 697 crashedAtCycle = runMonkeyCycles(); 698 } finally { 699 // Release the rotation lock if it's still held and restore the 700 // original orientation. 701 new MonkeyRotationEvent(Surface.ROTATION_0, false).injectEvent( 702 mWm, mAm, mVerbose); 703 } 704 mNetworkMonitor.stop(); 705 706 synchronized (this) { 707 if (mRequestAnrTraces) { 708 reportAnrTraces(); 709 mRequestAnrTraces = false; 710 } 711 if (mRequestAnrBugreport){ 712 Logger.out.println("Print the anr report"); 713 getBugreport("anr_" + mReportProcessName + "_"); 714 mRequestAnrBugreport = false; 715 } 716 if (mRequestWatchdogBugreport) { 717 Logger.out.println("Print the watchdog report"); 718 getBugreport("anr_watchdog_"); 719 mRequestWatchdogBugreport = false; 720 } 721 if (mRequestAppCrashBugreport){ 722 getBugreport("app_crash" + mReportProcessName + "_"); 723 mRequestAppCrashBugreport = false; 724 } 725 if (mRequestDumpsysMemInfo) { 726 reportDumpsysMemInfo(); 727 mRequestDumpsysMemInfo = false; 728 } 729 if (mRequestPeriodicBugreport){ 730 getBugreport("Bugreport_"); 731 mRequestPeriodicBugreport = false; 732 } 733 if (mWatchdogWaiting) { 734 mWatchdogWaiting = false; 735 notifyAll(); 736 } 737 } 738 739 if (mGenerateHprof) { 740 signalPersistentProcesses(); 741 if (mVerbose > 0) { 742 Logger.out.println("// Generated profiling reports in /data/misc"); 743 } 744 } 745 746 try { 747 mAm.setActivityController(null, true); 748 mNetworkMonitor.unregister(mAm); 749 } catch (RemoteException e) { 750 // just in case this was latent (after mCount cycles), make sure 751 // we report it 752 if (crashedAtCycle >= mCount) { 753 crashedAtCycle = mCount - 1; 754 } 755 } 756 757 // report dropped event stats 758 if (mVerbose > 0) { 759 Logger.out.println(":Dropped: keys=" + mDroppedKeyEvents 760 + " pointers=" + mDroppedPointerEvents 761 + " trackballs=" + mDroppedTrackballEvents 762 + " flips=" + mDroppedFlipEvents 763 + " rotations=" + mDroppedRotationEvents); 764 } 765 766 // report network stats 767 mNetworkMonitor.dump(); 768 769 if (crashedAtCycle < mCount - 1) { 770 Logger.err.println("** System appears to have crashed at event " + crashedAtCycle 771 + " of " + mCount + " using seed " + mSeed); 772 return crashedAtCycle; 773 } else { 774 if (mVerbose > 0) { 775 Logger.out.println("// Monkey finished"); 776 } 777 return 0; 778 } 779 } 780 781 /** 782 * Process the command-line options 783 * 784 * @return Returns true if options were parsed with no apparent errors. 785 */ processOptions()786 private boolean processOptions() { 787 // quick (throwaway) check for unadorned command 788 if (mArgs.length < 1) { 789 showUsage(); 790 return false; 791 } 792 793 try { 794 String opt; 795 Set<String> validPackages = new HashSet<>(); 796 while ((opt = nextOption()) != null) { 797 if (opt.equals("-s")) { 798 mSeed = nextOptionLong("Seed"); 799 } else if (opt.equals("-p")) { 800 validPackages.add(nextOptionData()); 801 } else if (opt.equals("-c")) { 802 mMainCategories.add(nextOptionData()); 803 } else if (opt.equals("-v")) { 804 mVerbose += 1; 805 } else if (opt.equals("--ignore-crashes")) { 806 mIgnoreCrashes = true; 807 } else if (opt.equals("--ignore-timeouts")) { 808 mIgnoreTimeouts = true; 809 } else if (opt.equals("--ignore-security-exceptions")) { 810 mIgnoreSecurityExceptions = true; 811 } else if (opt.equals("--monitor-native-crashes")) { 812 mMonitorNativeCrashes = true; 813 } else if (opt.equals("--ignore-native-crashes")) { 814 mIgnoreNativeCrashes = true; 815 } else if (opt.equals("--kill-process-after-error")) { 816 mKillProcessAfterError = true; 817 } else if (opt.equals("--hprof")) { 818 mGenerateHprof = true; 819 } else if (opt.equals("--match-description")) { 820 mMatchDescription = nextOptionData(); 821 } else if (opt.equals("--pct-touch")) { 822 int i = MonkeySourceRandom.FACTOR_TOUCH; 823 mFactors[i] = -nextOptionLong("touch events percentage"); 824 } else if (opt.equals("--pct-motion")) { 825 int i = MonkeySourceRandom.FACTOR_MOTION; 826 mFactors[i] = -nextOptionLong("motion events percentage"); 827 } else if (opt.equals("--pct-trackball")) { 828 int i = MonkeySourceRandom.FACTOR_TRACKBALL; 829 mFactors[i] = -nextOptionLong("trackball events percentage"); 830 } else if (opt.equals("--pct-rotation")) { 831 int i = MonkeySourceRandom.FACTOR_ROTATION; 832 mFactors[i] = -nextOptionLong("screen rotation events percentage"); 833 } else if (opt.equals("--pct-syskeys")) { 834 int i = MonkeySourceRandom.FACTOR_SYSOPS; 835 mFactors[i] = -nextOptionLong("system (key) operations percentage"); 836 } else if (opt.equals("--pct-nav")) { 837 int i = MonkeySourceRandom.FACTOR_NAV; 838 mFactors[i] = -nextOptionLong("nav events percentage"); 839 } else if (opt.equals("--pct-majornav")) { 840 int i = MonkeySourceRandom.FACTOR_MAJORNAV; 841 mFactors[i] = -nextOptionLong("major nav events percentage"); 842 } else if (opt.equals("--pct-appswitch")) { 843 int i = MonkeySourceRandom.FACTOR_APPSWITCH; 844 mFactors[i] = -nextOptionLong("app switch events percentage"); 845 } else if (opt.equals("--pct-flip")) { 846 int i = MonkeySourceRandom.FACTOR_FLIP; 847 mFactors[i] = -nextOptionLong("keyboard flip percentage"); 848 } else if (opt.equals("--pct-anyevent")) { 849 int i = MonkeySourceRandom.FACTOR_ANYTHING; 850 mFactors[i] = -nextOptionLong("any events percentage"); 851 } else if (opt.equals("--pct-pinchzoom")) { 852 int i = MonkeySourceRandom.FACTOR_PINCHZOOM; 853 mFactors[i] = -nextOptionLong("pinch zoom events percentage"); 854 } else if (opt.equals("--pct-permission")) { 855 int i = MonkeySourceRandom.FACTOR_PERMISSION; 856 mFactors[i] = -nextOptionLong("runtime permission toggle events percentage"); 857 } else if (opt.equals("--pkg-blacklist-file")) { 858 mPkgBlacklistFile = nextOptionData(); 859 } else if (opt.equals("--pkg-whitelist-file")) { 860 mPkgWhitelistFile = nextOptionData(); 861 } else if (opt.equals("--throttle")) { 862 mThrottle = nextOptionLong("delay (in milliseconds) to wait between events"); 863 } else if (opt.equals("--randomize-throttle")) { 864 mRandomizeThrottle = true; 865 } else if (opt.equals("--wait-dbg")) { 866 // do nothing - it's caught at the very start of run() 867 } else if (opt.equals("--dbg-no-events")) { 868 mSendNoEvents = true; 869 } else if (opt.equals("--port")) { 870 mServerPort = (int) nextOptionLong("Server port to listen on for commands"); 871 } else if (opt.equals("--setup")) { 872 mSetupFileName = nextOptionData(); 873 } else if (opt.equals("-f")) { 874 mScriptFileNames.add(nextOptionData()); 875 } else if (opt.equals("--profile-wait")) { 876 mProfileWaitTime = nextOptionLong("Profile delay" + 877 " (in milliseconds) to wait between user action"); 878 } else if (opt.equals("--device-sleep-time")) { 879 mDeviceSleepTime = nextOptionLong("Device sleep time" + 880 "(in milliseconds)"); 881 } else if (opt.equals("--randomize-script")) { 882 mRandomizeScript = true; 883 } else if (opt.equals("--script-log")) { 884 mScriptLog = true; 885 } else if (opt.equals("--bugreport")) { 886 mRequestBugreport = true; 887 } else if (opt.equals("--periodic-bugreport")){ 888 mGetPeriodicBugreport = true; 889 mBugreportFrequency = nextOptionLong("Number of iterations"); 890 } else if (opt.equals("--permission-target-system")){ 891 mPermissionTargetSystem = true; 892 } else if (opt.equals("-h")) { 893 showUsage(); 894 return false; 895 } else { 896 Logger.err.println("** Error: Unknown option: " + opt); 897 showUsage(); 898 return false; 899 } 900 } 901 MonkeyUtils.getPackageFilter().addValidPackages(validPackages); 902 } catch (RuntimeException ex) { 903 Logger.err.println("** Error: " + ex.toString()); 904 showUsage(); 905 return false; 906 } 907 908 // If a server port hasn't been specified, we need to specify 909 // a count 910 if (mServerPort == -1) { 911 String countStr = nextArg(); 912 if (countStr == null) { 913 Logger.err.println("** Error: Count not specified"); 914 showUsage(); 915 return false; 916 } 917 918 try { 919 mCount = Integer.parseInt(countStr); 920 } catch (NumberFormatException e) { 921 Logger.err.println("** Error: Count is not a number: \"" + countStr + "\""); 922 showUsage(); 923 return false; 924 } 925 } 926 927 return true; 928 } 929 930 /** 931 * Load a list of package names from a file. 932 * 933 * @param fileName The file name, with package names separated by new line. 934 * @param list The destination list. 935 * @return Returns false if any error occurs. 936 */ loadPackageListFromFile(String fileName, Set<String> list)937 private static boolean loadPackageListFromFile(String fileName, Set<String> list) { 938 BufferedReader reader = null; 939 try { 940 reader = new BufferedReader(new FileReader(fileName)); 941 String s; 942 while ((s = reader.readLine()) != null) { 943 s = s.trim(); 944 if ((s.length() > 0) && (!s.startsWith("#"))) { 945 list.add(s); 946 } 947 } 948 } catch (IOException ioe) { 949 Logger.err.println("" + ioe); 950 return false; 951 } finally { 952 if (reader != null) { 953 try { 954 reader.close(); 955 } catch (IOException ioe) { 956 Logger.err.println("" + ioe); 957 } 958 } 959 } 960 return true; 961 } 962 963 /** 964 * Load package blacklist or whitelist (if specified). 965 * 966 * @return Returns false if any error occurs. 967 */ loadPackageLists()968 private boolean loadPackageLists() { 969 if (((mPkgWhitelistFile != null) || (MonkeyUtils.getPackageFilter().hasValidPackages())) 970 && (mPkgBlacklistFile != null)) { 971 Logger.err.println("** Error: you can not specify a package blacklist " 972 + "together with a whitelist or individual packages (via -p)."); 973 return false; 974 } 975 Set<String> validPackages = new HashSet<>(); 976 if ((mPkgWhitelistFile != null) 977 && (!loadPackageListFromFile(mPkgWhitelistFile, validPackages))) { 978 return false; 979 } 980 MonkeyUtils.getPackageFilter().addValidPackages(validPackages); 981 Set<String> invalidPackages = new HashSet<>(); 982 if ((mPkgBlacklistFile != null) 983 && (!loadPackageListFromFile(mPkgBlacklistFile, invalidPackages))) { 984 return false; 985 } 986 MonkeyUtils.getPackageFilter().addInvalidPackages(invalidPackages); 987 return true; 988 } 989 990 /** 991 * Check for any internal configuration (primarily build-time) errors. 992 * 993 * @return Returns true if ready to rock. 994 */ checkInternalConfiguration()995 private boolean checkInternalConfiguration() { 996 return true; 997 } 998 999 /** 1000 * Attach to the required system interfaces. 1001 * 1002 * @return Returns true if all system interfaces were available. 1003 */ getSystemInterfaces()1004 private boolean getSystemInterfaces() { 1005 mAm = ActivityManager.getService(); 1006 if (mAm == null) { 1007 Logger.err.println("** Error: Unable to connect to activity manager; is the system " 1008 + "running?"); 1009 return false; 1010 } 1011 1012 mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 1013 if (mWm == null) { 1014 Logger.err.println("** Error: Unable to connect to window manager; is the system " 1015 + "running?"); 1016 return false; 1017 } 1018 1019 mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 1020 if (mPm == null) { 1021 Logger.err.println("** Error: Unable to connect to package manager; is the system " 1022 + "running?"); 1023 return false; 1024 } 1025 1026 try { 1027 mAm.setActivityController(new ActivityController(), true); 1028 mNetworkMonitor.register(mAm); 1029 } catch (RemoteException e) { 1030 Logger.err.println("** Failed talking with activity manager!"); 1031 return false; 1032 } 1033 1034 return true; 1035 } 1036 1037 /** 1038 * Using the restrictions provided (categories & packages), generate a list 1039 * of activities that we can actually switch to. 1040 * 1041 * @return Returns true if it could successfully build a list of target 1042 * activities 1043 */ getMainApps()1044 private boolean getMainApps() { 1045 try { 1046 final int N = mMainCategories.size(); 1047 for (int i = 0; i < N; i++) { 1048 Intent intent = new Intent(Intent.ACTION_MAIN); 1049 String category = mMainCategories.get(i); 1050 if (category.length() > 0) { 1051 intent.addCategory(category); 1052 } 1053 List<ResolveInfo> mainApps = mPm.queryIntentActivities(intent, null, 0, 1054 UserHandle.myUserId()).getList(); 1055 if (mainApps == null || mainApps.size() == 0) { 1056 Logger.err.println("// Warning: no activities found for category " + category); 1057 continue; 1058 } 1059 if (mVerbose >= 2) { // very verbose 1060 Logger.out.println("// Selecting main activities from category " + category); 1061 } 1062 final int NA = mainApps.size(); 1063 for (int a = 0; a < NA; a++) { 1064 ResolveInfo r = mainApps.get(a); 1065 String packageName = r.activityInfo.applicationInfo.packageName; 1066 if (MonkeyUtils.getPackageFilter().checkEnteringPackage(packageName)) { 1067 if (mVerbose >= 2) { // very verbose 1068 Logger.out.println("// + Using main activity " + r.activityInfo.name 1069 + " (from package " + packageName + ")"); 1070 } 1071 mMainApps.add(new ComponentName(packageName, r.activityInfo.name)); 1072 } else { 1073 if (mVerbose >= 3) { // very very verbose 1074 Logger.out.println("// - NOT USING main activity " 1075 + r.activityInfo.name + " (from package " + packageName + ")"); 1076 } 1077 } 1078 } 1079 } 1080 } catch (RemoteException e) { 1081 Logger.err.println("** Failed talking with package manager!"); 1082 return false; 1083 } 1084 1085 if (mMainApps.size() == 0) { 1086 Logger.out.println("** No activities found to run, monkey aborted."); 1087 return false; 1088 } 1089 1090 return true; 1091 } 1092 1093 /** 1094 * Run mCount cycles and see if we hit any crashers. 1095 * <p> 1096 * TODO: Meta state on keys 1097 * 1098 * @return Returns the last cycle which executed. If the value == mCount, no 1099 * errors detected. 1100 */ runMonkeyCycles()1101 private int runMonkeyCycles() { 1102 int eventCounter = 0; 1103 int cycleCounter = 0; 1104 1105 boolean shouldReportAnrTraces = false; 1106 boolean shouldReportDumpsysMemInfo = false; 1107 boolean shouldAbort = false; 1108 boolean systemCrashed = false; 1109 1110 try { 1111 // TO DO : The count should apply to each of the script file. 1112 while (!systemCrashed && cycleCounter < mCount) { 1113 synchronized (this) { 1114 if (mRequestProcRank) { 1115 reportProcRank(); 1116 mRequestProcRank = false; 1117 } 1118 if (mRequestAnrTraces) { 1119 mRequestAnrTraces = false; 1120 shouldReportAnrTraces = true; 1121 } 1122 if (mRequestAnrBugreport){ 1123 getBugreport("anr_" + mReportProcessName + "_"); 1124 mRequestAnrBugreport = false; 1125 } 1126 if (mRequestWatchdogBugreport) { 1127 Logger.out.println("Print the watchdog report"); 1128 getBugreport("anr_watchdog_"); 1129 mRequestWatchdogBugreport = false; 1130 } 1131 if (mRequestAppCrashBugreport){ 1132 getBugreport("app_crash" + mReportProcessName + "_"); 1133 mRequestAppCrashBugreport = false; 1134 } 1135 if (mRequestPeriodicBugreport){ 1136 getBugreport("Bugreport_"); 1137 mRequestPeriodicBugreport = false; 1138 } 1139 if (mRequestDumpsysMemInfo) { 1140 mRequestDumpsysMemInfo = false; 1141 shouldReportDumpsysMemInfo = true; 1142 } 1143 if (mMonitorNativeCrashes) { 1144 // first time through, when eventCounter == 0, just set up 1145 // the watcher (ignore the error) 1146 if (checkNativeCrashes() && (eventCounter > 0)) { 1147 Logger.out.println("** New native crash detected."); 1148 if (mRequestBugreport) { 1149 getBugreport("native_crash_"); 1150 } 1151 mAbort = mAbort || !mIgnoreNativeCrashes || mKillProcessAfterError; 1152 } 1153 } 1154 if (mAbort) { 1155 shouldAbort = true; 1156 } 1157 if (mWatchdogWaiting) { 1158 mWatchdogWaiting = false; 1159 notifyAll(); 1160 } 1161 } 1162 1163 // Report ANR, dumpsys after releasing lock on this. 1164 // This ensures the availability of the lock to Activity controller's appNotResponding 1165 if (shouldReportAnrTraces) { 1166 shouldReportAnrTraces = false; 1167 reportAnrTraces(); 1168 } 1169 1170 if (shouldReportDumpsysMemInfo) { 1171 shouldReportDumpsysMemInfo = false; 1172 reportDumpsysMemInfo(); 1173 } 1174 1175 if (shouldAbort) { 1176 shouldAbort = false; 1177 Logger.out.println("** Monkey aborted due to error."); 1178 Logger.out.println("Events injected: " + eventCounter); 1179 return eventCounter; 1180 } 1181 1182 // In this debugging mode, we never send any events. This is 1183 // primarily here so you can manually test the package or category 1184 // limits, while manually exercising the system. 1185 if (mSendNoEvents) { 1186 eventCounter++; 1187 cycleCounter++; 1188 continue; 1189 } 1190 1191 if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) { 1192 String calendarTime = MonkeyUtils.toCalendarTime(System.currentTimeMillis()); 1193 long systemUpTime = SystemClock.elapsedRealtime(); 1194 Logger.out.println(" //[calendar_time:" + calendarTime + " system_uptime:" 1195 + systemUpTime + "]"); 1196 Logger.out.println(" // Sending event #" + eventCounter); 1197 } 1198 1199 MonkeyEvent ev = mEventSource.getNextEvent(); 1200 if (ev != null) { 1201 int injectCode = ev.injectEvent(mWm, mAm, mVerbose); 1202 if (injectCode == MonkeyEvent.INJECT_FAIL) { 1203 Logger.out.println(" // Injection Failed"); 1204 if (ev instanceof MonkeyKeyEvent) { 1205 mDroppedKeyEvents++; 1206 } else if (ev instanceof MonkeyMotionEvent) { 1207 mDroppedPointerEvents++; 1208 } else if (ev instanceof MonkeyFlipEvent) { 1209 mDroppedFlipEvents++; 1210 } else if (ev instanceof MonkeyRotationEvent) { 1211 mDroppedRotationEvents++; 1212 } 1213 } else if (injectCode == MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION) { 1214 systemCrashed = true; 1215 Logger.err.println("** Error: RemoteException while injecting event."); 1216 } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) { 1217 systemCrashed = !mIgnoreSecurityExceptions; 1218 if (systemCrashed) { 1219 Logger.err.println("** Error: SecurityException while injecting event."); 1220 } 1221 } 1222 1223 // Don't count throttling as an event. 1224 if (!(ev instanceof MonkeyThrottleEvent)) { 1225 eventCounter++; 1226 if (mCountEvents) { 1227 cycleCounter++; 1228 } 1229 } 1230 } else { 1231 if (!mCountEvents) { 1232 cycleCounter++; 1233 writeScriptLog(cycleCounter); 1234 //Capture the bugreport after n iteration 1235 if (mGetPeriodicBugreport) { 1236 if ((cycleCounter % mBugreportFrequency) == 0) { 1237 mRequestPeriodicBugreport = true; 1238 } 1239 } 1240 } else { 1241 // Event Source has signaled that we have no more events to process 1242 break; 1243 } 1244 } 1245 } 1246 } catch (RuntimeException e) { 1247 Logger.error("** Error: A RuntimeException occurred:", e); 1248 } 1249 Logger.out.println("Events injected: " + eventCounter); 1250 return eventCounter; 1251 } 1252 1253 /** 1254 * Send SIGNAL_USR1 to all processes. This will generate large (5mb) 1255 * profiling reports in data/misc, so use with care. 1256 */ signalPersistentProcesses()1257 private void signalPersistentProcesses() { 1258 try { 1259 mAm.signalPersistentProcesses(Process.SIGNAL_USR1); 1260 1261 synchronized (this) { 1262 wait(2000); 1263 } 1264 } catch (RemoteException e) { 1265 Logger.err.println("** Failed talking with activity manager!"); 1266 } catch (InterruptedException e) { 1267 } 1268 } 1269 1270 /** 1271 * Watch for appearance of new tombstone files, which indicate native 1272 * crashes. 1273 * 1274 * @return Returns true if new files have appeared in the list 1275 */ checkNativeCrashes()1276 private boolean checkNativeCrashes() { 1277 String[] tombstones = TOMBSTONES_PATH.list(); 1278 1279 // shortcut path for usually empty directory, so we don't waste even 1280 // more objects 1281 if (tombstones == null || tombstones.length == 0) { 1282 mTombstones = null; 1283 return false; 1284 } 1285 1286 boolean result = false; 1287 1288 // use set logic to look for new files 1289 HashSet<Long> newStones = new HashSet<Long>(); 1290 for (String t : tombstones) { 1291 if (t.startsWith(TOMBSTONE_PREFIX)) { 1292 File f = new File(TOMBSTONES_PATH, t); 1293 newStones.add(f.lastModified()); 1294 if (mTombstones == null || !mTombstones.contains(f.lastModified())) { 1295 result = true; 1296 Logger.out.println("** New tombstone found: " + f.getAbsolutePath() 1297 + ", size: " + f.length()); 1298 } 1299 } 1300 } 1301 1302 // keep the new list for the next time 1303 mTombstones = newStones; 1304 1305 return result; 1306 } 1307 1308 /** 1309 * Return the next command line option. This has a number of special cases 1310 * which closely, but not exactly, follow the POSIX command line options 1311 * patterns: 1312 * 1313 * <pre> 1314 * -- means to stop processing additional options 1315 * -z means option z 1316 * -z ARGS means option z with (non-optional) arguments ARGS 1317 * -zARGS means option z with (optional) arguments ARGS 1318 * --zz means option zz 1319 * --zz ARGS means option zz with (non-optional) arguments ARGS 1320 * </pre> 1321 * 1322 * Note that you cannot combine single letter options; -abc != -a -b -c 1323 * 1324 * @return Returns the option string, or null if there are no more options. 1325 */ nextOption()1326 private String nextOption() { 1327 if (mNextArg >= mArgs.length) { 1328 return null; 1329 } 1330 String arg = mArgs[mNextArg]; 1331 if (!arg.startsWith("-")) { 1332 return null; 1333 } 1334 mNextArg++; 1335 if (arg.equals("--")) { 1336 return null; 1337 } 1338 if (arg.length() > 1 && arg.charAt(1) != '-') { 1339 if (arg.length() > 2) { 1340 mCurArgData = arg.substring(2); 1341 return arg.substring(0, 2); 1342 } else { 1343 mCurArgData = null; 1344 return arg; 1345 } 1346 } 1347 mCurArgData = null; 1348 Logger.err.println("arg=\"" + arg + "\" mCurArgData=\"" + mCurArgData + "\" mNextArg=" 1349 + mNextArg + " argwas=\"" + mArgs[mNextArg-1] + "\"" + " nextarg=\"" + 1350 mArgs[mNextArg] + "\""); 1351 return arg; 1352 } 1353 1354 /** 1355 * Return the next data associated with the current option. 1356 * 1357 * @return Returns the data string, or null of there are no more arguments. 1358 */ nextOptionData()1359 private String nextOptionData() { 1360 if (mCurArgData != null) { 1361 return mCurArgData; 1362 } 1363 if (mNextArg >= mArgs.length) { 1364 return null; 1365 } 1366 String data = mArgs[mNextArg]; 1367 Logger.err.println("data=\"" + data + "\""); 1368 mNextArg++; 1369 return data; 1370 } 1371 1372 /** 1373 * Returns a long converted from the next data argument, with error handling 1374 * if not available. 1375 * 1376 * @param opt The name of the option. 1377 * @return Returns a long converted from the argument. 1378 */ nextOptionLong(final String opt)1379 private long nextOptionLong(final String opt) { 1380 long result; 1381 try { 1382 result = Long.parseLong(nextOptionData()); 1383 } catch (NumberFormatException e) { 1384 Logger.err.println("** Error: " + opt + " is not a number"); 1385 throw e; 1386 } 1387 return result; 1388 } 1389 1390 /** 1391 * Return the next argument on the command line. 1392 * 1393 * @return Returns the argument string, or null if we have reached the end. 1394 */ nextArg()1395 private String nextArg() { 1396 if (mNextArg >= mArgs.length) { 1397 return null; 1398 } 1399 String arg = mArgs[mNextArg]; 1400 mNextArg++; 1401 return arg; 1402 } 1403 1404 /** 1405 * Print how to use this command. 1406 */ showUsage()1407 private void showUsage() { 1408 StringBuffer usage = new StringBuffer(); 1409 usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n"); 1410 usage.append(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n"); 1411 usage.append(" [--ignore-crashes] [--ignore-timeouts]\n"); 1412 usage.append(" [--ignore-security-exceptions]\n"); 1413 usage.append(" [--monitor-native-crashes] [--ignore-native-crashes]\n"); 1414 usage.append(" [--kill-process-after-error] [--hprof]\n"); 1415 usage.append(" [--match-description TEXT]\n"); 1416 usage.append(" [--pct-touch PERCENT] [--pct-motion PERCENT]\n"); 1417 usage.append(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n"); 1418 usage.append(" [--pct-nav PERCENT] [--pct-majornav PERCENT]\n"); 1419 usage.append(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n"); 1420 usage.append(" [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]\n"); 1421 usage.append(" [--pct-permission PERCENT]\n"); 1422 usage.append(" [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n"); 1423 usage.append(" [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n"); 1424 usage.append(" [--wait-dbg] [--dbg-no-events]\n"); 1425 usage.append(" [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n"); 1426 usage.append(" [--port port]\n"); 1427 usage.append(" [-s SEED] [-v [-v] ...]\n"); 1428 usage.append(" [--throttle MILLISEC] [--randomize-throttle]\n"); 1429 usage.append(" [--profile-wait MILLISEC]\n"); 1430 usage.append(" [--device-sleep-time MILLISEC]\n"); 1431 usage.append(" [--randomize-script]\n"); 1432 usage.append(" [--script-log]\n"); 1433 usage.append(" [--bugreport]\n"); 1434 usage.append(" [--periodic-bugreport]\n"); 1435 usage.append(" [--permission-target-system]\n"); 1436 usage.append(" COUNT\n"); 1437 Logger.err.println(usage.toString()); 1438 } 1439 } 1440