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