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