Fix how the monkey counts events for scripts.

Instead of the monkey counting up to the count times the number of events in the
script, the monkey will now only increment the count when the script reaches the
end of the file.  This allows us to have multiple script files with differing
event counts in each.
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index 521de16..838f274 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -1,19 +1,18 @@
-/**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package com.android.commands.monkey;
 
@@ -50,19 +49,25 @@
 
     /**
      * Monkey Debugging/Dev Support
-     *
+     * <p>
      * All values should be zero when checking in.
      */
     private final static int DEBUG_ALLOW_ANY_STARTS = 0;
+
     private final static int DEBUG_ALLOW_ANY_RESTARTS = 0;
+
     private IActivityManager mAm;
+
     private IWindowManager mWm;
+
     private IPackageManager mPm;
 
     /** Command line arguments */
     private String[] mArgs;
+
     /** Current argument being parsed */
     private int mNextArg;
+
     /** Data of current argument */
     private String mCurArgData;
 
@@ -82,16 +87,28 @@
     /** Monitor /data/tombstones and stop the monkey if new files appear. */
     private boolean mMonitorNativeCrashes;
 
-    /** Send no events.  Use with long throttle-time to watch user operations */
+    /** Send no events. Use with long throttle-time to watch user operations */
     private boolean mSendNoEvents;
 
     /** This is set when we would like to abort the running of the monkey. */
     private boolean mAbort;
 
-    /** This is set by the ActivityController thread to request collection of ANR trace files */
+    /**
+     * Count each event as a cycle. Set to false for scripts so that each time
+     * through the script increments the count.
+     */
+    private boolean mCountEvents = true;
+
+    /**
+     * This is set by the ActivityController thread to request collection of ANR
+     * trace files
+     */
     private boolean mRequestAnrTraces = false;
 
-    /** This is set by the ActivityController thread to request a "dumpsys meminfo" */
+    /**
+     * This is set by the ActivityController thread to request a
+     * "dumpsys meminfo"
+     */
     private boolean mRequestDumpsysMemInfo = false;
 
     /** Kill the process after a timeout or crash. */
@@ -102,8 +119,10 @@
 
     /** Packages we are allowed to run, or empty if no restriction. */
     private HashSet<String> mValidPackages = new HashSet<String>();
+
     /** Categories we are allowed to launch **/
-    ArrayList<String> mMainCategories = new ArrayList<String>();
+    private ArrayList<String> mMainCategories = new ArrayList<String>();
+
     /** Applications we can switch to. */
     private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>();
 
@@ -118,8 +137,11 @@
 
     /** Dropped-event statistics **/
     long mDroppedKeyEvents = 0;
+
     long mDroppedPointerEvents = 0;
+
     long mDroppedTrackballEvents = 0;
+
     long mDroppedFlipEvents = 0;
 
     /** a filename to the script (if any) **/
@@ -129,14 +151,18 @@
     private int mServerPort = -1;
 
     private static final File TOMBSTONES_PATH = new File("/data/tombstones");
+
     private HashSet<String> mTombstones = null;
 
     float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
+
     MonkeyEventSource mEventSource;
+
     private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
 
     // information on the current activity.
     public static Intent currentIntent;
