Test <path-permissions> on unprotected providers.

Tests new framework feature allows <path-permissions> to restrict
access to ContentProvider paths that would otherwise be allowed by
lack of a top-level <provider> permission.

Bug: 6131916
Change-Id: I52cd37b346eb6c367b457ff8426e1c72fe1562bf
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
index de71966..c8eb70d 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -28,6 +28,8 @@
     <permission android:name="com.android.cts.permissionNotUsedWithSignature"
         android:protectionLevel="signature" />
 
+    <permission android:name="com.android.cts.permissionNormal" />
+
     <application>
         <receiver android:name="GrantUriPermission" android:exported="true">
         </receiver>
@@ -82,6 +84,22 @@
                     android:writePermission="com.android.cts.permissionWithSignature" />
             <grant-uri-permission android:pathPattern=".*" />
         </provider>
-        
+
+        <!-- Target for tests that verify path permissions can restrict access
+             when no default top-level permission. -->
+        <provider android:name="PermissionContentProviderPathRestricting"
+                android:authorities="ctspermissionwithsignaturepathrestricting">
+            <!-- Require signature permission to get into path. -->
+            <path-permission
+                    android:pathPrefix="/foo.*"
+                    android:readPermission="com.android.cts.permissionWithSignature"
+                    android:writePermission="com.android.cts.permissionWithSignature" />
+            <!-- Allow access to a specific path inside. -->
+            <path-permission
+                    android:pathPrefix="/foo/bar"
+                    android:readPermission="com.android.cts.permissionNormal"
+                    android:writePermission="com.android.cts.permissionNormal" />
+        </provider>
+
     </application>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index e01bc90..b7307bb 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -21,6 +21,9 @@
          a different cert so it should fail. -->
     <uses-permission android:name="com.android.cts.permissionWithSignature"/>
 
+    <!-- Request a normal permission, which should be granted. -->
+    <uses-permission android:name="com.android.cts.permissionNormal"/>
+
     <!-- This is a permission we can have, which the other app can require for
          access. -->
     <permission android:name="com.android.cts.permissionAllowedWithSignature"
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index 3fb42c1..212c440 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -41,6 +41,8 @@
     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 PERM_URI_PATH_RESTRICTING = Uri.parse(
+            "content://ctspermissionwithsignaturepathrestricting");
     static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
     static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
 
@@ -55,6 +57,14 @@
         }
     }
 
+    public void assertReadingContentUriAllowed(Uri uri) {
+        try {
+            getContext().getContentResolver().query(uri, null, null, null, null);
+        } catch (SecurityException e) {
+            fail("unexpected SecurityException reading " + uri);
+        }
+    }
+
     public void assertReadingClipNotAllowed(ClipData clip, String msg) {
         for (int i=0; i<clip.getItemCount(); i++) {
             ClipData.Item item = clip.getItemAt(i);
@@ -84,6 +94,14 @@
         }
     }
 
+    public void assertWritingContentUriAllowed(Uri uri) {
+        try {
+            getContext().getContentResolver().insert(uri, new ContentValues());
+        } catch (SecurityException e) {
+            fail("unexpected SecurityException writing " + uri);
+        }
+    }
+
     public void assertWritingClipNotAllowed(ClipData clip, String msg) {
         for (int i=0; i<clip.getItemCount(); i++) {
             ClipData.Item item = clip.getItemAt(i);
@@ -1000,6 +1018,43 @@
         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);
+    }
+
+    /**
+     * Verify that paths under {@code path-permission} restriction aren't
+     * allowed, even though the {@code provider} requires no permissions.
+     */
+    public void testRestrictingProviderMatchingPath() {
+        final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("fo").build();
+        assertReadingContentUriAllowed(test1);
+        assertWritingContentUriAllowed(test1);
+
+        final Uri test2 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo").build();
+        assertReadingContentUriNotAllowed(test2, null);
+        assertWritingContentUriNotAllowed(test2, null);
+
+        final Uri test3 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo/bar2").build();
+        assertReadingContentUriNotAllowed(test3, null);
+        assertWritingContentUriNotAllowed(test3, 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() {
+        final Uri test = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo/bar").build();
+        assertReadingContentUriAllowed(test);
+        assertWritingContentUriAllowed(test);
+    }
+
     public void testGetMimeTypePermission() {
         // Precondition: no current access.
         assertWritingContentUriNotAllowed(PERM_URI, "shouldn't write when starting test");