Tests for issue #2845673: android:exported="false" is not obeyed
Add more tests for content provider access, including tests that
granting URIs is working correctly for both permissions and
private providers.
Change-Id: I6e0f7f11ad19dd3866682576ad6397287edf75ec
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
index 53ab2cf..b8b8ce6 100644
--- a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -24,12 +24,42 @@
<permission android:name="com.android.cts.permissionWithSignature"
android:protectionLevel="signature" />
+ <uses-permission android:name="com.android.cts.permissionWithSignature" />
+
<application>
+ <receiver android:name="GrantUriPermission" android:exported="true">
+ </receiver>
+
<!-- Need a way for another app to try to access the permission. So create a content
provider which is enforced by the permission -->
<provider android:name="PermissionContentProvider"
android:authorities="ctspermissionwithsignature"
android:readPermission="com.android.cts.permissionWithSignature"
- android:writePermission="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
+ grant uri access. -->
+ <provider android:name="PermissionContentProviderGranting"
+ android:authorities="ctspermissionwithsignaturegranting"
+ android:readPermission="com.android.cts.permissionWithSignature"
+ android:writePermission="com.android.cts.permissionWithSignature">
+ <grant-uri-permission android:pathPattern=".*" />
+ </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=".*" />
+ </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
new file mode 100644
index 0000000..2403d63
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
@@ -0,0 +1,14 @@
+package com.android.cts.permissiondeclareapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class GrantUriPermission extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent newIntent = (Intent)intent.getParcelableExtra("intent");
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(newIntent);
+ }
+}
diff --git a/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderGranting.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderGranting.java
new file mode 100644
index 0000000..46b1b3e
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PermissionContentProviderGranting.java
@@ -0,0 +1,61 @@
+/*
+ * 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.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 PermissionContentProviderGranting extends ContentProvider {
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // do nothing
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @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/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProvider.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProvider.java
new file mode 100644
index 0000000..dfbdbb7
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProvider.java
@@ -0,0 +1,61 @@
+/*
+ * 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.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 PrivateContentProvider extends ContentProvider {
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // do nothing
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @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/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProviderGranting.java b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProviderGranting.java
new file mode 100644
index 0000000..f81c27a
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/PrivateContentProviderGranting.java
@@ -0,0 +1,61 @@
+/*
+ * 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.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 PrivateContentProviderGranting extends ContentProvider {
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // do nothing
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @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 b915ebc..57db903 100644
--- a/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -25,6 +25,7 @@
<application>
<uses-library android:name="android.test.runner"/>
+ <activity android:name=".ReceiveUriActivity" android:exported="true" />
</application>
<instrumentation android:targetPackage="com.android.cts.usespermissiondiffcertapp"
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 510d5e4..3e8c4bb 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,21 +16,180 @@
package com.android.cts.usespermissiondiffcertapp;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Intent;
import android.net.Uri;
import android.test.AndroidTestCase;
+import android.util.Log;
/**
* Tests that signature-enforced permissions cannot be accessed by apps signed
* with different certs than app that declares the permission.
*/
public class AccessPermissionWithDiffSigTest extends AndroidTestCase {
+ static final ComponentName GRANT_URI_PERM_COMP
+ = new ComponentName("com.android.cts.permissiondeclareapp",
+ "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 PRIV_URI = Uri.parse("content://ctsprivateprovider");
+ static final Uri PRIV_GRANTING_URI = Uri.parse("content://ctsprivateprovidergranting");
+
+ public 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());
+ }
+ }
+
+ public void assertWritingContentUriNotAllowed(Uri uri, String msg) {
+ try {
+ getContext().getContentResolver().insert(uri, new ContentValues());
+ fail("expected SecurityException writing " + uri + ": " + msg);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+ }
/**
* Test that the ctspermissionwithsignature content provider cannot be read,
* since this app lacks the required certs
*/
public void testReadProviderWithDiff() {
- assertReadingContentUriRequiresPermission(Uri.parse("content://ctspermissionwithsignature"),
+ 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 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");
+ }
+
+ void doTestGrantUriReadPermission(Uri uri) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+
+ Intent grantIntent = new Intent();
+ grantIntent.setData(subUri);
+ grantIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ grantIntent.setClass(getContext(), ReceiveUriActivity.class);
+ Intent intent = new Intent();
+ intent.setComponent(GRANT_URI_PERM_COMP);
+ intent.putExtra("intent", grantIntent);
+
+ ReceiveUriActivity.clearStarted();
+ getContext().sendBroadcast(intent);
+ ReceiveUriActivity.waitForStart();
+
+ // See if we now have access to the provider.
+ getContext().getContentResolver().query(subUri, null, null, null, null);
+
+ // But not writing.
+ assertWritingContentUriNotAllowed(subUri, "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");
+
+ // Dispose of activity.
+ ReceiveUriActivity.finishCurInstanceSync();
+
+ // Ensure reading no longer allowed.
+ assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
+
+ }
+
+ void doTestGrantUriWritePermission(Uri uri) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+
+ Intent grantIntent = new Intent();
+ grantIntent.setData(subUri);
+ grantIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ grantIntent.setClass(getContext(), ReceiveUriActivity.class);
+ Intent intent = new Intent();
+ intent.setComponent(GRANT_URI_PERM_COMP);
+ intent.putExtra("intent", grantIntent);
+
+ ReceiveUriActivity.clearStarted();
+ getContext().sendBroadcast(intent);
+ ReceiveUriActivity.waitForStart();
+
+ // See if we now have access to the provider.
+ getContext().getContentResolver().insert(subUri, new ContentValues());
+
+ // But not reading.
+ assertReadingContentUriNotAllowed(subUri, "shouldn't read from granted read");
+
+ // 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");
+
+ // Dispose of activity.
+ ReceiveUriActivity.finishCurInstanceSync();
+
+ // Ensure reading no longer allowed.
+ assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
+ }
+
+ /**
+ * Test that the ctspermissionwithsignaturegranting content provider can grant a read
+ * permission.
+ */
+ public void testGrantReadPermissionFromStartActivity() {
+ doTestGrantUriReadPermission(PERM_URI_GRANTING);
+ }
+
+ /**
+ * Test that the ctspermissionwithsignaturegranting content provider can grant a write
+ * permission.
+ */
+ public void testGrantWritePermissionFromStartActivity() {
+ doTestGrantUriWritePermission(PERM_URI_GRANTING);
+ }
+
+ /**
+ * Test that the ctsprivateprovidergranting content provider can grant a read
+ * permission.
+ */
+ public void testGrantReadPrivateFromStartActivity() {
+ doTestGrantUriReadPermission(PRIV_GRANTING_URI);
+ }
+
+ /**
+ * Test that the ctsprivateprovidergranting content provider can grant a write
+ * permission.
+ */
+ public void testGrantWritePrivateFromStartActivity() {
+ doTestGrantUriWritePermission(PRIV_GRANTING_URI);
+ }
}
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
new file mode 100644
index 0000000..074d8ea
--- /dev/null
+++ b/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ReceiveUriActivity.java
@@ -0,0 +1,127 @@
+package com.android.cts.usespermissiondiffcertapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.MessageQueue.IdleHandler;
+
+public class ReceiveUriActivity extends Activity {
+ private static final Object sLock = new Object();
+ private static boolean sStarted;
+ private static boolean sNewIntent;
+ private static boolean sDestroyed;
+ private static ReceiveUriActivity sCurInstance;
+
+ Handler mHandler = new Handler();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ synchronized (sLock) {
+ if (sCurInstance != null) {
+ finishCurInstance();
+ }
+ sCurInstance = this;
+ sStarted = true;
+ sDestroyed = false;
+ sLock.notifyAll();
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ synchronized (sLock) {
+ sNewIntent = true;
+ sLock.notifyAll();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Looper.myQueue().addIdleHandler(new IdleHandler() {
+ @Override
+ public boolean queueIdle() {
+ synchronized (sLock) {
+ sDestroyed = true;
+ sLock.notifyAll();
+ }
+ return false;
+ }
+ });
+ }
+
+ public static void finishCurInstance() {
+ synchronized (sLock) {
+ if (sCurInstance != null) {
+ sCurInstance.finish();
+ sCurInstance = null;
+ }
+ }
+ }
+
+ public static void finishCurInstanceSync() {
+ finishCurInstance();
+
+ synchronized (sLock) {
+ final long startTime = SystemClock.uptimeMillis();
+ while (!sDestroyed) {
+ try {
+ sLock.wait(5000);
+ } catch (InterruptedException e) {
+ }
+ if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+ throw new RuntimeException("Timeout");
+ }
+ }
+ }
+ }
+
+ public static void clearStarted() {
+ synchronized (sLock) {
+ sStarted = false;
+ }
+ }
+
+ public static void clearNewIntent() {
+ synchronized (sLock) {
+ sNewIntent = false;
+ }
+ }
+
+ public static void waitForStart() {
+ synchronized (sLock) {
+ final long startTime = SystemClock.uptimeMillis();
+ while (!sStarted) {
+ try {
+ sLock.wait(5000);
+ } catch (InterruptedException e) {
+ }
+ if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+ throw new RuntimeException("Timeout");
+ }
+ }
+ }
+ }
+
+ public static void waitForNewIntent() {
+ synchronized (sLock) {
+ final long startTime = SystemClock.uptimeMillis();
+ while (!sNewIntent) {
+ try {
+ sLock.wait(5000);
+ } catch (InterruptedException e) {
+ }
+ if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+ throw new RuntimeException("Timeout");
+ }
+ }
+ }
+ }
+}