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/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 1f12b74..4970b8d 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -17,15 +17,16 @@
 package com.android.server.am;
 
 import android.content.Intent;
-import android.net.Uri;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Log;
+import android.util.Slog;
 
+import com.android.server.am.ActivityManagerService.GrantUri;
 import com.google.android.collect.Sets;
 
 import java.io.PrintWriter;
 import java.util.Comparator;
-import java.util.HashSet;
 
 /**
  * Description of a permission granted to an app to access a particular URI.
@@ -50,7 +51,7 @@
     /** Cached UID of {@link #targetPkg}; should not be persisted */
     final int targetUid;
 
-    final Uri uri;
+    final GrantUri uri;
 
     /**
      * Allowed modes. All permission enforcement should use this field. Must
@@ -61,12 +62,13 @@
      */
     int modeFlags = 0;
 
-    /** Allowed modes with explicit owner. */
+    /** Allowed modes with active owner. */
     int ownedModeFlags = 0;
     /** Allowed modes without explicit owner. */
     int globalModeFlags = 0;
     /** Allowed modes that have been offered for possible persisting. */
     int persistableModeFlags = 0;
+
     /** Allowed modes that should be persisted across device boots. */
     int persistedModeFlags = 0;
 
@@ -78,12 +80,12 @@
 
     private static final long INVALID_TIME = Long.MIN_VALUE;
 
-    private HashSet<UriPermissionOwner> mReadOwners;
-    private HashSet<UriPermissionOwner> mWriteOwners;
+    private ArraySet<UriPermissionOwner> mReadOwners;
+    private ArraySet<UriPermissionOwner> mWriteOwners;
 
     private String stringName;
 
-    UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) {
+    UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
         this.userHandle = UserHandle.getUserId(targetUid);
         this.sourcePkg = sourcePkg;
         this.targetPkg = targetPkg;
@@ -100,6 +102,9 @@
      * global or owner grants.
      */
     void initPersistedModes(int modeFlags, long createdTime) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
         persistableModeFlags = modeFlags;
         persistedModeFlags = modeFlags;
         persistedCreateTime = createdTime;
@@ -107,7 +112,11 @@
         updateModeFlags();
     }
 
-    void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) {
+    void grantModes(int modeFlags, UriPermissionOwner owner) {
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
         if (persistable) {
             persistableModeFlags |= modeFlags;
         }
@@ -130,10 +139,14 @@
      * @return if mode changes should trigger persisting.
      */
     boolean takePersistableModes(int modeFlags) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
         if ((modeFlags & persistableModeFlags) != modeFlags) {
-            throw new SecurityException("Requested flags 0x"
+            Slog.w(TAG, "Requested flags 0x"
                     + Integer.toHexString(modeFlags) + ", but only 0x"
                     + Integer.toHexString(persistableModeFlags) + " are allowed");
+            return false;
         }
 
         final int before = persistedModeFlags;
@@ -148,6 +161,9 @@
     }
 
     boolean releasePersistableModes(int modeFlags) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
         final int before = persistedModeFlags;
 
         persistableModeFlags &= ~modeFlags;
@@ -164,7 +180,11 @@
     /**
      * @return if mode changes should trigger persisting.
      */
-    boolean clearModes(int modeFlags, boolean persistable) {
+    boolean revokeModes(int modeFlags) {
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
         final int before = persistedModeFlags;
 
         if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
@@ -208,6 +228,8 @@
      * Return strength of this permission grant for the given flags.
      */
     public int getStrength(int modeFlags) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         if ((persistableModeFlags & modeFlags) == modeFlags) {
             return STRENGTH_PERSISTABLE;
         } else if ((globalModeFlags & modeFlags) == modeFlags) {
@@ -221,7 +243,7 @@
 
     private void addReadOwner(UriPermissionOwner owner) {
         if (mReadOwners == null) {
-            mReadOwners = Sets.newHashSet();
+            mReadOwners = Sets.newArraySet();
             ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
             updateModeFlags();
         }
@@ -246,7 +268,7 @@
 
     private void addWriteOwner(UriPermissionOwner owner) {
         if (mWriteOwners == null) {
-            mWriteOwners = Sets.newHashSet();
+            mWriteOwners = Sets.newArraySet();
             ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
             updateModeFlags();
         }
@@ -333,7 +355,7 @@
         final int userHandle;
         final String sourcePkg;
         final String targetPkg;
-        final Uri uri;
+        final GrantUri uri;
         final int persistedModeFlags;
         final long persistedCreateTime;
 
@@ -352,6 +374,6 @@
     }
 
     public android.content.UriPermission buildPersistedPublicApiObject() {
-        return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime);
+        return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
     }
 }