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