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