DO NOT MERGE SetMode: Don't call into PM with AppOps lock held

In the setmode paths do not call into package manager with the app-ops
lock held. Otherwise we might get dead-locks then someone calls into
app-ops manager with the package manager lock held.

Test: Booted and saw no errors from the changed code
Bug: 124731615
Bug: 146463528
Bug: 146590200
Change-Id: If074bed1bd246a81791a7d9fd656f42f1a755495
(cherry picked from commit ec142a52fe6da0a18a1274249ae452cc5a8669fa)
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 314e04c..8261eb3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1110,8 +1110,8 @@
             return Collections.emptyList();
         }
         synchronized (this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */,
-                    false /* uidMismatchExpected */);
+            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* isPrivileged */,
+                    false /* edit */);
             if (pkgOps == null) {
                 return null;
             }
@@ -1208,8 +1208,7 @@
 
     private void pruneOp(Op op, int uid, String packageName) {
         if (!op.hasAnyTime()) {
-            Ops ops = getOpsRawLocked(uid, packageName, false /* edit */,
-                    false /* uidMismatchExpected */);
+            Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */);
             if (ops != null) {
                 ops.remove(op.op);
                 if (ops.size() <= 0) {
@@ -1409,11 +1408,6 @@
         }
     }
 
-    @Override
-    public void setMode(int code, int uid, String packageName, int mode) {
-        setMode(code, uid, packageName, mode, true, false);
-    }
-
     /**
      * Sets the mode for a certain op and uid.
      *
@@ -1421,19 +1415,25 @@
      * @param uid The UID for which to set
      * @param packageName The package for which to set
      * @param mode The new mode to set
-     * @param verifyUid Iff {@code true}, check that the package name belongs to the uid
-     * @param isPrivileged Whether the package is privileged. (Only used if {@code verifyUid ==
-     *                     false})
      */
-    private void setMode(int code, int uid, @NonNull String packageName, int mode,
-            boolean verifyUid, boolean isPrivileged) {
+    @Override
+    public void setMode(int code, int uid, @NonNull String packageName, int mode) {
         enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
         verifyIncomingOp(code);
         ArraySet<ModeCallback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
+
+        boolean isPrivileged;
+        try {
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+        } catch (SecurityException e) {
+            Slog.e(TAG, "Cannot setMode", e);
+            return;
+        }
+
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
-            Op op = getOpLocked(code, uid, packageName, true, verifyUid, isPrivileged);
+            Op op = getOpLocked(code, uid, packageName, isPrivileged, true);
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
@@ -1822,11 +1822,19 @@
         if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
             return AppOpsManager.MODE_IGNORED;
         }
-        synchronized (this) {
+
+        boolean isPrivileged;
+        try {
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+        } catch (Exception e) {
             if (verify) {
-                checkPackage(uid, packageName);
+                throw e;
             }
-            if (isOpRestrictedLocked(uid, code, packageName)) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+
+        synchronized (this) {
+            if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -1836,7 +1844,7 @@
                 final int rawMode = uidState.opModes.get(code);
                 return raw ? rawMode : uidState.evalMode(code, rawMode);
             }
-            Op op = getOpLocked(code, uid, packageName, false, verify, false);
+            Op op = getOpLocked(code, uid, packageName, false, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -1941,14 +1949,12 @@
     @Override
     public int checkPackage(int uid, String packageName) {
         Preconditions.checkNotNull(packageName);
-        synchronized (this) {
-            Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
-                    true /* uidMismatchExpected */);
-            if (ops != null) {
-                return AppOpsManager.MODE_ALLOWED;
-            } else {
-                return AppOpsManager.MODE_ERRORED;
-            }
+        try {
+            verifyAndGetIsPrivileged(uid, packageName);
+
+            return AppOpsManager.MODE_ALLOWED;
+        } catch (SecurityException ignored) {
+            return AppOpsManager.MODE_ERRORED;
         }
     }
 
@@ -2011,9 +2017,16 @@
 
     private int noteOperationUnchecked(int code, int uid, String packageName,
             int proxyUid, String proxyPackageName, @OpFlags int flags) {
+        boolean isPrivileged;
+        try {
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+        } catch (SecurityException e) {
+            Slog.e(TAG, "Cannot startOperation", e);
+            return AppOpsManager.MODE_IGNORED;
+        }
+
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
-                    false /* uidMismatchExpected */);
+            final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */);
             if (ops == null) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
@@ -2022,7 +2035,7 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, true);
-            if (isOpRestrictedLocked(uid, code, packageName)) {
+            if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
                 return AppOpsManager.MODE_IGNORED;
@@ -2181,16 +2194,25 @@
             return  AppOpsManager.MODE_IGNORED;
         }
         ClientState client = (ClientState)token;
+
+        boolean isPrivileged;
+        try {
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+        } catch (SecurityException e) {
+            Slog.e(TAG, "Cannot startOperation", e);
+            return AppOpsManager.MODE_IGNORED;
+        }
+
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
-                    false /* uidMismatchExpected */);
+            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, isPrivileged,
+                    true /* edit */);
             if (ops == null) {
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                         + " package " + resolvedPackageName);
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, true);
-            if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
+            if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -2262,8 +2284,17 @@
             return;
         }
         ClientState client = (ClientState) token;
+
+        boolean isPrivileged;
+        try {
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+        } catch (SecurityException e) {
+            Slog.e(TAG, "Cannot finishOperation", e);
+            return;
+        }
+
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false);
+            Op op = getOpLocked(code, uid, resolvedPackageName, isPrivileged, true);
             if (op == null) {
                 return;
             }
@@ -2513,8 +2544,76 @@
         uidState.pendingStateCommitTime = 0;
     }
 
