Merge "Move some Gravity unit tests to CTS"
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 1723077..3fb42c1 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
@@ -17,10 +17,12 @@
 package com.android.cts.usespermissiondiffcertapp;
 
 import android.content.BroadcastReceiver;
+import android.content.ClipData;
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
@@ -53,6 +55,26 @@
         }
     }
 
+    public 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);
+                }
+            }
+        }
+    }
+
     public void assertWritingContentUriNotAllowed(Uri uri, String msg) {
         try {
             getContext().getContentResolver().insert(uri, new ContentValues());
@@ -62,6 +84,26 @@
         }
     }
 
+    public 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
@@ -98,16 +140,68 @@
                 "shouldn't write private provider");
     }
 
+    public static ClipData makeSingleClipData(Uri uri) {
+        return new ClipData("foo", new String[] { "foo/bar" },
+                new ClipData.Item(uri));
+    }
+
+    public 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;
+    }
+
+    public static Intent makeClipIntent(ClipData clip, int flags) {
+        Intent intent = new Intent();
+        intent.setClipData(clip);
+        intent.addFlags(flags);
+        return intent;
+    }
+
+    public static Intent makeClipIntent(Uri uri, int flags) {
+        return makeClipIntent(makeMultiClipData(uri), flags);
+    }
+
     public void doTryGrantUriActivityPermissionToSelf(Uri uri, int mode) {
+        Uri grantDataUri = Uri.withAppendedPath(uri, "data");
         Intent grantIntent = new Intent();
-        grantIntent.setData(uri);
+        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 " + uri + " to activity");
+            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.
         }
@@ -150,13 +244,23 @@
     }
 
     public void doTryGrantUriServicePermissionToSelf(Uri uri, int mode) {
+        Uri grantDataUri = Uri.withAppendedPath(uri, "data");
         Intent grantIntent = new Intent();
-        grantIntent.setData(uri);
+        grantIntent.setData(grantDataUri);
         grantIntent.addFlags(mode);
         grantIntent.setClass(getContext(), ReceiveUriService.class);
         try {
             getContext().startService(grantIntent);
-            fail("expected SecurityException granting " + uri + " to service");
+            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.
         }
@@ -266,10 +370,11 @@
             }
         }
     }
-    
+
     void grantUriPermissionFail(Uri uri, int mode, boolean service) {
+        Uri grantDataUri = Uri.withAppendedPath(uri, "data");
         Intent grantIntent = new Intent();
-        grantIntent.setData(uri);
+        grantIntent.setData(grantDataUri);
         grantIntent.addFlags(mode);
         grantIntent.setClass(getContext(),
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
@@ -279,7 +384,19 @@
         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");
+        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.putExtra("intent", grantIntent);
+        intent.putExtra("service", service);
+        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");
     }
 
     void doTestGrantUriPermissionFail(Uri uri) {
@@ -323,9 +440,20 @@
         doTestGrantUriPermissionFail(Uri.withAppendedPath(PRIV_URI_GRANTING, "invalid"));
     }
 
-    void grantUriPermission(Uri uri, int mode, boolean service) {
+    void grantClipUriPermission(ClipData clip, int mode, boolean service) {
         Intent grantIntent = new Intent();
-        grantIntent.setData(uri);
+        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);
@@ -336,27 +464,58 @@
         getContext().sendBroadcast(intent);
     }
 
-    void doTestGrantActivityUriReadPermission(Uri uri) {
+    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);
+                }
+            }
+        }
+    }
+
+    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.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read when starting test");
-        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read when starting test");
+        assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
 
         // --------------------------------
 
         ReceiveUriActivity.clearStarted();
-        grantUriPermission(subUri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
+        grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
         ReceiveUriActivity.waitForStart();
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().query(subUri, null, null, null, null);
+        assertReadingClipAllowed(subClip);
 
         // But not writing.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write from granted read");
+        assertWritingClipNotAllowed(subClip, "shouldn't write from granted read");
 
         // And not to the base path.
         assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
@@ -367,7 +526,7 @@
         // --------------------------------
 
         ReceiveUriActivity.clearNewIntent();
-        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
+        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
         ReceiveUriActivity.waitForNewIntent();
 
         if (false) {
@@ -381,13 +540,13 @@
         }
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().query(sub2Uri, null, null, null, null);
+        assertReadingClipAllowed(sub2Clip);
 
         // And still have access to the original URI.
-        getContext().getContentResolver().query(subUri, null, null, null, null);
+        assertReadingClipAllowed(subClip);
 
         // But not writing.
-        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write from granted read");
+        assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read");
 
         // And not to the base path.
         assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
@@ -417,31 +576,54 @@
         }
 
         // Ensure reading no longer allowed.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
