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