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 17package com.android.commands.monkey; 18 19import android.app.ActivityManagerNative; 20import android.app.IActivityController; 21import android.app.IActivityManager; 22import android.content.ComponentName; 23import android.content.Intent; 24import android.content.pm.IPackageManager; 25import android.content.pm.ResolveInfo; 26import android.os.Build; 27import android.os.Debug; 28import android.os.Environment; 29import android.os.Process; 30import android.os.RemoteException; 31import android.os.ServiceManager; 32import android.os.StrictMode; 33import android.os.SystemClock; 34import android.os.SystemProperties; 35import android.os.UserHandle; 36import android.view.IWindowManager; 37import android.view.Surface; 38 39import java.io.BufferedReader; 40import java.io.BufferedWriter; 41import java.io.ByteArrayInputStream; 42import java.io.DataInputStream; 43import java.io.File; 44import java.io.FileReader; 45import java.io.FileWriter; 46import java.io.IOException; 47import java.io.InputStream; 48import java.io.InputStreamReader; 49import java.io.Writer; 50import java.security.SecureRandom; 51import java.util.ArrayList; 52import java.util.HashSet; 53import java.util.Iterator; 54import java.util.List; 55import java.util.Random; 56 57/** 58 * Application that injects random key events and other actions into the system. 59 */ 60public 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 */ 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 { 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 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 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 332 public int appEarlyNotResponding(String processName, int pid, String annotation) { 333 return 0; 334 } 335 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 */ 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 */ 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 */ 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 */ 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 .getLegacyExternalStorageDirectory(), 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 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.getLegacyExternalStorageDirectory(), "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. 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 */ 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 */ 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 (mSeed == 0) { 520 mSeed = System.currentTimeMillis() + System.identityHashCode(this); 521 } 522 523 if (mVerbose > 0) { 524 System.out.println(":Monkey: seed=" + mSeed + " count=" + mCount); 525 if (mValidPackages.size() > 0) { 526 Iterator<String> it = mValidPackages.iterator(); 527 while (it.hasNext()) { 528 System.out.println(":AllowPackage: " + it.next()); 529 } 530 } 531 if (mInvalidPackages.size() > 0) { 532 Iterator<String> it = mInvalidPackages.iterator(); 533 while (it.hasNext()) { 534 System.out.println(":DisallowPackage: " + it.next()); 535 } 536 } 537 if (mMainCategories.size() != 0) { 538 Iterator<String> it = mMainCategories.iterator(); 539 while (it.hasNext()) { 540 System.out.println(":IncludeCategory: " + it.next()); 541 } 542 } 543 } 544 545 if (!checkInternalConfiguration()) { 546 return -2; 547 } 548 549 if (!getSystemInterfaces()) { 550 return -3; 551 } 552 553 if (!getMainApps()) { 554 return -4; 555 } 556 557 mRandom = new Random(mSeed); 558 559 if (mScriptFileNames != null && mScriptFileNames.size() == 1) { 560 // script mode, ignore other options 561 mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle, 562 mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime); 563 mEventSource.setVerbose(mVerbose); 564 565 mCountEvents = false; 566 } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) { 567 if (mSetupFileName != null) { 568 mEventSource = new MonkeySourceRandomScript(mSetupFileName, 569 mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom, 570 mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); 571 mCount++; 572 } else { 573 mEventSource = new MonkeySourceRandomScript(mScriptFileNames, 574 mThrottle, mRandomizeThrottle, mRandom, 575 mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); 576 } 577 mEventSource.setVerbose(mVerbose); 578 mCountEvents = false; 579 } else if (mServerPort != -1) { 580 try { 581 mEventSource = new MonkeySourceNetwork(mServerPort); 582 } catch (IOException e) { 583 System.out.println("Error binding to network socket."); 584 return -5; 585 } 586 mCount = Integer.MAX_VALUE; 587 } else { 588 // random source by default 589 if (mVerbose >= 2) { // check seeding performance 590 System.out.println("// Seeded: " + mSeed); 591 } 592 mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle); 593 mEventSource.setVerbose(mVerbose); 594 // set any of the factors that has been set 595 for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { 596 if (mFactors[i] <= 0.0f) { 597 ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]); 598 } 599 } 600 601 // in random mode, we start with a random activity 602 ((MonkeySourceRandom) mEventSource).generateActivity(); 603 } 604 605 // validate source generator 606 if (!mEventSource.validate()) { 607 return -5; 608 } 609 610 // If we're profiling, do it immediately before/after the main monkey 611 // loop 612 if (mGenerateHprof) { 613 signalPersistentProcesses(); 614 } 615 616 mNetworkMonitor.start(); 617 int crashedAtCycle = 0; 618 try { 619 crashedAtCycle = runMonkeyCycles(); 620 } finally { 621 // Release the rotation lock if it's still held and restore the 622 // original orientation. 623 new MonkeyRotationEvent(Surface.ROTATION_0, false).injectEvent( 624 mWm, mAm, mVerbose); 625 } 626 mNetworkMonitor.stop(); 627 628 synchronized (this) { 629 if (mRequestAnrTraces) { 630 reportAnrTraces(); 631 mRequestAnrTraces = false; 632 } 633 if (mRequestAnrBugreport){ 634 System.out.println("Print the anr report"); 635 getBugreport("anr_" + mReportProcessName + "_"); 636 mRequestAnrBugreport = false; 637 } 638 if (mRequestAppCrashBugreport){ 639 getBugreport("app_crash" + mReportProcessName + "_"); 640 mRequestAppCrashBugreport = false; 641 } 642 if (mRequestDumpsysMemInfo) { 643 reportDumpsysMemInfo(); 644 mRequestDumpsysMemInfo = false; 645 } 646 if (mRequestPeriodicBugreport){ 647 getBugreport("Bugreport_"); 648 mRequestPeriodicBugreport = false; 649 } 650 } 651 652 if (mGenerateHprof) { 653 signalPersistentProcesses(); 654 if (mVerbose > 0) { 655 System.out.println("// Generated profiling reports in /data/misc"); 656 } 657 } 658 659 try { 660 mAm.setActivityController(null); 661 mNetworkMonitor.unregister(mAm); 662 } catch (RemoteException e) { 663 // just in case this was latent (after mCount cycles), make sure 664 // we report it 665 if (crashedAtCycle >= mCount) { 666 crashedAtCycle = mCount - 1; 667 } 668 } 669 670 // report dropped event stats 671 if (mVerbose > 0) { 672 System.out.print(":Dropped: keys="); 673 System.out.print(mDroppedKeyEvents); 674 System.out.print(" pointers="); 675 System.out.print(mDroppedPointerEvents); 676 System.out.print(" trackballs="); 677 System.out.print(mDroppedTrackballEvents); 678 System.out.print(" flips="); 679 System.out.print(mDroppedFlipEvents); 680 System.out.print(" rotations="); 681 System.out.println(mDroppedRotationEvents); 682 } 683 684 // report network stats 685 mNetworkMonitor.dump(); 686 687 if (crashedAtCycle < mCount - 1) { 688 System.err.println("** System appears to have crashed at event " + crashedAtCycle 689 + " of " + mCount + " using seed " + mSeed); 690 return crashedAtCycle; 691 } else { 692 if (mVerbose > 0) { 693 System.out.println("// Monkey finished"); 694 } 695 return 0; 696 } 697 } 698 699 /** 700 * Process the command-line options 701 * 702 * @return Returns true if options were parsed with no apparent errors. 703 */ 704 private boolean processOptions() { 705 // quick (throwaway) check for unadorned command 706 if (mArgs.length < 1) { 707 showUsage(); 708 return false; 709 } 710 711 try { 712 String opt; 713 while ((opt = nextOption()) != null) { 714 if (opt.equals("-s")) { 715 mSeed = nextOptionLong("Seed"); 716 } else if (opt.equals("-p")) { 717 mValidPackages.add(nextOptionData()); 718 } else if (opt.equals("-c")) { 719 mMainCategories.add(nextOptionData()); 720 } else if (opt.equals("-v")) { 721 mVerbose += 1; 722 } else if (opt.equals("--ignore-crashes")) { 723 mIgnoreCrashes = true; 724 } else if (opt.equals("--ignore-timeouts")) { 725 mIgnoreTimeouts = true; 726 } else if (opt.equals("--ignore-security-exceptions")) { 727 mIgnoreSecurityExceptions = true; 728 } else if (opt.equals("--monitor-native-crashes")) { 729 mMonitorNativeCrashes = true; 730 } else if (opt.equals("--ignore-native-crashes")) { 731 mIgnoreNativeCrashes = true; 732 } else if (opt.equals("--kill-process-after-error")) { 733 mKillProcessAfterError = true; 734 } else if (opt.equals("--hprof")) { 735 mGenerateHprof = true; 736 } else if (opt.equals("--pct-touch")) { 737 int i = MonkeySourceRandom.FACTOR_TOUCH; 738 mFactors[i] = -nextOptionLong("touch events percentage"); 739 } else if (opt.equals("--pct-motion")) { 740 int i = MonkeySourceRandom.FACTOR_MOTION; 741 mFactors[i] = -nextOptionLong("motion events percentage"); 742 } else if (opt.equals("--pct-trackball")) { 743 int i = MonkeySourceRandom.FACTOR_TRACKBALL; 744 mFactors[i] = -nextOptionLong("trackball events percentage"); 745 } else if (opt.equals("--pct-rotation")) { 746 int i = MonkeySourceRandom.FACTOR_ROTATION; 747 mFactors[i] = -nextOptionLong("screen rotation events percentage"); 748 } else if (opt.equals("--pct-syskeys")) { 749 int i = MonkeySourceRandom.FACTOR_SYSOPS; 750 mFactors[i] = -nextOptionLong("system (key) operations percentage"); 751 } else if (opt.equals("--pct-nav")) { 752 int i = MonkeySourceRandom.FACTOR_NAV; 753 mFactors[i] = -nextOptionLong("nav events percentage"); 754 } else if (opt.equals("--pct-majornav")) { 755 int i = MonkeySourceRandom.FACTOR_MAJORNAV; 756 mFactors[i] = -nextOptionLong("major nav events percentage"); 757 } else if (opt.equals("--pct-appswitch")) { 758 int i = MonkeySourceRandom.FACTOR_APPSWITCH; 759 mFactors[i] = -nextOptionLong("app switch events percentage"); 760 } else if (opt.equals("--pct-flip")) { 761 int i = MonkeySourceRandom.FACTOR_FLIP; 762 mFactors[i] = -nextOptionLong("keyboard flip percentage"); 763 } else if (opt.equals("--pct-anyevent")) { 764 int i = MonkeySourceRandom.FACTOR_ANYTHING; 765 mFactors[i] = -nextOptionLong("any events percentage"); 766 } else if (opt.equals("--pct-pinchzoom")) { 767 int i = MonkeySourceRandom.FACTOR_PINCHZOOM; 768 mFactors[i] = -nextOptionLong("pinch zoom events percentage"); 769 } else if (opt.equals("--pkg-blacklist-file")) { 770 mPkgBlacklistFile = nextOptionData(); 771 } else if (opt.equals("--pkg-whitelist-file")) { 772 mPkgWhitelistFile = nextOptionData(); 773 } else if (opt.equals("--throttle")) { 774 mThrottle = nextOptionLong("delay (in milliseconds) to wait between events"); 775 } else if (opt.equals("--randomize-throttle")) { 776 mRandomizeThrottle = true; 777 } else if (opt.equals("--wait-dbg")) { 778 // do nothing - it's caught at the very start of run() 779 } else if (opt.equals("--dbg-no-events")) { 780 mSendNoEvents = true; 781 } else if (opt.equals("--port")) { 782 mServerPort = (int) nextOptionLong("Server port to listen on for commands"); 783 } else if (opt.equals("--setup")) { 784 mSetupFileName = nextOptionData(); 785 } else if (opt.equals("-f")) { 786 mScriptFileNames.add(nextOptionData()); 787 } else if (opt.equals("--profile-wait")) { 788 mProfileWaitTime = nextOptionLong("Profile delay" + 789 " (in milliseconds) to wait between user action"); 790 } else if (opt.equals("--device-sleep-time")) { 791 mDeviceSleepTime = nextOptionLong("Device sleep time" + 792 "(in milliseconds)"); 793 } else if (opt.equals("--randomize-script")) { 794 mRandomizeScript = true; 795 } else if (opt.equals("--script-log")) { 796 mScriptLog = true; 797 } else if (opt.equals("--bugreport")) { 798 mRequestBugreport = true; 799 } else if (opt.equals("--periodic-bugreport")){ 800 mGetPeriodicBugreport = true; 801 mBugreportFrequency = nextOptionLong("Number of iterations"); 802 } else if (opt.equals("-h")) { 803 showUsage(); 804 return false; 805 } else { 806 System.err.println("** Error: Unknown option: " + opt); 807 showUsage(); 808 return false; 809 } 810 } 811 } catch (RuntimeException ex) { 812 System.err.println("** Error: " + ex.toString()); 813 showUsage(); 814 return false; 815 } 816 817 // If a server port hasn't been specified, we need to specify 818 // a count 819 if (mServerPort == -1) { 820 String countStr = nextArg(); 821 if (countStr == null) { 822 System.err.println("** Error: Count not specified"); 823 showUsage(); 824 return false; 825 } 826 827 try { 828 mCount = Integer.parseInt(countStr); 829 } catch (NumberFormatException e) { 830 System.err.println("** Error: Count is not a number"); 831 showUsage(); 832 return false; 833 } 834 } 835 836 return true; 837 } 838 839 /** 840 * Load a list of package names from a file. 841 * 842 * @param fileName The file name, with package names separated by new line. 843 * @param list The destination list. 844 * @return Returns false if any error occurs. 845 */ 846 private static boolean loadPackageListFromFile(String fileName, HashSet<String> list) { 847 BufferedReader reader = null; 848 try { 849 reader = new BufferedReader(new FileReader(fileName)); 850 String s; 851 while ((s = reader.readLine()) != null) { 852 s = s.trim(); 853 if ((s.length() > 0) && (!s.startsWith("#"))) { 854 list.add(s); 855 } 856 } 857 } catch (IOException ioe) { 858 System.err.println(ioe); 859 return false; 860 } finally { 861 if (reader != null) { 862 try { 863 reader.close(); 864 } catch (IOException ioe) { 865 System.err.println(ioe); 866 } 867 } 868 } 869 return true; 870 } 871 872 /** 873 * Load package blacklist or whitelist (if specified). 874 * 875 * @return Returns false if any error occurs. 876 */ 877 private boolean loadPackageLists() { 878 if (((mPkgWhitelistFile != null) || (mValidPackages.size() > 0)) 879 && (mPkgBlacklistFile != null)) { 880 System.err.println("** Error: you can not specify a package blacklist " 881 + "together with a whitelist or individual packages (via -p)."); 882 return false; 883 } 884 if ((mPkgWhitelistFile != null) 885 && (!loadPackageListFromFile(mPkgWhitelistFile, mValidPackages))) { 886 return false; 887 } 888 if ((mPkgBlacklistFile != null) 889 && (!loadPackageListFromFile(mPkgBlacklistFile, mInvalidPackages))) { 890 return false; 891 } 892 return true; 893 } 894 895 /** 896 * Check for any internal configuration (primarily build-time) errors. 897 * 898 * @return Returns true if ready to rock. 899 */ 900 private boolean checkInternalConfiguration() { 901 return true; 902 } 903 904 /** 905 * Attach to the required system interfaces. 906 * 907 * @return Returns true if all system interfaces were available. 908 */ 909 private boolean getSystemInterfaces() { 910 mAm = ActivityManagerNative.getDefault(); 911 if (mAm == null) { 912 System.err.println("** Error: Unable to connect to activity manager; is the system " 913 + "running?"); 914 return false; 915 } 916 917 mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 918 if (mWm == null) { 919 System.err.println("** Error: Unable to connect to window manager; is the system " 920 + "running?"); 921 return false; 922 } 923 924 mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 925 if (mPm == null) { 926 System.err.println("** Error: Unable to connect to package manager; is the system " 927 + "running?"); 928 return false; 929 } 930 931 try { 932 mAm.setActivityController(new ActivityController()); 933 mNetworkMonitor.register(mAm); 934 } catch (RemoteException e) { 935 System.err.println("** Failed talking with activity manager!"); 936 return false; 937 } 938 939 return true; 940 } 941 942 /** 943 * Using the restrictions provided (categories & packages), generate a list 944 * of activities that we can actually switch to. 945 * 946 * @return Returns true if it could successfully build a list of target 947 * activities 948 */ 949 private boolean getMainApps() { 950 try { 951 final int N = mMainCategories.size(); 952 for (int i = 0; i < N; i++) { 953 Intent intent = new Intent(Intent.ACTION_MAIN); 954 String category = mMainCategories.get(i); 955 if (category.length() > 0) { 956 intent.addCategory(category); 957 } 958 List<ResolveInfo> mainApps = mPm.queryIntentActivities(intent, null, 0, 959 UserHandle.myUserId()); 960 if (mainApps == null || mainApps.size() == 0) { 961 System.err.println("// Warning: no activities found for category " + category); 962 continue; 963 } 964 if (mVerbose >= 2) { // very verbose 965 System.out.println("// Selecting main activities from category " + category); 966 } 967 final int NA = mainApps.size(); 968 for (int a = 0; a < NA; a++) { 969 ResolveInfo r = mainApps.get(a); 970 String packageName = r.activityInfo.applicationInfo.packageName; 971 if (checkEnteringPackage(packageName)) { 972 if (mVerbose >= 2) { // very verbose 973 System.out.println("// + Using main activity " + r.activityInfo.name 974 + " (from package " + packageName + ")"); 975 } 976 mMainApps.add(new ComponentName(packageName, r.activityInfo.name)); 977 } else { 978 if (mVerbose >= 3) { // very very verbose 979 System.out.println("// - NOT USING main activity " 980 + r.activityInfo.name + " (from package " + packageName + ")"); 981 } 982 } 983 } 984 } 985 } catch (RemoteException e) { 986 System.err.println("** Failed talking with package manager!"); 987 return false; 988 } 989 990 if (mMainApps.size() == 0) { 991 System.out.println("** No activities found to run, monkey aborted."); 992 return false; 993 } 994 995 return true; 996 } 997 998 /** 999 * Run mCount cycles and see if we hit any crashers. 1000 * <p> 1001 * TODO: Meta state on keys 1002 * 1003 * @return Returns the last cycle which executed. If the value == mCount, no 1004 * errors detected. 1005 */ 1006 private int runMonkeyCycles() { 1007 int eventCounter = 0; 1008 int cycleCounter = 0; 1009 1010 boolean shouldReportAnrTraces = false; 1011 boolean shouldReportDumpsysMemInfo = false; 1012 boolean shouldAbort = false; 1013 boolean systemCrashed = false; 1014 1015 // TO DO : The count should apply to each of the script file. 1016 while (!systemCrashed && cycleCounter < mCount) { 1017 synchronized (this) { 1018 if (mRequestProcRank) { 1019 reportProcRank(); 1020 mRequestProcRank = false; 1021 } 1022 if (mRequestAnrTraces) { 1023 mRequestAnrTraces = false; 1024 shouldReportAnrTraces = true; 1025 } 1026 if (mRequestAnrBugreport){ 1027 getBugreport("anr_" + mReportProcessName + "_"); 1028 mRequestAnrBugreport = false; 1029 } 1030 if (mRequestAppCrashBugreport){ 1031 getBugreport("app_crash" + mReportProcessName + "_"); 1032 mRequestAppCrashBugreport = false; 1033 } 1034 if (mRequestPeriodicBugreport){ 1035 getBugreport("Bugreport_"); 1036 mRequestPeriodicBugreport = false; 1037 } 1038 if (mRequestDumpsysMemInfo) { 1039 mRequestDumpsysMemInfo = false; 1040 shouldReportDumpsysMemInfo = true; 1041 } 1042 if (mMonitorNativeCrashes) { 1043 // first time through, when eventCounter == 0, just set up 1044 // the watcher (ignore the error) 1045 if (checkNativeCrashes() && (eventCounter > 0)) { 1046 System.out.println("** New native crash detected."); 1047 if (mRequestBugreport) { 1048 getBugreport("native_crash_"); 1049 } 1050 mAbort = mAbort || !mIgnoreNativeCrashes || mKillProcessAfterError; 1051 } 1052 } 1053 if (mAbort) { 1054 shouldAbort = true; 1055 } 1056 } 1057 1058 // Report ANR, dumpsys after releasing lock on this. 1059 // This ensures the availability of the lock to Activity controller's appNotResponding 1060 if (shouldReportAnrTraces) { 1061 shouldReportAnrTraces = false; 1062 reportAnrTraces(); 1063 } 1064 1065 if (shouldReportDumpsysMemInfo) { 1066 shouldReportDumpsysMemInfo = false; 1067 reportDumpsysMemInfo(); 1068 } 1069 1070 if (shouldAbort) { 1071 shouldAbort = false; 1072 System.out.println("** Monkey aborted due to error."); 1073 System.out.println("Events injected: " + eventCounter); 1074 return eventCounter; 1075 } 1076 1077 // In this debugging mode, we never send any events. This is 1078 // primarily here so you can manually test the package or category 1079 // limits, while manually exercising the system. 1080 if (mSendNoEvents) { 1081 eventCounter++; 1082 cycleCounter++; 1083 continue; 1084 } 1085 1086 if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) { 1087 String calendarTime = MonkeyUtils.toCalendarTime(System.currentTimeMillis()); 1088 long systemUpTime = SystemClock.elapsedRealtime(); 1089 System.out.println(" //[calendar_time:" + calendarTime + " system_uptime:" 1090 + systemUpTime + "]"); 1091 System.out.println(" // Sending event #" + eventCounter); 1092 } 1093 1094 MonkeyEvent ev = mEventSource.getNextEvent(); 1095 if (ev != null) { 1096 int injectCode = ev.injectEvent(mWm, mAm, mVerbose); 1097 if (injectCode == MonkeyEvent.INJECT_FAIL) { 1098 if (ev instanceof MonkeyKeyEvent) { 1099 mDroppedKeyEvents++; 1100 } else if (ev instanceof MonkeyMotionEvent) { 1101 mDroppedPointerEvents++; 1102 } else if (ev instanceof MonkeyFlipEvent) { 1103 mDroppedFlipEvents++; 1104 } else if (ev instanceof MonkeyRotationEvent) { 1105 mDroppedRotationEvents++; 1106 } 1107 } else if (injectCode == MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION) { 1108 systemCrashed = true; 1109 System.err.println("** Error: RemoteException while injecting event."); 1110 } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) { 1111 systemCrashed = !mIgnoreSecurityExceptions; 1112 if (systemCrashed) { 1113 System.err.println("** Error: SecurityException while injecting event."); 1114 } 1115 } 1116 1117 // Don't count throttling as an event. 1118 if (!(ev instanceof MonkeyThrottleEvent)) { 1119 eventCounter++; 1120 if (mCountEvents) { 1121 cycleCounter++; 1122 } 1123 } 1124 } else { 1125 if (!mCountEvents) { 1126 cycleCounter++; 1127 writeScriptLog(cycleCounter); 1128 //Capture the bugreport after n iteration 1129 if (mGetPeriodicBugreport) { 1130 if ((cycleCounter % mBugreportFrequency) == 0) { 1131 mRequestPeriodicBugreport = true; 1132 } 1133 } 1134 } else { 1135 // Event Source has signaled that we have no more events to process 1136 break; 1137 } 1138 } 1139 } 1140 System.out.println("Events injected: " + eventCounter); 1141 return eventCounter; 1142 } 1143 1144 /** 1145 * Send SIGNAL_USR1 to all processes. This will generate large (5mb) 1146 * profiling reports in data/misc, so use with care. 1147 */ 1148 private void signalPersistentProcesses() { 1149 try { 1150 mAm.signalPersistentProcesses(Process.SIGNAL_USR1); 1151 1152 synchronized (this) { 1153 wait(2000); 1154 } 1155 } catch (RemoteException e) { 1156 System.err.println("** Failed talking with activity manager!"); 1157 } catch (InterruptedException e) { 1158 } 1159 } 1160 1161 /** 1162 * Watch for appearance of new tombstone files, which indicate native 1163 * crashes. 1164 * 1165 * @return Returns true if new files have appeared in the list 1166 */ 1167 private boolean checkNativeCrashes() { 1168 String[] tombstones = TOMBSTONES_PATH.list(); 1169 1170 // shortcut path for usually empty directory, so we don't waste even 1171 // more objects 1172 if ((tombstones == null) || (tombstones.length == 0)) { 1173 mTombstones = null; 1174 return false; 1175 } 1176 1177 // use set logic to look for new files 1178 HashSet<String> newStones = new HashSet<String>(); 1179 for (String x : tombstones) { 1180 newStones.add(x); 1181 } 1182 1183 boolean result = (mTombstones == null) || !mTombstones.containsAll(newStones); 1184 1185 // keep the new list for the next time 1186 mTombstones = newStones; 1187 1188 return result; 1189 } 1190 1191 /** 1192 * Return the next command line option. This has a number of special cases 1193 * which closely, but not exactly, follow the POSIX command line options 1194 * patterns: 1195 * 1196 * <pre> 1197 * -- means to stop processing additional options 1198 * -z means option z 1199 * -z ARGS means option z with (non-optional) arguments ARGS 1200 * -zARGS means option z with (optional) arguments ARGS 1201 * --zz means option zz 1202 * --zz ARGS means option zz with (non-optional) arguments ARGS 1203 * </pre> 1204 * 1205 * Note that you cannot combine single letter options; -abc != -a -b -c 1206 * 1207 * @return Returns the option string, or null if there are no more options. 1208 */ 1209 private String nextOption() { 1210 if (mNextArg >= mArgs.length) { 1211 return null; 1212 } 1213 String arg = mArgs[mNextArg]; 1214 if (!arg.startsWith("-")) { 1215 return null; 1216 } 1217 mNextArg++; 1218 if (arg.equals("--")) { 1219 return null; 1220 } 1221 if (arg.length() > 1 && arg.charAt(1) != '-') { 1222 if (arg.length() > 2) { 1223 mCurArgData = arg.substring(2); 1224 return arg.substring(0, 2); 1225 } else { 1226 mCurArgData = null; 1227 return arg; 1228 } 1229 } 1230 mCurArgData = null; 1231 return arg; 1232 } 1233 1234 /** 1235 * Return the next data associated with the current option. 1236 * 1237 * @return Returns the data string, or null of there are no more arguments. 1238 */ 1239 private String nextOptionData() { 1240 if (mCurArgData != null) { 1241 return mCurArgData; 1242 } 1243 if (mNextArg >= mArgs.length) { 1244 return null; 1245 } 1246 String data = mArgs[mNextArg]; 1247 mNextArg++; 1248 return data; 1249 } 1250 1251 /** 1252 * Returns a long converted from the next data argument, with error handling 1253 * if not available. 1254 * 1255 * @param opt The name of the option. 1256 * @return Returns a long converted from the argument. 1257 */ 1258 private long nextOptionLong(final String opt) { 1259 long result; 1260 try { 1261 result = Long.parseLong(nextOptionData()); 1262 } catch (NumberFormatException e) { 1263 System.err.println("** Error: " + opt + " is not a number"); 1264 throw e; 1265 } 1266 return result; 1267 } 1268 1269 /** 1270 * Return the next argument on the command line. 1271 * 1272 * @return Returns the argument string, or null if we have reached the end. 1273 */ 1274 private String nextArg() { 1275 if (mNextArg >= mArgs.length) { 1276 return null; 1277 } 1278 String arg = mArgs[mNextArg]; 1279 mNextArg++; 1280 return arg; 1281 } 1282 1283 /** 1284 * Print how to use this command. 1285 */ 1286 private void showUsage() { 1287 StringBuffer usage = new StringBuffer(); 1288 usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n"); 1289 usage.append(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n"); 1290 usage.append(" [--ignore-crashes] [--ignore-timeouts]\n"); 1291 usage.append(" [--ignore-security-exceptions]\n"); 1292 usage.append(" [--monitor-native-crashes] [--ignore-native-crashes]\n"); 1293 usage.append(" [--kill-process-after-error] [--hprof]\n"); 1294 usage.append(" [--pct-touch PERCENT] [--pct-motion PERCENT]\n"); 1295 usage.append(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n"); 1296 usage.append(" [--pct-nav PERCENT] [--pct-majornav PERCENT]\n"); 1297 usage.append(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n"); 1298 usage.append(" [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]\n"); 1299 usage.append(" [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n"); 1300 usage.append(" [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n"); 1301 usage.append(" [--wait-dbg] [--dbg-no-events]\n"); 1302 usage.append(" [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n"); 1303 usage.append(" [--port port]\n"); 1304 usage.append(" [-s SEED] [-v [-v] ...]\n"); 1305 usage.append(" [--throttle MILLISEC] [--randomize-throttle]\n"); 1306 usage.append(" [--profile-wait MILLISEC]\n"); 1307 usage.append(" [--device-sleep-time MILLISEC]\n"); 1308 usage.append(" [--randomize-script]\n"); 1309 usage.append(" [--script-log]\n"); 1310 usage.append(" [--bugreport]\n"); 1311 usage.append(" [--periodic-bugreport]\n"); 1312 usage.append(" COUNT\n"); 1313 System.err.println(usage.toString()); 1314 } 1315} 1316