Merge changes from topic "revokeLocOnUpgrade"

* changes:
  Control revoke-on-upgrade behavior for loc perm
  Revoke location permissions on upgrade from Pre-Q
  No special case for location permission
  Inherit flags when inheriting permission state
diff --git a/api/system-current.txt b/api/system-current.txt
index 805bfb5..a15a8c5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6117,6 +6117,7 @@
     field public static final String LAST_SETUP_SHOWN = "last_setup_shown";
     field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
     field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
+    field public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE = "location_permissions_upgrade_to_q_mode";
     field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
     field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
     field public static final String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f8dfc58..ecbd673 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8563,6 +8563,19 @@
         public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
                 "location_access_check_delay_millis";
 
+        /**
+         * What should happen to the location permissions when upgraded to Android Q.
+         *
+         * <ul>
+         *     <li>0/unset == revoke permissions</li>
+         *     <li>anything else == Don't do anything</li>
+         * </ul>
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE =
+                "location_permissions_upgrade_to_q_mode";
 
         /**
          * Comma separated list of enabled overlay packages for all android.theme.customization.*
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6360a5f..08286a1 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -235,6 +235,10 @@
         optional SettingProto mode = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
         // The App or module that changes the location mode.
         optional SettingProto changer = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+        // What should happen to the location permissions when upgraded to Android Q.
+        // 0 == revoke permissions. Anything else == do nothing.
+        optional SettingProto permissions_upgrade_to_q_mode = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Location location = 31;
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 67e364b..43f8db1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -643,6 +643,7 @@
                  Settings.Secure.LAST_SETUP_SHOWN,
                  Settings.Secure.LOCATION_CHANGER,
                  Settings.Secure.LOCATION_MODE,
+                 Settings.Secure.LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE,
                  Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
                  Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                  Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f7f34f6..2d5606c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1977,6 +1977,9 @@
         dumpSetting(s, p,
                 Settings.Secure.LOCATION_CHANGER,
                 SecureSettingsProto.Location.CHANGER);
+        dumpSetting(s, p,
+                Settings.Secure.LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE,
+                SecureSettingsProto.Location.PERMISSIONS_UPGRADE_TO_Q_MODE);
         p.end(locationToken);
 
         final long locationAccessCheckToken = p.start(SecureSettingsProto.LOCATION_ACCESS_CHECK);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b64b7c64..251b34bc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2945,7 +2945,7 @@
                         + mSdkVersion + "; regranting permissions for internal storage");
             }
             mPermissionManager.updateAllPermissions(
-                    StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
+                    StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, false, mPackages.values(),
                     mPermissionCallback);
             ver.sdkVersion = mSdkVersion;
 
@@ -5382,7 +5382,7 @@
 
         synchronized (mPackages) {
             mPermissionManager.updateAllPermissions(
-                    StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
+                    StorageManager.UUID_PRIVATE_INTERNAL, false, false, mPackages.values(),
                     mPermissionCallback);
             for (int userId : UserManagerService.getInstance().getUserIds()) {
                 final int packageCount = mPackages.size();
@@ -20685,8 +20685,8 @@
         // try optimizing this.
         synchronized (mPackages) {
             mPermissionManager.updateAllPermissions(
-                    StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
-                    mPermissionCallback);
+                    StorageManager.UUID_PRIVATE_INTERNAL, false, mIsPreQUpgrade,
+                    mPackages.values(), mPermissionCallback);
         }
 
         // Watch for external volumes that come and go over time
@@ -21676,8 +21676,8 @@
                 logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
                         + mSdkVersion + "; regranting permissions for " + volumeUuid);
             }
-            mPermissionManager.updateAllPermissions(volumeUuid, sdkUpdated, mPackages.values(),
-                    mPermissionCallback);
+            mPermissionManager.updateAllPermissions(volumeUuid, sdkUpdated, false,
+                    mPackages.values(), mPermissionCallback);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
@@ -22676,7 +22676,7 @@
         synchronized(mPackages) {
             // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
             mPermissionManager.updateAllPermissions(
-                    StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
+                    StorageManager.UUID_PRIVATE_INTERNAL, true, false, mPackages.values(),
                     mPermissionCallback);
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 03da962..ac7338e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -16,14 +16,11 @@
 
 package com.android.server.pm.permission;
 
-import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
-import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.permissionToOp;
 import static android.app.AppOpsManager.permissionToOpCode;
@@ -77,6 +74,7 @@
 import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
 import android.permission.PermissionManagerInternal;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -140,6 +138,18 @@
     /** Empty array to avoid allocations */
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
+    /**
+     * When these flags are set, the system should not automatically modify the permission grant
+     * state.
+     */
+    private static final int BLOCKING_PERMISSION_FLAGS = FLAG_PERMISSION_SYSTEM_FIXED
+            | FLAG_PERMISSION_POLICY_FIXED
+            | FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+
+    /** Permission flags set by the user */
+    private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET
+            | FLAG_PERMISSION_USER_FIXED;
+
     /** If the permission of the value is granted, so is the key */
     private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
 
