Allow prefix-based Uri permission grants.

Define new FLAG_GRANT_PREFIX_URI_PERMISSION which indicates that a
Uri permission grant should also apply to any other Uris that have
matching scheme, authority, and path segments.  For example, a prefix
grant for /foo/ would allow /foo/bar/ but not /foo2/.

Allow persistable and prefix grants to be issued directly through
grantUriPermission().  Relaxing persistable is fine, since it still
requires the receiver to actively take the permission.

Since exact- and prefix-match grants for the same Uri can coexist,
we track them separately using a new UriGrant key.  (Consider the
case where an app separately extends READ|PREFIX and WRITE for
the same Uri: we can't let that become READ|WRITE|PREFIX.)

Fix revoke to always take away persisted permissions.  Move prefix
matching logic to Uri and add tests.  Add new flags to "am" tool, and
various internal uses around Intent and Context.  Switch some lagging
users to ArraySet.

Bug: 10607375
Change-Id: Ia8ce2b88421ff9f2fe5a979a27a026fc445d46f1
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 421956b..67b6737 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.ApplicationInfo;
 import android.util.ArraySet;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -864,8 +865,9 @@
         }
 
         // Migrate any clip data and flags from target.
-        int permFlags = target.getFlags()
-                & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
+        int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION
+                | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                | FLAG_GRANT_PREFIX_URI_PERMISSION);
         if (permFlags != 0) {
             ClipData targetClipData = target.getClipData();
             if (targetClipData == null && target.getData() != null) {
@@ -3425,11 +3427,29 @@
     // Intent flags (see mFlags variable).
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION})
+    @IntDef(flag = true, value = {
+            FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION,
+            FLAG_GRANT_PERSISTABLE_URI_PERMISSION, FLAG_GRANT_PREFIX_URI_PERMISSION })
     @Retention(RetentionPolicy.SOURCE)
     public @interface GrantUriMode {}
 
+    /** @hide */
+    @IntDef(flag = true, value = {
+            FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AccessUriMode {}
+
+    /**
+     * Test if given mode flags specify an access mode, which must be at least
+     * read and/or write.
+     *
+     * @hide
+     */
+    public static boolean isAccessUriMode(int modeFlags) {
+        return (modeFlags & (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) != 0;
+    }
+
     /**
      * If set, the recipient of this Intent will be granted permission to
      * perform read operations on the URI in the Intent's data and any URIs
@@ -3491,6 +3511,17 @@
     public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040;
 
     /**
+     * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the URI permission grant
+     * applies to any URI that is a prefix match against the original granted
+     * URI. (Without this flag, the URI must match exactly for access to be
+     * granted.) Another URI is considered a prefix match only when scheme,
+     * authority, and all path segments defined by the prefix are an exact
+     * match.
+     */
+    public static final int FLAG_GRANT_PREFIX_URI_PERMISSION = 0x00000080;
+
+    /**
      * If set, the new activity is not kept in the history stack.  As soon as
      * the user navigates away from it, the activity is finished.  This may also
      * be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory
@@ -3810,9 +3841,9 @@
     /**
      * @hide Flags that can't be changed with PendingIntent.
      */
-    public static final int IMMUTABLE_FLAGS =
-            FLAG_GRANT_READ_URI_PERMISSION
-            | FLAG_GRANT_WRITE_URI_PERMISSION;
+    public static final int IMMUTABLE_FLAGS = FLAG_GRANT_READ_URI_PERMISSION
+            | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+            | FLAG_GRANT_PREFIX_URI_PERMISSION;
 
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
@@ -6350,6 +6381,8 @@
      *
      * @see #FLAG_GRANT_READ_URI_PERMISSION
      * @see #FLAG_GRANT_WRITE_URI_PERMISSION
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     * @see #FLAG_GRANT_PREFIX_URI_PERMISSION
      * @see #FLAG_DEBUG_LOG_RESOLUTION
      * @see #FLAG_FROM_BACKGROUND
      * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT
@@ -7381,9 +7414,10 @@
                     // Since we migrated in child, we need to promote ClipData
                     // and flags to ourselves to grant.
                     setClipData(target.getClipData());
-                    addFlags(target.getFlags()
-                            & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION
-                                    | FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+                    addFlags(target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION
+                            | FLAG_GRANT_WRITE_URI_PERMISSION
+                            | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                            | FLAG_GRANT_PREFIX_URI_PERMISSION));
                     return true;
                 } else {
                     return false;