-        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read after losing granted URI");
+        assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI");
     }
 
-    void doTestGrantActivityUriWritePermission(Uri uri) {
+    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);
+                }
+            }
+        }
+    }
+
+    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.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write when starting test");
-        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write when starting test");
+        assertWritingClipNotAllowed(subClip, "shouldn't write when starting test");
+        assertWritingClipNotAllowed(sub2Clip, "shouldn't write when starting test");
 
         // --------------------------------
 
         ReceiveUriActivity.clearStarted();
-        grantUriPermission(subUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
         ReceiveUriActivity.waitForStart();
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().insert(subUri, new ContentValues());
+        assertWritingClipAllowed(subClip);
 
         // But not reading.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read from granted write");
+        assertReadingClipNotAllowed(subClip, "shouldn't read from granted write");
 
         // And not to the base path.
         assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
@@ -452,7 +634,7 @@
         // --------------------------------
 
         ReceiveUriActivity.clearNewIntent();
-        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
         ReceiveUriActivity.waitForNewIntent();
 
         if (false) {
@@ -466,13 +648,13 @@
         }
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().insert(sub2Uri, new ContentValues());
+        assertWritingClipAllowed(sub2Clip);
 
         // And still have access to the original URI.
-        getContext().getContentResolver().insert(subUri, new ContentValues());
+        assertWritingClipAllowed(subClip);
 
         // But not reading.
-        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read from granted write");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write");
 
         // And not to the base path.
         assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
@@ -502,8 +684,8 @@
         }
 
         // Ensure writing no longer allowed.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
-        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write after losing granted URI");
+        assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
+        assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI");
     }
 
     /**
@@ -511,7 +693,8 @@
      * permission.
      */
     public void testGrantReadPermissionFromStartActivity() {
-        doTestGrantActivityUriReadPermission(PERM_URI_GRANTING);
+        doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, false);
+        doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, true);
     }
 
     /**
@@ -519,7 +702,8 @@
      * permission.
      */
     public void testGrantWritePermissionFromStartActivity() {
-        doTestGrantActivityUriWritePermission(PERM_URI_GRANTING);
+        doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, true);
+        doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, false);
     }
 
     /**
@@ -527,7 +711,8 @@
      * permission.
      */
     public void testGrantReadPrivateFromStartActivity() {
-        doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING);
+        doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, false);
+        doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, true);
     }
 
     /**
@@ -535,10 +720,11 @@
      * permission.
      */
     public void testGrantWritePrivateFromStartActivity() {
-        doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING);
+        doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, true);
+        doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, false);
     }
 