@@ -1203,9 +1213,8 @@
 
                             int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 
-                            if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
-                                    | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
-                                    == 0 && supportsRuntimePermissions) {
+                            if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
+                                    && supportsRuntimePermissions) {
                                 int revokeResult = ps.revokeRuntimePermission(bp, userId);
                                 if (revokeResult != PERMISSION_OPERATION_FAILURE) {
                                     if (DEBUG_PERMISSIONS) {
@@ -1215,8 +1224,7 @@
                                     }
                                 }
 
-                                flagsToRemove |=
-                                        FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET;
+                                flagsToRemove |= USER_PERMISSION_FLAGS;
 
                                 List<String> fgPerms = mBackgroundPermissions.get(permission);
                                 if (fgPerms != null) {
@@ -1271,6 +1279,7 @@
         if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
             if (permissionToOp(newPerm) != null) {
                 int mostLenientSourceMode = MODE_ERRORED;
+                int flags = 0;
 
                 // Find most lenient source permission state.
                 int numSourcePerms = sourcePerms.size();
@@ -1284,20 +1293,19 @@
                             int mode = appOpsManager.unsafeCheckOpRaw(sourceOp,
                                     getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName);
 
-                            if (mode == MODE_FOREGROUND) {
-                                throw new IllegalArgumentException("split permission" + sourcePerm
-                                        + " has app-op state " + AppOpsManager.MODE_NAMES[mode]);
+                            if (mode == MODE_FOREGROUND || mode == MODE_ERRORED) {
+                                Log.wtf(TAG, "split permission" + sourcePerm + " has app-op state "
+                                        + AppOpsManager.MODE_NAMES[mode]);
+
+                                continue;
                             }
 
-                            // Leniency order: allowed > ignored > default
-                            if (mode == MODE_ALLOWED) {
-                                mostLenientSourceMode = MODE_ALLOWED;
-                                break;
-                            } else if (mode == MODE_IGNORED) {
-                                mostLenientSourceMode = MODE_IGNORED;
-                            } else if (mode == MODE_DEFAULT
-                                    && mostLenientSourceMode != MODE_IGNORED) {
-                                mostLenientSourceMode = MODE_DEFAULT;
+                            // Leniency order: allowed < ignored < default
+                            if (mode < mostLenientSourceMode) {
+                                mostLenientSourceMode = mode;
+                                flags = ps.getPermissionFlags(sourcePerm, userId);
+                            } else if (mode == mostLenientSourceMode) {
+                                flags |= ps.getPermissionFlags(sourcePerm, userId);
                             }
                         }
                     }
@@ -1310,21 +1318,31 @@
                     }
 
                     setAppOpMode(newPerm, pkg, userId, mostLenientSourceMode);
+
+                    // Add permission flags
+                    ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags,
+                            flags);
                 }
             }
         } else {
             boolean isGranted = false;
+            int flags = 0;
 
             int numSourcePerm = sourcePerms.size();
             for (int i = 0; i < numSourcePerm; i++) {
                 String sourcePerm = sourcePerms.valueAt(i);
-                if (ps.hasRuntimePermission(sourcePerm, userId)
-                        && ps.getRuntimePermissionState(sourcePerm, userId).isGranted()) {
+                if ((ps.hasRuntimePermission(sourcePerm, userId))
+                        || ps.hasInstallPermission(sourcePerm)) {
+                    if (!isGranted) {
+                        flags = 0;
+                    }
+
                     isGranted = true;
-                    break;
-                } else if (ps.hasInstallPermission(sourcePerm)) {
-                    isGranted = true;
-                    break;
+                    flags |= ps.getPermissionFlags(sourcePerm, userId);
+                } else {
+                    if (!isGranted) {
+                        flags |= ps.getPermissionFlags(sourcePerm, userId);
+                    }
                 }
             }
 
@@ -1336,6 +1354,9 @@
 
                 ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
             }
+
+            // Add permission flags
+            ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags);
         }
     }
 
