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