Implement shell commands for package and user services

Only implement the 'list' package service command. More will follow
in future CLs.

Change-Id: Iae225cd4ee63c7d468a4fd882d8cb4b6b76ccc09
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 2960cdc..ab781bb 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -25,7 +25,6 @@
 import android.accounts.IAccountManager;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -34,30 +33,25 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerificationParams;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IUserManager;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -80,11 +74,6 @@
 import java.io.OutputStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.WeakHashMap;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -96,9 +85,6 @@
     IUserManager mUm;
     IAccountManager mAm;
 
-    private WeakHashMap<String, Resources> mResourceCache
-            = new WeakHashMap<String, Resources>();
-
     private String[] mArgs;
     private int mNextArg;
     private String mCurArgData;
@@ -275,10 +261,10 @@
             if (args.length == 1) {
                 if (args[0].equalsIgnoreCase("-l")) {
                     validCommand = true;
-                    return runListPackages(false);
-                } else if (args[0].equalsIgnoreCase("-lf")){
+                    return runShellCommand("package", new String[] { "list", "package" });
+                } else if (args[0].equalsIgnoreCase("-lf")) {
                     validCommand = true;
-                    return runListPackages(true);
+                    return runShellCommand("package", new String[] { "list", "package", "-f" });
                 }
             } else if (args.length == 2) {
                 if (args[0].equalsIgnoreCase("-p")) {
@@ -297,6 +283,22 @@
         }
     }
 
+    private int runShellCommand(String serviceName, String[] args) {
+        final HandlerThread handlerThread = new HandlerThread("results");
+        handlerThread.start();
+        try {
+            ServiceManager.getService(serviceName).shellCommand(
+                    FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+                    args, new ResultReceiver(new Handler(handlerThread.getLooper())));
+            return 0;
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        } finally {
+            handlerThread.quitSafely();
+        }
+        return -1;
+    }
+
     /**
      * Execute the list sub-command.
      *
@@ -308,462 +310,11 @@
      * pm list instrumentation
      */
     private int runList() {
-        String type = nextArg();
-        if (type == null) {
-            System.err.println("Error: didn't specify type of data to list");
-            return 1;
+        final String type = nextArg();
+        if ("users".equals(type)) {
+            return runShellCommand("user", new String[] { "list" });
         }
-        if ("package".equals(type) || "packages".equals(type)) {
-            return runListPackages(false);
-        } else if ("permission-groups".equals(type)) {
-            return runListPermissionGroups();
-        } else if ("permissions".equals(type)) {
-            return runListPermissions();
-        } else if ("features".equals(type)) {
-            return runListFeatures();
-        } else if ("libraries".equals(type)) {
-            return runListLibraries();
-        } else if ("instrumentation".equals(type)) {
-            return runListInstrumentation();
-        } else if ("users".equals(type)) {
-            return runListUsers();
-        } else {
-            System.err.println("Error: unknown list type '" + type + "'");
-            return 1;
-        }
-    }
-
-    /**
-     * Lists all the installed packages.
-     */
-    private int runListPackages(boolean showApplicationPackage) {
-        int getFlags = 0;
-        boolean listDisabled = false, listEnabled = false;
-        boolean listSystem = false, listThirdParty = false;
-        boolean listInstaller = false;
-        int userId = UserHandle.USER_SYSTEM;
-        try {
-            String opt;
-            while ((opt=nextOption()) != null) {
-                if (opt.equals("-l")) {
-                    // old compat
-                } else if (opt.equals("-lf")) {
-                    showApplicationPackage = true;
-                } else if (opt.equals("-f")) {
-                    showApplicationPackage = true;
-                } else if (opt.equals("-d")) {
-                    listDisabled = true;
-                } else if (opt.equals("-e")) {
-                    listEnabled = true;
-                } else if (opt.equals("-s")) {
-                    listSystem = true;
-                } else if (opt.equals("-3")) {
-                    listThirdParty = true;
-                } else if (opt.equals("-i")) {
-                    listInstaller = true;
-                } else if (opt.equals("--user")) {
-                    userId = Integer.parseInt(nextArg());
-                } else if (opt.equals("-u")) {
-                    getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
-                } else {
-                    System.err.println("Error: Unknown option: " + opt);
-                    return 1;
-                }
-            }
-        } catch (RuntimeException ex) {
-            System.err.println("Error: " + ex.toString());
-            return 1;
-        }
-
-        String filter = nextArg();
-
-        try {
-            final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags, userId);
-
-            int count = packages.size();
-            for (int p = 0 ; p < count ; p++) {
-                PackageInfo info = packages.get(p);
-                if (filter != null && !info.packageName.contains(filter)) {
-                    continue;
-                }
-                final boolean isSystem =
-                        (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
-                if ((!listDisabled || !info.applicationInfo.enabled) &&
-                        (!listEnabled || info.applicationInfo.enabled) &&
-                        (!listSystem || isSystem) &&
-                        (!listThirdParty || !isSystem)) {
-                    System.out.print("package:");
-                    if (showApplicationPackage) {
-                        System.out.print(info.applicationInfo.sourceDir);
-                        System.out.print("=");
-                    }
-                    System.out.print(info.packageName);
-                    if (listInstaller) {
-                        System.out.print("  installer=");
-                        System.out.print(mPm.getInstallerPackageName(info.packageName));
-                    }
-                    System.out.println();
-                }
-            }
-            return 0;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags, int userId)
-            throws RemoteException {
-        ParceledListSlice<PackageInfo> slice = pm.getInstalledPackages(flags, userId);
-        return slice.getList();
-    }
-
-    /**
-     * Lists all of the features supported by the current device.
-     *
-     * pm list features
-     */
-    private int runListFeatures() {
-        try {
-            List<FeatureInfo> list = new ArrayList<FeatureInfo>();
-            FeatureInfo[] rawList = mPm.getSystemAvailableFeatures();
-            for (int i=0; i<rawList.length; i++) {
-                list.add(rawList[i]);
-            }
-
-
-            // Sort by name
-            Collections.sort(list, new Comparator<FeatureInfo>() {
-                public int compare(FeatureInfo o1, FeatureInfo o2) {
-                    if (o1.name == o2.name) return 0;
-                    if (o1.name == null) return -1;
-                    if (o2.name == null) return 1;
-                    return o1.name.compareTo(o2.name);
-                }
-            });
-
-            int count = (list != null) ? list.size() : 0;
-            for (int p = 0; p < count; p++) {
-                FeatureInfo fi = list.get(p);
-                System.out.print("feature:");
-                if (fi.name != null) System.out.println(fi.name);
-                else System.out.println("reqGlEsVersion=0x"
-                        + Integer.toHexString(fi.reqGlEsVersion));
-            }
-            return 0;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
-    /**
-     * Lists all of the libraries supported by the current device.
-     *
-     * pm list libraries
-     */
-    private int runListLibraries() {
-        try {
-            List<String> list = new ArrayList<String>();
-            String[] rawList = mPm.getSystemSharedLibraryNames();
-            for (int i=0; i<rawList.length; i++) {
-                list.add(rawList[i]);
-            }
-
-
-            // Sort by name
-            Collections.sort(list, new Comparator<String>() {
-                public int compare(String o1, String o2) {
-                    if (o1 == o2) return 0;
-                    if (o1 == null) return -1;
-                    if (o2 == null) return 1;
-                    return o1.compareTo(o2);
-                }
-            });
-
-            int count = (list != null) ? list.size() : 0;
-            for (int p = 0; p < count; p++) {
-                String lib = list.get(p);
-                System.out.print("library:");
-                System.out.println(lib);
-            }
-            return 0;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
-    /**
-     * Lists all of the installed instrumentation, or all for a given package
-     *
-     * pm list instrumentation [package] [-f]
-     */
-    private int runListInstrumentation() {
-        int flags = 0;      // flags != 0 is only used to request meta-data
-        boolean showPackage = false;
-        String targetPackage = null;
-
-        try {
-            String opt;
-            while ((opt=nextArg()) != null) {
-                if (opt.equals("-f")) {
-                    showPackage = true;
-                } else if (opt.charAt(0) != '-') {
-                    targetPackage = opt;
-                } else {
-                    System.err.println("Error: Unknown option: " + opt);
-                    return 1;
-                }
-            }
-        } catch (RuntimeException ex) {
-            System.err.println("Error: " + ex.toString());
-            return 1;
-        }
-
-        try {
-            List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags);
-
-            // Sort by target package
-            Collections.sort(list, new Comparator<InstrumentationInfo>() {
-                public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
-                    return o1.targetPackage.compareTo(o2.targetPackage);
-                }
-            });
-
-            int count = (list != null) ? list.size() : 0;
-            for (int p = 0; p < count; p++) {
-                InstrumentationInfo ii = list.get(p);
-                System.out.print("instrumentation:");
-                if (showPackage) {
-                    System.out.print(ii.sourceDir);
-                    System.out.print("=");
-                }
-                ComponentName cn = new ComponentName(ii.packageName, ii.name);
-                System.out.print(cn.flattenToShortString());
-                System.out.print(" (target=");
-                System.out.print(ii.targetPackage);
-                System.out.println(")");
-            }
-            return 0;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
-    /**
-     * Lists all the known permission groups.
-     */
-    private int runListPermissionGroups() {
-        try {
-            List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0);
-
-            int count = pgs.size();
-            for (int p = 0 ; p < count ; p++) {
-                PermissionGroupInfo pgi = pgs.get(p);
-                System.out.print("permission group:");
-                System.out.println(pgi.name);
-            }
-            return 0;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
-    private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) {
-        if (nonLocalized != null) {
-            return nonLocalized.toString();
-        }
-        if (res != 0) {
-            Resources r = getResources(pii);
-            if (r != null) {
-                try {
-                    return r.getString(res);
-                } catch (Resources.NotFoundException e) {
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Lists all the permissions in a group.
-     */
-    private int runListPermissions() {
-        try {
-            boolean labels = false;
-            boolean groups = false;
-            boolean userOnly = false;
-            boolean summary = false;
-            boolean dangerousOnly = false;
-            String opt;
-            while ((opt=nextOption()) != null) {
-                if (opt.equals("-f")) {
-                    labels = true;
-                } else if (opt.equals("-g")) {
-                    groups = true;
-                } else if (opt.equals("-s")) {
-                    groups = true;
-                    labels = true;
-                    summary = true;
-                } else if (opt.equals("-u")) {
-                    userOnly = true;
-                } else if (opt.equals("-d")) {
-                    dangerousOnly = true;
-                } else {
-                    System.err.println("Error: Unknown option: " + opt);
-                    return 1;
-                }
-            }
-
-            String grp = nextArg();
-            ArrayList<String> groupList = new ArrayList<String>();
-            if (groups) {
-                List<PermissionGroupInfo> infos =
-                        mPm.getAllPermissionGroups(0);
-                for (int i=0; i<infos.size(); i++) {
-                    groupList.add(infos.get(i).name);
-                }
-                groupList.add(null);
-            } else {
-                groupList.add(grp);
-            }
-
-            if (dangerousOnly) {
-                System.out.println("Dangerous Permissions:");
-                System.out.println("");
-                doListPermissions(groupList, groups, labels, summary,
-                        PermissionInfo.PROTECTION_DANGEROUS,
-                        PermissionInfo.PROTECTION_DANGEROUS);
-                if (userOnly) {
-                    System.out.println("Normal Permissions:");
-                    System.out.println("");
-                    doListPermissions(groupList, groups, labels, summary,
-                            PermissionInfo.PROTECTION_NORMAL,
-                            PermissionInfo.PROTECTION_NORMAL);
-                }
-            } else if (userOnly) {
-                System.out.println("Dangerous and Normal Permissions:");
-                System.out.println("");
-                doListPermissions(groupList, groups, labels, summary,
-                        PermissionInfo.PROTECTION_NORMAL,
-                        PermissionInfo.PROTECTION_DANGEROUS);
-            } else {
-                System.out.println("All Permissions:");
-                System.out.println("");
-                doListPermissions(groupList, groups, labels, summary,
-                        -10000, 10000);
-            }
-            return 0;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
-    private void doListPermissions(ArrayList<String> groupList,
-            boolean groups, boolean labels, boolean summary,
-            int startProtectionLevel, int endProtectionLevel)
-            throws RemoteException {
-        for (int i=0; i<groupList.size(); i++) {
-            String groupName = groupList.get(i);
-            String prefix = "";
-            if (groups) {
-                if (i > 0) System.out.println("");
-                if (groupName != null) {
-                    PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(
-                            groupName, 0);
-                    if (summary) {
-                        Resources res = getResources(pgi);
-                        if (res != null) {
-                            System.out.print(loadText(pgi, pgi.labelRes,
-                                    pgi.nonLocalizedLabel) + ": ");
-                        } else {
-                            System.out.print(pgi.name + ": ");
-
-                        }
-                    } else {
-                        System.out.println((labels ? "+ " : "")
-                                + "group:" + pgi.name);
-                        if (labels) {
-                            System.out.println("  package:" + pgi.packageName);
-                            Resources res = getResources(pgi);
-                            if (res != null) {
-                                System.out.println("  label:"
-                                        + loadText(pgi, pgi.labelRes,
-                                                pgi.nonLocalizedLabel));
-                                System.out.println("  description:"
-                                        + loadText(pgi, pgi.descriptionRes,
-                                                pgi.nonLocalizedDescription));
-                            }
-                        }
-                    }
-                } else {
-                    System.out.println(((labels && !summary)
-                            ? "+ " : "") + "ungrouped:");
-                }
-                prefix = "  ";
-            }
-            List<PermissionInfo> ps = mPm.queryPermissionsByGroup(
-                    groupList.get(i), 0);
-            int count = ps.size();
-            boolean first = true;
-            for (int p = 0 ; p < count ; p++) {
-                PermissionInfo pi = ps.get(p);
-                if (groups && groupName == null && pi.group != null) {
-                    continue;
-                }
-                final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-                if (base < startProtectionLevel
-                        || base > endProtectionLevel) {
-                    continue;
-                }
-                if (summary) {
-                    if (first) {
-                        first = false;
-                    } else {
-                        System.out.print(", ");
-                    }
-                    Resources res = getResources(pi);
-                    if (res != null) {
-                        System.out.print(loadText(pi, pi.labelRes,
-                                pi.nonLocalizedLabel));
-                    } else {
-                        System.out.print(pi.name);
-                    }
-                } else {
-                    System.out.println(prefix + (labels ? "+ " : "")
-                            + "permission:" + pi.name);
-                    if (labels) {
-                        System.out.println(prefix + "  package:" + pi.packageName);
-                        Resources res = getResources(pi);
-                        if (res != null) {
-                            System.out.println(prefix + "  label:"
-                                    + loadText(pi, pi.labelRes,
-                                            pi.nonLocalizedLabel));
-                            System.out.println(prefix + "  description:"
-                                    + loadText(pi, pi.descriptionRes,
-                                            pi.nonLocalizedDescription));
-                        }
-                        System.out.println(prefix + "  protectionLevel:"
-                                + PermissionInfo.protectionToString(pi.protectionLevel));
-                    }
-                }
-            }
-
-            if (summary) {
-                System.out.println("");
-            }
-        }
+        return runShellCommand("package", mArgs);
     }
 
     private int runPath() {
@@ -1467,29 +1018,6 @@
         }
     }
 
-    public int runListUsers() {
-        try {
-            IActivityManager am = ActivityManagerNative.getDefault();
-
-            List<UserInfo> users = mUm.getUsers(false);
-            if (users == null) {
-                System.err.println("Error: couldn't get users");
-                return 1;
-            } else {
-                System.out.println("Users:");
-                for (int i = 0; i < users.size(); i++) {
-                    String running = am.isUserRunning(users.get(i).id, false) ? " running" : "";
-                    System.out.println("\t" + users.get(i).toString() + running);
-                }
-                return 0;
-            }
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return 1;
-        }
-    }
-
     public int runGetMaxUsers() {
         System.out.println("Maximum supported users: " + UserManager.getMaxSupportedUsers());
         return 0;
@@ -1997,24 +1525,6 @@
         return 1;
     }
 
-    private Resources getResources(PackageItemInfo pii) {
-        Resources res = mResourceCache.get(pii.packageName);
-        if (res != null) return res;
-
-        try {
-            ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0, 0);
-            AssetManager am = new AssetManager();
-            am.addAssetPath(ai.publicSourceDir);
-            res = new Resources(am, null, null);
-            mResourceCache.put(pii.packageName, res);
-            return res;
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-            return null;
-        }
-    }
-
     private static String checkAbiArgument(String abi) {
         if (TextUtils.isEmpty(abi)) {
             throw new IllegalArgumentException("Missing ABI argument");
@@ -2110,14 +1620,7 @@
     }
 
     private static int showUsage() {
-        System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
-        System.err.println("       pm list permission-groups");
-        System.err.println("       pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
-        System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
-        System.err.println("       pm list features");
-        System.err.println("       pm list libraries");
-        System.err.println("       pm list users");
-        System.err.println("       pm path [--user USER_ID] PACKAGE");
+        System.err.println("usage: pm path [--user USER_ID] PACKAGE");
         System.err.println("       pm dump PACKAGE");
         System.err.println("       pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]");
         System.err.println("       pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
@@ -2151,34 +1654,8 @@
         System.err.println("       pm remove-user USER_ID");
         System.err.println("       pm get-max-users");
         System.err.println("");
-        System.err.println("pm list packages: prints all packages, optionally only");
-        System.err.println("  those whose package name contains the text in FILTER.  Options:");
-        System.err.println("    -f: see their associated file.");
-        System.err.println("    -d: filter to only show disbled packages.");
-        System.err.println("    -e: filter to only show enabled packages.");
-        System.err.println("    -s: filter to only show system packages.");
-        System.err.println("    -3: filter to only show third party packages.");
-        System.err.println("    -i: see the installer for the packages.");
-        System.err.println("    -u: also include uninstalled packages.");
-        System.err.println("");
-        System.err.println("pm list permission-groups: prints all known permission groups.");
-        System.err.println("");
-        System.err.println("pm list permissions: prints all known permissions, optionally only");
-        System.err.println("  those in GROUP.  Options:");
-        System.err.println("    -g: organize by group.");
-        System.err.println("    -f: print all information.");
-        System.err.println("    -s: short summary.");
-        System.err.println("    -d: only list dangerous permissions.");
-        System.err.println("    -u: list only the permissions users will see.");
-        System.err.println("");
-        System.err.println("pm list instrumentation: use to list all test packages; optionally");
-        System.err.println("  supply <TARGET-PACKAGE> to list the test packages for a particular");
-        System.err.println("  application.  Options:");
-        System.err.println("    -f: list the .apk file for the test package.");
-        System.err.println("");
-        System.err.println("pm list features: prints all features of the system.");
-        System.err.println("");
-        System.err.println("pm list users: prints all users on the system.");
+        System.err.println("NOTE: 'pm list' commands have moved! Run 'adb shell cmd package'");
+        System.err.println("  to display the new commands.");
         System.err.println("");
         System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
         System.err.println("");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 22bedc3..4863603 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -162,6 +162,7 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -15086,6 +15087,13 @@
     }
 
     @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out,
+            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+        (new PackageManagerShellCommand(this)).exec(
+                this, in, out, err, args, resultReceiver);
+    }
+
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
new file mode 100644
index 0000000..c259ac2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -0,0 +1,529 @@
+package com.android.server.pm;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.WeakHashMap;
+
+class PackageManagerShellCommand extends ShellCommand {
+    final IPackageManager mInterface;
+    final private WeakHashMap<String, Resources> mResourceCache =
+            new WeakHashMap<String, Resources>();
+
+    PackageManagerShellCommand(PackageManagerService service) {
+        mInterface = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch(cmd) {
+                case "list":
+                    return runList();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    private int runList() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final String type = getNextArg();
+        if (type == null) {
+            pw.println("Error: didn't specify type of data to list");
+            return -1;
+        }
+        switch(type) {
+            case "features":
+                return runListFeatures();
+            case "instrumentation":
+                return runListInstrumentation();
+            case "libraries":
+                return runListLibraries();
+            case "package":
+            case "packages":
+                return runListPackages(false /*showSourceDir*/);
+            case "permission-groups":
+                return runListPermissionGroups();
+            case "permissions":
+                return runListPermissions();
+        }
+        pw.println("Error: unknown list type '" + type + "'");
+        return -1;
+    }
+
+    private int runListFeatures() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final List<FeatureInfo> list = new ArrayList<FeatureInfo>();
+        final FeatureInfo[] rawList = mInterface.getSystemAvailableFeatures();
+        for (int i=0; i<rawList.length; i++) {
+            list.add(rawList[i]);
+        }
+
+        // sort by name
+        Collections.sort(list, new Comparator<FeatureInfo>() {
+            public int compare(FeatureInfo o1, FeatureInfo o2) {
+                if (o1.name == o2.name) return 0;
+                if (o1.name == null) return -1;
+                if (o2.name == null) return 1;
+                return o1.name.compareTo(o2.name);
+            }
+        });
+
+        final int count = (list != null) ? list.size() : 0;
+        for (int p = 0; p < count; p++) {
+            FeatureInfo fi = list.get(p);
+            pw.print("feature:");
+            if (fi.name != null) pw.println(fi.name);
+            else pw.println("reqGlEsVersion=0x"
+                    + Integer.toHexString(fi.reqGlEsVersion));
+        }
+        return 0;
+    }
+
+    private int runListInstrumentation() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        boolean showSourceDir = false;
+        String targetPackage = null;
+
+        try {
+            String opt;
+            while ((opt = getNextArg()) != null) {
+                switch (opt) {
+                    case "-f":
+                        showSourceDir = true;
+                        break;
+                    default:
+                        if (opt.charAt(0) != '-') {
+                            targetPackage = opt;
+                        } else {
+                            pw.println("Error: Unknown option: " + opt);
+                            return -1;
+                        }
+                        break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            pw.println("Error: " + ex.toString());
+            return -1;
+        }
+
+        final List<InstrumentationInfo> list =
+                mInterface.queryInstrumentation(targetPackage, 0 /*flags*/);
+
+        // sort by target package
+        Collections.sort(list, new Comparator<InstrumentationInfo>() {
+            public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
+                return o1.targetPackage.compareTo(o2.targetPackage);
+            }
+        });
+
+        final int count = (list != null) ? list.size() : 0;
+        for (int p = 0; p < count; p++) {
+            final InstrumentationInfo ii = list.get(p);
+            pw.print("instrumentation:");
+            if (showSourceDir) {
+                pw.print(ii.sourceDir);
+                pw.print("=");
+            }
+            final ComponentName cn = new ComponentName(ii.packageName, ii.name);
+            pw.print(cn.flattenToShortString());
+            pw.print(" (target=");
+            pw.print(ii.targetPackage);
+            pw.println(")");
+        }
+        return 0;
+    }
+
+    private int runListLibraries() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final List<String> list = new ArrayList<String>();
+        final String[] rawList = mInterface.getSystemSharedLibraryNames();
+        for (int i = 0; i < rawList.length; i++) {
+            list.add(rawList[i]);
+        }
+
+        // sort by name
+        Collections.sort(list, new Comparator<String>() {
+            public int compare(String o1, String o2) {
+                if (o1 == o2) return 0;
+                if (o1 == null) return -1;
+                if (o2 == null) return 1;
+                return o1.compareTo(o2);
+            }
+        });
+
+        final int count = (list != null) ? list.size() : 0;
+        for (int p = 0; p < count; p++) {
+            String lib = list.get(p);
+            pw.print("library:");
+            pw.println(lib);
+        }
+        return 0;
+    }
+
+    private int runListPackages(boolean showSourceDir) throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        int getFlags = 0;
+        boolean listDisabled = false, listEnabled = false;
+        boolean listSystem = false, listThirdParty = false;
+        boolean listInstaller = false;
+        int userId = UserHandle.USER_SYSTEM;
+        try {
+            String opt;
+            while ((opt = getNextOption()) != null) {
+                switch (opt) {
+                    case "-d":
+                        listDisabled = true;
+                        break;
+                    case "-e":
+                        listEnabled = true;
+                        break;
+                    case "-f":
+                        showSourceDir = true;
+                        break;
+                    case "-i":
+                        listInstaller = true;
+                        break;
+                    case "-l":
+                        // old compat
+                        break;
+                    case "-lf":
+                        showSourceDir = true;
+                        break;
+                    case "-s":
+                        listSystem = true;
+                        break;
+                    case "-u":
+                        getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
+                        break;
+                    case "-3":
+                        listThirdParty = true;
+                        break;
+                    case "--user":
+                        userId = Integer.parseInt(getNextArg());
+                        break;
+                    default:
+                        pw.println("Error: Unknown option: " + opt);
+                        return -1;
+                }
+            }
+        } catch (RuntimeException ex) {
+            pw.println("Error: " + ex.toString());
+            return -1;
+        }
+
+        final String filter = getNextArg();
+
+        @SuppressWarnings("unchecked")
+        final ParceledListSlice<PackageInfo> slice =
+                mInterface.getInstalledPackages(getFlags, userId);
+        final List<PackageInfo> packages = slice.getList();
+
+        final int count = packages.size();
+        for (int p = 0; p < count; p++) {
+            final PackageInfo info = packages.get(p);
+            if (filter != null && !info.packageName.contains(filter)) {
+                continue;
+            }
+            final boolean isSystem =
+                    (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
+            if ((!listDisabled || !info.applicationInfo.enabled) &&
+                    (!listEnabled || info.applicationInfo.enabled) &&
+                    (!listSystem || isSystem) &&
+                    (!listThirdParty || !isSystem)) {
+                pw.print("package:");
+                if (showSourceDir) {
+                    pw.print(info.applicationInfo.sourceDir);
+                    pw.print("=");
+                }
+                pw.print(info.packageName);
+                if (listInstaller) {
+                    pw.print("  installer=");
+                    pw.print(mInterface.getInstallerPackageName(info.packageName));
+                }
+                pw.println();
+            }
+        }
+        return 0;
+    }
+
+    private int runListPermissionGroups() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0);
+
+        final int count = pgs.size();
+        for (int p = 0; p < count ; p++) {
+            final PermissionGroupInfo pgi = pgs.get(p);
+            pw.print("permission group:");
+            pw.println(pgi.name);
+        }
+        return 0;
+    }
+
+    private int runListPermissions() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        boolean labels = false;
+        boolean groups = false;
+        boolean userOnly = false;
+        boolean summary = false;
+        boolean dangerousOnly = false;
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-d":
+                    dangerousOnly = true;
+                    break;
+                case "-f":
+                    labels = true;
+                    break;
+                case "-g":
+                    groups = true;
+                    break;
+                case "-s":
+                    groups = true;
+                    labels = true;
+                    summary = true;
+                    break;
+                case "-u":
+                    userOnly = true;
+                    break;
+                default:
+                    pw.println("Error: Unknown option: " + opt);
+                    return 1;
+            }
+        }
+
+        final ArrayList<String> groupList = new ArrayList<String>();
+        if (groups) {
+            final List<PermissionGroupInfo> infos =
+                    mInterface.getAllPermissionGroups(0 /*flags*/);
+            final int count = infos.size();
+            for (int i = 0; i < count; i++) {
+                groupList.add(infos.get(i).name);
+            }
+            groupList.add(null);
+        } else {
+            final String grp = getNextArg();
+            groupList.add(grp);
+        }
+
+        if (dangerousOnly) {
+            pw.println("Dangerous Permissions:");
+            pw.println("");
+            doListPermissions(groupList, groups, labels, summary,
+                    PermissionInfo.PROTECTION_DANGEROUS,
+                    PermissionInfo.PROTECTION_DANGEROUS);
+            if (userOnly) {
+                pw.println("Normal Permissions:");
+                pw.println("");
+                doListPermissions(groupList, groups, labels, summary,
+                        PermissionInfo.PROTECTION_NORMAL,
+                        PermissionInfo.PROTECTION_NORMAL);
+            }
+        } else if (userOnly) {
+            pw.println("Dangerous and Normal Permissions:");
+            pw.println("");
+            doListPermissions(groupList, groups, labels, summary,
+                    PermissionInfo.PROTECTION_NORMAL,
+                    PermissionInfo.PROTECTION_DANGEROUS);
+        } else {
+            pw.println("All Permissions:");
+            pw.println("");
+            doListPermissions(groupList, groups, labels, summary,
+                    -10000, 10000);
+        }
+        return 0;
+    }
+
+    private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
+            boolean summary, int startProtectionLevel, int endProtectionLevel)
+                    throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final int groupCount = groupList.size();
+        for (int i = 0; i < groupCount; i++) {
+            String groupName = groupList.get(i);
+            String prefix = "";
+            if (groups) {
+                if (i > 0) {
+                    pw.println("");
+                }
+                if (groupName != null) {
+                    PermissionGroupInfo pgi =
+                            mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/);
+                    if (summary) {
+                        Resources res = getResources(pgi);
+                        if (res != null) {
+                            pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": ");
+                        } else {
+                            pw.print(pgi.name + ": ");
+
+                        }
+                    } else {
+                        pw.println((labels ? "+ " : "") + "group:" + pgi.name);
+                        if (labels) {
+                            pw.println("  package:" + pgi.packageName);
+                            Resources res = getResources(pgi);
+                            if (res != null) {
+                                pw.println("  label:"
+                                        + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel));
+                                pw.println("  description:"
+                                        + loadText(pgi, pgi.descriptionRes,
+                                                pgi.nonLocalizedDescription));
+                            }
+                        }
+                    }
+                } else {
+                    pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:");
+                }
+                prefix = "  ";
+            }
+            List<PermissionInfo> ps =
+                    mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
+            final int count = ps.size();
+            boolean first = true;
+            for (int p = 0 ; p < count ; p++) {
+                PermissionInfo pi = ps.get(p);
+                if (groups && groupName == null && pi.group != null) {
+                    continue;
+                }
+                final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+                if (base < startProtectionLevel
+                        || base > endProtectionLevel) {
+                    continue;
+                }
+                if (summary) {
+                    if (first) {
+                        first = false;
+                    } else {
+                        pw.print(", ");
+                    }
+                    Resources res = getResources(pi);
+                    if (res != null) {
+                        pw.print(loadText(pi, pi.labelRes,
+                                pi.nonLocalizedLabel));
+                    } else {
+                        pw.print(pi.name);
+                    }
+                } else {
+                    pw.println(prefix + (labels ? "+ " : "")
+                            + "permission:" + pi.name);
+                    if (labels) {
+                        pw.println(prefix + "  package:" + pi.packageName);
+                        Resources res = getResources(pi);
+                        if (res != null) {
+                            pw.println(prefix + "  label:"
+                                    + loadText(pi, pi.labelRes,
+                                            pi.nonLocalizedLabel));
+                            pw.println(prefix + "  description:"
+                                    + loadText(pi, pi.descriptionRes,
+                                            pi.nonLocalizedDescription));
+                        }
+                        pw.println(prefix + "  protectionLevel:"
+                                + PermissionInfo.protectionToString(pi.protectionLevel));
+                    }
+                }
+            }
+
+            if (summary) {
+                pw.println("");
+            }
+        }
+    }
+
+    private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized)
+            throws RemoteException {
+        if (nonLocalized != null) {
+            return nonLocalized.toString();
+        }
+        if (res != 0) {
+            Resources r = getResources(pii);
+            if (r != null) {
+                try {
+                    return r.getString(res);
+                } catch (Resources.NotFoundException e) {
+                }
+            }
+        }
+        return null;
+    }
+
+    private Resources getResources(PackageItemInfo pii) throws RemoteException {
+        Resources res = mResourceCache.get(pii.packageName);
+        if (res != null) return res;
+
+        ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, 0, 0);
+        AssetManager am = new AssetManager();
+        am.addAssetPath(ai.publicSourceDir);
+        res = new Resources(am, null, null);
+        mResourceCache.put(pii.packageName, res);
+        return res;
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("Package manager (package) commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println("");
+        pw.println("  list features");
+        pw.println("    Prints all features of the system.");
+        pw.println("  list instrumentation [-f] [TARGET-PACKAGE]");
+        pw.println("    Prints all test packages; optionally only those targetting TARGET-PACKAGE");
+        pw.println("    Options:");
+        pw.println("      -f: dump the name of the .apk file containing the test package");
+        pw.println("  list libraries");
+        pw.println("    Prints all system libraries.");
+        pw.println("  list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
+        pw.println("    Prints all packages; optionally only those whose name contains");
+        pw.println("    the text in FILTER.");
+        pw.println("    Options:");
+        pw.println("      -f: see their associated file");
+        pw.println("      -d: filter to only show disbled packages");
+        pw.println("      -e: filter to only show enabled packages");
+        pw.println("      -s: filter to only show system packages");
+        pw.println("      -3: filter to only show third party packages");
+        pw.println("      -i: see the installer for the packages");
+        pw.println("      -u: also include uninstalled packages");
+        pw.println("  list permission-groups");
+        pw.println("    Prints all known permission groups.");
+        pw.println("  list permissions [-g] [-f] [-d] [-u] [GROUP]");
+        pw.println("    Prints all known permissions; optionally only those in GROUP.");
+        pw.println("    Options:");
+        pw.println("      -g: organize by group");
+        pw.println("      -f: print all information");
+        pw.println("      -s: short summary");
+        pw.println("      -d: only list dangerous permissions");
+        pw.println("      -u: list only the permissions users will see");
+        pw.println("");
+    }
+}
+
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5d0e83d..0b13fb1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -44,7 +45,9 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCommand;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -2150,6 +2153,45 @@
     }
 
     @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out,
