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