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