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 485 try (Writer logOutput = mRequestBugreport ? 486 new BufferedWriter(new FileWriter(new File(Environment 487 .getLegacyExternalStorageDirectory(), reportName), true)) : null) { 488 // Process must be fully qualified here because android.os.Process 489 // is used elsewhere 490 java.lang.Process p = Runtime.getRuntime().exec(command); 491 492 // pipe everything from process stdout -> System.err 493 InputStream inStream = p.getInputStream(); 494 InputStreamReader inReader = new InputStreamReader(inStream); 495 BufferedReader inBuffer = new BufferedReader(inReader); 496 String s; 497 while ((s = inBuffer.readLine()) != null) { 498 if (mRequestBugreport) { 499 try { 500 // When no space left on the device the write will 501 // occurs an I/O exception, so we needed to catch it 502 // and continue to read the data of the sync pipe to 503 // aviod the bugreport hang forever. 504 logOutput.write(s); 505 logOutput.write("\n"); 506 } catch (IOException e) { 507 while(inBuffer.readLine() != null) {} 508 Logger.err.println(e.toString()); 509 break; 510 } 511 } else { 512 Logger.err.println(s); 513 } 514 } 515 516 int status = p.waitFor(); 517 Logger.err.println("// " + reportName + " status was " + status); 518 } catch (Exception e) { 519 Logger.err.println("// Exception from " + reportName + ":"); 520 Logger.err.println(e.toString()); 521 } 522 } 523 524 // Write the numbe of iteration to the log writeScriptLog(int count)525 private void writeScriptLog(int count) { 526 // TO DO: Add the script file name to the log. 527 try { 528 Writer output = new BufferedWriter(new FileWriter(new File( 529 Environment.getLegacyExternalStorageDirectory(), "scriptlog.txt"), true)); 530 output.write("iteration: " + count + " time: " 531 + MonkeyUtils.toCalendarTime(System.currentTimeMillis()) + "\n"); 532 output.close(); 533 } catch (IOException e) { 534 Logger.err.println(e.toString()); 535 } 536 } 537 538 // Write the bugreport to the sdcard. getBugreport(String reportName)539 private void getBugreport(String reportName) { 540 reportName += MonkeyUtils.toCalendarTime(System.currentTimeMillis()); 541 String bugreportName = reportName.replaceAll("[ ,:]", "_"); 542 commandLineReport(bugreportName + ".txt", "bugreport"); 543 } 544 545 // UncaughtExceptionHandler set by RuntimeInit will report crash to system_server, which 546 // is not necessary for monkey and even causes deadlock. So we override it. 547 private static class KillSelfHandler implements Thread.UncaughtExceptionHandler { 548 @Override uncaughtException(Thread t, Throwable e)549 public void uncaughtException(Thread t, Throwable e) { 550 Process.killProcess(Process.myPid()); 551 System.exit(10); 552 } 553 } 554 555 /** 556 * Command-line entry point. 557 * 558 * @param args The command-line arguments 559 */ main(String[] args)560 public static void main(String[] args) { 561 // Set the process name showing in "ps" or "top" 562 Process.setArgV0("com.android.commands.monkey"); 563 564 Thread.setDefaultUncaughtExceptionHandler(new KillSelfHandler()); 565 Logger.err.println("args: " + Arrays.toString(args)); 566 int resultCode = (new Monkey()).run(args); 567 System.exit(resultCode); 568 } 569 570 /** 571 * Run the command! 572 * 573 * @param args The command-line arguments 574 * @return Returns a posix-style result code. 0 for no error. 575 */ run(String[] args)576 private int run(String[] args) { 577 // Super-early debugger wait 578 for (String s : args) { 579 if ("--wait-dbg".equals(s)) { 580 Debug.waitForDebugger(); 581 } 582 } 583 584 // Default values for some command-line options 585 mVerbose = 0; 586 mCount = 1000; 587 mSeed = 0; 588 mThrottle = 0; 589 590 // prepare for command-line processing 591 mArgs = args; 592 for (String a: args) { 593 Logger.err.println(" arg: \"" + a + "\""); 594 } 595 mNextArg = 0; 596 597 // set a positive value, indicating none of the factors is provided yet 598 for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { 599 mFactors[i] = 1.0f; 600 } 601 602 if (!processOptions()) { 603 return -1; 604 } 605 606 if (!loadPackageLists()) { 607 return -1; 608 } 609 610 // now set up additional data in preparation for launch 611 if (mMainCategories.size() == 0) { 612 mMainCategories.add(Intent.CATEGORY_LAUNCHER); 613 mMainCategories.add(Intent.CATEGORY_MONKEY); 614 } 615 616 if (mSeed == 0) { 617 mSeed = System.currentTimeMillis() + System.identityHashCode(this); 618 } 619 620 if (mVerbose > 0) { 621 Logger.out.println(":Monkey: seed=" + mSeed + " count=" + mCount); 622 MonkeyUtils.getPackageFilter().dump(); 623 if (mMainCategories.size() != 0) { 624 Iterator<String> it = mMainCategories.iterator(); 625 while (it.hasNext()) { 626 Logger.out.println(":IncludeCategory: " + it.next()); 627 } 628 } 629 } 630 631 if (!checkInternalConfiguration()) { 632 return -2; 633 } 634 635 if (!getSystemInterfaces()) { 636 return -3; 637 } 638 639 if (!getMainApps()) { 640 return -4; 641 } 642 643 mRandom = new Random(mSeed); 644 645 if (mScriptFileNames != null && mScriptFileNames.size() == 1) { 646 // script mode, ignore other options 647 mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle, 648 mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime); 649 mEventSource.setVerbose(mVerbose); 650 651 mCountEvents = false; 652 } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) { 653 if (mSetupFileName != null) { 654 mEventSource = new MonkeySourceRandomScript(mSetupFileName, 655 mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom, 656 mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); 657 mCount++; 658 } else { 659 mEventSource = new MonkeySourceRandomScript(mScriptFileNames, 660 mThrottle, mRandomizeThrottle, mRandom, 661 mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); 662 } 663 mEventSource.setVerbose(mVerbose); 664 mCountEvents = false; 665 } else if (mServerPort != -1) { 666 try { 667 mEventSource = new MonkeySourceNetwork(mServerPort); 668 } catch (IOException e) { 669 Logger.out.println("Error binding to network socket."); 670 return -5; 671 } 672 mCount = Integer.MAX_VALUE; 673 } else { 674 // random source by default 675 if (mVerbose >= 2) { // check seeding performance 676 Logger.out.println("// Seeded: " + mSeed); 677 } 678 mEventSource = new MonkeySourceRandom(mRandom, mMainApps, 679 mThrottle, mRandomizeThrottle, mPermissionTargetSystem); 680 mEventSource.setVerbose(mVerbose); 681 // set any of the factors that has been set 682 for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { 683 if (mFactors[i] <= 0.0f) { 684 ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]); 685 } 686 } 687 688 // in random mode, we start with a random activity 689 ((MonkeySourceRandom) mEventSource).generateActivity(); 690 } 691 692 // validate source generator 693 if (!mEventSource.validate()) { 694 return -5; 695 } 696 697 // If we're profiling, do it immediately before/after the main monkey 698 // loop 699 if (mGenerateHprof) { 700 signalPersistentProcesses(); 701 } 702 703 mNetworkMonitor.start(); 704 int crashedAtCycle = 0; 705 try { 706 crashedAtCycle = runMonkeyCycles(); 707 } finally { 708 // Release the rotation lock if it's still held and restore the 709 // original orientation. 710 new MonkeyRotationEvent(Surface.ROTATION_0, false).injectEvent( 711 mWm, mAm, mVerbose); 712 } 713 mNetworkMonitor.stop(); 714 715 synchronized (this) { 716 if (mRequestAnrTraces) { 717 reportAnrTraces(); 718 mRequestAnrTraces = false; 719 } 720 if (mRequestAnrBugreport){ 721 Logger.out.println("Print the anr report"); 722 getBugreport("anr_" + mReportProcessName + "_"); 723 mRequestAnrBugreport = false; 724 } 725 if (mRequestWatchdogBugreport) { 726 Logger.out.println("Print the watchdog report"); 727 getBugreport("anr_watchdog_"); 728 mRequestWatchdogBugreport = false; 729 } 730 if (mRequestAppCrashBugreport){ 731 getBugreport("app_crash" + mReportProcessName + "_"); 732 mRequestAppCrashBugreport = false; 733 } 734 if (mRequestDumpsysMemInfo) { 735 reportDumpsysMemInfo(); 736 mRequestDumpsysMemInfo = false; 737 } 738 if (mRequestPeriodicBugreport){ 739 getBugreport("Bugreport_"); 740 mRequestPeriodicBugreport = false; 741 } 742 if (mWatchdogWaiting) { 743 mWatchdogWaiting = false; 744 notifyAll(); 745 } 746 } 747 748 if (mGenerateHprof) { 749 signalPersistentProcesses(); 750 if (mVerbose > 0) { 751 Logger.out.println("// Generated profiling reports in /data/misc"); 752 } 753 } 754 755 try { 756 mAm.setActivityController(null, true); 757 mNetworkMonitor.unregister(mAm); 758 } catch (RemoteException e) { 759 // just in case this was latent (after mCount cycles), make sure 760 // we report it 761 if (crashedAtCycle >= mCount) { 762 crashedAtCycle = mCount - 1; 763 } 764 } 765 766 // report dropped event stats 767 if (mVerbose > 0) { 768 Logger.out.println(":Dropped: keys=" + mDroppedKeyEvents 769 + " pointers=" + mDroppedPointerEvents 770 + " trackballs=" + mDroppedTrackballEvents 771 + " flips=" + mDroppedFlipEvents 772 + " rotations=" + mDroppedRotationEvents); 773 } 774 775 // report network stats 776 mNetworkMonitor.dump(); 777 778 if (crashedAtCycle < mCount - 1) { 779 Logger.err.println("** System appears to have crashed at event " + crashedAtCycle 780 + " of " + mCount + " using seed " + mSeed); 781 return crashedAtCycle; 782 } else { 783 if (mVerbose > 0) { 784 Logger.out.println("// Monkey finished"); 785 } 786 return 0; 787 } 788 } 789 790 /** 791 * Process the command-line options 792 * 793 * @return Returns true if options were parsed with no apparent errors. 794 */ processOptions()795 private boolean processOptions() { 796 // quick (throwaway) check for unadorned command 797 if (mArgs.length < 1) { 798 showUsage(); 799 return false; 800 } 801 802 try { 803 String opt; 804 Set<String> validPackages = new HashSet<>(); 805 while ((opt = nextOption()) != null) { 806 if (opt.equals("-s")) { 807 mSeed = nextOptionLong("Seed"); 808 } else if (opt.equals("-p")) { 809 validPackages.add(nextOptionData()); 810 } else if (opt.equals("-c")) { 811 mMainCategories.add(nextOptionData()); 812 } else if (opt.equals("-v")) { 813 mVerbose += 1; 814 } else if (opt.equals("--ignore-crashes")) { 815 mIgnoreCrashes = true; 816 } else if (opt.equals("--ignore-timeouts")) { 817 mIgnoreTimeouts = true; 818 } else if (opt.equals("--ignore-security-exceptions")) { 819 mIgnoreSecurityExceptions = true; 820 } else if (opt.equals("--monitor-native-crashes")) { 821 mMonitorNativeCrashes = true; 822 } else if (opt.equals("--ignore-native-crashes")) { 823 mIgnoreNativeCrashes = true; 824 } else if (opt.equals("--kill-process-after-error")) { 825 mKillProcessAfterError = true; 826 } else if (opt.equals("--hprof")) { 827 mGenerateHprof = true; 828 } else if (opt.equals("--match-description")) { 829 mMatchDescription = nextOptionData(); 830 } else if (opt.equals("--pct-touch")) { 831 int i = MonkeySourceRandom.FACTOR_TOUCH; 832 mFactors[i] = -nextOptionLong("touch events percentage"); 833 } else if (opt.equals("--pct-motion")) { 834 int i = MonkeySourceRandom.FACTOR_MOTION; 835 mFactors[i] = -nextOptionLong("motion events percentage"); 836 } else if (opt.equals("--pct-trackball")) { 837 int i = MonkeySourceRandom.FACTOR_TRACKBALL; 838 mFactors[i] = -nextOptionLong("trackball events percentage"); 839 } else if (opt.equals("--pct-rotation")) { 840 int i = MonkeySourceRandom.FACTOR_ROTATION; 841 mFactors[i] = -nextOptionLong("screen rotation events percentage"); 842 } else if (opt.equals("--pct-syskeys")) { 843 int i = MonkeySourceRandom.FACTOR_SYSOPS; 844 mFactors[i] = -nextOptionLong("system (key) operations percentage"); 845 } else if (opt.equals("--pct-nav")) { 846 int i = MonkeySourceRandom.FACTOR_NAV; 847 mFactors[i] = -nextOptionLong("nav events percentage"); 848 } else if (opt.equals("--pct-majornav")) { 849 int i = MonkeySourceRandom.FACTOR_MAJORNAV; 850 mFactors[i] = -nextOptionLong("major nav events percentage"); 851 } else if (opt.equals("--pct-appswitch")) { 852 int i = MonkeySourceRandom.FACTOR_APPSWITCH; 853 mFactors[i] = -nextOptionLong("app switch events percentage"); 854 } else if (opt.equals("--pct-flip")) { 855 int i = MonkeySourceRandom.FACTOR_FLIP; 856 mFactors[i] = -nextOptionLong("keyboard flip percentage"); 857 } else if (opt.equals("--pct-anyevent")) { 858 int i = MonkeySourceRandom.FACTOR_ANYTHING; 859 mFactors[i] = -nextOptionLong("any events percentage"); 860 } else if (opt.equals("--pct-pinchzoom")) { 861 int i = MonkeySourceRandom.FACTOR_PINCHZOOM; 862 mFactors[i] = -nextOptionLong("pinch zoom events percentage"); 863 } else if (opt.equals("--pct-permission")) { 864 int i = MonkeySourceRandom.FACTOR_PERMISSION; 865 mFactors[i] = -nextOptionLong("runtime permission toggle events percentage"); 866 } else if (opt.equals("--pkg-blacklist-file")) { 867 mPkgBlacklistFile = nextOptionData(); 868 } else if (opt.equals("--pkg-whitelist-file")) { 869 mPkgWhitelistFile = nextOptionData(); 870 } else if (opt.equals("--throttle")) { 871 mThrottle = nextOptionLong("delay (in milliseconds) to wait between events"); 872 } else if (opt.equals("--randomize-throttle")) { 873 mRandomizeThrottle = true; 874 } else if (opt.equals("--wait-dbg")) { 875 // do nothing - it's caught at the very start of run() 876 } else if (opt.equals("--dbg-no-events")) { 877 mSendNoEvents = true; 878 } else if (opt.equals("--port")) { 879 mServerPort = (int) nextOptionLong("Server port to listen on for commands"); 880 } else if (opt.equals("--setup")) { 881 mSetupFileName = nextOptionData(); 882 } else if (opt.equals("-f")) { 883 mScriptFileNames.add(nextOptionData()); 884 } else if (opt.equals("--profile-wait")) { 885 mProfileWaitTime = nextOptionLong("Profile delay" + 886 " (in milliseconds) to wait between user action"); 887 } else if (opt.equals("--device-sleep-time")) { 888 mDeviceSleepTime = nextOptionLong("Device sleep time" + 889 "(in milliseconds)"); 890 } else if (opt.equals("--randomize-script")) { 891 mRandomizeScript = true; 892 } else if (opt.equals("--script-log")) { 893 mScriptLog = true; 894 } else if (opt.equals("--bugreport")) { 895 mRequestBugreport = true; 896 } else if (opt.equals("--periodic-bugreport")){ 897 mGetPeriodicBugreport = true; 898 mBugreportFrequency = nextOptionLong("Number of iterations"); 899 } else if (opt.equals("--permission-target-system")){ 900 mPermissionTargetSystem = true; 901 } else if (opt.equals("-h")) { 902 showUsage(); 903 return false; 904 } else { 905 Logger.err.println("** Error: Unknown option: " + opt); 906 showUsage(); 907 return false; 908 } 909 } 910 MonkeyUtils.getPackageFilter().addValidPackages(validPackages); 911 } catch (RuntimeException ex) { 912 Logger.err.println("** Error: " + ex.toString()); 913 showUsage(); 914 return false; 915 } 916 917 // If a server port hasn't been specified, we need to specify 918 // a count 919 if (mServerPort == -1) { 920 String countStr = nextArg(); 921 if (countStr == null) { 922 Logger.err.println("** Error: Count not specified"); 923 showUsage(); 924 return false; 925 } 926 927 try { 928 mCount = Integer.parseInt(countStr); 929 } catch (NumberFormatException e) { 930 Logger.err.println("** Error: Count is not a number: \"" + countStr + "\""); 931 showUsage(); 932 return false; 933 } 934 } 935 936 return true; 937 } 938 939 /** 940 * Load a list of package names from a file. 941 * 942 * @param fileName The file name, with package names separated by new line. 943 * @param list The destination list. 944 * @return Returns false if any error occurs. 945 */ loadPackageListFromFile(String fileName, Set<String> list)946 private static boolean loadPackageListFromFile(String fileName, Set<String> list) { 947 BufferedReader reader = null; 948 try { 949 reader = new BufferedReader(new FileReader(fileName)); 950 String s; 951 while ((s = reader.readLine()) != null) { 952 s = s.trim(); 953 if ((s.length() > 0) && (!s.startsWith("#"))) { 954 list.add(s); 955 } 956 } 957 } catch (IOException ioe) { 958 Logger.err.println("" + ioe); 959 return false; 960 } finally { 961 if (reader != null) { 962 try { 963 reader.close(); 964 } catch (IOException ioe) { 965 Logger.err.println("" + ioe); 966 } 967 } 968 } 969 return true; 970 } 971 972 /** 973 * Load package denylist or allowlist (if specified). 974 * 975 * @return Returns false if any error occurs. 976 */ loadPackageLists()977 private boolean loadPackageLists() { 978 if (((mPkgWhitelistFile != null) || (MonkeyUtils.getPackageFilter().hasValidPackages())) 979 && (mPkgBlacklistFile != null)) { 980 Logger.err.println("** Error: you can not specify a package blacklist " 981 + "together with a whitelist or individual packages (via -p)."); 982 return false; 983 } 984 Set<String> validPackages = new HashSet<>(); 985 if ((mPkgWhitelistFile != null) 986 && (!loadPackageListFromFile(mPkgWhitelistFile, validPackages))) { 987 return false; 988 } 989 MonkeyUtils.getPackageFilter().addValidPackages(validPackages); 990 Set<String> invalidPackages = new HashSet<>(); 991 if ((mPkgBlacklistFile != null) 992 && (!loadPackageListFromFile(mPkgBlacklistFile, invalidPackages))) { 993 return false; 994 } 995 MonkeyUtils.getPackageFilter().addInvalidPackages(invalidPackages); 996 return true; 997 } 998 999 /** 1000 * Check for any internal configuration (primarily build-time) errors. 1001 * 1002 * @return Returns true if ready to rock. 1003 */ checkInternalConfiguration()1004 private boolean checkInternalConfiguration() { 1005 return true; 1006 } 1007 1008 /** 1009 * Attach to the required system interfaces. 1010 * 1011 * @return Returns true if all system interfaces were available. 1012 */ getSystemInterfaces()1013 private boolean getSystemInterfaces() { 1014 mAm = ActivityManager.getService(); 1015 if (mAm == null) { 1016 Logger.err.println("** Error: Unable to connect to activity manager; is the system " 1017 + "running?"); 1018 return false; 1019 } 1020 1021 mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 1022 if (mWm == null) { 1023 Logger.err.println("** Error: Unable to connect to window manager; is the system " 1024 + "running?"); 1025 return false; 1026 } 1027 1028 mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 1029 if (mPm == null) { 1030 Logger.err.println("** Error: Unable to connect to package manager; is the system " 1031 + "running?"); 1032 return false; 1033 } 1034 1035 try { 1036 mAm.setActivityController(new ActivityController(), true); 1037 mNetworkMonitor.register(mAm); 1038 } catch (RemoteException e) { 1039 Logger.err.println("** Failed talking with activity manager!"); 1040 return false; 1041 } 1042 1043 return true; 1044 } 1045 1046 /** 1047 * Using the restrictions provided (categories & packages), generate a list 1048 * of activities that we can actually switch to. 1049 * 1050 * @return Returns true if it could successfully build a list of target 1051 * activities 1052 */ getMainApps()1053 private boolean getMainApps() { 1054 try { 1055 final int N = mMainCategories.size(); 1056 for (int i = 0; i < N; i++) { 1057 Intent intent = new Intent(Intent.ACTION_MAIN); 1058 String category = mMainCategories.get(i); 1059 if (category.length() > 0) { 1060 intent.addCategory(category); 1061 } 1062 List<ResolveInfo> mainApps = mPm.queryIntentActivities(intent, null, 0, 1063 ActivityManager.getCurrentUser()).getList(); 1064 if (mainApps == null || mainApps.size() == 0) { 1065 Logger.err.println("// Warning: no activities found for category " + category); 1066 continue; 1067 } 1068 if (mVerbose >= 2) { // very verbose 1069 Logger.out.println("// Selecting main activities from category " + category); 1070 } 1071 final int NA = mainApps.size(); 1072 for (int a = 0; a < NA; a++) { 1073 ResolveInfo r = mainApps.get(a); 1074 String packageName = r.activityInfo.applicationInfo.packageName; 1075 if (MonkeyUtils.getPackageFilter().checkEnteringPackage(packageName)) { 1076 if (mVerbose >= 2) { // very verbose 1077 Logger.out.println("// + Using main activity " + r.activityInfo.name 1078 + " (from package " + packageName + ")"); 1079 } 1080 mMainApps.put( 1081 new ComponentName(packageName, r.activityInfo.name), category); 1082 } else { 1083 if (mVerbose >= 3) { // very very verbose 1084 Logger.out.println("// - NOT USING main activity " 1085 + r.activityInfo.name + " (from package " + packageName + ")"); 1086 } 1087 } 1088 } 1089 } 1090 } catch (RemoteException e) { 1091 Logger.err.println("** Failed talking with package manager!"); 1092 return false; 1093 } 1094 1095 if (mMainApps.size() == 0) { 1096 Logger.out.println("** No activities found to run, monkey aborted."); 1097 return false; 1098 } 1099 1100 return true; 1101 } 1102 1103 /** 1104 * Run mCount cycles and see if we hit any crashers. 1105 * <p> 1106 * TODO: Meta state on keys 1107 * 1108 * @return Returns the last cycle which executed. If the value == mCount, no 1109 * errors detected. 1110 */ runMonkeyCycles()1111 private int runMonkeyCycles() { 1112 int eventCounter = 0; 1113 int cycleCounter = 0; 1114 1115 boolean shouldReportAnrTraces = false; 1116 boolean shouldReportDumpsysMemInfo = false; 1117 boolean shouldAbort = false; 1118 boolean systemCrashed = false; 1119 1120 try { 1121 // TO DO : The count should apply to each of the script file. 1122 while (!systemCrashed && cycleCounter < mCount) { 1123 synchronized (this) { 1124 if (mRequestProcRank) { 1125 reportProcRank(); 1126 mRequestProcRank = false; 1127 } 1128 if (mRequestAnrTraces) { 1129 mRequestAnrTraces = false; 1130 shouldReportAnrTraces = true; 1131 } 1132 if (mRequestAnrBugreport){ 1133 getBugreport("anr_" + mReportProcessName + "_"); 1134 mRequestAnrBugreport = false; 1135 } 1136 if (mRequestWatchdogBugreport) { 1137 Logger.out.println("Print the watchdog report"); 1138 getBugreport("anr_watchdog_"); 1139 mRequestWatchdogBugreport = false; 1140 } 1141 if (mRequestAppCrashBugreport){ 1142 getBugreport("app_crash" + mReportProcessName + "_"); 1143 mRequestAppCrashBugreport = false; 1144 } 1145 if (mRequestPeriodicBugreport){ 1146 getBugreport("Bugreport_"); 1147 mRequestPeriodicBugreport = false; 1148 } 1149 if (mRequestDumpsysMemInfo) { 1150 mRequestDumpsysMemInfo = false; 1151 shouldReportDumpsysMemInfo = true; 1152 } 1153 if (mMonitorNativeCrashes) { 1154 // first time through, when eventCounter == 0, just set up 1155 // the watcher (ignore the error) 1156 if (checkNativeCrashes() && (eventCounter > 0)) { 1157 Logger.out.println("** New native crash detected."); 1158 if (mRequestBugreport) { 1159 getBugreport("native_crash_"); 1160 } 1161 mAbort = mAbort || !mIgnoreNativeCrashes || mKillProcessAfterError; 1162 } 1163 } 1164 if (mAbort) { 1165 shouldAbort = true; 1166 } 1167 if (mWatchdogWaiting) { 1168 mWatchdogWaiting = false; 1169 notifyAll(); 1170 } 1171 } 1172 1173 // Report ANR, dumpsys after releasing lock on this. 1174 // This ensures the availability of the lock to Activity controller's appNotResponding 1175 if (shouldReportAnrTraces) { 1176 shouldReportAnrTraces = false; 1177 reportAnrTraces(); 1178 } 1179 1180 if (shouldReportDumpsysMemInfo) { 1181 shouldReportDumpsysMemInfo = false; 1182 reportDumpsysMemInfo(); 1183 } 1184 1185 if (shouldAbort) { 1186 shouldAbort = false; 1187 Logger.out.println("** Monkey aborted due to error."); 1188 Logger.out.println("Events injected: " + eventCounter); 1189 return eventCounter; 1190 } 1191 1192 // In this debugging mode, we never send any events. This is 1193 // primarily here so you can manually test the package or category 1194 // limits, while manually exercising the system. 1195 if (mSendNoEvents) { 1196 eventCounter++; 1197 cycleCounter++; 1198 continue; 1199 } 1200 1201 if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) { 1202 String calendarTime = MonkeyUtils.toCalendarTime(System.currentTimeMillis()); 1203 long systemUpTime = SystemClock.elapsedRealtime(); 1204 Logger.out.println(" //[calendar_time:" + calendarTime + " system_uptime:" 1205 + systemUpTime + "]"); 1206 Logger.out.println(" // Sending event #" + eventCounter); 1207 } 1208 1209 MonkeyEvent ev = mEventSource.getNextEvent(); 1210 if (ev != null) { 1211 int injectCode = ev.injectEvent(mWm, mAm, mVerbose); 1212 if (injectCode == MonkeyEvent.INJECT_FAIL) { 1213 Logger.out.println(" // Injection Failed"); 1214 if (ev instanceof MonkeyKeyEvent) { 1215 mDroppedKeyEvents++; 1216 } else if (ev instanceof MonkeyMotionEvent) { 1217 mDroppedPointerEvents++; 1218 } else if (ev instanceof MonkeyFlipEvent) { 1219 mDroppedFlipEvents++; 1220 } else if (ev instanceof MonkeyRotationEvent) { 1221 mDroppedRotationEvents++; 1222 } 1223 } else if (injectCode == MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION) { 1224 systemCrashed = true; 1225 Logger.err.println("** Error: RemoteException while injecting event."); 1226 } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) { 1227 systemCrashed = !mIgnoreSecurityExceptions; 1228 if (systemCrashed) { 1229 Logger.err.println("** Error: SecurityException while injecting event."); 1230 } 1231 } 1232 1233 // Don't count throttling as an event. 1234 if (!(ev instanceof MonkeyThrottleEvent)) { 1235 eventCounter++; 1236 if (mCountEvents) { 1237 cycleCounter++; 1238 } 1239 } 1240 } else { 1241 if (!mCountEvents) { 1242 cycleCounter++; 1243 writeScriptLog(cycleCounter); 1244 //Capture the bugreport after n iteration 1245 if (mGetPeriodicBugreport) { 1246 if ((cycleCounter % mBugreportFrequency) == 0) { 1247 mRequestPeriodicBugreport = true; 1248 } 1249 } 1250 } else { 1251 // Event Source has signaled that we have no more events to process 1252 break; 1253 } 1254 } 1255 } 1256 } catch (RuntimeException e) { 1257 Logger.error("** Error: A RuntimeException occurred:", e); 1258 } 1259 Logger.out.println("Events injected: " + eventCounter); 1260 return eventCounter; 1261 } 1262 1263 /** 1264 * Send SIGNAL_USR1 to all processes. This will generate large (5mb) 1265 * profiling reports in data/misc, so use with care. 1266 */ signalPersistentProcesses()1267 private void signalPersistentProcesses() { 1268 try { 1269 mAm.signalPersistentProcesses(Process.SIGNAL_USR1); 1270 1271 synchronized (this) { 1272 wait(2000); 1273 } 1274 } catch (RemoteException e) { 1275 Logger.err.println("** Failed talking with activity manager!"); 1276 } catch (InterruptedException e) { 1277 } 1278 } 1279 1280 /** 1281 * Watch for appearance of new tombstone files, which indicate native 1282 * crashes. 1283 * 1284 * @return Returns true if new files have appeared in the list 1285 */ checkNativeCrashes()1286 private boolean checkNativeCrashes() { 1287 String[] tombstones = TOMBSTONES_PATH.list(); 1288 1289 // shortcut path for usually empty directory, so we don't waste even 1290 // more objects 1291 if (tombstones == null || tombstones.length == 0) { 1292 mTombstones = null; 1293 return false; 1294 } 1295 1296 boolean result = false; 1297 1298 // use set logic to look for new files 1299 HashSet<Long> newStones = new HashSet<Long>(); 1300 for (String t : tombstones) { 1301 if (t.startsWith(TOMBSTONE_PREFIX)) { 1302 File f = new File(TOMBSTONES_PATH, t); 1303 newStones.add(f.lastModified()); 1304 if (mTombstones == null || !mTombstones.contains(f.lastModified())) { 1305 result = true; 1306 waitForTombstoneToBeWritten(Paths.get(TOMBSTONES_PATH.getPath(), t)); 1307 Logger.out.println("** New tombstone found: " + f.getAbsolutePath() 1308 + ", size: " + f.length()); 1309 } 1310 } 1311 } 1312 1313 // keep the new list for the next time 1314 mTombstones = newStones; 1315 1316 return result; 1317 } 1318 1319 /** 1320 * Wait for the given tombstone file to be completely written. 1321 * 1322 * @param path The path of the tombstone file. 1323 */ waitForTombstoneToBeWritten(Path path)1324 private void waitForTombstoneToBeWritten(Path path) { 1325 boolean isWritten = false; 1326 try { 1327 // Ensure file is done writing by sleeping and comparing the previous and current size 1328 for (int i = 0; i < NUM_READ_TOMBSTONE_RETRIES; i++) { 1329 long size = Files.size(path); 1330 try { 1331 Thread.sleep(1000); 1332 } catch (InterruptedException e) { } 1333 if (size > 0 && Files.size(path) == size) { 1334 //File size is bigger than 0 and hasn't changed 1335 isWritten = true; 1336 break; 1337 } 1338 } 1339 } catch (IOException e) { 1340 Logger.err.println("Failed to get tombstone file size: " + e.toString()); 1341 } 1342 if (!isWritten) { 1343 Logger.err.println("Incomplete tombstone file."); 1344 return; 1345 } 1346 } 1347 1348 /** 1349 * Return the next command line option. This has a number of special cases 1350 * which closely, but not exactly, follow the POSIX command line options 1351 * patterns: 1352 * 1353 * <pre> 1354 * -- means to stop processing additional options 1355 * -z means option z 1356 * -z ARGS means option z with (non-optional) arguments ARGS 1357 * -zARGS means option z with (optional) arguments ARGS 1358 * --zz means option zz 1359 * --zz ARGS means option zz with (non-optional) arguments ARGS 1360 * </pre> 1361 * 1362 * Note that you cannot combine single letter options; -abc != -a -b -c 1363 * 1364 * @return Returns the option string, or null if there are no more options. 1365 */ nextOption()1366 private String nextOption() { 1367 if (mNextArg >= mArgs.length) { 1368 return null; 1369 } 1370 String arg = mArgs[mNextArg]; 1371 if (!arg.startsWith("-")) { 1372 return null; 1373 } 1374 mNextArg++; 1375 if (arg.equals("--")) { 1376 return null; 1377 } 1378 if (arg.length() > 1 && arg.charAt(1) != '-') { 1379 if (arg.length() > 2) { 1380 mCurArgData = arg.substring(2); 1381 return arg.substring(0, 2); 1382 } else { 1383 mCurArgData = null; 1384 return arg; 1385 } 1386 } 1387 mCurArgData = null; 1388 Logger.err.println("arg=\"" + arg + "\" mCurArgData=\"" + mCurArgData + "\" mNextArg=" 1389 + mNextArg + " argwas=\"" + mArgs[mNextArg-1] + "\"" + " nextarg=\"" + 1390 mArgs[mNextArg] + "\""); 1391 return arg; 1392 } 1393 1394 /** 1395 * Return the next data associated with the current option. 1396 * 1397 * @return Returns the data string, or null of there are no more arguments. 1398 */ nextOptionData()1399 private String nextOptionData() { 1400 if (mCurArgData != null) { 1401 return mCurArgData; 1402 } 1403 if (mNextArg >= mArgs.length) { 1404 return null; 1405 } 1406 String data = mArgs[mNextArg]; 1407 Logger.err.println("data=\"" + data + "\""); 1408 mNextArg++; 1409 return data; 1410 } 1411 1412 /** 1413 * Returns a long converted from the next data argument, with error handling 1414 * if not available. 1415 * 1416 * @param opt The name of the option. 1417 * @return Returns a long converted from the argument. 1418 */ nextOptionLong(final String opt)1419 private long nextOptionLong(final String opt) { 1420 long result; 1421 try { 1422 result = Long.parseLong(nextOptionData()); 1423 } catch (NumberFormatException e) { 1424 Logger.err.println("** Error: " + opt + " is not a number"); 1425 throw e; 1426 } 1427 return result; 1428 } 1429 1430 /** 1431 * Return the next argument on the command line. 1432 * 1433 * @return Returns the argument string, or null if we have reached the end. 1434 */ nextArg()1435 private String nextArg() { 1436 if (mNextArg >= mArgs.length) { 1437 return null; 1438 } 1439 String arg = mArgs[mNextArg]; 1440 mNextArg++; 1441 return arg; 1442 } 1443 1444 /** 1445 * Print how to use this command. 1446 */ showUsage()1447 private void showUsage() { 1448 StringBuffer usage = new StringBuffer(); 1449 usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n"); 1450 usage.append(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n"); 1451 usage.append(" [--ignore-crashes] [--ignore-timeouts]\n"); 1452 usage.append(" [--ignore-security-exceptions]\n"); 1453 usage.append(" [--monitor-native-crashes] [--ignore-native-crashes]\n"); 1454 usage.append(" [--kill-process-after-error] [--hprof]\n"); 1455 usage.append(" [--match-description TEXT]\n"); 1456 usage.append(" [--pct-touch PERCENT] [--pct-motion PERCENT]\n"); 1457 usage.append(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n"); 1458 usage.append(" [--pct-nav PERCENT] [--pct-majornav PERCENT]\n"); 1459 usage.append(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n"); 1460 usage.append(" [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]\n"); 1461 usage.append(" [--pct-permission PERCENT]\n"); 1462 usage.append(" [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n"); 1463 usage.append(" [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n"); 1464 usage.append(" [--wait-dbg] [--dbg-no-events]\n"); 1465 usage.append(" [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n"); 1466 usage.append(" [--port port]\n"); 1467 usage.append(" [-s SEED] [-v [-v] ...]\n"); 1468 usage.append(" [--throttle MILLISEC] [--randomize-throttle]\n"); 1469 usage.append(" [--profile-wait MILLISEC]\n"); 1470 usage.append(" [--device-sleep-time MILLISEC]\n"); 1471 usage.append(" [--randomize-script]\n"); 1472 usage.append(" [--script-log]\n"); 1473 usage.append(" [--bugreport]\n"); 1474 usage.append(" [--periodic-bugreport]\n"); 1475 usage.append(" [--permission-target-system]\n"); 1476 usage.append(" COUNT\n"); 1477 Logger.err.println(usage.toString()); 1478 } 1479 } 1480