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