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