@@ -1353,8 +1374,6 @@
             @NonNull PermissionsState origPs,
             @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
             @NonNull int[] updatedUserIds) {
-        AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
-
         String pkgName = pkg.packageName;
         ArraySet<String> newImplicitPermissions = new ArraySet<>();
 
@@ -1417,49 +1436,29 @@
                                 FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
                         updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
 
-                        // SPECIAL BEHAVIOR for background location. Foreground only by default.
-                        if (newPerm.equals(ACCESS_BACKGROUND_LOCATION)) {
-                            int numSourcePerms = sourcePerms.size();
-                            for (int sourcePermNum = 0; sourcePermNum < numSourcePerms;
-                                    sourcePermNum++) {
-                                String sourcePerm = sourcePerms.valueAt(sourcePermNum);
-
-                                if (ps.hasRuntimePermission(sourcePerm, userId)
-                                        && ps.getRuntimePermissionState(sourcePerm, userId)
-                                        .isGranted()
-                                        && appOpsManager.unsafeCheckOpNoThrow(
-                                                permissionToOp(sourcePerm), getUid(userId,
-                                                getAppId(pkg.applicationInfo.uid)), pkgName)
-                                        == MODE_ALLOWED) {
-                                    setAppOpMode(sourcePerm, pkg, userId, MODE_FOREGROUND);
-                                }
-                            }
-                        } else {
-                            boolean inheritsFromInstallPerm = false;
-                            for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
-                                    sourcePermNum++) {
-                                if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
-                                    inheritsFromInstallPerm = true;
-                                    break;
-                                }
-                            }
-
-                            if (!origPs.hasRequestedPermission(sourcePerms)
-                                    && !inheritsFromInstallPerm) {
-                                // Both permissions are new so nothing to inherit.
-                                if (DEBUG_PERMISSIONS) {
-                                    Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
-                                            + " for " + pkgName
-                                            + " as split permission is also new");
-                                }
-
+                        boolean inheritsFromInstallPerm = false;
+                        for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
+                                sourcePermNum++) {
+                            if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
+                                inheritsFromInstallPerm = true;
                                 break;
-                            } else {
-                                // Inherit from new install or existing runtime permissions
-                                inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
-                                        newPerm, ps, pkg, userId);
                             }
                         }
+
+                        if (!origPs.hasRequestedPermission(sourcePerms)
+                                && !inheritsFromInstallPerm) {
+                            // Both permissions are new so nothing to inherit.
+                            if (DEBUG_PERMISSIONS) {
+                                Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+                                        + " for " + pkgName + " as split permission is also new");
+                            }
+
+                            break;
+                        } else {
+                            // Inherit from new install or existing runtime permissions
+                            inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
+                                    newPerm, ps, pkg, userId);
+                        }
                     }
                 }
             }
@@ -2275,12 +2274,62 @@
     }
 
     private void updateAllPermissions(String volumeUuid, boolean sdkUpdated,
-            Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
+            boolean updatePermissionsOnPreQUpdate, Collection<PackageParser.Package> allPackages,
+            PermissionCallback callback) {
         final int flags = UPDATE_PERMISSIONS_ALL |
                 (sdkUpdated
                         ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
                         : 0);
         updatePermissions(null, null, volumeUuid, flags, allPackages, callback);
+
+        if (updatePermissionsOnPreQUpdate) {
+            final int[] userIds = UserManagerService.getInstance().getUserIds();
+
+            for (PackageParser.Package pkg : allPackages) {
+                final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                if (ps == null) {
+                    return;
+                }
+
+                final boolean appSupportsRuntimePermissions =
+                        pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
+                final PermissionsState permsState = ps.getPermissionsState();
+
+                for (String permName : new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
+                        Manifest.permission.ACCESS_COARSE_LOCATION,
+                        Manifest.permission.ACCESS_BACKGROUND_LOCATION}) {
+                    final BasePermission bp = mSettings.getPermissionLocked(permName);
+
+                    for (int userId : userIds) {
+                        if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE, 0, userId)
+                                != 0) {
+                            continue;
+                        }
+
+                        final PermissionState permState = permsState.getRuntimePermissionState(
+                                permName, userId);
+
+                        if (permState != null
+                                && (permState.getFlags() & BLOCKING_PERMISSION_FLAGS) == 0) {
+                            if (permState.isGranted()) {
+                                permsState.updatePermissionFlags(bp, userId,
+                                        USER_PERMISSION_FLAGS, 0);
+                            }
+
+                            if (appSupportsRuntimePermissions) {
+                                permsState.revokeRuntimePermission(bp, userId);
+                            } else {
+                                // Force a review even for apps that were already installed
+                                permsState.updatePermissionFlags(bp, userId,
+                                        FLAG_PERMISSION_REVIEW_REQUIRED,
+                                        FLAG_PERMISSION_REVIEW_REQUIRED);
+                            }
+                        }
+                    }
+                }
+            }
+        }
     }
 
     private void updatePermissions(String changingPkgName, PackageParser.Package changingPkg,
@@ -2735,9 +2784,10 @@
         }
         @Override
         public void updateAllPermissions(String volumeUuid, boolean sdkUpdated,
-                Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
+                boolean updatePermissionsOnPreQUpdate, Collection<PackageParser.Package> allPackages,
+                PermissionCallback callback) {
             PermissionManagerService.this.updateAllPermissions(
-                    volumeUuid, sdkUpdated, allPackages, callback);
+                    volumeUuid, sdkUpdated, updatePermissionsOnPreQUpdate, allPackages, callback);
         }
         @Override
         public String[] getAppOpPermissionPackages(String permName) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 305f165..6c09fa0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -83,7 +83,8 @@
     public abstract void updatePermissions(@Nullable String packageName,
             @Nullable PackageParser.Package pkg, boolean replaceGrant,
             @NonNull Collection<PackageParser.Package> allPacakges, PermissionCallback callback);
-    public abstract void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated,
+    public abstract void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate,
+            boolean updatePermissionsOnPreQUpdate,
             @NonNull Collection<PackageParser.Package> allPacakges, PermissionCallback callback);
 
     /**