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