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