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