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