-    void doTestGrantServiceUriReadPermission(Uri uri) {
+    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");
@@ -546,23 +732,26 @@
 
         ReceiveUriService.stop(getContext());
 
+        final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
+        final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
+
         // Precondition: no current access.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read when starting test");
-        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read when starting test");
+        assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
 
         // --------------------------------
 
         ReceiveUriService.clearStarted();
-        grantUriPermission(subUri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
+        grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
         ReceiveUriService.waitForStart();
 
         int firstStartId = ReceiveUriService.getCurStartId();
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().query(subUri, null, null, null, null);
+        assertReadingClipAllowed(subClip);
 
         // But not writing.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write from granted read");
+        assertWritingClipNotAllowed(subClip, "shouldn't write from granted read");
 
         // And not to the base path.
         assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
@@ -574,7 +763,7 @@
 
         // Send another Intent to it.
         ReceiveUriService.clearStarted();
-        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
+        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
         ReceiveUriService.waitForStart();
 
         if (false) {
@@ -588,13 +777,13 @@
         }
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().query(sub2Uri, null, null, null, null);
+        assertReadingClipAllowed(sub2Clip);
 
         // And still to the previous URI.
-        getContext().getContentResolver().query(subUri, null, null, null, null);
+        assertReadingClipAllowed(subClip);
 
         // But not writing.
-        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write from granted read");
+        assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read");
 
         // And not to the base path.
         assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
@@ -608,7 +797,7 @@
         ReceiveUriService.stopCurWithId(firstStartId);
 
         // Ensure reading no longer allowed.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
+        assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
 
         // And make sure we can't generate a permission to a running service.
         doTryGrantUriActivityPermissionToSelf(subUri,
@@ -622,11 +811,11 @@
         ReceiveUriService.stopSync(getContext());
 
         // Ensure reading no longer allowed.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read after losing granted URI");
-        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read after losing granted URI");
+        assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI");
     }
 
-    void doTestGrantServiceUriWritePermission(Uri uri) {
+    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");
@@ -634,23 +823,26 @@
 
         ReceiveUriService.stop(getContext());
 
+        final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
+        final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
+
         // Precondition: no current access.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write when starting test");
-        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write when starting test");
+        assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
 
         // --------------------------------
 
         ReceiveUriService.clearStarted();
-        grantUriPermission(subUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
+        grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
         ReceiveUriService.waitForStart();
 
         int firstStartId = ReceiveUriService.getCurStartId();
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().insert(subUri, new ContentValues());
+        assertWritingClipAllowed(subClip);
 
         // But not reading.
-        assertReadingContentUriNotAllowed(subUri, "shouldn't read from granted write");
+        assertReadingClipNotAllowed(subClip, "shouldn't read from granted write");
 
         // And not to the base path.
         assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
@@ -662,17 +854,17 @@
 
         // Send another Intent to it.
         ReceiveUriService.clearStarted();
-        grantUriPermission(sub2Uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
+        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
         ReceiveUriService.waitForStart();
 
         // See if we now have access to the provider.
-        getContext().getContentResolver().insert(sub2Uri, new ContentValues());
+        assertWritingClipAllowed(sub2Clip);
 
         // And still to the previous URI.
-        getContext().getContentResolver().insert(subUri, new ContentValues());
+        assertWritingClipAllowed(subClip);
 
         // But not reading.
-        assertReadingContentUriNotAllowed(sub2Uri, "shouldn't read from granted write");
+        assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write");
 
         // And not to the base path.
         assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
@@ -696,7 +888,7 @@
         ReceiveUriService.stopCurWithId(firstStartId);
 
         // Ensure writing no longer allowed.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
+        assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
 
         // And make sure we can't generate a permission to a running service.
         doTryGrantUriActivityPermissionToSelf(subUri,
@@ -710,24 +902,28 @@
         ReceiveUriService.stopSync(getContext());
 
         // Ensure writing no longer allowed.
-        assertWritingContentUriNotAllowed(subUri, "shouldn't write after losing granted URI");
-        assertWritingContentUriNotAllowed(sub2Uri, "shouldn't write after losing granted URI");
+        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);
+        doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, false);
+        doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, true);
     }
 
     public void testGrantWritePermissionFromStartService() {
-        doTestGrantServiceUriWritePermission(PERM_URI_GRANTING);
+        doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, false);
+        doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, true);
     }
 
     public void testGrantReadPrivateFromStartService() {
-        doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING);
+        doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, false);
+        doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, true);
     }
 
     public void testGrantWritePrivateFromStartService() {
-        doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING);
+        doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, false);
+        doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, true);
     }
 
     /**
@@ -773,7 +969,8 @@
      * permission.
      */
     public void testGrantReadPathPermissionFromStartActivity() {
-        doTestGrantActivityUriReadPermission(PERM_URI_PATH);
+        doTestGrantActivityUriReadPermission(PERM_URI_PATH, false);
+        doTestGrantActivityUriReadPermission(PERM_URI_PATH, true);
     }
 
     /**
@@ -781,7 +978,8 @@
      * permission.
      */
     public void testGrantWritePathPermissionFromStartActivity() {
-        doTestGrantActivityUriWritePermission(PERM_URI_PATH);
+        doTestGrantActivityUriWritePermission(PERM_URI_PATH, false);
+        doTestGrantActivityUriWritePermission(PERM_URI_PATH, true);
     }
 
     /**
@@ -789,7 +987,8 @@
      * permission.
      */
     public void testGrantReadPathPermissionFromStartService() {
-        doTestGrantServiceUriReadPermission(PERM_URI_PATH);
+        doTestGrantServiceUriReadPermission(PERM_URI_PATH, false);
+        doTestGrantServiceUriReadPermission(PERM_URI_PATH, true);
     }
 
     /**
@@ -797,7 +996,8 @@
      * permission.
      */
     public void testGrantWritePathPermissionFromStartService() {
-        doTestGrantServiceUriWritePermission(PERM_URI_PATH);
+        doTestGrantServiceUriWritePermission(PERM_URI_PATH, false);
+        doTestGrantServiceUriWritePermission(PERM_URI_PATH, true);
     }
 
     public void testGetMimeTypePermission() {
diff --git a/tests/Android.mk b/tests/Android.mk
index 4868654..708f051 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -28,7 +28,8 @@
 LOCAL_JNI_SHARED_LIBRARIES := libcts_jni
 
 # Resource unit tests use a private locale and some densities
-LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c 320dpi -c 240dpi -c 160dpi -c 32dpi \
+LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
+        -c 320dpi -c 240dpi -c 160dpi -c 32dpi \
         --preferred-configurations 320dpi --preferred-configurations 240dpi \
         --preferred-configurations 160dpi --preferred-configurations 32dpi
 
diff --git a/tests/res/raw/set_target_api_11.bc b/tests/res/raw/set_target_api_11.bc
index cd98c6a..babbd9e 100644
--- a/tests/res/raw/set_target_api_11.bc
+++ b/tests/res/raw/set_target_api_11.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_12.bc b/tests/res/raw/set_target_api_12.bc
index 20576d6..92ee8de 100644
--- a/tests/res/raw/set_target_api_12.bc
+++ b/tests/res/raw/set_target_api_12.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_13.bc b/tests/res/raw/set_target_api_13.bc
index 339876a..c842b98 100644
--- a/tests/res/raw/set_target_api_13.bc
+++ b/tests/res/raw/set_target_api_13.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_14.bc b/tests/res/raw/set_target_api_14.bc
index 60fe08b..24d2481 100644
--- a/tests/res/raw/set_target_api_14.bc
+++ b/tests/res/raw/set_target_api_14.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_15.bc b/tests/res/raw/set_target_api_15.bc
index d3f1beff..a6e86d4 100644
--- a/tests/res/raw/set_target_api_15.bc
+++ b/tests/res/raw/set_target_api_15.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_16.bc b/tests/res/raw/set_target_api_16.bc
index 1cabc4d..5ded6fd 100644
--- a/tests/res/raw/set_target_api_16.bc
+++ b/tests/res/raw/set_target_api_16.bc
Binary files differ
diff --git a/tests/res/values-sw590dp-hdpi/configVarying.xml b/tests/res/values-sw590dp-hdpi/configVarying.xml
new file mode 100755
index 0000000..9864e50
--- /dev/null
+++ b/tests/res/values-sw590dp-hdpi/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple sw590 hdpi</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag sw590 hdpi</item>
+    </bag>
+    <item type="configVarying" name="sw">590 hdpi</item>
+</resources>
diff --git a/tests/res/values-sw590dp-mdpi/configVarying.xml b/tests/res/values-sw590dp-mdpi/configVarying.xml
new file mode 100755
index 0000000..7089ca9
--- /dev/null
+++ b/tests/res/values-sw590dp-mdpi/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple sw590 mdpi</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag sw590 mdpi</item>
+    </bag>
+    <item type="configVarying" name="sw">590 mdpi</item>
+</resources>
diff --git a/tests/res/values-sw590dp-xhdpi/configVarying.xml b/tests/res/values-sw590dp-xhdpi/configVarying.xml
new file mode 100755
index 0000000..5c55f2c
--- /dev/null
+++ b/tests/res/values-sw590dp-xhdpi/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple sw590 xhdpi</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag sw590 xhdpi</item>
+    </bag>
+    <item type="configVarying" name="sw">590 xhdpi</item>
+</resources>
diff --git a/tests/res/values-sw590dp/configVarying.xml b/tests/res/values-sw590dp/configVarying.xml
new file mode 100755
index 0000000..8563921
--- /dev/null
+++ b/tests/res/values-sw590dp/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple sw590</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag sw590</item>
+    </bag>
+    <item type="configVarying" name="sw">590</item>
+</resources>
diff --git a/tests/res/values-sw591dp-hdpi/configVarying.xml b/tests/res/values-sw591dp-hdpi/configVarying.xml
new file mode 100755
index 0000000..880dced
--- /dev/null
+++ b/tests/res/values-sw591dp-hdpi/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple sw591 hdpi</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag sw591 hdpi</item>
+    </bag>
+    <item type="configVarying" name="sw">591 hdpi</item>
+</resources>
diff --git a/tests/res/values-sw591dp/configVarying.xml b/tests/res/values-sw591dp/configVarying.xml
new file mode 100755
index 0000000..4a0ee85
--- /dev/null
+++ b/tests/res/values-sw591dp/configVarying.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple sw591</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag sw591</item>
+    </bag>
+    <item type="configVarying" name="sw">591</item>
+</resources>
diff --git a/tests/tests/content/src/android/content/res/cts/ConfigTest.java b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
index 144c2f2..60197b6 100755
--- a/tests/tests/content/src/android/content/res/cts/ConfigTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
@@ -733,9 +733,9 @@
         // ensure that swNNNdp, wNNNdp, and hNNNdp are working correctly
         // for various common screen configurations.
         TotalConfig config = makeClassicConfig();
-        config.setProperty(Properties.SWIDTH_DP, 599);
-        config.setProperty(Properties.WIDTH_DP, 599);
-        config.setProperty(Properties.HEIGHT_DP, 549);
+        config.setProperty(Properties.SWIDTH_DP, 589);
+        config.setProperty(Properties.WIDTH_DP, 589);
+        config.setProperty(Properties.HEIGHT_DP, 500);
         config.setProperty(Properties.ORIENTATION, Configuration.ORIENTATION_LANDSCAPE);
         config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_LARGE);
         Resources res = config.getResources();
