am cfd19c81: am 584004fe: Merge "Tests for persistable Uri permission grants." into klp-dev

* commit 'cfd19c8174f02f50be02cd5f1e2e954a002d6a90':
  Tests for persistable Uri permission grants.
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
index 31a3f47..46e1dff 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
@@ -19,27 +19,82 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.UriPermission;
+import android.net.Uri;
 import android.util.Log;
 
+import java.util.List;
+
 public class GrantUriPermission extends BroadcastReceiver {
+    public static final String ACTION_GRANT_URI = "grantUri";
+    public static final String ACTION_REVOKE_URI = "revokeUri";
+    public static final String ACTION_START_ACTIVITY = "startActivity";
+    public static final String ACTION_START_SERVICE = "startService";
+    public static final String ACTION_VERIFY_OUTGOING_PERSISTED = "verifyOutgoingPersisted";
+
+    public static final String EXTRA_PACKAGE_NAME = "packageName";
+    public static final String EXTRA_INTENT = Intent.EXTRA_INTENT;
+    public static final String EXTRA_URI = "uri";
+    public static final String EXTRA_MODE = "mode";
+
+    public static final int SUCCESS = 101;
+    public static final int FAILURE = 100;
+
     @Override
     public void onReceive(Context context, Intent intent) {
-        Intent newIntent = (Intent)intent.getParcelableExtra("intent");
-        boolean service = intent.getBooleanExtra("service", false);
         try {
-            if (!service) {
+            final String action = intent.getAction();
+            if (ACTION_GRANT_URI.equals(action)) {
+                final Uri uri = intent.getParcelableExtra(EXTRA_URI);
+                context.grantUriPermission(intent.getStringExtra(EXTRA_PACKAGE_NAME), uri,
+                        intent.getIntExtra(EXTRA_MODE, 0));
+
+            } else if (ACTION_REVOKE_URI.equals(action)) {
+                final Uri uri = intent.getParcelableExtra(EXTRA_URI);
+                context.revokeUriPermission(uri, intent.getIntExtra(EXTRA_MODE, 0));
+
+            } else if (ACTION_START_ACTIVITY.equals(action)) {
+                final Intent newIntent = intent.getParcelableExtra(EXTRA_INTENT);
                 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 context.startActivity(newIntent);
-            } else {
+
+            } else if (ACTION_START_SERVICE.equals(action)) {
+                final Intent newIntent = intent.getParcelableExtra(EXTRA_INTENT);
                 context.startService(newIntent);
+
+            } else if (ACTION_VERIFY_OUTGOING_PERSISTED.equals(action)) {
+                verifyOutgoingPersisted(context, intent);
             }
+
             if (isOrderedBroadcast()) {
-                setResultCode(101);
+                setResultCode(SUCCESS);
             }
         } catch (SecurityException e) {
             Log.i("GrantUriPermission", "Security exception", e);
             if (isOrderedBroadcast()) {
-                setResultCode(100);
+                setResultCode(FAILURE);
+            }
+        }
+    }
+
+    private void verifyOutgoingPersisted(Context context, Intent intent) {
+        final Uri uri = intent.getParcelableExtra(EXTRA_URI);
+        final List<UriPermission> perms = context.getContentResolver()
+                .getOutgoingPersistedUriPermissions();
+        if (uri != null) {
+            // Should have a single persisted perm
+            if (perms.size() != 1) {
+                throw new SecurityException("Missing grant");
+            }
+            final UriPermission perm = perms.get(0);
+            if (!perm.getUri().equals(uri)) {
+                throw new SecurityException(
+                        "Expected " + uri + " but found " + perm.getUri());
+            }
+        } else {
+            // Should have zero persisted perms
+            if (perms.size() != 0) {
+                throw new SecurityException("Unexpected grant");
             }
         }
     }
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
index acf9f6f..d836042 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
@@ -18,7 +18,8 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
 
 LOCAL_SDK_VERSION := current
 
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 8f40cf1..db26eec 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
@@ -23,13 +23,17 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.UriPermission;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.cts.permissiondeclareapp.GrantUriPermission;
+
 import java.io.IOException;
+import java.util.List;
 
 /**
  * Tests that signature-enforced permissions cannot be accessed by apps signed
@@ -54,6 +58,14 @@
     private static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME";
     private static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider");
 
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        // Always dispose, usually to clean up from failed tests
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
     private void assertReadingContentUriNotAllowed(Uri uri, String msg) {
         try {
             getContext().getContentResolver().query(uri, null, null, null, null);
@@ -423,11 +435,11 @@
             synchronized (this) {
                 mHaveResult = true;
                 switch (getResultCode()) {
-                    case 100:
+                    case GrantUriPermission.FAILURE:
                         mGoodResult = true;
                         mSucceeded = false;
                         break;
-                    case 101:
+                    case GrantUriPermission.SUCCESS:
                         mGoodResult = true;
                         mSucceeded = true;
                         break;
@@ -491,8 +503,9 @@
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
         Intent intent = new Intent();
         intent.setComponent(GRANT_URI_PERM_COMP);
-        intent.putExtra("intent", grantIntent);
-        intent.putExtra("service", service);
+        intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE
+                : GrantUriPermission.ACTION_START_ACTIVITY);
+        intent.putExtra(GrantUriPermission.EXTRA_INTENT, grantIntent);
         GrantResultReceiver receiver = new GrantResultReceiver();
         getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
         receiver.assertFailure("Able to grant URI permission to " + grantDataUri + " when should not");
@@ -502,8 +515,9 @@
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
         intent = new Intent();
         intent.setComponent(GRANT_URI_PERM_COMP);
-        intent.putExtra("intent", grantIntent);
-        intent.putExtra("service", service);
+        intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE
+                : GrantUriPermission.ACTION_START_ACTIVITY);
+        intent.putExtra(GrantUriPermission.EXTRA_INTENT, grantIntent);
         receiver = new GrantResultReceiver();
         getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
         receiver.assertFailure("Able to grant URI permission to " + grantIntent.getClipData()
@@ -578,8 +592,9 @@
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
         Intent intent = new Intent();
         intent.setComponent(GRANT_URI_PERM_COMP);
-        intent.putExtra("intent", grantIntent);
-        intent.putExtra("service", service);
+        intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE
+                : GrantUriPermission.ACTION_START_ACTIVITY);
+        intent.putExtra(GrantUriPermission.EXTRA_INTENT, grantIntent);
         getContext().sendBroadcast(intent);
     }
 
@@ -843,7 +858,7 @@
         doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, false);
     }
 
-    void doTestGrantServiceUriReadPermission(Uri uri, boolean useClip) {
+    private void doTestGrantServiceUriReadPermission(Uri uri, boolean useClip) {
         final Uri subUri = Uri.withAppendedPath(uri, "foo");
         final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
         final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
@@ -1207,4 +1222,137 @@
         assertEquals(EXPECTED_MIME_TYPE_AMBIGUOUS,
                 getContext().getContentResolver().getType(AMBIGUOUS_URI_COMPAT));
     }
+
+    /**
+     * Validate behavior of persistable permission grants.
+     */
+    public void testGrantPersistableUriPermission() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo");
+        final ClipData clip = makeSingleClipData(target);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+
+        // Make sure we can't take a grant we don't have
+        try {
+            resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            fail("taking read should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // And since we were just installed, no persisted grants yet
+        assertNoPersistedUriPermission();
+
+        // Now, let's grant ourselves some access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // We should now have reading access, even before taking the persistable
+        // grant. Persisted grants should still be empty.
+        assertReadingClipAllowed(clip);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Take the read grant and verify we have it!
+        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);
+
+        // Make sure we can't take a grant we don't have
+        try {
+            resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            fail("taking write should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // Launch again giving ourselves persistable read and write access
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        // Previous persisted grant should be unchanged
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // We should have both read and write; read is persisted, and write
+        // isn't persisted yet.
+        assertReadingClipAllowed(clip);
+        assertWritingClipAllowed(clip);
+
+        // Take again, but still only read; should just update timestamp
+        before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // And take yet again, both read and write
+        before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        after = System.currentTimeMillis();
+        assertPersistedUriPermission(target,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                before, after);
+
+        // Now drop the persisted grant; write first, then read
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertNoPersistedUriPermission();
+
+        // And even though we dropped the persistable grants, our activity is
+        // still running with the global grants (until reboot).
+        assertReadingClipAllowed(clip);
+        assertWritingClipAllowed(clip);
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    private void assertNoPersistedUriPermission() {
+        assertPersistedUriPermission(null, 0, -1, -1);
+    }
+
+    private void assertPersistedUriPermission(Uri uri, int flags, long before, long after) {
+        // Assert local
+        final List<UriPermission> perms = getContext()
+                .getContentResolver().getPersistedUriPermissions();
+        if (uri != null) {
+            assertEquals("expected exactly one permission", 1, perms.size());
+
+            final UriPermission perm = perms.get(0);
+            assertEquals("unexpected uri", uri, perm.getUri());
+
+            final long actual = perm.getPersistedTime();
+            if (before != -1) {
+                assertTrue("found " + actual + " before " + before, actual >= before);
+            }
+            if (after != -1) {
+                assertTrue("found " + actual + " after " + after, actual <= after);
+            }
+
+            final boolean expectedRead = (flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+            final boolean expectedWrite = (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+            assertEquals("unexpected read status", expectedRead, perm.isReadPermission());
+            assertEquals("unexpected write status", expectedWrite, perm.isWritePermission());
+
+        } else {
+            assertEquals("expected zero permissions", 0, perms.size());
+        }
+
+        // And assert remote
+        Intent intent = new Intent();
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_VERIFY_OUTGOING_PERSISTED);
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        GrantResultReceiver receiver = new GrantResultReceiver();
+        getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
+        receiver.assertSuccess("unexpected outgoing persisted Uri status");
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
index 851d8da..dd9a5c5 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
@@ -32,7 +32,7 @@
     private static final Object sLock = new Object();
     private static boolean sStarted;
     private static boolean sNewIntent;
-    private static boolean sDestroyed;
+    private static boolean sDestroyed = true;
     private static ReceiveUriActivity sCurInstance;
 
     private static final long TIMEOUT_MILLIS = 30 * SECOND_IN_MILLIS;