+
     public static String currentPackage;
 
     /**
@@ -146,8 +172,8 @@
         public boolean activityStarting(Intent intent, String pkg) {
             boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0);
             if (mVerbose > 0) {
-                System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
-                        + " start of " + intent + " in package " + pkg);
+                System.out.println("    // " + (allow ? "Allowing" : "Rejecting") + " start of "
+                        + intent + " in package " + pkg);
             }
             currentPackage = pkg;
             currentIntent = intent;
@@ -160,7 +186,7 @@
             if (!allow) {
                 if (mVerbose > 0) {
                     System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
-                                       + " resume of package " + pkg);
+                            + " resume of package " + pkg);
                 }
             }
             currentPackage = pkg;
@@ -171,7 +197,8 @@
             if (pkg == null) {
                 return true;
             }
-            // preflight the hash lookup to avoid the cost of hash key generation
+            // preflight the hash lookup to avoid the cost of hash key
+            // generation
             if (mValidPackages.size() == 0) {
                 return true;
             } else {
@@ -179,26 +206,22 @@
             }
         }
 
-        public boolean appCrashed(String processName, int pid, String shortMsg,
-                String longMsg, byte[] crashData) {
-            System.err.println("// CRASH: " + processName + " (pid " + pid
-                    + ")");
+        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
+                byte[] crashData) {
+            System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
             System.err.println("// Short Msg: " + shortMsg);
             System.err.println("// Long Msg: " + longMsg);
             if (crashData != null) {
                 try {
-                    CrashData cd = new CrashData(new DataInputStream(
-                            new ByteArrayInputStream(crashData)));
-                    System.err.println("// Build Label: "
-                            + cd.getBuildData().getFingerprint());
+                    CrashData cd = new CrashData(new DataInputStream(new ByteArrayInputStream(
+                            crashData)));
+                    System.err.println("// Build Label: " + cd.getBuildData().getFingerprint());
                     System.err.println("// Build Changelist: "
                             + cd.getBuildData().getIncrementalVersion());
-                    System.err.println("// Build Time: "
-                            + cd.getBuildData().getTime());
+                    System.err.println("// Build Time: " + cd.getBuildData().getTime());
                     System.err.println("// ID: " + cd.getId());
                     System.err.println("// Tag: " + cd.getActivity());
-                    System.err.println(cd.getThrowableData().toString(
-                            "// "));
+                    System.err.println(cd.getThrowableData().toString("// "));
                 } catch (IOException e) {
                     System.err.println("// BAD STACK CRAWL");
                 }
@@ -214,10 +237,8 @@
             return false;
         }
 
-        public int appNotResponding(String processName, int pid,
-                String processStats) {
-            System.err.println("// NOT RESPONDING: " + processName
-                    + " (pid " + pid + ")");
+        public int appNotResponding(String processName, int pid, String processStats) {
+            System.err.println("// NOT RESPONDING: " + processName + " (pid " + pid + ")");
             System.err.println(processStats);
             reportProcRank();
             synchronized (Monkey.this) {
@@ -235,15 +256,16 @@
     }
 
     /**
-     * Run the procrank tool to insert system status information into the debug report.
+     * Run the procrank tool to insert system status information into the debug
+     * report.
      */
     private void reportProcRank() {
-      commandLineReport("procrank", "procrank");
+        commandLineReport("procrank", "procrank");
     }
 
     /**
-     * Run "cat /data/anr/traces.txt".  Wait about 5 seconds first, to let the asynchronous
-     * report writing complete.
+     * Run "cat /data/anr/traces.txt". Wait about 5 seconds first, to let the
+     * asynchronous report writing complete.
      */
     private void reportAnrTraces() {
         try {
@@ -255,9 +277,10 @@
 
     /**
      * Run "dumpsys meminfo"
-     *
-     * NOTE:  You cannot perform a dumpsys call from the ActivityController callback, as it will
-     * deadlock.  This should only be called from the main loop of the monkey.
+     * <p>
+     * NOTE: You cannot perform a dumpsys call from the ActivityController
+     * callback, as it will deadlock. This should only be called from the main
+     * loop of the monkey.
      */
     private void reportDumpsysMemInfo() {
         commandLineReport("meminfo", "dumpsys meminfo");
@@ -265,16 +288,20 @@
 
     /**
      * Print report from a single command line.
-     * @param reportName Simple tag that will print before the report and in various annotations.
+     * <p>
+     * TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both
+     * streams (might be important for some command lines)
+     *
+     * @param reportName Simple tag that will print before the report and in
+     *            various annotations.
      * @param command Command line to execute.
-     * TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both streams (might be
-     * important for some command lines)
      */
     private void commandLineReport(String reportName, String command) {
         System.err.println(reportName + ":");
         Runtime rt = Runtime.getRuntime();
         try {
-            // Process must be fully qualified here because android.os.Process is used elsewhere
+            // Process must be fully qualified here because android.os.Process
+            // is used elsewhere
             java.lang.Process p = Runtime.getRuntime().exec(command);
 
             // pipe everything from process stdout -> System.err
@@ -308,7 +335,7 @@
      * Run the command!
      *
      * @param args The command-line arguments
-     * @return Returns a posix-style result code.  0 for no error.
+     * @return Returns a posix-style result code. 0 for no error.
      */
     private int run(String[] args) {
         // Super-early debugger wait
@@ -328,7 +355,7 @@
         mArgs = args;
         mNextArg = 0;
 
-        //set a positive value, indicating none of the factors is provided yet
+        // set a positive value, indicating none of the factors is provided yet
         for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
             mFactors[i] = 1.0f;
         }
@@ -375,6 +402,8 @@
             // script mode, ignore other options
             mEventSource = new MonkeySourceScript(mScriptFileName, mThrottle);
             mEventSource.setVerbose(mVerbose);
+
+            mCountEvents = false;
         } else if (mServerPort != -1) {
             try {
                 mEventSource = new MonkeySourceNetwork(mServerPort);
@@ -385,37 +414,29 @@
             mCount = Integer.MAX_VALUE;
         } else {
             // random source by default
-            if (mVerbose >= 2) {    // check seeding performance
+            if (mVerbose >= 2) { // check seeding performance
                 System.out.println("// Seeded: " + mSeed);
             }
             mEventSource = new MonkeySourceRandom(mSeed, mMainApps, mThrottle);
             mEventSource.setVerbose(mVerbose);
-            //set any of the factors that has been set
+            // set any of the factors that has been set
             for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
                 if (mFactors[i] <= 0.0f) {
                     ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
                 }
             }
 
-            //in random mode, we start with a random activity
+            // in random mode, we start with a random activity
             ((MonkeySourceRandom) mEventSource).generateActivity();
         }
 
-        //validate source generator
+        // validate source generator
         if (!mEventSource.validate()) {
             return -5;
         }
 
-        if (mScriptFileName != null) {
-            // in random mode, count is the number of single events
-            // while in script mode, count is the number of repetition
-            // for a sequence of events, so we need do multiply the length of
-            // that sequence
-            mCount = mCount * ((MonkeySourceScript) mEventSource)
-                .getOneRoundEventCount();
-        }
-
-        // If we're profiling, do it immediately before/after the main monkey loop
+        // If we're profiling, do it immediately before/after the main monkey
+        // loop
         if (mGenerateHprof) {
             signalPersistentProcesses();
         }
@@ -469,9 +490,9 @@
         mNetworkMonitor.dump();
 
         if (crashedAtCycle < mCount - 1) {
-            System.err.println("** System appears to have crashed at event "
-                    + crashedAtCycle + " of " + mCount + " using seed " + mSeed);
-           return crashedAtCycle;
+            System.err.println("** System appears to have crashed at event " + crashedAtCycle
+                    + " of " + mCount + " using seed " + mSeed);
+            return crashedAtCycle;
         } else {
             if (mVerbose > 0) {
                 System.out.println("// Monkey finished");
@@ -516,29 +537,29 @@
                 } else if (opt.equals("--hprof")) {
                     mGenerateHprof = true;
                 } else if (opt.equals("--pct-touch")) {
-                    mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
-                        -nextOptionLong("touch events percentage");
+                    int i = MonkeySourceRandom.FACTOR_TOUCH;
+                    mFactors[i] = -nextOptionLong("touch events percentage");
                 } else if (opt.equals("--pct-motion")) {
-                    mFactors[MonkeySourceRandom.FACTOR_MOTION] =
-                        -nextOptionLong("motion events percentage");
+                    int i = MonkeySourceRandom.FACTOR_MOTION;
+                    mFactors[i] = -nextOptionLong("motion events percentage");
                 } else if (opt.equals("--pct-trackball")) {
-                    mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
-                        -nextOptionLong("trackball events percentage");
+                    int i = MonkeySourceRandom.FACTOR_TRACKBALL;
+                    mFactors[i] = -nextOptionLong("trackball events percentage");
                 } else if (opt.equals("--pct-nav")) {
-                    mFactors[MonkeySourceRandom.FACTOR_NAV] =
-                        -nextOptionLong("nav events percentage");
+                    int i = MonkeySourceRandom.FACTOR_NAV;
+                    mFactors[i] = -nextOptionLong("nav events percentage");
                 } else if (opt.equals("--pct-majornav")) {
-                    mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
-                        -nextOptionLong("major nav events percentage");
+                    int i = MonkeySourceRandom.FACTOR_MAJORNAV;
+                    mFactors[i] = -nextOptionLong("major nav events percentage");
                 } else if (opt.equals("--pct-appswitch")) {
-                    mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
-                        -nextOptionLong("app switch events percentage");
+                    int i = MonkeySourceRandom.FACTOR_APPSWITCH;
+                    mFactors[i] = -nextOptionLong("app switch events percentage");
                 } else if (opt.equals("--pct-flip")) {
-                    mFactors[MonkeySourceRandom.FACTOR_FLIP] =
-                        -nextOptionLong("keyboard flip percentage");
+                    int i = MonkeySourceRandom.FACTOR_FLIP;
+                    mFactors[i] = -nextOptionLong("keyboard flip percentage");
                 } else if (opt.equals("--pct-anyevent")) {
-                    mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
-                        -nextOptionLong("any events percentage");
+                    int i = MonkeySourceRandom.FACTOR_ANYTHING;
+                    mFactors[i] = -nextOptionLong("any events percentage");
                 } else if (opt.equals("--throttle")) {
                     mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
                 } else if (opt.equals("--wait-dbg")) {
@@ -615,19 +636,22 @@
     private boolean getSystemInterfaces() {
         mAm = ActivityManagerNative.getDefault();
         if (mAm == null) {
-            System.err.println("** Error: Unable to connect to activity manager; is the system running?");
+            System.err.println("** Error: Unable to connect to activity manager; is the system "
+                    + "running?");
             return false;
         }
 
         mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
         if (mWm == null) {
-            System.err.println("** Error: Unable to connect to window manager; is the system running?");
+            System.err.println("** Error: Unable to connect to window manager; is the system "
+                    + "running?");
             return false;
         }
 
         mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
         if (mPm == null) {
-            System.err.println("** Error: Unable to connect to package manager; is the system running?");
+            System.err.println("** Error: Unable to connect to package manager; is the system "
+                    + "running?");
             return false;
         }
 
@@ -643,15 +667,16 @@
     }
 
     /**
-     * Using the restrictions provided (categories & packages), generate a list of activities
-     * that we can actually switch to.
+     * Using the restrictions provided (categories & packages), generate a list
+     * of activities that we can actually switch to.
      *
-     * @return Returns true if it could successfully build a list of target activities
+     * @return Returns true if it could successfully build a list of target
+     *         activities
      */
     private boolean getMainApps() {
         try {
             final int N = mMainCategories.size();
-            for (int i = 0; i< N; i++) {
+            for (int i = 0; i < N; i++) {
                 Intent intent = new Intent(Intent.ACTION_MAIN);
                 String category = mMainCategories.get(i);
                 if (category.length() > 0) {
@@ -662,31 +687,26 @@
                     System.err.println("// Warning: no activities found for category " + category);
                     continue;
                 }
-                if (mVerbose >= 2) {     // very verbose
+                if (mVerbose >= 2) { // very verbose
                     System.out.println("// Selecting main activities from category " + category);
                 }
                 final int NA = mainApps.size();
                 for (int a = 0; a < NA; a++) {
                     ResolveInfo r = mainApps.get(a);
-                    if (mValidPackages.size() == 0 ||
-                            mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
-                        if (mVerbose >= 2) {     // very verbose
-                            System.out.println("//   + Using main activity "
-                                    + r.activityInfo.name
+                    if (mValidPackages.size() == 0
+                            || mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
+                        if (mVerbose >= 2) { // very verbose
+                            System.out.println("//   + Using main activity " + r.activityInfo.name
                                     + " (from package "
-                                    + r.activityInfo.applicationInfo.packageName
-                                    + ")");
+                                    + r.activityInfo.applicationInfo.packageName + ")");
                         }
-                        mMainApps.add(new ComponentName(
-                                r.activityInfo.applicationInfo.packageName,
+                        mMainApps.add(new ComponentName(r.activityInfo.applicationInfo.packageName,
                                 r.activityInfo.name));
                     } else {
-                        if (mVerbose >= 3) {     // very very verbose
+                        if (mVerbose >= 3) { // very very verbose
                             System.out.println("//   - NOT USING main activity "
-                                    + r.activityInfo.name
-                                    + " (from package "
-                                    + r.activityInfo.applicationInfo.packageName
-                                    + ")");
+                                    + r.activityInfo.name + " (from package "
+                                    + r.activityInfo.applicationInfo.packageName + ")");
                         }
                     }
                 }
@@ -706,18 +726,19 @@
 
     /**
      * Run mCount cycles and see if we hit any crashers.
-     *
+     * <p>
      * TODO: Meta state on keys
      *
-     * @return Returns the last cycle which executed. If the value == mCount, no errors detected.
+     * @return Returns the last cycle which executed. If the value == mCount, no
+     *         errors detected.
      */
     private int runMonkeyCycles() {
-        int i = 0;
-        int lastKey = 0;
+        int eventCounter = 0;
+        int cycleCounter = 0;
 
         boolean systemCrashed = false;
 
-        while (!systemCrashed && i < mCount) {
+        while (!systemCrashed && cycleCounter < mCount) {
             synchronized (this) {
                 if (mRequestAnrTraces) {
                     reportAnrTraces();
@@ -728,37 +749,36 @@
                     mRequestDumpsysMemInfo = false;
                 }
                 if (mMonitorNativeCrashes) {
-                    // first time through, when i == 0, just set up the watcher (ignore the error)
-                    if (checkNativeCrashes() && (i > 0)) {
+                    // first time through, when eventCounter == 0, just set up
+                    // the watcher (ignore the error)
+                    if (checkNativeCrashes() && (eventCounter > 0)) {
                         System.out.println("** New native crash detected.");
                         mAbort = mAbort || mKillProcessAfterError;
                     }
                 }
                 if (mAbort) {
                     System.out.println("** Monkey aborted due to error.");
-                    System.out.println("Events injected: " + i);
-                    return i;
+                    System.out.println("Events injected: " + eventCounter);
+                    return eventCounter;
                 }
             }
 
-            // In this debugging mode, we never send any events.  This is primarily
-            // here so you can manually test the package or category limits, while manually
-            // exercising the system.
+            // In this debugging mode, we never send any events. This is
+            // primarily here so you can manually test the package or category
+            // limits, while manually exercising the system.
             if (mSendNoEvents) {
-                i++;
+                eventCounter++;
+                cycleCounter++;
                 continue;
             }
 
-            if ((mVerbose > 0) && (i % 100) == 0 && i != 0 && lastKey == 0) {
-                System.out.println("    // Sending event #" + i);
+            if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) {
+                System.out.println("    // Sending event #" + eventCounter);
             }
 
             MonkeyEvent ev = mEventSource.getNextEvent();
             if (ev != null) {
-                // We don't want to count throttling as an event.
-                if (!(ev instanceof MonkeyThrottleEvent)) {
-                    i++;
-                }
+
                 int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
                 if (injectCode == MonkeyEvent.INJECT_FAIL) {
                     if (ev instanceof MonkeyKeyEvent) {
@@ -773,18 +793,29 @@
                 } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
                     systemCrashed = !mIgnoreSecurityExceptions;
                 }
+
+                // Don't count throttling as an event.
+                if (!(ev instanceof MonkeyThrottleEvent)) {
+                    eventCounter++;
+                    if (mCountEvents) {
+                        cycleCounter++;
+                    }
+                }
             } else {
-                // Event Source has signaled that we have no more events to process
-                break;
+                if (!mCountEvents) {
+                    cycleCounter++;
+                } else {
+                    break;
+                }
             }
         }
         // If we got this far, we succeeded!
-        return mCount;
+        return eventCounter;
     }
 
     /**
-     * Send SIGNAL_USR1 to all processes.  This will generate large (5mb) profiling reports
-     * in data/misc, so use with care.
+     * Send SIGNAL_USR1 to all processes. This will generate large (5mb)
+     * profiling reports in data/misc, so use with care.
      */
     private void signalPersistentProcesses() {
         try {
@@ -800,14 +831,16 @@
     }
 
     /**
-     * Watch for appearance of new tombstone files, which indicate native crashes.
+     * Watch for appearance of new tombstone files, which indicate native
+     * crashes.
      *
      * @return Returns true if new files have appeared in the list
      */
     private boolean checkNativeCrashes() {
         String[] tombstones = TOMBSTONES_PATH.list();
 
-        // shortcut path for usually empty directory, so we don't waste even more objects
+        // shortcut path for usually empty directory, so we don't waste even
+        // more objects
         if ((tombstones == null) || (tombstones.length == 0)) {
             mTombstones = null;
             return false;
@@ -828,17 +861,20 @@
     }
 
     /**
-     * Return the next command line option.  This has a number of special cases which
-     * closely, but not exactly, follow the POSIX command line options patterns:
+     * Return the next command line option. This has a number of special cases
+     * which closely, but not exactly, follow the POSIX command line options
+     * patterns:
      *
+     * <pre>
      * -- means to stop processing additional options
      * -z means option z
      * -z ARGS means option z with (non-optional) arguments ARGS
      * -zARGS means option z with (optional) arguments ARGS
      * --zz means option zz
      * --zz ARGS means option zz with (non-optional) arguments ARGS
+     * </pre>
      *
-     * Note that you cannot combine single letter options;  -abc != -a -b -c
+     * Note that you cannot combine single letter options; -abc != -a -b -c
      *
      * @return Returns the option string, or null if there are no more options.
      */
@@ -885,7 +921,8 @@
     }
 
     /**
-     * Returns a long converted from the next data argument, with error handling if not available.
+     * Returns a long converted from the next data argument, with error handling
+     * if not available.
      *
      * @param opt The name of the option.
      * @return Returns a long converted from the argument.
@@ -919,19 +956,21 @@
      * Print how to use this command.
      */
     private void showUsage() {
-      System.err.println("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]");
-      System.err.println("              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]");
-      System.err.println("              [--ignore-crashes] [--ignore-timeouts]");
-      System.err.println("              [--ignore-security-exceptions] [--monitor-native-crashes]");
-      System.err.println("              [--kill-process-after-error] [--hprof]");
-      System.err.println("              [--pct-touch PERCENT] [--pct-motion PERCENT]");
-      System.err.println("              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]");
-      System.err.println("              [--pct-nav PERCENT] [--pct-majornav PERCENT]");
-      System.err.println("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
-      System.err.println("              [--pct-anyevent PERCENT]");
-      System.err.println("              [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
-      System.err.println("              [--port port]");
-      System.err.println("              [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
-      System.err.println("              COUNT");
-  }
+        StringBuffer usage = new StringBuffer();
+        usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n");
+        usage.append("              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n");
+        usage.append("              [--ignore-crashes] [--ignore-timeouts]\n");
+        usage.append("              [--ignore-security-exceptions] [--monitor-native-crashes]\n");
+        usage.append("              [--kill-process-after-error] [--hprof]\n");
+        usage.append("              [--pct-touch PERCENT] [--pct-motion PERCENT]\n");
+        usage.append("              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n");
+        usage.append("              [--pct-nav PERCENT] [--pct-majornav PERCENT]\n");
+        usage.append("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n");
+        usage.append("              [--pct-anyevent PERCENT]\n");
+        usage.append("              [--wait-dbg] [--dbg-no-events] [-f scriptfile]\n");
+        usage.append("              [--port port]\n");
+        usage.append("              [-s SEED] [-v [-v] ...] [--throttle MILLISEC]\n");
+        usage.append("              COUNT");
+        System.err.println(usage.toString());
+    }
 }
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyEventSource.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyEventSource.java
index a236554..af8b4ec 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeyEventSource.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyEventSource.java
@@ -19,23 +19,24 @@
 /**
  * event source interface
  */
-public interface MonkeyEventSource {    
+public interface MonkeyEventSource {
     /**
-     * 
      * @return the next monkey event from the source
      */
     public MonkeyEvent getNextEvent();
-    
+
     /**
      * set verbose to allow different level of log
+     *
      * @param verbose output mode? 1= verbose, 2=very verbose
      */
     public void setVerbose(int verbose);
-    
+
     /**
      * check whether precondition is satisfied
-     * @return false if something fails, e.g. factor failure in random source
-     * or file can not open from script source etc
+     *
+     * @return false if something fails, e.g. factor failure in random source or
+     *         file can not open from script source etc
      */
     public boolean validate();
 }
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
index d93f726..5c7fdbc 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
@@ -475,7 +475,7 @@
      */
     public MonkeyEvent getNextEvent() {
         if (mQ.isEmpty()) {
-                generateEvents();
+            generateEvents();
         }
         mEventCount++;
         MonkeyEvent e = mQ.getFirst();
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
index 2c7df48..e372385 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
@@ -23,9 +23,9 @@
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.NoSuchElementException;
 
 /**
  * monkey event queue. It takes a script to produce events sample script format:
@@ -77,10 +77,6 @@
     // maximum number of events that we read at one time
     private static final int MAX_ONE_TIME_READS = 100;
 
-    // number of additional events added to the script
-    // add HOME_KEY down and up events to make start UI consistent in each round
-    private static final int POLICY_ADDITIONAL_EVENT_COUNT = 0;
-
     // event key word in the capture log
     private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
 
@@ -111,81 +107,99 @@
 
     DataInputStream mInputStream;
 
-    BufferedReader mBufferReader;
+    BufferedReader mBufferedReader;
 
+    /**
+     * Creates a MonkeySourceScript instance.
+     *
+     * @param filename The filename of the script (on the device).
+     * @param throttle The amount of time in ms to sleep between events.
+     */
     public MonkeySourceScript(String filename, long throttle) {
         mScriptFileName = filename;
         mQ = new MonkeyEventQueue(throttle);
     }
 
     /**
-     * @return the number of total events that will be generated in a round
+     * Resets the globals used to timeshift events.
      */
-    public int getOneRoundEventCount() {
-        // plus one home key down and up event
-        return mEventCountInScript + POLICY_ADDITIONAL_EVENT_COUNT;
-    }
-
     private void resetValue() {
         mLastRecordedDownTimeKey = 0;
         mLastRecordedDownTimeMotion = 0;
+        mLastRecordedEventTime = -1;
         mLastExportDownTimeKey = 0;
         mLastExportDownTimeMotion = 0;
-        mLastRecordedEventTime = -1;
         mLastExportEventTime = -1;
     }
 
-    private boolean readScriptHeader() {
-        mEventCountInScript = -1;
-        mFileOpened = false;
-        try {
-            if (THIS_DEBUG) {
-                System.out.println("reading script header");
-            }
+    /**
+     * Reads the header of the script file.
+     *
+     * @return True if the file header could be parsed, and false otherwise.
+     * @throws IOException If there was an error reading the file.
+     */
+    private boolean readHeader() throws IOException {
+        mFileOpened = true;
 
-            mFStream = new FileInputStream(mScriptFileName);
-            mInputStream = new DataInputStream(mFStream);
-            mBufferReader = new BufferedReader(new InputStreamReader(mInputStream));
-            String sLine;
-            while ((sLine = mBufferReader.readLine()) != null) {
-                sLine = sLine.trim();
+        mFStream = new FileInputStream(mScriptFileName);
+        mInputStream = new DataInputStream(mFStream);
+        mBufferedReader = new BufferedReader(new InputStreamReader(mInputStream));
 
-                if (sLine.indexOf(HEADER_COUNT) >= 0) {
-                    try {
-                        mEventCountInScript = Integer.parseInt(sLine.substring(
-                                HEADER_COUNT.length() + 1).trim());
-                    } catch (NumberFormatException e) {
-                        System.err.println(e);
-                    }
-                } else if (sLine.indexOf(HEADER_SPEED) >= 0) {
-                    try {
-                        mSpeed = Double.parseDouble(sLine.substring(HEADER_SPEED.length() + 1)
-                                .trim());
+        String line;
 
-                    } catch (NumberFormatException e) {
-                        System.err.println(e);
-                    }
-                } else if (sLine.indexOf(STARTING_DATA_LINE) >= 0) {
-                    // header ends until we read the start data mark
-                    mFileOpened = true;
-                    if (THIS_DEBUG) {
-                        System.out.println("read script header success");
-                    }
-                    return true;
+        while ((line = mBufferedReader.readLine()) != null) {
+            line = line.trim();
+
+            if (line.indexOf(HEADER_COUNT) >= 0) {
+                try {
+                    String value = line.substring(HEADER_COUNT.length() + 1).trim();
+                    mEventCountInScript = Integer.parseInt(value);
+                } catch (NumberFormatException e) {
+                    System.err.println(e);
+                    return false;
                 }
+            } else if (line.indexOf(HEADER_SPEED) >= 0) {
+                try {
+                    String value = line.substring(HEADER_COUNT.length() + 1).trim();
+                    mSpeed = Double.parseDouble(value);
+                } catch (NumberFormatException e) {
+                    System.err.println(e);
+                    return false;
+                }
+            } else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
+                return true;
             }
-        } catch (FileNotFoundException e) {
-            System.err.println(e);
-        } catch (IOException e) {
-            System.err.println(e);
         }
 
-        if (THIS_DEBUG) {
-            System.out.println("Error in reading script header");
-        }
         return false;
     }
 
+    /**
+     * Reads a number of lines and passes the lines to be processed.
+     *
+     * @return The number of lines read.
+     * @throws IOException If there was an error reading the file.
+     */
+    private int readLines() throws IOException {
+        String line;
+        for (int i = 0; i < MAX_ONE_TIME_READS; i++) {
+            line = mBufferedReader.readLine();
+            if (line == null) {
+                return i;
+            }
+            line.trim();
+            processLine(line);
+        }
+        return MAX_ONE_TIME_READS;
+    }
+
+    /**
+     * Creates an event and adds it to the event queue. If the parameters are
+     * not understood, they are ignored and no events are added.
+     *
+     * @param s The entire string from the script file.
+     * @param args An array of arguments extracted from the script file line.
+     */
     private void handleEvent(String s, String[] args) {
         // Handle key event
         if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) {
@@ -291,82 +305,77 @@
         }
     }
 
-    private void processLine(String s) {
-        int index1 = s.indexOf('(');
-        int index2 = s.indexOf(')');
+    /**
+     * Extracts an event and a list of arguments from a line. If the line does
+     * not match the format required, it is ignored.
+     *
+     * @param line A string in the form {@code cmd(arg1,arg2,arg3)}.
+     */
+    private void processLine(String line) {
+        int index1 = line.indexOf('(');
+        int index2 = line.indexOf(')');
 
         if (index1 < 0 || index2 < 0) {
             return;
         }
 
-        String[] args = s.substring(index1 + 1, index2).split(",");
+        String[] args = line.substring(index1 + 1, index2).split(",");
 
-        handleEvent(s, args);
+        for (int i = 0; i < args.length; i++) {
+            args[i] = args[i].trim();
+        }
+
+        handleEvent(line, args);
     }
 
-    private void closeFile() {
+    /**
+     * Closes the script file.
+     *
+     * @throws IOException If there was an error closing the file.
+     */
+    private void closeFile() throws IOException {
         mFileOpened = false;
-        if (THIS_DEBUG) {
-            System.out.println("closing script file");
-        }
 
         try {
             mFStream.close();
             mInputStream.close();
-        } catch (IOException e) {
-            System.out.println(e);
+        } catch (NullPointerException e) {
+            // File was never opened so it can't be closed.
         }
     }
 
     /**
-     * read next batch of events from the provided script file
+     * Read next batch of events from the script file into the event queue.
+     * Checks if the script is open and then reads the next MAX_ONE_TIME_READS
+     * events or reads until the end of the file. If no events are read, then
+     * the script is closed.
      *
-     * @return true if success
+     * @throws IOException If there was an error reading the file.
      */
-    private boolean readNextBatch() {
-        /*
-         * The script should restore the original state when it run multiple
-         * times.
-         */
-        String sLine = null;
-        int readCount = 0;
+    private void readNextBatch() throws IOException {
+        int linesRead = 0;
 
         if (THIS_DEBUG) {
             System.out.println("readNextBatch(): reading next batch of events");
         }
 
         if (!mFileOpened) {
-            if (!readScriptHeader()) {
-                closeFile();
-                return false;
-            }
             resetValue();
+            readHeader();
         }
 
-        try {
-            while (readCount++ < MAX_ONE_TIME_READS && (sLine = mBufferReader.readLine()) != null) {
-                sLine = sLine.trim();
-                processLine(sLine);
-            }
-        } catch (IOException e) {
-            System.err.println(e);
-            return false;
-        }
+        linesRead = readLines();
 
-        if (sLine == null) {
-            // to the end of the file
-            if (THIS_DEBUG) {
-                System.out.println("readNextBatch(): to the end of file");
-            }
+        if (linesRead == 0) {
             closeFile();
         }
-        return true;
     }
 
     /**
-     * sleep for a period of given time, introducing latency among events
+     * Sleep for a period of given time. Used to introduce latency between
+     * events.
      *
-     * @param time to sleep in millisecond
+     * @param time The amount of time to sleep in ms
      */
     private void needSleep(long time) {
         if (time < 1) {
@@ -379,14 +388,23 @@
     }
 
     /**
-     * check whether we can successfully read the header of the script file
+     * Checks if the file can be opened and if the header is valid.
+     *
+     * @return True if the file exists and the header is valid, false otherwise.
      */
     public boolean validate() {
-        boolean b = readNextBatch();
+        boolean validHeader;
+        try {
+            validHeader = readHeader();
+            closeFile();
+        } catch (IOException e) {
+            return false;
+        }
+
         if (mVerbose > 0) {
             System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
         }
-        return b;
+        return validHeader;
     }
 
     public void setVerbose(int verbose) {
@@ -394,10 +412,10 @@
     }
 
     /**
-     * adjust key downtime and eventtime according to both recorded values and
-     * current system time
+     * Adjust key downtime and eventtime according to both recorded values and
+     * current system time.
      *
-     * @param e KeyEvent
+     * @param e A KeyEvent
      */
     private void adjustKeyEventTime(MonkeyKeyEvent e) {
         if (e.getEventTime() < 0) {
@@ -431,10 +449,10 @@
     }
 
     /**
-     * adjust motion downtime and eventtime according to both recorded values
-     * and current system time
+     * Adjust motion downtime and eventtime according to both recorded values
+     * and current system time.
      *
-     * @param e KeyEvent
+     * @param e A KeyEvent
      */
     private void adjustMotionEventTime(MonkeyMotionEvent e) {
         if (e.getEventTime() < 0) {
@@ -469,25 +487,40 @@
     }
 
     /**
-     * if the queue is empty, we generate events first
+     * Gets the next event to be injected from the script. If the event queue is
+     * empty, reads the next n events from the script into the queue, where n is
+     * the lesser of the number of remaining events and the value specified by
+     * MAX_ONE_TIME_READS. If the end of the file is reached, no events are
+     * added to the queue and null is returned.
      *
-     * @return the first event in the queue, if null, indicating the system
-     *         crashes
+     * @return The first event in the event queue or null if the end of the file
+     *         is reached or if an error is encountered reading the file.
      */
     public MonkeyEvent getNextEvent() {
         long recordedEventTime = -1;
+        MonkeyEvent ev;
 
         if (mQ.isEmpty()) {
-            readNextBatch();
+            try {
+                readNextBatch();
+            } catch (IOException e) {
+                return null;
+            }
         }
-        MonkeyEvent e = mQ.getFirst();
-        mQ.removeFirst();
-        if (e.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
-            adjustKeyEventTime((MonkeyKeyEvent) e);
-        } else if (e.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER
-                || e.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
-            adjustMotionEventTime((MonkeyMotionEvent) e);
+
+        try {
+            ev = mQ.getFirst();
+            mQ.removeFirst();
+        } catch (NoSuchElementException e) {
+            return null;
         }
-        return e;
+
+        if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
+            adjustKeyEventTime((MonkeyKeyEvent) ev);
+        } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER
+                || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
+            adjustMotionEventTime((MonkeyMotionEvent) ev);
+        }
+        return ev;
     }
 }