Add tests for granting URIs with path-based permissions.
Change-Id: Ib83f2967a49ee08cd823448ce8ca6e27fd42f7bb
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
index b8b8ce6..683ec9e 100644
--- a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -20,12 +20,14 @@
An app that declares a permission that requires a matching signature to
access.
-->
-
<permission android:name="com.android.cts.permissionWithSignature"
android:protectionLevel="signature" />
-
<uses-permission android:name="com.android.cts.permissionWithSignature" />
+ <!-- A permission this app will not hold. -->
+ <permission android:name="com.android.cts.permissionNotUsedWithSignature"
+ android:protectionLevel="signature" />
+
<application>
<receiver android:name="GrantUriPermission" android:exported="true">
</receiver>
@@ -36,7 +38,6 @@
android:authorities="ctspermissionwithsignature"
android:readPermission="com.android.cts.permissionWithSignature"
android:writePermission="com.android.cts.permissionWithSignature">
- <grant-uri-permission android:pathPattern=".*" />
</provider>
<!-- Need a way for another app to try to access the permission, but will
@@ -45,21 +46,40 @@
android:authorities="ctspermissionwithsignaturegranting"
android:readPermission="com.android.cts.permissionWithSignature"
android:writePermission="com.android.cts.permissionWithSignature">
- <grant-uri-permission android:pathPattern=".*" />
+ <grant-uri-permission android:pathPattern="/foo.*" />
+ <grant-uri-permission android:pathPattern="/yes.*" />
</provider>
<!-- Nobody else should get access to this -->
<provider android:name="PrivateContentProvider"
android:authorities="ctsprivateprovider"
android:exported="false">
- <grant-uri-permission android:pathPattern=".*" />
</provider>
<!-- Nobody else should get access to this, but we will grant uri access -->
<provider android:name="PrivateContentProviderGranting"
android:authorities="ctsprivateprovidergranting"
android:exported="false">
+ <grant-uri-permission android:pathPattern="/foo.*" />
+ <grant-uri-permission android:pathPattern="/yes.*" />
+ </provider>
+
+ <!-- Target for tests about how path permissions interact with granting
+ URI permissions. -->
+ <provider android:name="PermissionContentProviderPath"
+ android:authorities="ctspermissionwithsignaturepath"
+ android:readPermission="com.android.cts.permissionNotUsedWithSignature"
+ android:writePermission="com.android.cts.permissionNotUsedWithSignature">
+ <path-permission
+ android:pathPrefix="/foo"
+ android:readPermission="com.android.cts.permissionWithSignature"
+ android:writePermission="com.android.cts.permissionWithSignature" />
+ <path-permission
+ android:pathPrefix="/yes"
+ android:readPermission="com.android.cts.permissionWithSignature"
+ android:writePermission="com.android.cts.permissionWithSignature" />
<grant-uri-permission android:pathPattern=".*" />
</provider>
+
</application>
</manifest>
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
index 3322f45..8c14575 100644
--- a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
@@ -3,17 +3,28 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.util.Log;
public class GrantUriPermission extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent newIntent = (Intent)intent.getParcelableExtra("intent");
boolean service = intent.getBooleanExtra("service", false);
- if (!service) {
- newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(newIntent);
- } else {
- context.startService(newIntent);
+ try {
+ if (!service) {
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(newIntent);
+ } else {
+ context.startService(newIntent);
+ }
+ if (isOrderedBroadcast()) {
+ setResultCode(101);
+ }
+ } catch (SecurityException e) {
+ Log.i("GrantUriPermission", "Security exception", e);
+ if (isOrderedBroadcast()) {
+ setResultCode(100);
+ }
}
}
}
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderPath.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderPath.java
new file mode 100644
index 0000000..4f301a3
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderPath.java
@@ -0,0 +1,45 @@
+package com.android.cts.permissiondeclareapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Empty content provider, all permissions are enforced in manifest
+ */
+public class PermissionContentProviderPath extends ContentProvider {
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // do nothing
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "got/theMIME";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index d86fcc5..e01bc90 100644
--- a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -17,12 +17,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.usespermissiondiffcertapp">
- <!--
- This app declares it uses a permission that requires same signature as app that declares it.
- -->
-
+ <!-- We say we want to use the other app's permission, but it is signed with
+ a different cert so it should fail. -->
<uses-permission android:name="com.android.cts.permissionWithSignature"/>
+ <!-- This is a permission we can have, which the other app can require for
+ access. -->
+ <permission android:name="com.android.cts.permissionAllowedWithSignature"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="com.android.cts.permissionAllowedWithSignature"/>
+
<application>
<uses-library android:name="android.test.runner"/>
<activity android:name=".ReceiveUriActivity" android:exported="true"
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index d40195c..63a65e4 100644
--- a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -16,10 +16,13 @@
package com.android.cts.usespermissiondiffcertapp;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -35,6 +38,7 @@
"com.android.cts.permissiondeclareapp.GrantUriPermission");
static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
+ static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
@@ -194,6 +198,131 @@
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
+ 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 100:
+ mGoodResult = true;
+ mSucceeded = false;
+ break;
+ case 101:
+ 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);
+ }
+ }
+ }
+ }
+
+ void grantUriPermissionFail(Uri uri, int mode, boolean service) {
+ Intent grantIntent = new Intent();
+ grantIntent.setData(uri);
+ grantIntent.addFlags(mode);
+ grantIntent.setClass(getContext(),
+ service ? ReceiveUriService.class : ReceiveUriActivity.class);
+ Intent intent = new Intent();
+ intent.setComponent(GRANT_URI_PERM_COMP);
+ intent.putExtra("intent", grantIntent);
+ intent.putExtra("service", service);
+ GrantResultReceiver receiver = new GrantResultReceiver();
+ getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
+ receiver.assertFailure("Able to grant URI permission to " + uri + " when should not");
+ }
+
+ 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 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"));
+ }
+
void grantUriPermission(Uri uri, int mode, boolean service) {
Intent grantIntent = new Intent();
grantIntent.setData(uri);
@@ -217,6 +346,8 @@
assertReadingContentUriNotAllowed(subUri, "shouldn't read when starting test");
assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read when starting test");
+ // --------------------------------
+
ReceiveUriActivity.clearStarted();
grantUriPermission(subUri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
ReceiveUriActivity.waitForStart();
@@ -233,11 +364,63 @@
// And not to a sub path.
assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
+ // --------------------------------
+
+ ReceiveUriActivity.clearNewIntent();
+ grantUriPermission(sub2Uri, 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.
+ getContext().getContentResolver().query(sub2Uri, null, null, null, null);
+
+ // And still have access to the original URI.
+ getContext().getContentResolver().query(subUri, null, null, null, null);
+
+ // But not writing.
+ assertWritingContentUriNotAllowed(sub2Uri, "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();
+ if (false) {
+ synchronized (this) {
+ Log.i("**", "******************************* WAITING!!!");
+ try {
+ wait(10000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
// Ensure reading no longer allowed.
assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
+ assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read after losing granted URI");
}
void doTestGrantActivityUriWritePermission(Uri uri) {
@@ -331,7 +514,7 @@
* Test that the ctspermissionwithsignaturegranting content provider can grant a read
* permission.
*/
- public void testGrantActivityReadPermissionFromStartActivity() {
+ public void testGrantReadPermissionFromStartActivity() {
doTestGrantActivityUriReadPermission(PERM_URI_GRANTING);
}
@@ -339,7 +522,7 @@
* Test that the ctspermissionwithsignaturegranting content provider can grant a write
* permission.
*/
- public void testGrantActivityWritePermissionFromStartActivity() {
+ public void testGrantWritePermissionFromStartActivity() {
doTestGrantActivityUriWritePermission(PERM_URI_GRANTING);
}
@@ -347,7 +530,7 @@
* Test that the ctsprivateprovidergranting content provider can grant a read
* permission.
*/
- public void testGrantActivityReadPrivateFromStartActivity() {
+ public void testGrantReadPrivateFromStartActivity() {
doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING);
}
@@ -355,7 +538,7 @@
* Test that the ctsprivateprovidergranting content provider can grant a write
* permission.
*/
- public void testGrantActivityWritePrivateFromStartActivity() {
+ public void testGrantWritePrivateFromStartActivity() {
doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING);
}
@@ -535,22 +718,92 @@
assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write after losing granted URI");
}
- public void testGrantActivityReadPermissionFromStartService() {
+ public void testGrantReadPermissionFromStartService() {
doTestGrantServiceUriReadPermission(PERM_URI_GRANTING);
}
- public void testGrantActivityWritePermissionFromStartService() {
+ public void testGrantWritePermissionFromStartService() {
doTestGrantServiceUriWritePermission(PERM_URI_GRANTING);
}
- public void testGrantActivityReadPrivateFromStartService() {
+ public void testGrantReadPrivateFromStartService() {
doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING);
}
- public void testGrantActivityWritePrivateFromStartService() {
+ public void testGrantWritePrivateFromStartService() {
doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING);
}
+ /**
+ * 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);
+ }
+
+ /**
+ * Test that the ctspermissionwithsignaturepath content provider can grant a write
+ * permission.
+ */
+ public void testGrantWritePathPermissionFromStartActivity() {
+ doTestGrantActivityUriWritePermission(PERM_URI_PATH);
+ }
+
+ /**
+ * Test that the ctspermissionwithsignaturepath content provider can grant a read
+ * permission.
+ */
+ public void testGrantReadPathPermissionFromStartService() {
+ doTestGrantServiceUriReadPermission(PERM_URI_PATH);
+ }
+
+ /**
+ * Test that the ctspermissionwithsignaturepath content provider can grant a write
+ * permission.
+ */
+ public void testGrantWritePathPermissionFromStartService() {
+ doTestGrantServiceUriWritePermission(PERM_URI_PATH);
+ }
+
public void testGetMimeTypePermission() {
// Precondition: no current access.
assertWritingContentUriNotAllowed(PERM_URI, "shouldn't write when starting test");
diff --git a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
index c6a2386..0ddc4a5 100644
--- a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
@@ -23,8 +23,10 @@
import android.os.Looper;
import android.os.SystemClock;
import android.os.MessageQueue.IdleHandler;
+import android.util.Log;
public class ReceiveUriActivity extends Activity {
+ static final String TAG = "ReceiveUriActivity";
private static final Object sLock = new Object();
private static boolean sStarted;
private static boolean sNewIntent;
@@ -38,6 +40,7 @@
super.onCreate(savedInstanceState);
synchronized (sLock) {
+ Log.i(TAG, "onCreate: sCurInstance=" + sCurInstance);
if (sCurInstance != null) {
finishCurInstance();
}
@@ -53,6 +56,7 @@
super.onNewIntent(intent);
synchronized (sLock) {
+ Log.i(TAG, "onNewIntent: sCurInstance=" + sCurInstance);
sNewIntent = true;
sLock.notifyAll();
}
@@ -61,6 +65,7 @@
@Override
protected void onDestroy() {
super.onDestroy();
+ Log.i(TAG, "onDestroy: sCurInstance=" + sCurInstance);
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {