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