@@ -746,6 +746,44 @@
         checkValue(res, R.configVarying.wh, "default");
 
         config = makeClassicConfig();
+        config.setProperty(Properties.SWIDTH_DP, 590);
+        config.setProperty(Properties.WIDTH_DP, 590);
+        config.setProperty(Properties.HEIGHT_DP, 500);
+        config.setProperty(Properties.ORIENTATION, Configuration.ORIENTATION_LANDSCAPE);
+        config.setProperty(Properties.SCREENLAYOUT, Configuration.SCREENLAYOUT_SIZE_LARGE);
+        config.setProperty(Properties.DENSITY, DisplayMetrics.DENSITY_MEDIUM);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple sw590 mdpi");
+        checkValue(res, R.configVarying.sw, "590 mdpi");
+
+        config.setProperty(Properties.DENSITY, DisplayMetrics.DENSITY_HIGH);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple sw590 hdpi");
+        checkValue(res, R.configVarying.sw, "590 hdpi");
+
+        config.setProperty(Properties.DENSITY, DisplayMetrics.DENSITY_XHIGH);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple sw590 xhdpi");
+        checkValue(res, R.configVarying.sw, "590 xhdpi");
+
+        config.setProperty(Properties.SWIDTH_DP, 591);
+        config.setProperty(Properties.WIDTH_DP, 591);
+        config.setProperty(Properties.DENSITY, DisplayMetrics.DENSITY_MEDIUM);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple sw591");
+        checkValue(res, R.configVarying.sw, "591");
+
+        config.setProperty(Properties.DENSITY, DisplayMetrics.DENSITY_HIGH);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple sw591 hdpi");
+        checkValue(res, R.configVarying.sw, "591 hdpi");
+
+        config.setProperty(Properties.DENSITY, DisplayMetrics.DENSITY_XHIGH);
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple sw591 hdpi");
+        checkValue(res, R.configVarying.sw, "591 hdpi");
+
+        config = makeClassicConfig();
         config.setProperty(Properties.SWIDTH_DP, 480);
         config.setProperty(Properties.WIDTH_DP, 800);
         config.setProperty(Properties.HEIGHT_DP, 480);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
