Allow appops command to modify uid ops.

The get and set commands can now operate on per-uid
app ops as well as per-package ones.

The to parse a uid string in to a more generic place, but
for now we'll leave it here.

Test: none

Change-Id: I7efbb3966ddb79639dafabbe4bf1df659bed6240
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index dcf6fd7..1a5ec61 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -416,6 +416,29 @@
         return resOps;
     }
 
+    private ArrayList<AppOpsManager.OpEntry> collectOps(SparseIntArray uidOps, int[] ops) {
+        ArrayList<AppOpsManager.OpEntry> resOps = null;
+        if (ops == null) {
+            resOps = new ArrayList<>();
+            for (int j=0; j<uidOps.size(); j++) {
+                resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(j), uidOps.valueAt(j),
+                        0, 0, 0, -1, null));
+            }
+        } else {
+            for (int j=0; j<ops.length; j++) {
+                int index = uidOps.indexOfKey(ops[j]);
+                if (index >= 0) {
+                    if (resOps == null) {
+                        resOps = new ArrayList<>();
+                    }
+                    resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(index), uidOps.valueAt(index),
+                            0, 0, 0, -1, null));
+                }
+            }
+        }
+        return resOps;
+    }
+
     @Override
     public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
@@ -473,6 +496,27 @@
         }
     }
 
+    @Override
+    public List<AppOpsManager.PackageOps> getUidOps(int uid, int[] ops) {
+        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), null);
+        synchronized (this) {
+            UidState uidState = getUidStateLocked(uid, false);
+            if (uidState == null) {
+                return null;
+            }
+            ArrayList<AppOpsManager.OpEntry> resOps = collectOps(uidState.opModes, ops);
+            if (resOps == null) {
+                return null;
+            }
+            ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
+            AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
+                    null, uidState.uid, resOps);
+            res.add(resPackage);
+            return res;
+        }
+    }
+
     private void pruneOp(Op op, int uid, String packageName) {
         if (op.time == 0 && op.rejectTime == 0) {
             Ops ops = getOpsRawLocked(uid, packageName, false);
@@ -1669,6 +1713,7 @@
         int op;
         int mode;
         int packageUid;
+        int nonpackageUid;
 
         Shell(IAppOpsService iface, AppOpsService internal) {
             mInterface = iface;
@@ -1790,15 +1835,59 @@
             if (userId == UserHandle.USER_CURRENT) {
                 userId = ActivityManager.getCurrentUser();
             }
-            if ("root".equals(packageName)) {
-                packageUid = 0;
-            } else {
-                packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+            nonpackageUid = -1;
+            try {
+                nonpackageUid = Integer.parseInt(packageName);
+            } catch (NumberFormatException e) {
             }
-            if (packageUid < 0) {
-                err.println("Error: No UID for " + packageName + " in user " + userId);
-                return -1;
+            if (nonpackageUid == -1 && packageName.length() > 1 && packageName.charAt(0) == 'u'
+                    && packageName.indexOf('.') < 0) {
+                int i = 1;
+                while (i < packageName.length() && packageName.charAt(i) >= '0'
+                        && packageName.charAt(i) <= '9') {
+                    i++;
+                }
+                if (i > 1 && i < packageName.length()) {
+                    String userStr = packageName.substring(1, i);
+                    try {
+                        int user = Integer.parseInt(userStr);
+                        char type = packageName.charAt(i);
+                        i++;
+                        int startTypeVal = i;
+                        while (i < packageName.length() && packageName.charAt(i) >= '0'
+                                && packageName.charAt(i) <= '9') {
+                            i++;
+                        }
+                        if (i > startTypeVal) {
+                            String typeValStr = packageName.substring(startTypeVal, i);
+                            try {
+                                int typeVal = Integer.parseInt(typeValStr);
+                                if (type == 'a') {
+                                    nonpackageUid = UserHandle.getUid(user,
+                                            typeVal + Process.FIRST_APPLICATION_UID);
+                                } else if (type == 's') {
+                                    nonpackageUid = UserHandle.getUid(user, typeVal);
+                                }
+                            } catch (NumberFormatException e) {
+                            }
+                        }
+                    } catch (NumberFormatException e) {
+                    }
+                }
+            }
+            if (nonpackageUid != -1) {
+                packageName = null;
+            } else {
+                if ("root".equals(packageName)) {
+                    packageUid = 0;
+                } else {
+                    packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
+                            PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+                }
+                if (packageUid < 0) {
+                    err.println("Error: No UID for " + packageName + " in user " + userId);
+                    return -1;
+                }
             }
             return 0;
         }
@@ -1814,9 +1903,9 @@
         pw.println("AppOps service (appops) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  set [--user <USER_ID>] <PACKAGE> <OP> <MODE>");
+        pw.println("  set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>");
         pw.println("    Set the mode for a particular application and operation.");
-        pw.println("  get [--user <USER_ID>] <PACKAGE> [<OP>]");
+        pw.println("  get [--user <USER_ID>] <PACKAGE | UID> [<OP>]");
         pw.println("    Return the mode for a particular application and optional operation.");
         pw.println("  query-op [--user <USER_ID>] <OP> [<MODE>]");
         pw.println("    Print all packages that currently have the given op in the given mode.");
@@ -1858,7 +1947,12 @@
                         return -1;
                     }
 
-                    shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode);
+                    if (shell.packageName != null) {
+                        shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName,
+                                mode);
+                    } else {
+                        shell.mInterface.setUidMode(shell.op, shell.nonpackageUid, mode);
+                    }
                     return 0;
                 }
                 case "get": {
@@ -1867,9 +1961,16 @@
                         return res;
                     }
 
-                    List<AppOpsManager.PackageOps> ops = shell.mInterface.getOpsForPackage(
-                            shell.packageUid, shell.packageName,
-                            shell.op != AppOpsManager.OP_NONE ? new int[] {shell.op} : null);
+                    List<AppOpsManager.PackageOps> ops;
+                    if (shell.packageName != null) {
+                        ops = shell.mInterface.getOpsForPackage(
+                                shell.packageUid, shell.packageName,
+                                shell.op != AppOpsManager.OP_NONE ? new int[]{shell.op} : null);
+                    } else {
+                        ops = shell.mInterface.getUidOps(
+                                shell.nonpackageUid,
+                                shell.op != AppOpsManager.OP_NONE ? new int[]{shell.op} : null);
+                    }
                     if (ops == null || ops.size() <= 0) {
                         pw.println("No operations.");
                         return 0;