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