index 77d634f..6ad1748 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
index 343d99d..dc772d5 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
index cf06a6b..e1580e4 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
index 1b9b213..42d08c6 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
index 4ed95b9..6acfede 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
index 3a7f006..f111005 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
diff --git a/tests/tests/view/src/android/view/cts/ChoreographerTest.java b/tests/tests/view/src/android/view/cts/ChoreographerTest.java
index c76d495..749f5da 100644
--- a/tests/tests/view/src/android/view/cts/ChoreographerTest.java
+++ b/tests/tests/view/src/android/view/cts/ChoreographerTest.java
@@ -22,6 +22,7 @@
 public class ChoreographerTest extends AndroidTestCase {
     private static final long NOMINAL_VSYNC_PERIOD = 16;
     private static final long DELAY_PERIOD = NOMINAL_VSYNC_PERIOD * 5;
+    private static final Object TOKEN = new Object();
 
     private Choreographer mChoreographer = Choreographer.getInstance();
 
@@ -42,10 +43,10 @@
         MockRunnable removedCallback = new MockRunnable();
         try {
             // Add and remove a few callbacks.
-            mChoreographer.postAnimationCallback(addedCallback1);
-            mChoreographer.postAnimationCallbackDelayed(addedCallback2, 0);
-            mChoreographer.postAnimationCallback(removedCallback);
-            mChoreographer.removeAnimationCallbacks(removedCallback);
+            mChoreographer.postAnimationCallback(addedCallback1, null);
+            mChoreographer.postAnimationCallbackDelayed(addedCallback2, null, 0);
+            mChoreographer.postAnimationCallback(removedCallback, null);
+            mChoreographer.removeAnimationCallbacks(removedCallback, null);
 
             // Sleep for a couple of frames.
             sleep(NOMINAL_VSYNC_PERIOD * 3);
@@ -56,16 +57,33 @@
             assertEquals(0, removedCallback.invocationCount);
 
             // If we post a callback again, then it should be invoked again.
-            mChoreographer.postAnimationCallback(addedCallback1);
+            mChoreographer.postAnimationCallback(addedCallback1, null);
             sleep(NOMINAL_VSYNC_PERIOD * 3);
 
             assertEquals(2, addedCallback1.invocationCount);
             assertEquals(1, addedCallback2.invocationCount);
             assertEquals(0, removedCallback.invocationCount);
+
+            // If the token matches, the the callback should be removed.
+            mChoreographer.postAnimationCallback(addedCallback1, null);
+            mChoreographer.postAnimationCallback(removedCallback, TOKEN);
+            mChoreographer.removeAnimationCallbacks(null, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+            assertEquals(3, addedCallback1.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
+
+            // If the action and token matches, then the callback should be removed.
+            // If only the token matches, then the callback should not be removed.
+            mChoreographer.postAnimationCallback(addedCallback1, TOKEN);
+            mChoreographer.postAnimationCallback(removedCallback, TOKEN);
+            mChoreographer.removeAnimationCallbacks(removedCallback, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+            assertEquals(4, addedCallback1.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
         } finally {
-            mChoreographer.removeAnimationCallbacks(addedCallback1);
-            mChoreographer.removeAnimationCallbacks(addedCallback2);
-            mChoreographer.removeAnimationCallbacks(removedCallback);
+            mChoreographer.removeAnimationCallbacks(addedCallback1, null);
+            mChoreographer.removeAnimationCallbacks(addedCallback2, null);
+            mChoreographer.removeAnimationCallbacks(removedCallback, null);
         }
     }
 
@@ -74,9 +92,9 @@
         MockRunnable removedCallback = new MockRunnable();
         try {
             // Add and remove a few callbacks.
-            mChoreographer.postAnimationCallbackDelayed(addedCallback, DELAY_PERIOD);
-            mChoreographer.postAnimationCallbackDelayed(removedCallback, DELAY_PERIOD);
-            mChoreographer.removeAnimationCallbacks(removedCallback);
+            mChoreographer.postAnimationCallbackDelayed(addedCallback, null, DELAY_PERIOD);
+            mChoreographer.postAnimationCallbackDelayed(removedCallback, null, DELAY_PERIOD);
+            mChoreographer.removeAnimationCallbacks(removedCallback, null);
 
             // Sleep for a couple of frames.
             sleep(NOMINAL_VSYNC_PERIOD * 3);
@@ -91,9 +109,26 @@
             // We expect the remaining callbacks to have been invoked.
             assertEquals(1, addedCallback.invocationCount);
             assertEquals(0, removedCallback.invocationCount);
+
+            // If the token matches, the the callback should be removed.
+            mChoreographer.postAnimationCallbackDelayed(addedCallback, null, DELAY_PERIOD);
+            mChoreographer.postAnimationCallbackDelayed(removedCallback, TOKEN, DELAY_PERIOD);
+            mChoreographer.removeAnimationCallbacks(null, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD);
+            assertEquals(2, addedCallback.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
+
+            // If the action and token matches, then the callback should be removed.
+            // If only the token matches, then the callback should not be removed.
+            mChoreographer.postAnimationCallbackDelayed(addedCallback, TOKEN, DELAY_PERIOD);
+            mChoreographer.postAnimationCallbackDelayed(removedCallback, TOKEN, DELAY_PERIOD);
+            mChoreographer.removeAnimationCallbacks(removedCallback, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD);
+            assertEquals(3, addedCallback.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
         } finally {
-            mChoreographer.removeAnimationCallbacks(addedCallback);
-            mChoreographer.removeAnimationCallbacks(removedCallback);
+            mChoreographer.removeAnimationCallbacks(addedCallback, null);
+            mChoreographer.removeAnimationCallbacks(removedCallback, null);
         }
     }
 
@@ -103,10 +138,10 @@
         MockRunnable removedCallback = new MockRunnable();
         try {
             // Add and remove a few callbacks.
-            mChoreographer.postDrawCallback(addedCallback1);
-            mChoreographer.postDrawCallbackDelayed(addedCallback2, 0);
-            mChoreographer.postDrawCallback(removedCallback);
-            mChoreographer.removeDrawCallbacks(removedCallback);
+            mChoreographer.postDrawCallback(addedCallback1, null);
+            mChoreographer.postDrawCallbackDelayed(addedCallback2, null, 0);
+            mChoreographer.postDrawCallback(removedCallback, null);
+            mChoreographer.removeDrawCallbacks(removedCallback, null);
 
             // Sleep for a couple of frames.
             sleep(NOMINAL_VSYNC_PERIOD * 3);
@@ -117,16 +152,33 @@
             assertEquals(0, removedCallback.invocationCount);
 
             // If we post a callback again, then it should be invoked again.
-            mChoreographer.postDrawCallback(addedCallback1);
+            mChoreographer.postDrawCallback(addedCallback1, null);
             sleep(NOMINAL_VSYNC_PERIOD * 3);
 
             assertEquals(2, addedCallback1.invocationCount);
             assertEquals(1, addedCallback2.invocationCount);
             assertEquals(0, removedCallback.invocationCount);
+
+            // If the token matches, the the callback should be removed.
+            mChoreographer.postDrawCallback(addedCallback1, null);
+            mChoreographer.postDrawCallback(removedCallback, TOKEN);
+            mChoreographer.removeDrawCallbacks(null, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+            assertEquals(3, addedCallback1.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
+
+            // If the action and token matches, then the callback should be removed.
+            // If only the token matches, then the callback should not be removed.
+            mChoreographer.postDrawCallback(addedCallback1, TOKEN);
+            mChoreographer.postDrawCallback(removedCallback, TOKEN);
+            mChoreographer.removeDrawCallbacks(removedCallback, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+            assertEquals(4, addedCallback1.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
         } finally {
-            mChoreographer.removeDrawCallbacks(addedCallback1);
-            mChoreographer.removeDrawCallbacks(addedCallback2);
-            mChoreographer.removeDrawCallbacks(removedCallback);
+            mChoreographer.removeDrawCallbacks(addedCallback1, null);
+            mChoreographer.removeDrawCallbacks(addedCallback2, null);
+            mChoreographer.removeDrawCallbacks(removedCallback, null);
         }
     }
 
@@ -135,9 +187,9 @@
         MockRunnable removedCallback = new MockRunnable();
         try {
             // Add and remove a few callbacks.
-            mChoreographer.postDrawCallbackDelayed(addedCallback, DELAY_PERIOD);
-            mChoreographer.postDrawCallbackDelayed(removedCallback, DELAY_PERIOD);
-            mChoreographer.removeDrawCallbacks(removedCallback);
+            mChoreographer.postDrawCallbackDelayed(addedCallback, null, DELAY_PERIOD);
+            mChoreographer.postDrawCallbackDelayed(removedCallback, null, DELAY_PERIOD);
+            mChoreographer.removeDrawCallbacks(removedCallback, null);
 
             // Sleep for a couple of frames.
             sleep(NOMINAL_VSYNC_PERIOD * 3);
@@ -152,15 +204,32 @@
             // We expect the remaining callbacks to have been invoked.
             assertEquals(1, addedCallback.invocationCount);
             assertEquals(0, removedCallback.invocationCount);
+
+            // If the token matches, the the callback should be removed.
+            mChoreographer.postDrawCallbackDelayed(addedCallback, null, DELAY_PERIOD);
+            mChoreographer.postDrawCallbackDelayed(removedCallback, TOKEN, DELAY_PERIOD);
+            mChoreographer.removeDrawCallbacks(null, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD);
+            assertEquals(2, addedCallback.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
+
+            // If the action and token matches, then the callback should be removed.
+            // If only the token matches, then the callback should not be removed.
+            mChoreographer.postDrawCallbackDelayed(addedCallback, TOKEN, DELAY_PERIOD);
+            mChoreographer.postDrawCallbackDelayed(removedCallback, TOKEN, DELAY_PERIOD);
+            mChoreographer.removeDrawCallbacks(removedCallback, TOKEN);
+            sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD);
+            assertEquals(3, addedCallback.invocationCount);
+            assertEquals(0, removedCallback.invocationCount);
         } finally {
-            mChoreographer.removeDrawCallbacks(addedCallback);
-            mChoreographer.removeDrawCallbacks(removedCallback);
+            mChoreographer.removeDrawCallbacks(addedCallback, null);
+            mChoreographer.removeDrawCallbacks(removedCallback, null);
         }
     }
 
     public void testPostAnimationCallbackThrowsIfRunnableIsNull() {
         try {
-            mChoreographer.postAnimationCallback(null);
+            mChoreographer.postAnimationCallback(null, TOKEN);
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex) {
             // expected
@@ -169,16 +238,7 @@
 
     public void testPostAnimationCallbackDelayedThrowsIfRunnableIsNull() {
         try {
-            mChoreographer.postAnimationCallbackDelayed(null, DELAY_PERIOD);
-            fail("Expected IllegalArgumentException");
-        } catch (IllegalArgumentException ex) {
-            // expected
-        }
-    }
-
-    public void testRemoveAnimationCallbackThrowsIfRunnableIsNull() {
-        try {
-            mChoreographer.removeAnimationCallbacks(null);
+            mChoreographer.postAnimationCallbackDelayed(null, TOKEN, DELAY_PERIOD);
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex) {
             // expected
@@ -187,7 +247,7 @@
 
     public void testPostDrawCallbackThrowsIfRunnableIsNull() {
         try {
-            mChoreographer.postDrawCallback(null);
+            mChoreographer.postDrawCallback(null, TOKEN);
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex) {
             // expected
@@ -196,16 +256,7 @@
 
     public void testPostDrawCallbackDelayedThrowsIfRunnableIsNull() {
         try {
-            mChoreographer.postDrawCallbackDelayed(null, DELAY_PERIOD);
-            fail("Expected IllegalArgumentException");
-        } catch (IllegalArgumentException ex) {
-            // expected
-        }
-    }
-
-    public void testRemoveDrawCallbackThrowsIfRunnableIsNull() {
-        try {
-            mChoreographer.removeDrawCallbacks(null);
+            mChoreographer.postDrawCallbackDelayed(null, TOKEN, DELAY_PERIOD);
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex) {
             // expected