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