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