-    private Ops getOpsRawLocked(int uid, String packageName, boolean edit,
-            boolean uidMismatchExpected) {
+    /**
+     * Verify that package belongs to uid and return whether the package is privileged.
+     *
+     * @param uid The uid the package belongs to
+     * @param packageName The package the might belong to the uid
+     *
+     * @return {@code true} iff the package is privileged
+     */
+    private boolean verifyAndGetIsPrivileged(int uid, String packageName) {
+        if (uid == Process.ROOT_UID) {
+            // For backwards compatibility, don't check package name for root UID.
+            return false;
+        }
+
+        // Do not check if uid/packageName is already known
+        synchronized (this) {
+            UidState uidState = mUidStates.get(uid);
+            if (uidState != null && uidState.pkgOps != null) {
+                Ops ops = uidState.pkgOps.get(packageName);
+
+                if (ops != null) {
+                    return ops.isPrivileged;
+                }
+            }
+        }
+
+        boolean isPrivileged = false;
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            int pkgUid;
+
+            ApplicationInfo appInfo = LocalServices.getService(PackageManagerInternal.class)
+                    .getApplicationInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+                                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                    | PackageManager.MATCH_INSTANT,
+                            Process.SYSTEM_UID, UserHandle.getUserId(uid));
+            if (appInfo != null) {
+                pkgUid = appInfo.uid;
+                isPrivileged = (appInfo.privateFlags
+                        & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+            } else {
+                pkgUid = resolveUid(packageName);
+                if (pkgUid >= 0) {
+                    isPrivileged = false;
+                }
+            }
+            if (pkgUid != uid) {
+                throw new SecurityException("Specified package " + packageName + " under uid " + uid
+                        + " but it is really " + pkgUid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        return isPrivileged;
+    }
+
+    /**
+     * Get (and potentially create) ops.
+     *
+     * @param uid The uid the package belongs to
+     * @param packageName The name of the package
+     * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false)
+     * @param edit If an ops does not exist, create the ops?
+
+     * @return
+     */
+    private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, boolean edit) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
             return null;
@@ -2532,47 +2631,6 @@
             if (!edit) {
                 return null;
             }
-            boolean isPrivileged = false;
-            // This is the first time we have seen this package name under this uid,
-            // so let's make sure it is valid.
-            if (uid != 0) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    int pkgUid = -1;
-                    try {
-                        ApplicationInfo appInfo = ActivityThread.getPackageManager()
-                                .getApplicationInfo(packageName,
-                                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                                        UserHandle.getUserId(uid));
-                        if (appInfo != null) {
-                            pkgUid = appInfo.uid;
-                            isPrivileged = (appInfo.privateFlags
-                                    & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
-                        } else {
-                            pkgUid = resolveUid(packageName);
-                            if (pkgUid >= 0) {
-                                isPrivileged = false;
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Could not contact PackageManager", e);
-                    }
-                    if (pkgUid != uid) {
-                        // Oops!  The package name is not valid for the uid they are calling
-                        // under.  Abort.
-                        if (!uidMismatchExpected) {
-                            RuntimeException ex = new RuntimeException("here");
-                            ex.fillInStackTrace();
-                            Slog.w(TAG, "Bad call: specified package " + packageName
-                                    + " under uid " + uid + " but it is really " + pkgUid, ex);
-                        }
-                        return null;
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
             ops = new Ops(packageName, uidState, isPrivileged);
             uidState.pkgOps.put(packageName, ops);
         }
@@ -2580,7 +2638,7 @@
     }
 
     /**
-     * Get the state of all ops for a package, <b>don't verify that package belongs to uid</b>.
+     * Get the state of all ops for a package.
      *
      * <p>Usually callers should use {@link #getOpLocked} and not call this directly.
      *
@@ -2638,23 +2696,15 @@
      * @param code The code of the op
      * @param uid The uid the of the package
      * @param packageName The package name for which to get the state for
+     * @param isPrivileged Whether the package is privileged or not (only used if {@code edit
+     *                     == true})
      * @param edit Iff {@code true} create the {@link Op} object if not yet created
-     * @param verifyUid Iff {@code true} check that the package belongs to the uid
-     * @param isPrivileged Whether the package is privileged or not (only used if {@code verifyUid
-     *                     == false})
      *
      * @return The {@link Op state} of the op
      */
-    private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit,
-            boolean verifyUid, boolean isPrivileged) {
-        Ops ops;
-
-        if (verifyUid) {
-            ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */);
-        }  else {
-            ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
-        }
-
+    private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
+            boolean isPrivileged, boolean edit) {
+        Ops ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
         if (ops == null) {
             return null;
         }
@@ -2684,7 +2734,8 @@
         return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
     }
 
-    private boolean isOpRestrictedLocked(int uid, int code, String packageName) {
+    private boolean isOpRestrictedLocked(int uid, int code, String packageName,
+            boolean isPrivileged) {
         int userHandle = UserHandle.getUserId(uid);
         final int restrictionSetCount = mOpUserRestrictions.size();
 
@@ -2696,8 +2747,8 @@
                 if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
-                        Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
-                                false /* uidMismatchExpected */);
+                        Ops ops = getOpsRawLocked(uid, packageName, isPrivileged,
+                                true /* edit */);
                         if ((ops != null) && ops.isPrivileged) {
                             return false;
                         }
@@ -3068,7 +3119,7 @@
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
                         synchronized (this) {
                             Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(),
-                                    false /* edit */, false /* uidMismatchExpected */);
+                                    false /* isPrivileged */, false /* edit */);
                             // Should always be present as the list of PackageOps is generated
                             // from Ops.
                             if (ops != null) {