Create recents protectionLevel

In preparation for permission BROADCAST_CLOSE_SYSTEM_DIALOGS
(go/close-system-dialogs), which will be signature|recents in order to
lock down Intent.ACTION_CLOSE_SYSTEM_DIALOGS. That intent is used by
recents when it's invoked.

Currently, recents is configured via existing config
config_recentsComponentName, so using that to determine the recents
package in PM.

It's my understanding that the package defined in such config will be
responsible for recents no matter which launcher is active. It's also my
understanding that the package being explicit in the system config means
that package has to come from the system image, hence it's fine to use
ensureSystemPackageName() in PM. Please advise if any of
this is wrong.

Test: Will work on CTS once we agree on the proposal here.
Bug: 159105552
Change-Id: I9f73e1f05d8df26666da156b672e370dce5030de
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 05dbf00..d54c2ac 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2276,6 +2276,7 @@
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
+    field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
     field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index c6450ff..cd9ba6a 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -251,6 +251,16 @@
     @SystemApi
     public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>recents</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int PROTECTION_FLAG_RECENTS = 0x2000000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -274,6 +284,7 @@
             PROTECTION_FLAG_APP_PREDICTOR,
             PROTECTION_FLAG_COMPANION,
             PROTECTION_FLAG_RETAIL_DEMO,
+            PROTECTION_FLAG_RECENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -532,6 +543,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
             protLevel.append("|retailDemo");
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0) {
+            protLevel.append("|recents");
+        }
         return protLevel.toString();
     }
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index fc9f670..5dac650 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -303,6 +303,9 @@
         <!-- Additional flag from base permission type: this permission will be granted to the
              retail demo app, as defined by the OEM. -->
         <flag name="retailDemo" value="0x1000000" />
+        <!-- Additional flag from base permission type: this permission will be granted to the
+             recents app. -->
+        <flag name="recents" value="0x2000000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index f205662..6989e32 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -77,6 +77,7 @@
             PACKAGE_WIFI,
             PACKAGE_COMPANION,
             PACKAGE_RETAIL_DEMO,
+            PACKAGE_RECENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
@@ -97,9 +98,10 @@
     public static final int PACKAGE_WIFI = 13;
     public static final int PACKAGE_COMPANION = 14;
     public static final int PACKAGE_RETAIL_DEMO = 15;
+    public static final int PACKAGE_RECENTS = 16;
     // Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
     // Please note the numbers should be continuous.
-    public static final int LAST_KNOWN_PACKAGE = PACKAGE_RETAIL_DEMO;
+    public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS;
 
     @IntDef(flag = true, prefix = "RESOLVE_", value = {
             RESOLVE_NON_BROWSER_ONLY,
@@ -1060,6 +1062,8 @@
                 return "Retail Demo";
             case PACKAGE_OVERLAY_CONFIG_SIGNATURE:
                 return "Overlay Config Signature";
+            case PACKAGE_RECENTS:
+                return "Recents";
         }
         return "Unknown";
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a03c405c..cb13c14 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -137,6 +137,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
@@ -342,7 +343,6 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -397,7 +397,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -1215,6 +1214,7 @@
         public ViewCompiler viewCompiler;
         public @Nullable String wellbeingPackage;
         public @Nullable String retailDemoPackage;
+        public @Nullable String recentsPackage;
         public ComponentName resolveComponentName;
         public ArrayMap<String, AndroidPackage> packages;
         public boolean enableFreeCacheV2;
@@ -1747,6 +1747,7 @@
     final @Nullable String mSharedSystemSharedLibraryPackageName;
     final @Nullable String mRetailDemoPackage;
     final @Nullable String mOverlayConfigSignaturePackage;
+    final @Nullable String mRecentsPackage;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
     private final CompilerStats mCompilerStats = new CompilerStats();
@@ -2990,6 +2991,7 @@
         mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
         mWellbeingPackage = testParams.wellbeingPackage;
         mRetailDemoPackage = testParams.retailDemoPackage;
+        mRecentsPackage = testParams.recentsPackage;
         mDocumenterPackage = testParams.documenterPackage;
         mConfiguratorPackage = testParams.configuratorPackage;
         mAppPredictionServicePackage = testParams.appPredictionServicePackage;
@@ -3574,6 +3576,7 @@
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
             mRetailDemoPackage = getRetailDemoPackageName();
             mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName();
+            mRecentsPackage = getRecentsPackageName();
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -21239,15 +21242,8 @@
 
     @Override
     public @Nullable String getAttentionServicePackageName() {
-        final String flattenedComponentName =
-                mContext.getString(R.string.config_defaultAttentionService);
-        if (flattenedComponentName != null) {
-            ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName);
-            if (componentName != null && componentName.getPackageName() != null) {
-                return ensureSystemPackageName(componentName.getPackageName());
-            }
-        }
-        return null;
+        return ensureSystemPackageName(
+                getPackageFromComponentString(R.string.config_defaultAttentionService));
     }
 
     private @Nullable String getDocumenterPackageName() {
@@ -21282,17 +21278,8 @@
 
     @Override
     public String getAppPredictionServicePackageName() {
-        String flattenedAppPredictionServiceComponentName =
-                mContext.getString(R.string.config_defaultAppPredictionService);
-        if (flattenedAppPredictionServiceComponentName == null) {
-            return null;
-        }
-        ComponentName appPredictionServiceComponentName =
-                ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName);
-        if (appPredictionServiceComponentName == null) {
-            return null;
-        }
-        return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
+        return ensureSystemPackageName(
+                getPackageFromComponentString(R.string.config_defaultAppPredictionService));
     }
 
     private @NonNull String[] dropNonSystemPackages(@NonNull String[] pkgNames) {
@@ -21309,19 +21296,8 @@
 
     @Override
     public String getSystemCaptionsServicePackageName() {
-        String flattenedSystemCaptionsServiceComponentName =
-                mContext.getString(R.string.config_defaultSystemCaptionsService);
-
-        if (TextUtils.isEmpty(flattenedSystemCaptionsServiceComponentName)) {
-            return null;
-        }
-
-        ComponentName systemCaptionsServiceComponentName =
-                ComponentName.unflattenFromString(flattenedSystemCaptionsServiceComponentName);
-        if (systemCaptionsServiceComponentName == null) {
-            return null;
-        }
-        return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName());
+        return ensureSystemPackageName(
+                getPackageFromComponentString(R.string.config_defaultSystemCaptionsService));
     }
 
     @Override
