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
17package com.android.commands.monkey;
18
19import android.app.ActivityManagerNative;
20import android.app.IActivityController;
21import android.app.IActivityManager;
22import android.content.ComponentName;
23import android.content.Intent;
24import android.content.pm.IPackageManager;
25import android.content.pm.ResolveInfo;
26import android.os.Build;
27import android.os.Debug;
28import android.os.Environment;
29import android.os.Process;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.StrictMode;
33import android.os.SystemClock;
34import android.os.SystemProperties;
35import android.os.UserHandle;
36import android.view.IWindowManager;
37import android.view.Surface;
38
39import java.io.BufferedReader;
40import java.io.BufferedWriter;
41import java.io.ByteArrayInputStream;
42import java.io.DataInputStream;
43import java.io.File;
44import java.io.FileReader;
45import java.io.FileWriter;
46import java.io.IOException;
47import java.io.InputStream;
48import java.io.InputStreamReader;
49import java.io.Writer;
50import java.security.SecureRandom;
51import java.util.ArrayList;
52import java.util.HashSet;
53import java.util.Iterator;
54import java.util.List;
55import java.util.Random;
56
57/**
58 * Application that injects random key events and other actions into the system.
59 */
60public 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     */
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 {
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
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
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
332        public int appEarlyNotResponding(String processName, int pid, String annotation) {
333            return 0;
334        }
335
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     */
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     */
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     */
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     */
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                                .getLegacyExternalStorageDirectory(), 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
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.getLegacyExternalStorageDirectory(), "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.
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     */
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     */
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 (mSeed == 0) {
520            mSeed = System.currentTimeMillis() + System.identityHashCode(this);
521        }
522
523        if (mVerbose > 0) {
524            System.out.println(":Monkey: seed=" + mSeed + " count=" + mCount);
525            if (mValidPackages.size() > 0) {
526                Iterator<String> it = mValidPackages.iterator();
527                while (it.hasNext()) {
528                    System.out.println(":AllowPackage: " + it.next());
529                }
530            }
531            if (mInvalidPackages.size() > 0) {
532                Iterator<String> it = mInvalidPackages.iterator();
533                while (it.hasNext()) {
534                    System.out.println(":DisallowPackage: " + it.next());
535                }
536            }
537            if (mMainCategories.size() != 0) {
538                Iterator<String> it = mMainCategories.iterator();
539                while (it.hasNext()) {
540                    System.out.println(":IncludeCategory: " + it.next());
541                }
542            }
543        }
544
545        if (!checkInternalConfiguration()) {
546            return -2;
547        }
548
549        if (!getSystemInterfaces()) {
550            return -3;
551        }
552
553        if (!getMainApps()) {
554            return -4;
555        }
556
557        mRandom = new Random(mSeed);
558
559        if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
560            // script mode, ignore other options
561            mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
562                    mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
563            mEventSource.setVerbose(mVerbose);
564
565            mCountEvents = false;
566        } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
567            if (mSetupFileName != null) {
568                mEventSource = new MonkeySourceRandomScript(mSetupFileName,
569                        mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom,
570                        mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
571                mCount++;
572            } else {
573                mEventSource = new MonkeySourceRandomScript(mScriptFileNames,
574                        mThrottle, mRandomizeThrottle, mRandom,
575                        mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
576            }
577            mEventSource.setVerbose(mVerbose);
578            mCountEvents = false;
579        } else if (mServerPort != -1) {
580            try {
581                mEventSource = new MonkeySourceNetwork(mServerPort);
582            } catch (IOException e) {
583                System.out.println("Error binding to network socket.");
584                return -5;
585            }
586            mCount = Integer.MAX_VALUE;
587        } else {
588            // random source by default
589            if (mVerbose >= 2) { // check seeding performance
590                System.out.println("// Seeded: " + mSeed);
591            }
592            mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle);
593            mEventSource.setVerbose(mVerbose);
594            // set any of the factors that has been set
595            for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
596                if (mFactors[i] <= 0.0f) {
597                    ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
598                }
599            }
600
601            // in random mode, we start with a random activity
602            ((MonkeySourceRandom) mEventSource).generateActivity();
603        }
604
605        // validate source generator
606        if (!mEventSource.validate()) {
607            return -5;
608        }
609
610        // If we're profiling, do it immediately before/after the main monkey
611        // loop
612        if (mGenerateHprof) {
613            signalPersistentProcesses();
614        }
615
616        mNetworkMonitor.start();
617        int crashedAtCycle = 0;
618        try {
619            crashedAtCycle = runMonkeyCycles();
620        } finally {
621            // Release the rotation lock if it's still held and restore the
622            // original orientation.
623            new MonkeyRotationEvent(Surface.ROTATION_0, false).injectEvent(
624                mWm, mAm, mVerbose);
625        }
626        mNetworkMonitor.stop();
627
628        synchronized (this) {
629            if (mRequestAnrTraces) {
630                reportAnrTraces();
631                mRequestAnrTraces = false;
632            }
633            if (mRequestAnrBugreport){
634                System.out.println("Print the anr report");
635                getBugreport("anr_" + mReportProcessName + "_");
636                mRequestAnrBugreport = false;
637            }
638            if (mRequestAppCrashBugreport){
639                getBugreport("app_crash" + mReportProcessName + "_");
640                mRequestAppCrashBugreport = false;
641            }
642            if (mRequestDumpsysMemInfo) {
643                reportDumpsysMemInfo();
644                mRequestDumpsysMemInfo = false;
645            }
646            if (mRequestPeriodicBugreport){
647                getBugreport("Bugreport_");
648                mRequestPeriodicBugreport = false;
649            }
650        }
651
652        if (mGenerateHprof) {
653            signalPersistentProcesses();
654            if (mVerbose > 0) {
655                System.out.println("// Generated profiling reports in /data/misc");
656            }
657        }
658
659        try {
660            mAm.setActivityController(null);
661            mNetworkMonitor.unregister(mAm);
662        } catch (RemoteException e) {
663            // just in case this was latent (after mCount cycles), make sure
664            // we report it
665            if (crashedAtCycle >= mCount) {
666                crashedAtCycle = mCount - 1;
667            }
668        }
669
670        // report dropped event stats
671        if (mVerbose > 0) {
672            System.out.print(":Dropped: keys=");
673            System.out.print(mDroppedKeyEvents);
674            System.out.print(" pointers=");
675            System.out.print(mDroppedPointerEvents);
676            System.out.print(" trackballs=");
677            System.out.print(mDroppedTrackballEvents);
678            System.out.print(" flips=");
679            System.out.print(mDroppedFlipEvents);
680            System.out.print(" rotations=");
681            System.out.println(mDroppedRotationEvents);
682        }
683
684        // report network stats
685        mNetworkMonitor.dump();
686
687        if (crashedAtCycle < mCount - 1) {
688            System.err.println("** System appears to have crashed at event " + crashedAtCycle
689                    + " of " + mCount + " using seed " + mSeed);
690            return crashedAtCycle;
691        } else {
692            if (mVerbose > 0) {
693                System.out.println("// Monkey finished");
694            }
695            return 0;
696        }
697    }
698
699    /**
700     * Process the command-line options
701     *
702     * @return Returns true if options were parsed with no apparent errors.
703     */
704    private boolean processOptions() {
705        // quick (throwaway) check for unadorned command
706        if (mArgs.length < 1) {
707            showUsage();
708            return false;
709        }
710
711        try {
712            String opt;
713            while ((opt = nextOption()) != null) {
714                if (opt.equals("-s")) {
715                    mSeed = nextOptionLong("Seed");
716                } else if (opt.equals("-p")) {
717                    mValidPackages.add(nextOptionData());
718                } else if (opt.equals("-c")) {
719                    mMainCategories.add(nextOptionData());
720                } else if (opt.equals("-v")) {
721                    mVerbose += 1;
722                } else if (opt.equals("--ignore-crashes")) {
723                    mIgnoreCrashes = true;
724                } else if (opt.equals("--ignore-timeouts")) {
725                    mIgnoreTimeouts = true;
726                } else if (opt.equals("--ignore-security-exceptions")) {
727                    mIgnoreSecurityExceptions = true;
728                } else if (opt.equals("--monitor-native-crashes")) {
729                    mMonitorNativeCrashes = true;
730                } else if (opt.equals("--ignore-native-crashes")) {
731                    mIgnoreNativeCrashes = true;
732                } else if (opt.equals("--kill-process-after-error")) {
733                    mKillProcessAfterError = true;
734                } else if (opt.equals("--hprof")) {
735                    mGenerateHprof = true;
736                } else if (opt.equals("--pct-touch")) {
737                    int i = MonkeySourceRandom.FACTOR_TOUCH;
738                    mFactors[i] = -nextOptionLong("touch events percentage");
739                } else if (opt.equals("--pct-motion")) {
740                    int i = MonkeySourceRandom.FACTOR_MOTION;
741                    mFactors[i] = -nextOptionLong("motion events percentage");
742                } else if (opt.equals("--pct-trackball")) {
743                    int i = MonkeySourceRandom.FACTOR_TRACKBALL;
744                    mFactors[i] = -nextOptionLong("trackball events percentage");
745                } else if (opt.equals("--pct-rotation")) {
746                    int i = MonkeySourceRandom.FACTOR_ROTATION;
747                    mFactors[i] = -nextOptionLong("screen rotation events percentage");
748                } else if (opt.equals("--pct-syskeys")) {
749                    int i = MonkeySourceRandom.FACTOR_SYSOPS;
750                    mFactors[i] = -nextOptionLong("system (key) operations percentage");
751                } else if (opt.equals("--pct-nav")) {
752                    int i = MonkeySourceRandom.FACTOR_NAV;
753                    mFactors[i] = -nextOptionLong("nav events percentage");
754                } else if (opt.equals("--pct-majornav")) {
755                    int i = MonkeySourceRandom.FACTOR_MAJORNAV;
756                    mFactors[i] = -nextOptionLong("major nav events percentage");
757                } else if (opt.equals("--pct-appswitch")) {
758                    int i = MonkeySourceRandom.FACTOR_APPSWITCH;
759                    mFactors[i] = -nextOptionLong("app switch events percentage");
760                } else if (opt.equals("--pct-flip")) {
761                    int i = MonkeySourceRandom.FACTOR_FLIP;
762                    mFactors[i] = -nextOptionLong("keyboard flip percentage");
763                } else if (opt.equals("--pct-anyevent")) {
764                    int i = MonkeySourceRandom.FACTOR_ANYTHING;
765                    mFactors[i] = -nextOptionLong("any events percentage");
766                } else if (opt.equals("--pct-pinchzoom")) {
767                    int i = MonkeySourceRandom.FACTOR_PINCHZOOM;
768                    mFactors[i] = -nextOptionLong("pinch zoom events percentage");
769                } else if (opt.equals("--pkg-blacklist-file")) {
770                    mPkgBlacklistFile = nextOptionData();
771                } else if (opt.equals("--pkg-whitelist-file")) {
772                    mPkgWhitelistFile = nextOptionData();
773                } else if (opt.equals("--throttle")) {
774                    mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
775                } else if (opt.equals("--randomize-throttle")) {
776                    mRandomizeThrottle = true;
777                } else if (opt.equals("--wait-dbg")) {
778                    // do nothing - it's caught at the very start of run()
779                } else if (opt.equals("--dbg-no-events")) {
780                    mSendNoEvents = true;
781                } else if (opt.equals("--port")) {
782                    mServerPort = (int) nextOptionLong("Server port to listen on for commands");
783                } else if (opt.equals("--setup")) {
784                    mSetupFileName = nextOptionData();
785                } else if (opt.equals("-f")) {
786                    mScriptFileNames.add(nextOptionData());
787                } else if (opt.equals("--profile-wait")) {
788                    mProfileWaitTime = nextOptionLong("Profile delay" +
789                                " (in milliseconds) to wait between user action");
790                } else if (opt.equals("--device-sleep-time")) {
791                    mDeviceSleepTime = nextOptionLong("Device sleep time" +
792                                                      "(in milliseconds)");
793                } else if (opt.equals("--randomize-script")) {
794                    mRandomizeScript = true;
795                } else if (opt.equals("--script-log")) {
796                    mScriptLog = true;
797                } else if (opt.equals("--bugreport")) {
798                    mRequestBugreport = true;
799                } else if (opt.equals("--periodic-bugreport")){
800                    mGetPeriodicBugreport = true;
801                    mBugreportFrequency = nextOptionLong("Number of iterations");
802                } else if (opt.equals("-h")) {
803                    showUsage();
804                    return false;
805                } else {
806                    System.err.println("** Error: Unknown option: " + opt);
807                    showUsage();
808                    return false;
809                }
810            }
811        } catch (RuntimeException ex) {
812            System.err.println("** Error: " + ex.toString());
813            showUsage();
814            return false;
815        }
816
817        // If a server port hasn't been specified, we need to specify
818        // a count
819        if (mServerPort == -1) {
820            String countStr = nextArg();
821            if (countStr == null) {
822                System.err.println("** Error: Count not specified");
823                showUsage();
824                return false;
825            }
826
827            try {
828                mCount = Integer.parseInt(countStr);
829            } catch (NumberFormatException e) {
830                System.err.println("** Error: Count is not a number");
831                showUsage();
832                return false;
833            }
834        }
835
836        return true;
837    }
838
839    /**
840     * Load a list of package names from a file.
841     *
842     * @param fileName The file name, with package names separated by new line.
843     * @param list The destination list.
844     * @return Returns false if any error occurs.
845     */
846    private static boolean loadPackageListFromFile(String fileName, HashSet<String> list) {
847        BufferedReader reader = null;
848        try {
849            reader = new BufferedReader(new FileReader(fileName));
850            String s;
851            while ((s = reader.readLine()) != null) {
852                s = s.trim();
853                if ((s.length() > 0) && (!s.startsWith("#"))) {
854                    list.add(s);
855                }
856            }
857        } catch (IOException ioe) {
858            System.err.println(ioe);
859            return false;
860        } finally {
861            if (reader != null) {
862                try {
863                    reader.close();
864                } catch (IOException ioe) {
865                    System.err.println(ioe);
866                }
867            }
868        }
869        return true;
870    }
871
872    /**
873     * Load package blacklist or whitelist (if specified).
874     *
875     * @return Returns false if any error occurs.
876     */
877    private boolean loadPackageLists() {
878        if (((mPkgWhitelistFile != null) || (mValidPackages.size() > 0))
879                && (mPkgBlacklistFile != null)) {
880            System.err.println("** Error: you can not specify a package blacklist "
881                    + "together with a whitelist or individual packages (via -p).");
882            return false;
883        }
884        if ((mPkgWhitelistFile != null)
885                && (!loadPackageListFromFile(mPkgWhitelistFile, mValidPackages))) {
886            return false;
887        }
888        if ((mPkgBlacklistFile != null)
889                && (!loadPackageListFromFile(mPkgBlacklistFile, mInvalidPackages))) {
890            return false;
891        }
892        return true;
893    }
894
895    /**
896     * Check for any internal configuration (primarily build-time) errors.
897     *
898     * @return Returns true if ready to rock.
899     */
900    private boolean checkInternalConfiguration() {
901        return true;
902    }
903
904    /**
905     * Attach to the required system interfaces.
906     *
907     * @return Returns true if all system interfaces were available.
908     */
909    private boolean getSystemInterfaces() {
910        mAm = ActivityManagerNative.getDefault();
911        if (mAm == null) {
912            System.err.println("** Error: Unable to connect to activity manager; is the system "
913                    + "running?");
914            return false;
915        }
916
917        mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
918        if (mWm == null) {
919            System.err.println("** Error: Unable to connect to window manager; is the system "
920                    + "running?");
921            return false;
922        }
923
924        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
925        if (mPm == null) {
926            System.err.println("** Error: Unable to connect to package manager; is the system "
927                    + "running?");
928            return false;
929        }
930
931        try {
932            mAm.setActivityController(new ActivityController());
933            mNetworkMonitor.register(mAm);
934        } catch (RemoteException e) {
935            System.err.println("** Failed talking with activity manager!");
936            return false;
937        }
938
939        return true;
940    }
941
942    /**
943     * Using the restrictions provided (categories & packages), generate a list
944     * of activities that we can actually switch to.
945     *
946     * @return Returns true if it could successfully build a list of target
947     *         activities
948     */
949    private boolean getMainApps() {
950        try {
951            final int N = mMainCategories.size();
952            for (int i = 0; i < N; i++) {
953                Intent intent = new Intent(Intent.ACTION_MAIN);
954                String category = mMainCategories.get(i);
955                if (category.length() > 0) {
956                    intent.addCategory(category);
957                }
958                List<ResolveInfo> mainApps = mPm.queryIntentActivities(intent, null, 0,
959                        UserHandle.myUserId());
960                if (mainApps == null || mainApps.size() == 0) {
961                    System.err.println("// Warning: no activities found for category " + category);
962                    continue;
963                }
964                if (mVerbose >= 2) { // very verbose
965                    System.out.println("// Selecting main activities from category " + category);
966                }
967                final int NA = mainApps.size();
968                for (int a = 0; a < NA; a++) {
969                    ResolveInfo r = mainApps.get(a);
970                    String packageName = r.activityInfo.applicationInfo.packageName;
971                    if (checkEnteringPackage(packageName)) {
972                        if (mVerbose >= 2) { // very verbose
973                            System.out.println("//   + Using main activity " + r.activityInfo.name
974                                    + " (from package " + packageName + ")");
975                        }
976                        mMainApps.add(new ComponentName(packageName, r.activityInfo.name));
977                    } else {
978                        if (mVerbose >= 3) { // very very verbose
979                            System.out.println("//   - NOT USING main activity "
980                                    + r.activityInfo.name + " (from package " + packageName + ")");
981                        }
982                    }
983                }
984            }
985        } catch (RemoteException e) {
986            System.err.println("** Failed talking with package manager!");
987            return false;
988        }
989
990        if (mMainApps.size() == 0) {
991            System.out.println("** No activities found to run, monkey aborted.");
992            return false;
993        }
994
995        return true;
996    }
997
998    /**
999     * Run mCount cycles and see if we hit any crashers.
1000     * <p>
1001     * TODO: Meta state on keys
1002     *
1003     * @return Returns the last cycle which executed. If the value == mCount, no
1004     *         errors detected.
1005     */
1006    private int runMonkeyCycles() {
1007        int eventCounter = 0;
1008        int cycleCounter = 0;
1009
1010        boolean shouldReportAnrTraces = false;
1011        boolean shouldReportDumpsysMemInfo = false;
1012        boolean shouldAbort = false;
1013        boolean systemCrashed = false;
1014
1015        // TO DO : The count should apply to each of the script file.
1016        while (!systemCrashed && cycleCounter < mCount) {
1017            synchronized (this) {
1018                if (mRequestProcRank) {
1019                    reportProcRank();
1020                    mRequestProcRank = false;
1021                }
1022                if (mRequestAnrTraces) {
1023                    mRequestAnrTraces = false;
1024                    shouldReportAnrTraces = true;
1025                }
1026                if (mRequestAnrBugreport){
1027                    getBugreport("anr_" + mReportProcessName + "_");
1028                    mRequestAnrBugreport = false;
1029                }
1030                if (mRequestAppCrashBugreport){
1031                    getBugreport("app_crash" + mReportProcessName + "_");
1032                    mRequestAppCrashBugreport = false;
1033                }
1034                if (mRequestPeriodicBugreport){
1035                    getBugreport("Bugreport_");
1036                    mRequestPeriodicBugreport = false;
1037                }
1038                if (mRequestDumpsysMemInfo) {
1039                    mRequestDumpsysMemInfo = false;
1040                    shouldReportDumpsysMemInfo = true;
1041                }
1042                if (mMonitorNativeCrashes) {
1043                    // first time through, when eventCounter == 0, just set up
1044                    // the watcher (ignore the error)
1045                    if (checkNativeCrashes() && (eventCounter > 0)) {
1046                        System.out.println("** New native crash detected.");
1047                        if (mRequestBugreport) {
1048                            getBugreport("native_crash_");
1049                        }
1050                        mAbort = mAbort || !mIgnoreNativeCrashes || mKillProcessAfterError;
1051                    }
1052                }
1053                if (mAbort) {
1054                    shouldAbort = true;
1055                }
1056            }
1057
1058            // Report ANR, dumpsys after releasing lock on this.
1059            // This ensures the availability of the lock to Activity controller's appNotResponding
1060            if (shouldReportAnrTraces) {
1061               shouldReportAnrTraces = false;
1062               reportAnrTraces();
1063            }
1064
1065            if (shouldReportDumpsysMemInfo) {
1066               shouldReportDumpsysMemInfo = false;
1067               reportDumpsysMemInfo();
1068            }
1069
1070            if (shouldAbort) {
1071               shouldAbort = false;
1072               System.out.println("** Monkey aborted due to error.");
1073               System.out.println("Events injected: " + eventCounter);
1074               return eventCounter;
1075            }
1076
1077            // In this debugging mode, we never send any events. This is
1078            // primarily here so you can manually test the package or category
1079            // limits, while manually exercising the system.
1080            if (mSendNoEvents) {
1081                eventCounter++;
1082                cycleCounter++;
1083                continue;
1084            }
1085
1086            if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) {
1087                String calendarTime = MonkeyUtils.toCalendarTime(System.currentTimeMillis());
1088                long systemUpTime = SystemClock.elapsedRealtime();
1089                System.out.println("    //[calendar_time:" + calendarTime + " system_uptime:"
1090                                   + systemUpTime + "]");
1091                System.out.println("    // Sending event #" + eventCounter);
1092            }
1093
1094            MonkeyEvent ev = mEventSource.getNextEvent();
1095            if (ev != null) {
1096                int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
1097                if (injectCode == MonkeyEvent.INJECT_FAIL) {
1098                    if (ev instanceof MonkeyKeyEvent) {
1099                        mDroppedKeyEvents++;
1100                    } else if (ev instanceof MonkeyMotionEvent) {
1101                        mDroppedPointerEvents++;
1102                    } else if (ev instanceof MonkeyFlipEvent) {
1103                        mDroppedFlipEvents++;
1104                    } else if (ev instanceof MonkeyRotationEvent) {
1105                        mDroppedRotationEvents++;
1106                    }
1107                } else if (injectCode == MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION) {
1108                    systemCrashed = true;
1109                    System.err.println("** Error: RemoteException while injecting event.");
1110                } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
1111                    systemCrashed = !mIgnoreSecurityExceptions;
1112                    if (systemCrashed) {
1113                        System.err.println("** Error: SecurityException while injecting event.");
1114                    }
1115                }
1116
1117                // Don't count throttling as an event.
1118                if (!(ev instanceof MonkeyThrottleEvent)) {
1119                    eventCounter++;
1120                    if (mCountEvents) {
1121                        cycleCounter++;
1122                    }
1123                }
1124            } else {
1125                if (!mCountEvents) {
1126                    cycleCounter++;
1127                    writeScriptLog(cycleCounter);
1128                    //Capture the bugreport after n iteration
1129                    if (mGetPeriodicBugreport) {
1130                        if ((cycleCounter % mBugreportFrequency) == 0) {
1131                            mRequestPeriodicBugreport = true;
1132                        }
1133                    }
1134                } else {
1135                    // Event Source has signaled that we have no more events to process
1136                    break;
1137                }
1138            }
1139        }
1140        System.out.println("Events injected: " + eventCounter);
1141        return eventCounter;
1142    }
1143
1144    /**
1145     * Send SIGNAL_USR1 to all processes. This will generate large (5mb)
1146     * profiling reports in data/misc, so use with care.
1147     */
1148    private void signalPersistentProcesses() {
1149        try {
1150            mAm.signalPersistentProcesses(Process.SIGNAL_USR1);
1151
1152            synchronized (this) {
1153                wait(2000);
1154            }
1155        } catch (RemoteException e) {
1156            System.err.println("** Failed talking with activity manager!");
1157        } catch (InterruptedException e) {
1158        }
1159    }
1160
1161    /**
1162     * Watch for appearance of new tombstone files, which indicate native
1163     * crashes.
1164     *
1165     * @return Returns true if new files have appeared in the list
1166     */
1167    private boolean checkNativeCrashes() {
1168        String[] tombstones = TOMBSTONES_PATH.list();
1169
1170        // shortcut path for usually empty directory, so we don't waste even
1171        // more objects
1172        if ((tombstones == null) || (tombstones.length == 0)) {
1173            mTombstones = null;
1174            return false;
1175        }
1176
1177        // use set logic to look for new files
1178        HashSet<String> newStones = new HashSet<String>();
1179        for (String x : tombstones) {
1180            newStones.add(x);
1181        }
1182
1183        boolean result = (mTombstones == null) || !mTombstones.containsAll(newStones);
1184
1185        // keep the new list for the next time
1186        mTombstones = newStones;
1187
1188        return result;
1189    }
1190
1191    /**
1192     * Return the next command line option. This has a number of special cases
1193     * which closely, but not exactly, follow the POSIX command line options
1194     * patterns:
1195     *
1196     * <pre>
1197     * -- means to stop processing additional options
1198     * -z means option z
1199     * -z ARGS means option z with (non-optional) arguments ARGS
1200     * -zARGS means option z with (optional) arguments ARGS
1201     * --zz means option zz
1202     * --zz ARGS means option zz with (non-optional) arguments ARGS
1203     * </pre>
1204     *
1205     * Note that you cannot combine single letter options; -abc != -a -b -c
1206     *
1207     * @return Returns the option string, or null if there are no more options.
1208     */
1209    private String nextOption() {
1210        if (mNextArg >= mArgs.length) {
1211            return null;
1212        }
1213        String arg = mArgs[mNextArg];
1214        if (!arg.startsWith("-")) {
1215            return null;
1216        }
1217        mNextArg++;
1218        if (arg.equals("--")) {
1219            return null;
1220        }
1221        if (arg.length() > 1 && arg.charAt(1) != '-') {
1222            if (arg.length() > 2) {
1223                mCurArgData = arg.substring(2);
1224                return arg.substring(0, 2);
1225            } else {
1226                mCurArgData = null;
1227                return arg;
1228            }
1229        }
1230        mCurArgData = null;
1231        return arg;
1232    }
1233
1234    /**
1235     * Return the next data associated with the current option.
1236     *
1237     * @return Returns the data string, or null of there are no more arguments.
1238     */
1239    private String nextOptionData() {
1240        if (mCurArgData != null) {
1241            return mCurArgData;
1242        }
1243        if (mNextArg >= mArgs.length) {
1244            return null;
1245        }
1246        String data = mArgs[mNextArg];
1247        mNextArg++;
1248        return data;
1249    }
1250
1251    /**
1252     * Returns a long converted from the next data argument, with error handling
1253     * if not available.
1254     *
1255     * @param opt The name of the option.
1256     * @return Returns a long converted from the argument.
1257     */
1258    private long nextOptionLong(final String opt) {
1259        long result;
1260        try {
1261            result = Long.parseLong(nextOptionData());
1262        } catch (NumberFormatException e) {
1263            System.err.println("** Error: " + opt + " is not a number");
1264            throw e;
1265        }
1266        return result;
1267    }
1268
1269    /**
1270     * Return the next argument on the command line.
1271     *
1272     * @return Returns the argument string, or null if we have reached the end.
1273     */
1274    private String nextArg() {
1275        if (mNextArg >= mArgs.length) {
1276            return null;
1277        }
1278        String arg = mArgs[mNextArg];
1279        mNextArg++;
1280        return arg;
1281    }
1282
1283    /**
1284     * Print how to use this command.
1285     */
1286    private void showUsage() {
1287        StringBuffer usage = new StringBuffer();
1288        usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n");
1289        usage.append("              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n");
1290        usage.append("              [--ignore-crashes] [--ignore-timeouts]\n");
1291        usage.append("              [--ignore-security-exceptions]\n");
1292        usage.append("              [--monitor-native-crashes] [--ignore-native-crashes]\n");
1293        usage.append("              [--kill-process-after-error] [--hprof]\n");
1294        usage.append("              [--pct-touch PERCENT] [--pct-motion PERCENT]\n");
1295        usage.append("              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n");
1296        usage.append("              [--pct-nav PERCENT] [--pct-majornav PERCENT]\n");
1297        usage.append("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n");
1298        usage.append("              [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]\n");
1299        usage.append("              [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n");
1300        usage.append("              [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n");
1301        usage.append("              [--wait-dbg] [--dbg-no-events]\n");
1302        usage.append("              [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n");
1303        usage.append("              [--port port]\n");
1304        usage.append("              [-s SEED] [-v [-v] ...]\n");
1305        usage.append("              [--throttle MILLISEC] [--randomize-throttle]\n");
1306        usage.append("              [--profile-wait MILLISEC]\n");
1307        usage.append("              [--device-sleep-time MILLISEC]\n");
1308        usage.append("              [--randomize-script]\n");
1309        usage.append("              [--script-log]\n");
1310        usage.append("              [--bugreport]\n");
1311        usage.append("              [--periodic-bugreport]\n");
1312        usage.append("              COUNT\n");
1313        System.err.println(usage.toString());
1314    }
1315}
1316