| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cts.usespermissiondiffcertapp; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.ClipData; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| 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 |
| * with different certs than app that declares the permission. |
| * |
| * Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/... |
| */ |
| public class AccessPermissionWithDiffSigTest extends AndroidTestCase { |
| private static final ComponentName GRANT_URI_PERM_COMP |
| = new ComponentName("com.android.cts.permissiondeclareapp", |
| "com.android.cts.permissiondeclareapp.GrantUriPermission"); |
| private static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature"); |
| private static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting"); |
| private static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath"); |
| private static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse( |
| "content://ctspermissionwithsignaturepathrestricting"); |
| private static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider"); |
| private static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting"); |
| private static final String EXPECTED_MIME_TYPE = "got/theMIME"; |
| |
| private static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat"); |
| 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); |
| fail("expected SecurityException reading " + uri + ": " + msg); |
| } catch (SecurityException expected) { |
| assertNotNull("security exception's error message.", expected.getMessage()); |
| } |
| } |
| |
| private void assertReadingContentUriAllowed(Uri uri) { |
| try { |
| getContext().getContentResolver().query(uri, null, null, null, null); |
| } catch (SecurityException e) { |
| fail("unexpected SecurityException reading " + uri + ": " + e.getMessage()); |
| } |
| } |
| |
| private void assertReadingClipNotAllowed(ClipData clip, String msg) { |
| for (int i=0; i<clip.getItemCount(); i++) { |
| ClipData.Item item = clip.getItemAt(i); |
| Uri uri = item.getUri(); |
| if (uri != null) { |
| assertReadingContentUriNotAllowed(uri, msg); |
| } else { |
| Intent intent = item.getIntent(); |
| uri = intent.getData(); |
| if (uri != null) { |
| assertReadingContentUriNotAllowed(uri, msg); |
| } |
| ClipData intentClip = intent.getClipData(); |
| if (intentClip != null) { |
| assertReadingClipNotAllowed(intentClip, msg); |
| } |
| } |
| } |
| } |
| |
| private void assertOpenFileDescriptorModeNotAllowed(Uri uri, String msg, String mode) { |
| try { |
| getContext().getContentResolver().openFileDescriptor(uri, mode).close(); |
| fail("expected SecurityException writing " + uri + ": " + msg); |
| } catch (IOException e) { |
| throw new IllegalStateException(e); |
| } catch (SecurityException expected) { |
| assertNotNull("security exception's error message.", expected.getMessage()); |
| } |
| } |
| |
| private void assertWritingContentUriNotAllowed(Uri uri, String msg) { |
| final ContentResolver resolver = getContext().getContentResolver(); |
| try { |
| resolver.insert(uri, new ContentValues()); |
| fail("expected SecurityException inserting " + uri + ": " + msg); |
| } catch (SecurityException expected) { |
| assertNotNull("security exception's error message.", expected.getMessage()); |
| } |
| |
| try { |
| resolver.update(uri, new ContentValues(), null, null); |
| fail("expected SecurityException updating " + uri + ": " + msg); |
| } catch (SecurityException expected) { |
| assertNotNull("security exception's error message.", expected.getMessage()); |
| } |
| |
| try { |
| resolver.delete(uri, null, null); |
| fail("expected SecurityException deleting " + uri + ": " + msg); |
| } catch (SecurityException expected) { |
| assertNotNull("security exception's error message.", expected.getMessage()); |
| } |
| |
| try { |
| getContext().getContentResolver().openOutputStream(uri).close(); |
| fail("expected SecurityException writing " + uri + ": " + msg); |
| } catch (IOException e) { |
| throw new IllegalStateException(e); |
| } catch (SecurityException expected) { |
| assertNotNull("security exception's error message.", expected.getMessage()); |
| } |
| |
| assertOpenFileDescriptorModeNotAllowed(uri, msg, "w"); |
| assertOpenFileDescriptorModeNotAllowed(uri, msg, "wt"); |
| assertOpenFileDescriptorModeNotAllowed(uri, msg, "wa"); |
| assertOpenFileDescriptorModeNotAllowed(uri, msg, "rw"); |
| assertOpenFileDescriptorModeNotAllowed(uri, msg, "rwt"); |
| } |
| |
| private void assertWritingContentUriAllowed(Uri uri) { |
| final ContentResolver resolver = getContext().getContentResolver(); |
| try { |
| resolver.insert(uri, new ContentValues()); |
| resolver.update(uri, new ContentValues(), null, null); |
| resolver.delete(uri, null, null); |
| |
| resolver.openOutputStream(uri).close(); |
| resolver.openFileDescriptor(uri, "w").close(); |
| resolver.openFileDescriptor(uri, "wt").close(); |
| resolver.openFileDescriptor(uri, "wa").close(); |
| resolver.openFileDescriptor(uri, "rw").close(); |
| resolver.openFileDescriptor(uri, "rwt").close(); |
| } catch (IOException e) { |
| fail("unexpected IOException writing " + uri + ": " + e.getMessage()); |
| } catch (SecurityException e) { |
| fail("unexpected SecurityException writing " + uri + ": " + e.getMessage()); |
| } |
| } |
| |
| private void assertWritingClipNotAllowed(ClipData clip, String msg) { |
| for (int i=0; i<clip.getItemCount(); i++) { |
| ClipData.Item item = clip.getItemAt(i); |
| Uri uri = item.getUri(); |
| if (uri != null) { |
| assertWritingContentUriNotAllowed(uri, msg); |
| } else { |
| Intent intent = item.getIntent(); |
| uri = intent.getData(); |
| if (uri != null) { |
| assertWritingContentUriNotAllowed(uri, msg); |
| } |
| ClipData intentClip = intent.getClipData(); |
| if (intentClip != null) { |
| assertWritingClipNotAllowed(intentClip, msg); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignature content provider cannot be read, |
| * since this app lacks the required certs |
| */ |
| public void testReadProviderWithDiff() { |
| assertReadingContentUriRequiresPermission(PERM_URI, |
| "com.android.cts.permissionWithSignature"); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignature content provider cannot be written, |
| * since this app lacks the required certs |
| */ |
| public void testWriteProviderWithDiff() { |
| assertWritingContentUriRequiresPermission(PERM_URI, |
| "com.android.cts.permissionWithSignature"); |
| } |
| |
| /** |
| * Test that the ctsprivateprovider content provider cannot be read, |
| * since it is not exported from its app. |
| */ |
| public void testReadProviderWhenPrivate() { |
| assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read private provider"); |
| } |
| |
| /** |
| * Test that the ctsambiguousprovider content provider cannot be read, |
| * since it doesn't have an "exported=" line. |
| */ |
| public void testReadProviderWhenAmbiguous() { |
| assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read ambiguous provider"); |
| } |
| |
| /** |
| * Old App Compatibility Test |
| * |
| * Test that the ctsambiguousprovidercompat content provider can be read for older |
| * API versions, because it didn't specify either exported=true or exported=false. |
| */ |
| public void testReadProviderWhenAmbiguousCompat() { |
| assertReadingContentUriAllowed(AMBIGUOUS_URI_COMPAT); |
| } |
| |
| /** |
| * Old App Compatibility Test |
| * |
| * Test that the ctsambiguousprovidercompat content provider can be written for older |
| * API versions, because it didn't specify either exported=true or exported=false. |
| */ |
| public void testWriteProviderWhenAmbiguousCompat() { |
| assertWritingContentUriAllowed(AMBIGUOUS_URI_COMPAT); |
| } |
| |
| /** |
| * Test that the ctsprivateprovider content provider cannot be written, |
| * since it is not exported from its app. |
| */ |
| public void testWriteProviderWhenPrivate() { |
| assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write private provider"); |
| } |
| |
| /** |
| * Test that the ctsambiguousprovider content provider cannot be written, |
| * since it doesn't have an exported= line. |
| */ |
| public void testWriteProviderWhenAmbiguous() { |
| assertWritingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't write ambiguous provider"); |
| } |
| |
| private static ClipData makeSingleClipData(Uri uri) { |
| return new ClipData("foo", new String[] { "foo/bar" }, |
| new ClipData.Item(uri)); |
| } |
| |
| private static ClipData makeMultiClipData(Uri uri) { |
| Uri grantClip1Uri = Uri.withAppendedPath(uri, "clip1"); |
| Uri grantClip2Uri = Uri.withAppendedPath(uri, "clip2"); |
| Uri grantClip3Uri = Uri.withAppendedPath(uri, "clip3"); |
| Uri grantClip4Uri = Uri.withAppendedPath(uri, "clip4"); |
| Uri grantClip5Uri = Uri.withAppendedPath(uri, "clip5"); |
| ClipData clip = new ClipData("foo", new String[] { "foo/bar" }, |
| new ClipData.Item(grantClip1Uri)); |
| clip.addItem(new ClipData.Item(grantClip2Uri)); |
| // Intents in the ClipData should allow their data: and clip URIs |
| // to be granted, but only respect the grant flags of the top-level |
| // Intent. |
| clip.addItem(new ClipData.Item(new Intent(Intent.ACTION_VIEW, grantClip3Uri))); |
| Intent intent = new Intent(Intent.ACTION_VIEW, grantClip4Uri); |
| intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
| | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| clip.addItem(new ClipData.Item(intent)); |
| intent = new Intent(Intent.ACTION_VIEW); |
| intent.setClipData(new ClipData("foo", new String[] { "foo/bar" }, |
| new ClipData.Item(grantClip5Uri))); |
| clip.addItem(new ClipData.Item(intent)); |
| return clip; |
| } |
| |
| private static Intent makeClipIntent(ClipData clip, int flags) { |
| Intent intent = new Intent(); |
| intent.setClipData(clip); |
| intent.addFlags(flags); |
| return intent; |
| } |
| |
| private static Intent makeClipIntent(Uri uri, int flags) { |
| return makeClipIntent(makeMultiClipData(uri), flags); |
| } |
| |
| private void doTryGrantUriActivityPermissionToSelf(Uri uri, int mode) { |
| Uri grantDataUri = Uri.withAppendedPath(uri, "data"); |
| Intent grantIntent = new Intent(); |
| grantIntent.setData(grantDataUri); |
| grantIntent.addFlags(mode | Intent.FLAG_ACTIVITY_NEW_TASK); |
| grantIntent.setClass(getContext(), ReceiveUriActivity.class); |
| try { |
| ReceiveUriActivity.clearStarted(); |
| getContext().startActivity(grantIntent); |
| ReceiveUriActivity.waitForStart(); |
| fail("expected SecurityException granting " + grantDataUri + " to activity"); |
| } catch (SecurityException e) { |
| // This is what we want. |
| } |
| |
| grantIntent = makeClipIntent(uri, mode | Intent.FLAG_ACTIVITY_NEW_TASK); |
| grantIntent.setClass(getContext(), ReceiveUriActivity.class); |
| try { |
| ReceiveUriActivity.clearStarted(); |
| getContext().startActivity(grantIntent); |
| ReceiveUriActivity.waitForStart(); |
| fail("expected SecurityException granting " + grantIntent.getClipData() + " to activity"); |
| } catch (SecurityException e) { |
| // This is what we want. |
| } |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantReadUriActivityPermissionToSelf() { |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(PERM_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantWriteUriActivityPermissionToSelf() { |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(PERM_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantReadUriActivityPrivateToSelf() { |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantWriteUriActivityPrivateToSelf() { |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| } |
| |
| private void doTryGrantUriServicePermissionToSelf(Uri uri, int mode) { |
| Uri grantDataUri = Uri.withAppendedPath(uri, "data"); |
| Intent grantIntent = new Intent(); |
| grantIntent.setData(grantDataUri); |
| grantIntent.addFlags(mode); |
| grantIntent.setClass(getContext(), ReceiveUriService.class); |
| try { |
| getContext().startService(grantIntent); |
| fail("expected SecurityException granting " + grantDataUri + " to service"); |
| } catch (SecurityException e) { |
| // This is what we want. |
| } |
| |
| grantIntent = makeClipIntent(uri, mode); |
| grantIntent.setClass(getContext(), ReceiveUriService.class); |
| try { |
| getContext().startService(grantIntent); |
| fail("expected SecurityException granting " + grantIntent.getClipData() + " to service"); |
| } catch (SecurityException e) { |
| // This is what we want. |
| } |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantReadUriServicePermissionToSelf() { |
| doTryGrantUriServicePermissionToSelf( |
| Uri.withAppendedPath(PERM_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantWriteUriServicePermissionToSelf() { |
| doTryGrantUriServicePermissionToSelf( |
| Uri.withAppendedPath(PERM_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantReadUriServicePrivateToSelf() { |
| doTryGrantUriServicePermissionToSelf( |
| Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that we can't grant a permission to ourself. |
| */ |
| public void testGrantWriteUriServicePrivateToSelf() { |
| doTryGrantUriServicePermissionToSelf( |
| Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| } |
| |
| private static class GrantResultReceiver extends BroadcastReceiver { |
| boolean mHaveResult = false; |
| boolean mGoodResult = false; |
| boolean mSucceeded = false; |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| synchronized (this) { |
| mHaveResult = true; |
| switch (getResultCode()) { |
| case GrantUriPermission.FAILURE: |
| mGoodResult = true; |
| mSucceeded = false; |
| break; |
| case GrantUriPermission.SUCCESS: |
| mGoodResult = true; |
| mSucceeded = true; |
| break; |
| default: |
| mGoodResult = false; |
| break; |
| } |
| notifyAll(); |
| } |
| } |
| |
| void assertSuccess(String failureMessage) { |
| synchronized (this) { |
| final long startTime = SystemClock.uptimeMillis(); |
| while (!mHaveResult) { |
| try { |
| wait(5000); |
| } catch (InterruptedException e) { |
| } |
| if (SystemClock.uptimeMillis() >= (startTime+5000)) { |
| throw new RuntimeException("Timeout"); |
| } |
| } |
| if (!mGoodResult) { |
| fail("Broadcast receiver did not return good result"); |
| } |
| if (!mSucceeded) { |
| fail(failureMessage); |
| } |
| } |
| } |
| |
| void assertFailure(String failureMessage) { |
| synchronized (this) { |
| final long startTime = SystemClock.uptimeMillis(); |
| while (!mHaveResult) { |
| try { |
| wait(5000); |
| } catch (InterruptedException e) { |
| } |
| if (SystemClock.uptimeMillis() >= (startTime+5000)) { |
| throw new RuntimeException("Timeout"); |
| } |
| } |
| if (!mGoodResult) { |
| fail("Broadcast receiver did not return good result"); |
| } |
| if (mSucceeded) { |
| fail(failureMessage); |
| } |
| } |
| } |
| } |
| |
| private void grantUriPermissionFail(Uri uri, int mode, boolean service) { |
| Uri grantDataUri = Uri.withAppendedPath(uri, "data"); |
| Intent grantIntent = new Intent(); |
| grantIntent.setData(grantDataUri); |
| grantIntent.addFlags(mode); |
| grantIntent.setClass(getContext(), |
| service ? ReceiveUriService.class : ReceiveUriActivity.class); |
| Intent intent = new Intent(); |
| intent.setComponent(GRANT_URI_PERM_COMP); |
| 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"); |
| |
| grantIntent = makeClipIntent(uri, mode); |
| grantIntent.setClass(getContext(), |
| service ? ReceiveUriService.class : ReceiveUriActivity.class); |
| intent = new Intent(); |
| intent.setComponent(GRANT_URI_PERM_COMP); |
| 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() |
| + " when should not"); |
| } |
| |
| private void doTestGrantUriPermissionFail(Uri uri) { |
| grantUriPermissionFail(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false); |
| grantUriPermissionFail(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false); |
| grantUriPermissionFail(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true); |
| grantUriPermissionFail(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignature content provider can not grant |
| * URI permissions to others. |
| */ |
| public void testGrantPermissionNonGrantingFail() { |
| doTestGrantUriPermissionFail(PERM_URI); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturegranting content provider can not grant |
| * URI permissions to paths outside of the grant tree |
| */ |
| public void testGrantPermissionOutsideGrantingFail() { |
| doTestGrantUriPermissionFail(PERM_URI_GRANTING); |
| doTestGrantUriPermissionFail(Uri.withAppendedPath(PERM_URI_GRANTING, "invalid")); |
| } |
| |
| /** |
| * Test that the ctsprivateprovider content provider can not grant |
| * URI permissions to others. |
| */ |
| public void testGrantPrivateNonGrantingFail() { |
| doTestGrantUriPermissionFail(PRIV_URI); |
| } |
| |
| /** |
| * Test that the ctsambiguousprovider content provider can not grant |
| * URI permissions to others. |
| */ |
| public void testGrantAmbiguousNonGrantingFail() { |
| doTestGrantUriPermissionFail(AMBIGUOUS_URI); |
| } |
| |
| /** |
| * Test that the ctsprivateprovidergranting content provider can not grant |
| * URI permissions to paths outside of the grant tree |
| */ |
| public void testGrantPrivateOutsideGrantingFail() { |
| doTestGrantUriPermissionFail(PRIV_URI_GRANTING); |
| doTestGrantUriPermissionFail(Uri.withAppendedPath(PRIV_URI_GRANTING, "invalid")); |
| } |
| |
| private void grantClipUriPermission(ClipData clip, int mode, boolean service) { |
| Intent grantIntent = new Intent(); |
| if (clip.getItemCount() == 1) { |
| grantIntent.setData(clip.getItemAt(0).getUri()); |
| } else { |
| grantIntent.setClipData(clip); |
| // Make this Intent unique from the one that started it. |
| for (int i=0; i<clip.getItemCount(); i++) { |
| Uri uri = clip.getItemAt(i).getUri(); |
| if (uri != null) { |
| grantIntent.addCategory(uri.toString()); |
| } |
| } |
| } |
| grantIntent.addFlags(mode); |
| 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); |
| intent.putExtra(GrantUriPermission.EXTRA_INTENT, grantIntent); |
| 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); |
| Uri uri = item.getUri(); |
| if (uri != null) { |
| Cursor c = getContext().getContentResolver().query(uri, |
| null, null, null, null); |
| if (c != null) { |
| c.close(); |
| } |
| } else { |
| Intent intent = item.getIntent(); |
| uri = intent.getData(); |
| if (uri != null) { |
| Cursor c = getContext().getContentResolver().query(uri, |
| null, null, null, null); |
| if (c != null) { |
| c.close(); |
| } |
| } |
| ClipData intentClip = intent.getClipData(); |
| if (intentClip != null) { |
| assertReadingClipAllowed(intentClip); |
| } |
| } |
| } |
| } |
| |
| private void doTestGrantActivityUriReadPermission(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"); |
| final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no"); |
| |
| final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri); |
| final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri); |
| |
| // Precondition: no current access. |
| assertReadingClipNotAllowed(subClip, "shouldn't read when starting test"); |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test"); |
| |
| // -------------------------------- |
| |
| ReceiveUriActivity.clearStarted(); |
| grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false); |
| ReceiveUriActivity.waitForStart(); |
| |
| // See if we now have access to the provider. |
| assertReadingClipAllowed(subClip); |
| |
| // But not writing. |
| assertWritingClipNotAllowed(subClip, "shouldn't write from granted read"); |
| |
| // And not to the base path. |
| assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI"); |
| |
| // And not to a sub path. |
| assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI"); |
| |
| // -------------------------------- |
| |
| ReceiveUriActivity.clearNewIntent(); |
| grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false); |
| ReceiveUriActivity.waitForNewIntent(); |
| |
| if (false) { |
| synchronized (this) { |
| Log.i("**", "******************************* WAITING!!!"); |
| try { |
| wait(10000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| // See if we now have access to the provider. |
| assertReadingClipAllowed(sub2Clip); |
| |
| // And still have access to the original URI. |
| assertReadingClipAllowed(subClip); |
| |
| // But not writing. |
| assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read"); |
| |
| // And not to the base path. |
| assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI"); |
| |
| // And not to a sub path. |
| assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI"); |
| |
| // And make sure we can't generate a permission to a running activity. |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(uri, "hah"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(uri, "hah"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| |
| // -------------------------------- |
| |
| // Dispose of activity. |
| ReceiveUriActivity.finishCurInstanceSync(); |
| |
| synchronized (this) { |
| Log.i("**", "******************************* WAITING!!!"); |
| try { |
| wait(100); |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| // Ensure reading no longer allowed. |
| assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI"); |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI"); |
| } |
| |
| private void assertWritingClipAllowed(ClipData clip) { |
| for (int i=0; i<clip.getItemCount(); i++) { |
| ClipData.Item item = clip.getItemAt(i); |
| Uri uri = item.getUri(); |
| if (uri != null) { |
| getContext().getContentResolver().insert(uri, new ContentValues()); |
| } else { |
| Intent intent = item.getIntent(); |
| uri = intent.getData(); |
| if (uri != null) { |
| getContext().getContentResolver().insert(uri, new ContentValues()); |
| } |
| ClipData intentClip = intent.getClipData(); |
| if (intentClip != null) { |
| assertWritingClipAllowed(intentClip); |
| } |
| } |
| } |
| } |
| |
| private void doTestGrantActivityUriWritePermission(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"); |
| final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no"); |
| |
| final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri); |
| final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri); |
| |
| // Precondition: no current access. |
| assertWritingClipNotAllowed(subClip, "shouldn't write when starting test"); |
| assertWritingClipNotAllowed(sub2Clip, "shouldn't write when starting test"); |
| |
| // -------------------------------- |
| |
| ReceiveUriActivity.clearStarted(); |
| grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false); |
| ReceiveUriActivity.waitForStart(); |
| |
| // See if we now have access to the provider. |
| assertWritingClipAllowed(subClip); |
| |
| // But not reading. |
| assertReadingClipNotAllowed(subClip, "shouldn't read from granted write"); |
| |
| // And not to the base path. |
| assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI"); |
| |
| // And not a sub-path. |
| assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI"); |
| |
| // -------------------------------- |
| |
| ReceiveUriActivity.clearNewIntent(); |
| grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false); |
| ReceiveUriActivity.waitForNewIntent(); |
| |
| if (false) { |
| synchronized (this) { |
| Log.i("**", "******************************* WAITING!!!"); |
| try { |
| wait(10000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| // See if we now have access to the provider. |
| assertWritingClipAllowed(sub2Clip); |
| |
| // And still have access to the original URI. |
| assertWritingClipAllowed(subClip); |
| |
| // But not reading. |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write"); |
| |
| // And not to the base path. |
| assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI"); |
| |
| // And not a sub-path. |
| assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI"); |
| |
| // And make sure we can't generate a permission to a running activity. |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(uri, "hah"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(uri, "hah"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| |
| // -------------------------------- |
| |
| // Dispose of activity. |
| ReceiveUriActivity.finishCurInstanceSync(); |
| |
| synchronized (this) { |
| Log.i("**", "******************************* WAITING!!!"); |
| try { |
| wait(100); |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| // Ensure writing no longer allowed. |
| assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI"); |
| assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI"); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturegranting content provider can grant a read |
| * permission. |
| */ |
| public void testGrantReadPermissionFromStartActivity() { |
| doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, false); |
| doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, true); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturegranting content provider can grant a write |
| * permission. |
| */ |
| public void testGrantWritePermissionFromStartActivity() { |
| doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, true); |
| doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, false); |
| } |
| |
| /** |
| * Test that the ctsprivateprovidergranting content provider can grant a read |
| * permission. |
| */ |
| public void testGrantReadPrivateFromStartActivity() { |
| doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, false); |
| doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, true); |
| } |
| |
| /** |
| * Test that the ctsprivateprovidergranting content provider can grant a write |
| * permission. |
| */ |
| public void testGrantWritePrivateFromStartActivity() { |
| doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, true); |
| doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, false); |
| } |
| |
| 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"); |
| final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no"); |
| |
| ReceiveUriService.stop(getContext()); |
| |
| final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri); |
| final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri); |
| |
| // Precondition: no current access. |
| assertReadingClipNotAllowed(subClip, "shouldn't read when starting test"); |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test"); |
| |
| // -------------------------------- |
| |
| ReceiveUriService.clearStarted(); |
| grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true); |
| ReceiveUriService.waitForStart(); |
| |
| int firstStartId = ReceiveUriService.getCurStartId(); |
| |
| // See if we now have access to the provider. |
| assertReadingClipAllowed(subClip); |
| |
| // But not writing. |
| assertWritingClipNotAllowed(subClip, "shouldn't write from granted read"); |
| |
| // And not to the base path. |
| assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI"); |
| |
| // And not to a sub path. |
| assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI"); |
| |
| // -------------------------------- |
| |
| // Send another Intent to it. |
| ReceiveUriService.clearStarted(); |
| grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true); |
| ReceiveUriService.waitForStart(); |
| |
| if (false) { |
| synchronized (this) { |
| Log.i("**", "******************************* WAITING!!!"); |
| try { |
| wait(10000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| // See if we now have access to the provider. |
| assertReadingClipAllowed(sub2Clip); |
| |
| // And still to the previous URI. |
| assertReadingClipAllowed(subClip); |
| |
| // But not writing. |
| assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read"); |
| |
| // And not to the base path. |
| assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI"); |
| |
| // And not to a sub path. |
| assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI"); |
| |
| // -------------------------------- |
| |
| // Stop the first command. |
| ReceiveUriService.stopCurWithId(firstStartId); |
| |
| // Ensure reading no longer allowed. |
| assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI"); |
| |
| // And make sure we can't generate a permission to a running service. |
| doTryGrantUriActivityPermissionToSelf(subUri, |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| doTryGrantUriActivityPermissionToSelf(subUri, |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| |
| // -------------------------------- |
| |
| // Dispose of service. |
| ReceiveUriService.stopSync(getContext()); |
| |
| // Ensure reading no longer allowed. |
| assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI"); |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI"); |
| } |
| |
| private void doTestGrantServiceUriWritePermission(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"); |
| final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no"); |
| |
| ReceiveUriService.stop(getContext()); |
| |
| final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri); |
| final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri); |
| |
| // Precondition: no current access. |
| assertReadingClipNotAllowed(subClip, "shouldn't read when starting test"); |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test"); |
| |
| // -------------------------------- |
| |
| ReceiveUriService.clearStarted(); |
| grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true); |
| ReceiveUriService.waitForStart(); |
| |
| int firstStartId = ReceiveUriService.getCurStartId(); |
| |
| // See if we now have access to the provider. |
| assertWritingClipAllowed(subClip); |
| |
| // But not reading. |
| assertReadingClipNotAllowed(subClip, "shouldn't read from granted write"); |
| |
| // And not to the base path. |
| assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI"); |
| |
| // And not a sub-path. |
| assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI"); |
| |
| // -------------------------------- |
| |
| // Send another Intent to it. |
| ReceiveUriService.clearStarted(); |
| grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true); |
| ReceiveUriService.waitForStart(); |
| |
| // See if we now have access to the provider. |
| assertWritingClipAllowed(sub2Clip); |
| |
| // And still to the previous URI. |
| assertWritingClipAllowed(subClip); |
| |
| // But not reading. |
| assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write"); |
| |
| // And not to the base path. |
| assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI"); |
| |
| // And not a sub-path. |
| assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI"); |
| |
| if (false) { |
| synchronized (this) { |
| Log.i("**", "******************************* WAITING!!!"); |
| try { |
| wait(10000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| // -------------------------------- |
| |
| // Stop the first command. |
| ReceiveUriService.stopCurWithId(firstStartId); |
| |
| // Ensure writing no longer allowed. |
| assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI"); |
| |
| // And make sure we can't generate a permission to a running service. |
| doTryGrantUriActivityPermissionToSelf(subUri, |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| doTryGrantUriActivityPermissionToSelf(subUri, |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| |
| // -------------------------------- |
| |
| // Dispose of service. |
| ReceiveUriService.stopSync(getContext()); |
| |
| // Ensure writing no longer allowed. |
| assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI"); |
| assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI"); |
| } |
| |
| public void testGrantReadPermissionFromStartService() { |
| doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, false); |
| doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, true); |
| } |
| |
| public void testGrantWritePermissionFromStartService() { |
| doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, false); |
| doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, true); |
| } |
| |
| public void testGrantReadPrivateFromStartService() { |
| doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, false); |
| doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, true); |
| } |
| |
| public void testGrantWritePrivateFromStartService() { |
| doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, false); |
| doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, true); |
| } |
| |
| /** |
| * Test that ctspermissionwithsignaturepath can't grant read permissions |
| * on paths it doesn't have permission to. |
| */ |
| public void testGrantReadUriActivityPathPermissionToSelf() { |
| doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH, |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that ctspermissionwithsignaturepath can't grant write permissions |
| * on paths it doesn't have permission to. |
| */ |
| public void testGrantWriteUriActivityPathPermissionToSelf() { |
| doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH, |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that ctspermissionwithsignaturepath can't grant read permissions |
| * on paths it doesn't have permission to. |
| */ |
| public void testGrantReadUriActivitySubPathPermissionToSelf() { |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(PERM_URI_PATH, "foo"), |
| Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that ctspermissionwithsignaturepath can't grant write permissions |
| * on paths it doesn't have permission to. |
| */ |
| public void testGrantWriteUriActivitySubPathPermissionToSelf() { |
| doTryGrantUriActivityPermissionToSelf( |
| Uri.withAppendedPath(PERM_URI_PATH, "foo"), |
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturepath content provider can grant a read |
| * permission. |
| */ |
| public void testGrantReadPathPermissionFromStartActivity() { |
| doTestGrantActivityUriReadPermission(PERM_URI_PATH, false); |
| doTestGrantActivityUriReadPermission(PERM_URI_PATH, true); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturepath content provider can grant a write |
| * permission. |
| */ |
| public void testGrantWritePathPermissionFromStartActivity() { |
| doTestGrantActivityUriWritePermission(PERM_URI_PATH, false); |
| doTestGrantActivityUriWritePermission(PERM_URI_PATH, true); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturepath content provider can grant a read |
| * permission. |
| */ |
| public void testGrantReadPathPermissionFromStartService() { |
| doTestGrantServiceUriReadPermission(PERM_URI_PATH, false); |
| doTestGrantServiceUriReadPermission(PERM_URI_PATH, true); |
| } |
| |
| /** |
| * Test that the ctspermissionwithsignaturepath content provider can grant a write |
| * permission. |
| */ |
| public void testGrantWritePathPermissionFromStartService() { |
| doTestGrantServiceUriWritePermission(PERM_URI_PATH, false); |
| doTestGrantServiceUriWritePermission(PERM_URI_PATH, true); |
| } |
| |
| /** |
| * Verify that we can access paths outside the {@code path-permission} |
| * protections, which should only rely on {@code provider} permissions. |
| */ |
| public void testRestrictingProviderNoMatchingPath() { |
| assertReadingContentUriAllowed(PERM_URI_PATH_RESTRICTING); |
| assertWritingContentUriAllowed(PERM_URI_PATH_RESTRICTING); |
| |
| // allowed by no top-level permission |
| final Uri test = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("fo").build(); |
| assertReadingContentUriAllowed(test); |
| assertWritingContentUriAllowed(test); |
| } |
| |
| /** |
| * Verify that paths under {@code path-permission} restriction aren't |
| * allowed, even though the {@code provider} requires no permissions. |
| */ |
| public void testRestrictingProviderMatchingPathDenied() { |
| // rejected by "foo" prefix |
| final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo").build(); |
| assertReadingContentUriNotAllowed(test1, null); |
| assertWritingContentUriNotAllowed(test1, null); |
| |
| // rejected by "foo" prefix |
| final Uri test2 = PERM_URI_PATH_RESTRICTING.buildUpon() |
| .appendPath("foo").appendPath("ba").build(); |
| assertReadingContentUriNotAllowed(test2, null); |
| assertWritingContentUriNotAllowed(test2, null); |
| } |
| |
| /** |
| * Verify that at least one {@code path-permission} rule will grant access, |
| * even if the caller doesn't hold another matching {@code path-permission}. |
| */ |
| public void testRestrictingProviderMultipleMatchingPath() { |
| // allowed by narrow "foo/bar" prefix |
| final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon() |
| .appendPath("foo").appendPath("bar").build(); |
| assertReadingContentUriAllowed(test1); |
| assertWritingContentUriAllowed(test1); |
| |
| // allowed by narrow "foo/bar" prefix |
| final Uri test2 = PERM_URI_PATH_RESTRICTING.buildUpon() |
| .appendPath("foo").appendPath("bar2").build(); |
| assertReadingContentUriAllowed(test2); |
| assertWritingContentUriAllowed(test2); |
| } |
| |
| public void testGetMimeTypePermission() { |
| // Precondition: no current access. |
| assertReadingContentUriNotAllowed(PERM_URI, "shouldn't read when starting test"); |
| assertWritingContentUriNotAllowed(PERM_URI, "shouldn't write when starting test"); |
| |
| // All apps should be able to get MIME type regardless of permission. |
| assertEquals(getContext().getContentResolver().getType(PERM_URI), EXPECTED_MIME_TYPE); |
| } |
| |
| public void testGetMimeTypePrivate() { |
| // Precondition: no current access. |
| assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read when starting test"); |
| assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write when starting test"); |
| |
| // All apps should be able to get MIME type even if provider is private. |
| assertEquals(getContext().getContentResolver().getType(PRIV_URI), EXPECTED_MIME_TYPE); |
| } |
| |
| public void testGetMimeTypeAmbiguous() { |
| // Precondition: no current access. |
| assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read when starting test"); |
| assertWritingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't write when starting test"); |
| |
| // All apps should be able to get MIME type even if provider is private. |
| assertEquals(getContext().getContentResolver().getType(AMBIGUOUS_URI), EXPECTED_MIME_TYPE); |
| } |
| |
| /** |
| * Old App Compatibility Test |
| * |
| * We should be able to access the mime type of a content provider of an older |
| * application, even if that application didn't explicitly declare either |
| * exported=true or exported=false |
| */ |
| public void testGetMimeTypeAmbiguousCompat() { |
| // All apps should be able to get MIME type even if provider is private. |
| 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"); |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| /** |
| * Validate behavior of a direct permission grant, where the receiver of |
| * that permission revokes it. |
| */ |
| public void testDirectGrantReceiverRevokeUriPermission() 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 |
| getContext().revokeUriPermission(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
| |
| // 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 |
| getContext().revokeUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| 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(); |
| } |
| } |