Package blacklist/whitelist file support for Monkey.

With this CL, you can specify a package whitelist file or a package blacklist file,
but not both. You can not specify both individual packages via -p and blacklist file,
either. But you can specify both whitelist file and individual packages via -p and they
will be summed up.
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index bb0663f..9202230 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -34,6 +34,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -117,9 +118,18 @@
     /** Generate hprof reports before/after monkey runs */
     private boolean mGenerateHprof;
 
+    /** Package blacklist file. */
+    private String mPkgBlacklistFile;
+
+    /** Package whitelist file. */
+    private String mPkgWhitelistFile;
+
     /** Packages we are allowed to run, or empty if no restriction. */
     private HashSet<String> mValidPackages = new HashSet<String>();
 
+    /** Packages we are not allowed to run. */
+    private HashSet<String> mInvalidPackages = new HashSet<String>();
+
     /** Categories we are allowed to launch **/
     private ArrayList<String> mMainCategories = new ArrayList<String>();
 
@@ -169,6 +179,25 @@
     public static String currentPackage;
 
     /**
+     * Check whether we should run against the givn package.
+     *
+     * @param pkg The package name.
+     * @return Returns true if we should run against pkg.
+     */
+    private boolean checkEnteringPackage(String pkg) {
+        if (mInvalidPackages.size() > 0) {
+            if (mInvalidPackages.contains(pkg)) {
+                return false;
+            }
+        } else if (mValidPackages.size() > 0) {
+            if (!mValidPackages.contains(pkg)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Monitor operations happening in the system.
      */
     private class ActivityController extends IActivityController.Stub {
@@ -196,19 +225,6 @@
             return allow;
         }
 
-        private boolean checkEnteringPackage(String pkg) {
-            if (pkg == null) {
-                return true;
-            }
-            // preflight the hash lookup to avoid the cost of hash key
-            // generation
-            if (mValidPackages.size() == 0) {
-                return true;
-            } else {
-                return mValidPackages.contains(pkg);
-            }
-        }
-
         public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
                 byte[] crashData) {
             System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
@@ -367,6 +383,10 @@
             return -1;
         }
 
+        if (!loadPackageLists()) {
+            return -1;
+        }
+
         // now set up additional data in preparation for launch
         if (mMainCategories.size() == 0) {
             mMainCategories.add(Intent.CATEGORY_LAUNCHER);
@@ -381,6 +401,12 @@
                     System.out.println(":AllowPackage: " + it.next());
                 }
             }
+            if (mInvalidPackages.size() > 0) {
+                Iterator<String> it = mInvalidPackages.iterator();
+                while (it.hasNext()) {
+                    System.out.println(":DisallowPackage: " + it.next());
+                }
+            }
             if (mMainCategories.size() != 0) {
                 Iterator<String> it = mMainCategories.iterator();
                 while (it.hasNext()) {
@@ -573,6 +599,10 @@
                 } else if (opt.equals("--pct-anyevent")) {
                     int i = MonkeySourceRandom.FACTOR_ANYTHING;
                     mFactors[i] = -nextOptionLong("any events percentage");
+                } else if (opt.equals("--pkg-blacklist-file")) {
+                    mPkgBlacklistFile = nextOptionData();
+                } else if (opt.equals("--pkg-whitelist-file")) {
+                    mPkgWhitelistFile = nextOptionData();
                 } else if (opt.equals("--throttle")) {
                     mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
                 } else if (opt.equals("--wait-dbg")) {
@@ -623,6 +653,62 @@
     }
 
     /**
+     * Load a list of package names from a file.
+     *
+     * @param fileName The file name, with package names separated by new line.
+     * @param list The destination list.
+     * @return Returns false if any error occurs.
+     */
+    private static boolean loadPackageListFromFile(String fileName, HashSet<String> list) {
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(fileName));
+            String s;
+            while ((s = reader.readLine()) != null) {
+                s = s.trim();
+                if ((s.length() > 0) && (!s.startsWith("#"))) {
+                    list.add(s);
+                }
+            }
+        } catch (IOException ioe) {
+            System.err.println(ioe);
+            return false;
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException ioe) {
+                    System.err.println(ioe);
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Load package blacklist or whitelist (if specified).
+     *
+     * @return Returns false if any error occurs.
+     */
+    private boolean loadPackageLists() {
+        if (((mPkgWhitelistFile != null) || (mValidPackages.size() > 0))
+                && (mPkgBlacklistFile != null)) {
+            System.err.println("** Error: you can not specify a package blacklist "
+                    + "together with a whitelist or individual packages (via -p).");
+            return false;
+        }
+        if ((mPkgWhitelistFile != null)
+                && (!loadPackageListFromFile(mPkgWhitelistFile, mValidPackages))) {
+            return false;
+        }
+        if ((mPkgBlacklistFile != null)
+                && (!loadPackageListFromFile(mPkgBlacklistFile, mInvalidPackages))) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Check for any internal configuration (primarily build-time) errors.
      *
      * @return Returns true if ready to rock.
@@ -708,20 +794,17 @@
                 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)) {
+                    String packageName = r.activityInfo.applicationInfo.packageName;
+                    if (checkEnteringPackage(packageName)) {
                         if (mVerbose >= 2) { // very verbose
                             System.out.println("//   + Using main activity " + r.activityInfo.name
-                                    + " (from package "
-                                    + r.activityInfo.applicationInfo.packageName + ")");
+                                    + " (from package " + packageName + ")");
                         }
-                        mMainApps.add(new ComponentName(r.activityInfo.applicationInfo.packageName,
-                                r.activityInfo.name));
+                        mMainApps.add(new ComponentName(packageName, r.activityInfo.name));
                     } else {
                         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 " + packageName + ")");
                         }
                     }
                 }
@@ -982,6 +1065,8 @@
         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("              [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n");
+        usage.append("              [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n");
         usage.append("              [--wait-dbg] [--dbg-no-events]\n");
         usage.append("              [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n");
         usage.append("              [--port port]\n");