CTS tests for prefix-based Uri permission grants.

Verify that new prefix-based grants work correctly, including
how they interact with persistable grants.  Also verify the direct
Context grant/revoke methods.

Bug: 10607375
Change-Id: I594b33b26b517ec72fb2581615bbebae7cde10b9
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index db26eec..8629a87 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -591,6 +591,7 @@
         grantIntent.setClass(getContext(),
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
         Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setComponent(GRANT_URI_PERM_COMP);
         intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE
                 : GrantUriPermission.ACTION_START_ACTIVITY);
@@ -598,6 +599,27 @@
         getContext().sendBroadcast(intent);
     }
 
+    private void grantClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_GRANT_URI);
+        intent.putExtra(GrantUriPermission.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        intent.putExtra(GrantUriPermission.EXTRA_MODE, mode);
+        getContext().sendBroadcast(intent);
+    }
+
+    private void revokeClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_REVOKE_URI);
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        intent.putExtra(GrantUriPermission.EXTRA_MODE, mode);
+        getContext().sendBroadcast(intent);
+    }
+
     private void assertReadingClipAllowed(ClipData clip) {
         for (int i=0; i<clip.getItemCount(); i++) {
             ClipData.Item item = clip.getItemAt(i);
@@ -1355,4 +1377,166 @@
         getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
         receiver.assertSuccess("unexpected outgoing persisted Uri status");
     }
+
+    /**
+     * Validate behavior of prefix permission grants.
+     */
+    public void testGrantPrefixUriPermission() throws Exception {
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        // Now give ourselves exact write access
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        // Verify we have exact write access, but not prefix write
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipAllowed(clip);
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    public void testGrantPersistablePrefixUriPermission() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // Verify we can persist direct grant
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // But we can't take anywhere under the prefix
+        try {
+            resolver.takePersistableUriPermission(targetMeow,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            fail("taking under prefix should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // Should still have access regardless of taking
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // And clean up our grants
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertNoPersistedUriPermission();
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    /**
+     * Validate behavior of directly granting/revoking permission grants.
+     */
+    public void testDirectGrantRevokeUriPermission() throws Exception {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+        // Give ourselves some grants:
+        // /meow/cat  WRITE|PERSISTABLE
+        // /meow      READ|PREFIX
+        // /meow      WRITE
+        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        SystemClock.sleep(2000);
+
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+        // Verify they look good
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipAllowed(clipMeow);
+        assertWritingClipAllowed(clipMeowCat);
+
+        // Revoke anyone with write under meow
+        revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        SystemClock.sleep(2000);
+
+        // This should have nuked persisted permission at lower level, but it
+        // shoulnd't have touched our prefix read.
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Revoking read at top of tree should nuke everything else
+        revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        SystemClock.sleep(2000);
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+    }
 }