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() {