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