@@ -21339,19 +21315,8 @@
 
     @Override
     public String getContentCaptureServicePackageName() {
-        final String flattenedContentCaptureService =
-                mContext.getString(R.string.config_defaultContentCaptureService);
-
-        if (TextUtils.isEmpty(flattenedContentCaptureService)) {
-            return null;
-        }
-
-        final ComponentName contentCaptureServiceComponentName =
-                ComponentName.unflattenFromString(flattenedContentCaptureService);
-        if (contentCaptureServiceComponentName == null) {
-            return null;
-        }
-        return ensureSystemPackageName(contentCaptureServiceComponentName.getPackageName());
+        return ensureSystemPackageName(
+                getPackageFromComponentString(R.string.config_defaultContentCaptureService));
     }
 
     public String getOverlayConfigSignaturePackageName() {
@@ -21395,6 +21360,26 @@
     }
 
     @Nullable
+    private String getRecentsPackageName() {
+        return ensureSystemPackageName(
+                getPackageFromComponentString(R.string.config_recentsComponentName));
+
+    }
+
+    @Nullable
+    private String getPackageFromComponentString(@StringRes int stringResId) {
+        final String componentString = mContext.getString(stringResId);
+        if (TextUtils.isEmpty(componentString)) {
+            return null;
+        }
+        final ComponentName component = ComponentName.unflattenFromString(componentString);
+        if (component == null) {
+            return null;
+        }
+        return component.getPackageName();
+    }
+
+    @Nullable
     private String ensureSystemPackageName(@Nullable String packageName) {
         if (packageName == null) {
             return null;
@@ -25002,6 +24987,8 @@
                             : new String[] {mRetailDemoPackage};
                 case PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE:
                     return filterOnlySystemPackages(getOverlayConfigSignaturePackageName());
+                case PackageManagerInternal.PACKAGE_RECENTS:
+                    return filterOnlySystemPackages(mRecentsPackage);
                 default:
                     return ArrayUtils.emptyArray(String.class);
             }
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 0245b28..0ee61f8 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -331,6 +331,10 @@
         return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
     }
 
+    public boolean isRecents() {
+        return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0;
+    }
+
     public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) {
         if (!oldPackageName.equals(mPermissionInfo.packageName)) {
             return;
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 f819063..3a33fa8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3635,6 +3635,13 @@
             // Special permission granted only to the OEM specified retail demo app
             allowed = true;
         }
+        if (!allowed && bp.isRecents()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                PackageManagerInternal.PACKAGE_RECENTS, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // Special permission for the recents app.
+            allowed = true;
+        }
         return allowed;
     }