Merge "Listen to changes in unused threshold" into sc-dev am: f71b630674 am: 517b0d3b06

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Permission/+/14923257

Change-Id: I38da789799ce1dc021798463b3de6343545da245
diff --git a/OWNERS b/OWNERS
index dafdf0f..8f1443b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,3 @@
 include platform/frameworks/base:/core/java/android/permission/OWNERS
+
+include platform/packages/modules/common:/MODULES_OWNERS  # see go/mainline-owners-policy
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 8a168d0..4b404d4 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -797,6 +797,39 @@
     </role>
 
     <!---
+      ~ A role for the system package that is allowed to manage documents (e.g., attach files etc.)
+      ~ on the device.
+      ~ A package holding this role must comply with the requirements outlined in the Android CDD
+      ~ section "2.2.3. Software" under heading "3.2.3.1/H-0-1".
+      ~ Example link for Android 11:
+      ~ https://source.android.com/compatibility/11/android-11-cdd#2_2_3_software
+    -->
+    <role
+        name="android.app.role.SYSTEM_DOCUMENT_MANAGER"
+        behavior="DocumentManagerRoleBehavior"
+        exclusive="true"
+        minSdkVersion="33"
+        static="true"
+        systemOnly="true"
+        visible="false">
+        <required-components>
+            <!--- Flag value is MATCH_DISABLED_COMPONENTS-->
+            <activity queryFlags="0x00000200">
+                <intent-filter>
+                    <action name="android.intent.action.OPEN_DOCUMENT" />
+                    <category name="android.intent.category.OPENABLE" />
+                    <data mimeType="*/*" />
+                </intent-filter>
+            </activity>
+        </required-components>
+        <permissions>
+            <permission name="android.permission.MANAGE_DOCUMENTS" />
+            <permission name="android.permission.CACHE_CONTENT" />
+            <permission name="android.permission.REMOVE_TASKS" />
+        </permissions>
+    </role>
+
+    <!---
       ~ A role for the system package that serves as the activity recognizer on the device.
       ~ This is the application that provides the data behind the activity recognition
       ~ runtime permission.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java b/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java
index 1e0e3d0..d61c13a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java
@@ -54,7 +54,11 @@
             return false;
         }
         String appOp = AppOpsManager.permissionToOp(appOpPermission);
-        return setAppOpMode(packageName, appOp, AppOpsManager.MODE_ALLOWED, context);
+        boolean changed = setAppOpMode(packageName, appOp, AppOpsManager.MODE_ALLOWED, context);
+        if (changed) {
+            Permissions.setPermissionGrantedByRole(packageName, appOpPermission, true, context);
+        }
+        return changed;
     }
 
     /**
@@ -68,9 +72,14 @@
      */
     public static boolean revoke(@NonNull String packageName, @NonNull String appOpPermission,
             @NonNull Context context) {
+        if (!Permissions.isPermissionGrantedByRole(packageName, appOpPermission, context)) {
+            return false;
+        }
         String appOp = AppOpsManager.permissionToOp(appOpPermission);
         int defaultMode = Permissions.getDefaultAppOpMode(appOp);
-        return setAppOpMode(packageName, appOp, defaultMode, context);
+        boolean changed = setAppOpMode(packageName, appOp, defaultMode, context);
+        Permissions.setPermissionGrantedByRole(packageName, appOpPermission, false, context);
+        return changed;
     }
 
     private static boolean setAppOpMode(@NonNull String packageName, @NonNull String appOp,
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java
new file mode 100644
index 0000000..ce307fd
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.role.model;
+
+import android.content.Context;
+import android.os.Process;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class for behavior of the Document Manager role.
+ */
+public class DocumentManagerRoleBehavior implements RoleBehavior {
+    private static final String TAG = "DocumentManagerRoleBehavior";
+
+    @NonNull
+    @Override
+    public List<String> getDefaultHolders(@NonNull Role role, @NonNull Context context) {
+        List<String> qualifyingPackageNames = role.getQualifyingPackagesAsUser(
+                Process.myUserHandle(), context);
+        if (qualifyingPackageNames.size() == 1) {
+            return qualifyingPackageNames;
+        } else {
+            Log.e(TAG, "There should be exactly one documenter; found "
+                    + qualifyingPackageNames.size() + ": matches=" + qualifyingPackageNames);
+            return Collections.emptyList();
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java b/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
index dee675b..e01ba52 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
@@ -618,7 +618,7 @@
         return (flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
     }
 
-    private static boolean isPermissionGrantedByRole(@NonNull String packageName,
+    static boolean isPermissionGrantedByRole(@NonNull String packageName,
             @NonNull String permission, @NonNull Context context) {
         int flags = getPermissionFlags(packageName, permission, context);
         return (flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE) != 0;
@@ -637,6 +637,13 @@
         packageManager.updatePermissionFlags(permission, packageName, mask, flags, user);
     }
 
+    static void setPermissionGrantedByRole(@NonNull String packageName,
+            @NonNull String permission, boolean grantedByRole, @NonNull Context context) {
+        setPermissionFlags(packageName, permission,
+                grantedByRole ? PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE : 0,
+                PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, context);
+    }
+
     /**
      * Most of the time {@link #isPermissionAndAppOpGranted(String, String, Context)} should be used
      * instead.