+            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+        (new Shell()).exec(this, in, out, err, args, resultReceiver);
+    }
+
+    int onShellCommand(Shell shell, String cmd) {
+        if (cmd == null) {
+            return shell.handleDefaultCommands(cmd);
+        }
+
+        final PrintWriter pw = shell.getOutPrintWriter();
+        try {
+            switch(cmd) {
+                case "list":
+                    return runList(pw);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    private int runList(PrintWriter pw) throws RemoteException {
+        final IActivityManager am = ActivityManagerNative.getDefault();
+        final List<UserInfo> users = getUsers(false);
+        if (users == null) {
+            pw.println("Error: couldn't get users");
+            return 1;
+        } else {
+            pw.println("Users:");
+            for (int i = 0; i < users.size(); i++) {
+                String running = am.isUserRunning(users.get(i).id, false) ? " running" : "";
+                pw.println("\t" + users.get(i).toString() + running);
+            }
+            return 0;
+        }
+    }
+
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -2273,4 +2315,22 @@
             }
         }
     }
+
+    private class Shell extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            return onShellCommand(this, cmd);
+        }
+
+        @Override
+        public void onHelp() {
+            final PrintWriter pw = getOutPrintWriter();
+            pw.println("User manager (user) commands:");
+            pw.println("  help");
+            pw.println("    Print this help text.");
+            pw.println("");
+            pw.println("  list");
+            pw.println("    Prints all users on the system.");
+        }
+    }
 }