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