Merge "Configure CtsSyncContentHostTestCases with not_multi_abi." into qt-dev
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d577815..e06e5ad 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2228,7 +2228,7 @@
         3. Verify that the background image contains a suitcase.\n
         4. Verify that the background color of the remaining image is blue.\n
         5. Verify that the header text says \"CtsVerifier\".\n
-        6. Confirm your credentials and verify that the credentials you entered previously work.
+        6. Confirm your credentials and verify that the credentials you entered previously work.\n
         7. The work app should be launched.
     </string>
     <string name="provisioning_byod_confirm_work_credentials_header">
@@ -2240,10 +2240,11 @@
 
         1. Verify that you get sent to the page for Choosing a new work lock.\n
         2. Set a pattern lock.\n
-        3. Open a work app.\n
-        4. Verify that a screen asking you for your work credentials is shown.\n
-        5. Confirm your credentials and verify that the credentials you entered previously work.\n
-        6. The work app should be launched.
+        3. Press the power button to turn the screen off and then back on and swipe to unlock.\n
+        4. Open a work app.\n
+        5. Verify that a screen asking you for your work credentials is shown.\n
+        6. Confirm your credentials and verify that the credentials you entered previously work.\n
+        7. The work app should be launched.
     </string>
     <string name="provisioning_byod_recents">Recents redaction test</string>
     <string name="provisioning_byod_recents_info">
@@ -4950,7 +4951,9 @@
     <string name="bubbles_notification_title">Bubble Notification Tests</string>
     <string name="bubbles_notification_description">This test checks the behaviour of bubble
         notifications, ensuring that notifications appear or disappear appropriately based on how
-        BubbleMetadata is configured on the notification and user actions.</string>
+        BubbleMetadata is configured on the notification and user actions. Bubbles are special
+        notifications that appear as a floating button on the screen, in addition to the notification
+        in the notification shade.</string>
     <string name="bubbles_notification_test_title_1">Step 1: send a bubble with notification</string>
     <string name="bubbles_notification_test_verify_1">Click the button below and verify that there is a
         bubble on the screen and a notification in the notification shade.
@@ -4980,6 +4983,23 @@
     <string name="bubbles_notification_test_verify_6">Tap on the bubble to open it, then tap on the
         bubble again to collapse it and return to this screen. Verify that after opening the bubble,
         there is no longer a notification for it visible in the notification shade.</string>
+    <string name="bubbles_notification_test_title_7">Step 7: drag and dismiss bubble</string>
+    <string name="bubbles_notification_test_verify_7">Click the button below and verify that the bubble
+        is still on screen, and the notification is visible in the notification shade.\n\n
+        Drag the bubble, while dragging a UI affordance should show. Verify that: \n\n
+        1. Dragging and dropping the bubble on that UI affordance removes it from the screen.\n\n
+        2. The notification should remain in the notification shade.</string>
+    <string name="bubbles_notification_test_button_7">Update bubble to show notification</string>
+    <string name="bubbles_notification_test_title_8">Step 8: dismiss notification</string>
+    <string name="bubbles_notification_test_verify_8">Click the button below and verify that a bubble
+        appears on screen, and the notification is visible in the notification shade.\n\n
+        Dismiss the notification from the notification shade and verify that the bubble remains on
+        screen.</string>
+    <string name="bubbles_notification_test_button_8">Send bubble notification</string>
+    <string name="bubbles_notification_test_title_9">Step 9: auto expand bubble</string>
+    <string name="bubbles_notification_test_verify_9">Click the button below and verify that a bubble
+        appears on screen, auto-expanded.</string>
+    <string name="bubbles_notification_test_button_9">Send auto-expanded bubble notification</string>
     <string name="bubbles_test_summary_title">Test Complete</string>
     <string name="bubbles_test_summary">%1$d out of %2$d tests passed</string>
     <string name="bubble_activity_title">Bubble Activity</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index 998f8a8..d20e047 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -70,7 +70,7 @@
         final String[] settingsIntentActions = new String[] {
             Settings.ACTION_DATE_SETTINGS,
             Settings.ACTION_SETTINGS,
-            Settings.ACTION_SECURITY_SETTINGS,
+            Settings.ACTION_DISPLAY_SETTINGS,
             Settings.ACTION_DISPLAY_SETTINGS,
             Settings.ACTION_SETTINGS,
             Settings.ACTION_ACCESSIBILITY_SETTINGS,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index f004ace..53b1bf3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -262,7 +262,8 @@
                 // This test should not run on watch
                 return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
             case UserManager.DISALLOW_OUTGOING_BEAM:
-                return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+                return pm.hasSystemFeature(PackageManager.FEATURE_NFC)
+                        && pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM);
             case UserManager.DISALLOW_SHARE_LOCATION:
                 return pm.hasSystemFeature(PackageManager.FEATURE_LOCATION);
             case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
index f0ec49f..d5c81fe 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -106,6 +106,9 @@
         mTests.add(new RemoveMetadataTest());
         mTests.add(new AddMetadataTest());
         mTests.add(new ExpandBubbleTest());
+        mTests.add(new DismissBubbleTest());
+        mTests.add(new DismissNotificationTest());
+        mTests.add(new AutoExpandBubbleTest());
 
         setPassFailButtonClickListeners();
 
@@ -195,7 +198,7 @@
         @Override
         public void performTestAction() {
             Notification.Builder builder =
-                    getBasicNotifBuilder("Bubble notification", "SendBubbleTest");
+                    getBasicNotifBuilder("Bubble notification", "1: SendBubbleTest");
             builder.setBubbleMetadata(getBasicBubbleBuilder().build());
 
             mNotificationManager.notify(NOTIFICATION_ID, builder.build());
@@ -222,7 +225,7 @@
         @Override
         public void performTestAction() {
             Notification.Builder builder =
-                    getBasicNotifBuilder("Bubble notification", "SuppressNotifTest");
+                    getBasicNotifBuilder("Bubble notification", "2: SuppressNotifTest");
 
             Notification.BubbleMetadata metadata = getBasicBubbleBuilder()
                     .setSuppressNotification(true)
@@ -253,7 +256,7 @@
         @Override
         public void performTestAction() {
             Notification.Builder builder =
-                    getBasicNotifBuilder("Bubble notification", "AddNotifTest");
+                    getBasicNotifBuilder("Bubble notification", "3: AddNotifTest");
 
             Notification.BubbleMetadata metadata = getBasicBubbleBuilder()
                     .setSuppressNotification(false)
@@ -284,7 +287,7 @@
         @Override
         public void performTestAction() {
             Notification.Builder builder =
-                    getBasicNotifBuilder("Bubble notification", "RemoveMetadataTest");
+                    getBasicNotifBuilder("Bubble notification", "4: RemoveMetadataTest");
             mNotificationManager.notify(NOTIFICATION_ID, builder.build());
         }
     }
@@ -309,7 +312,7 @@
         @Override
         public void performTestAction() {
             Notification.Builder builder =
-                    getBasicNotifBuilder("Bubble notification", "AddMetadataTest");
+                    getBasicNotifBuilder("Bubble notification", "5: AddMetadataTest");
 
             Notification.BubbleMetadata metadata = getBasicBubbleBuilder().build();
             builder.setBubbleMetadata(metadata);
@@ -330,6 +333,93 @@
         }
     }
 
+    private class DismissBubbleTest extends BubblesTestStep {
+        @Override
+        public int getButtonText() {
+            return R.string.bubbles_notification_test_button_7;
+        }
+
+        @Override
+        public int getTestTitle() {
+            return R.string.bubbles_notification_test_title_7;
+        }
+
+        @Override
+        public int getTestDescription() {
+            return R.string.bubbles_notification_test_verify_7;
+        }
+
+        @Override
+        public void performTestAction() {
+            Notification.Builder builder =
+                    getBasicNotifBuilder("Bubble notification", "7: DismissBubbleTest");
+
+            Notification.BubbleMetadata metadata = getBasicBubbleBuilder().build();
+            builder.setBubbleMetadata(metadata);
+
+            mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+        }
+    }
+
+    private class DismissNotificationTest extends BubblesTestStep {
+        @Override
+        public int getButtonText() {
+            return R.string.bubbles_notification_test_button_8;
+        }
+
+        @Override
+        public int getTestTitle() {
+            return R.string.bubbles_notification_test_title_8;
+        }
+
+        @Override
+        public int getTestDescription() {
+            return R.string.bubbles_notification_test_verify_8;
+        }
+
+        @Override
+        public void performTestAction() {
+            Notification.Builder builder =
+                    getBasicNotifBuilder("Bubble notification",
+                            "8: DismissNotificationTest: Dismiss me!!");
+
+            Notification.BubbleMetadata metadata = getBasicBubbleBuilder().build();
+            builder.setBubbleMetadata(metadata);
+
+            mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+        }
+    }
+
+    private class AutoExpandBubbleTest extends BubblesTestStep {
+        @Override
+        public int getButtonText() {
+            return R.string.bubbles_notification_test_button_9;
+        }
+
+        @Override
+        public int getTestTitle() {
+            return R.string.bubbles_notification_test_title_9;
+        }
+
+        @Override
+        public int getTestDescription() {
+            return R.string.bubbles_notification_test_verify_9;
+        }
+
+        @Override
+        public void performTestAction() {
+            Notification.Builder builder =
+                    getBasicNotifBuilder("Bubble notification", "9: Auto expanded bubble");
+
+            Notification.BubbleMetadata metadata =
+                    getBasicBubbleBuilder().setAutoExpandBubble(true).build();
+            builder.setBubbleMetadata(metadata);
+
+            mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+        }
+    }
+
+
     /** Creates a minimally filled out {@link android.app.Notification.BubbleMetadata.Builder} */
     private Notification.BubbleMetadata.Builder getBasicBubbleBuilder() {
         Context context = getApplicationContext();
diff --git a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
index 0fb1d65..60991b2 100644
--- a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
+++ b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
@@ -36,7 +36,7 @@
 
 public class NotificationBot extends BroadcastReceiver {
     private static final String TAG = "NotificationBot";
-    private static final String NOTIFICATION_CHANNEL_ID = TAG;
+    private static final String NOTIFICATION_CHANNEL_ID = TAG + "_high";
     private static final String EXTRA_ID = "ID";
     private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
     private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST";
@@ -139,7 +139,7 @@
                 context.getSystemService(NotificationManager.class);
         notificationManager.createNotificationChannel(new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
-                NotificationManager.IMPORTANCE_DEFAULT));
+                NotificationManager.IMPORTANCE_HIGH));
         final Notification.Builder nb = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
                 .setContentTitle(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE))
                 .setSmallIcon(android.R.drawable.ic_popup_sync)
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java
index 91cc58d..ba1cefe 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDeviceInfo.java
@@ -46,11 +46,15 @@
 
             store.startGroup();
             store.addResult("name", info.getName());
+            store.addResult("canonical", info.getCanonicalName());
             store.addResult("encoder", info.isEncoder());
+            store.addResult("alias", info.isAlias());
+            store.addResult("software", info.isSoftwareOnly());
+            store.addResult("hardware", info.isHardwareAccelerated());
+            store.addResult("vendor", info.isVendor());
 
             store.startArray("supported_type");
             for (String type : info.getSupportedTypes()) {
-
                 store.startGroup();
                 store.addResult("type", type);
                 CodecCapabilities codecCapabilities = info.getCapabilitiesForType(type);
@@ -64,6 +68,13 @@
                     }
                     store.endArray(); // codec_profile_level
                 }
+                if (codecCapabilities.colorFormats.length > 0) {
+                    store.startArray("codec_color_format");
+                    for (int format : codecCapabilities.colorFormats) {
+                        store.addResult("format", format);
+                    }
+                    store.endArray(); // codec_color_format
+                }
                 store.addResult("supported_secure_playback", codecCapabilities.isFeatureSupported(
                         CodecCapabilities.FEATURE_SecurePlayback));
                 VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
index 16c42b2..772009d 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
@@ -205,9 +205,12 @@
     // returns the list of codecs that support any one of the formats
     private static String[] getCodecNames(
             boolean isEncoder, Boolean isGoog, MediaFormat... formats) {
-        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         ArrayList<String> result = new ArrayList<>();
-        for (MediaCodecInfo info : mcl.getCodecInfos()) {
+        for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+            if (info.isAlias()) {
+                // don't consider aliases here
+                continue;
+            }
             if (info.isEncoder() != isEncoder) {
                 continue;
             }
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
index 3b76b5d7..5373a3b 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
@@ -92,6 +92,9 @@
     public void testEnableAngleForAll() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_SEC_PKG,
@@ -99,9 +102,6 @@
 
         setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALL_USE_ANGLE, "1");
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_ANGLE_METHOD);
@@ -117,11 +117,11 @@
     public void testUseDefaultDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_DEFAULT_METHOD);
@@ -134,11 +134,11 @@
     public void testUseAngleDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_ANGLE_METHOD);
@@ -151,11 +151,11 @@
     public void testUseNativeDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_NATIVE_METHOD);
@@ -168,13 +168,13 @@
     public void testSettingsLengthMismatch() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
                         ANGLE_DRIVER_TEST_SEC_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_DEFAULT_METHOD);
@@ -191,10 +191,10 @@
     public void testUseInvalidDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, "timtim");
-
         installPackage(ANGLE_DRIVER_TEST_APP);
 
+        setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, "timtim");
+
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_DEFAULT_METHOD);
@@ -229,14 +229,14 @@
     public void testMultipleDevOptionsAngleNative() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
                         ANGLE_DRIVER_TEST_SEC_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
                         sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
         waitThenRunDeviceTests(this, ANGLE_DRIVER_TEST_PKG,
                 ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS,
                 ANGLE_DRIVER_TEST_ANGLE_METHOD);
@@ -315,12 +315,12 @@
     public void testDefaultNotInSettings() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
-                sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
-
         // Install the package so the setting isn't removed because the package isn't present.
         installPackage(ANGLE_DRIVER_TEST_APP);
 
+        setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
+                sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+
         // Run the ANGLE activity so it'll clear up any 'default' settings.
         startActivity(getDevice(), ANGLE_MAIN_ACTIVTY);
 
@@ -376,13 +376,13 @@
     public void testMultipleDevOptionsAngleDefault() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_APP);
+        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
                         sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
         // Run the ANGLE activity so it'll clear up any 'default' settings.
         startActivity(getDevice(), ANGLE_MAIN_ACTIVTY);
 
@@ -408,12 +408,12 @@
     public void testMultipleDevOptionsAngleNativeUninstall() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
+        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
                         sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
 
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
-
         // Run the ANGLE activity so it'll clear up any 'default' settings.
         startActivity(getDevice(), ANGLE_MAIN_ACTIVTY);
 
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 9e9752d..be68d85 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -86,7 +86,7 @@
     }
 
     private void doSandboxed(boolean sandboxed) throws Exception {
-        assertEquals(sandboxed, Environment.isExternalStorageLegacy());
+        assertEquals(!sandboxed, Environment.isExternalStorageLegacy());
 
         // We can always see mounted state
         assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
@@ -256,7 +256,14 @@
         }
         try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "r")) {
         }
-        try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+        if (Environment.isExternalStorageLegacy()) {
+            try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+            }
+        } else {
+            try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+                fail("Expected write access to be blocked");
+            } catch (SecurityException | FileNotFoundException expected) {
+            }
         }
     }
 
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk b/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
new file mode 100644
index 0000000..75adae4
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/StorageAppA/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.test.rules \
+    ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../StorageApp/src/)
+
+LOCAL_PACKAGE_NAME := CtsStorageAppA
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppA/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/StorageAppA/AndroidManifest.xml
new file mode 100644
index 0000000..a9ae731
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/StorageAppA/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="com.android.cts.storageapp_a">
+
+    <application android:requestLegacyExternalStorage="true" android:appCategory="video">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.cts.storageapp.UtilsActivity"  />
+        <receiver android:name="com.android.cts.storageapp.UtilsReceiver" android:exported="true" />
+        <provider
+            android:name="com.android.cts.storageapp.UtilsProvider"
+            android:authorities="com.android.cts.storageapp_a"
+            android:exported="true" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.storageapp_a" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk b/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
new file mode 100644
index 0000000..bdcccaf
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/StorageAppB/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.test.rules \
+    ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../StorageApp/src/)
+
+LOCAL_PACKAGE_NAME := CtsStorageAppB
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/StorageAppB/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/StorageAppB/AndroidManifest.xml
new file mode 100644
index 0000000..019815a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/StorageAppB/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="com.android.cts.storageapp_b">
+
+    <application android:requestLegacyExternalStorage="true">
+        <uses-library android:name="android.test.runner" />
+        <receiver android:name="com.android.cts.storageapp.UtilsReceiver" android:exported="true" />
+        <provider
+            android:name="com.android.cts.storageapp.UtilsProvider"
+            android:authorities="com.android.cts.storageapp_b"
+            android:exported="true" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.storageapp_b" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index 6a01ea6..fe2621e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -23,6 +23,8 @@
  * Tests for {@link DevicePolicyManager#resetPassword} for complex cases.
  *
  * This needs to be run as device owner, because in NYC DA can't clear or change the password.
+ * @deprecated New tests related to password quality and reset password API should be added to
+ * {@code com.android.cts.deviceandprofileowner.ResetPasswordWithTokenTest}
  */
 public class DeviceOwnerPasswordTest extends BaseDeviceAdminTest {
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
index 6cab658..72f01aa 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
@@ -15,6 +15,13 @@
  */
 package com.android.cts.deviceandprofileowner;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
 
@@ -45,6 +52,9 @@
         if (mShouldRun) {
             cleanUpResetPasswordToken();
         }
+        resetComplexPasswordRestrictions();
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                PASSWORD_QUALITY_UNSPECIFIED);
         super.tearDown();
     }
 
@@ -66,8 +76,7 @@
                 SHORT_PASSWORD, TOKEN0, 0));
 
         // Set a strong password constraint and expect the sufficiency check to fail
-        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
-                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
         mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
         assertPasswordSufficiency(false);
 
@@ -81,8 +90,7 @@
         if (!mShouldRun) {
             return;
         }
-        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
-                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
         mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
 
         assertFalse(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT,
@@ -97,7 +105,7 @@
             return;
         }
         mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
-                DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+                PASSWORD_QUALITY_COMPLEX);
         mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 1);
         mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 1);
         mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 0);
@@ -131,6 +139,369 @@
         assertFalse(km.isDeviceSecure());
     }
 
+    public void testPasswordQuality_something() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                PASSWORD_QUALITY_SOMETHING);
+        assertEquals(PASSWORD_QUALITY_SOMETHING,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);
+
+        String caseDescription = "initial";
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription); // can't change.
+        assertPasswordSucceeds("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+        caseDescription = "minimum password length = 10";
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(true); // length not checked for this quality
+
+        // TODO(ascull): fix resetPassword() logic so these succeed
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(true);
+
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+    }
+
+    public void testPasswordQuality_numeric() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
+        assertEquals(PASSWORD_QUALITY_NUMERIC,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);            // failure
+
+        String caseDescription = "initial";
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+        caseDescription = "minimum password length = 10";
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);
+
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(true);
+
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+    }
+
+    public void testPasswordQuality_alphabetic() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                PASSWORD_QUALITY_ALPHABETIC);
+        assertEquals(PASSWORD_QUALITY_ALPHABETIC,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);
+
+        String caseDescription = "initial";
+        assertPasswordFails("1234", caseDescription);      // can't change
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+        caseDescription = "minimum password length = 10";
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);
+
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(true);
+
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+    }
+
+    public void testPasswordQuality_alphanumeric() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                PASSWORD_QUALITY_ALPHANUMERIC);
+        assertEquals(PASSWORD_QUALITY_ALPHANUMERIC,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);
+
+        String caseDescription = "initial";
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
+        caseDescription = "minimum password length = 10";
+        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(false);
+
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
+
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                ADMIN_RECEIVER_COMPONENT));
+        assertPasswordSufficiency(true);
+
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
+    }
+
+    public void testPasswordQuality_complexUpperCase() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        assertEquals(PASSWORD_QUALITY_COMPLEX,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        resetComplexPasswordRestrictions();
+
+        String caseDescription = "minimum UpperCase=0";
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("aBc1", caseDescription);
+        assertPasswordSucceeds("ABC1", caseDescription);
+        assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 1);
+        assertEquals(1, mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum UpperCase=1";
+        assertPasswordFails("abc1", caseDescription);
+        assertPasswordSucceeds("aBc1", caseDescription);
+        assertPasswordSucceeds("ABC1", caseDescription);
+        assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum UpperCase=3";
+        assertPasswordFails("abc1", caseDescription);
+        assertPasswordFails("aBC1", caseDescription);
+        assertPasswordSucceeds("ABC1", caseDescription);
+        assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+    }
+
+    public void testPasswordQuality_complexLowerCase() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        assertEquals(PASSWORD_QUALITY_COMPLEX,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        resetComplexPasswordRestrictions();
+
+        String caseDescription = "minimum LowerCase=0";
+        assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordSucceeds("aBC1", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 1);
+        assertEquals(1, mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum LowerCase=1";
+        assertPasswordFails("ABCD", caseDescription);
+        assertPasswordSucceeds("aBC1", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum LowerCase=3";
+        assertPasswordFails("ABCD", caseDescription);
+        assertPasswordFails("aBC1", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+    }
+
+    public void testPasswordQuality_complexLetters() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        assertEquals(PASSWORD_QUALITY_COMPLEX,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        resetComplexPasswordRestrictions();
+
+        String caseDescription = "minimum Letters=0";
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("a123", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 1);
+        assertEquals(1, mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum Letters=1";
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordSucceeds("a123", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum Letters=3";
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("a123", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+    }
+
+    public void testPasswordQuality_complexNumeric() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        assertEquals(PASSWORD_QUALITY_COMPLEX,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        resetComplexPasswordRestrictions();
+
+        String caseDescription = "minimum Numeric=0";
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("1abc", caseDescription);
+        assertPasswordSucceeds("123a", caseDescription);
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 1);
+        assertEquals(1, mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum Numeric=1";
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordSucceeds("1abc", caseDescription);
+        assertPasswordSucceeds("123a", caseDescription);
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum Numeric=3";
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("1abc", caseDescription);
+        assertPasswordSucceeds("123a", caseDescription);
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+    }
+
+    public void testPasswordQuality_complexSymbols() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        assertEquals(PASSWORD_QUALITY_COMPLEX,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        resetComplexPasswordRestrictions();
+
+        String caseDescription = "minimum Symbols=0";
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("_bc1", caseDescription);
+        assertPasswordSucceeds("@#!1", caseDescription);
+        assertPasswordSucceeds("_@#!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 1);
+        assertEquals(1, mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum Symbols=1";
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordSucceeds("_bc1", caseDescription);
+        assertPasswordSucceeds("@#!1", caseDescription);
+        assertPasswordSucceeds("_@#!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum Symbols=3";
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("_bc1", caseDescription);
+        assertPasswordSucceeds("@#!1", caseDescription);
+        assertPasswordSucceeds("_@#!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+    }
+
+    public void testPasswordQuality_complexNonLetter() {
+        if (!mShouldRun) {
+            return;
+        }
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        assertEquals(PASSWORD_QUALITY_COMPLEX,
+                mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT));
+        resetComplexPasswordRestrictions();
+
+        String caseDescription = "minimum NonLetter=0";
+        assertPasswordSucceeds("Abcd", caseDescription);
+        assertPasswordSucceeds("_bcd", caseDescription);
+        assertPasswordSucceeds("3bcd", caseDescription);
+        assertPasswordSucceeds("_@3c", caseDescription);
+        assertPasswordSucceeds("_25!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 1);
+        assertEquals(1, mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum NonLetter=1";
+        assertPasswordFails("Abcd", caseDescription);
+        assertPasswordSucceeds("_bcd", caseDescription);
+        assertPasswordSucceeds("3bcd", caseDescription);
+        assertPasswordSucceeds("_@3c", caseDescription);
+        assertPasswordSucceeds("_25!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+
+        mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 3);
+        assertEquals(3, mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+        caseDescription = "minimum NonLetter=3";
+        assertPasswordFails("Abcd", caseDescription);
+        assertPasswordFails("_bcd", caseDescription);
+        assertPasswordFails("3bcd", caseDescription);
+        assertPasswordSucceeds("_@3c", caseDescription);
+        assertPasswordSucceeds("_25!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
+    }
+
     private boolean setUpResetPasswordToken(boolean acceptFailure) {
         // set up a token
         assertFalse(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
@@ -166,4 +537,32 @@
         assertFalse(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT,
                 SHORT_PASSWORD, TOKEN0, 0));
     }
+
+    private void assertPasswordFails(String password, String restriction) {
+        try {
+            boolean passwordResetResult = mDevicePolicyManager.resetPasswordWithToken(
+                    ADMIN_RECEIVER_COMPONENT, password, TOKEN0, /* flags= */0);
+            assertFalse("Password '" + password + "' should have failed on " + restriction,
+                    passwordResetResult);
+        } catch (IllegalArgumentException e) {
+            // yesss, we have failed!
+        }
+    }
+
+    private void assertPasswordSucceeds(String password, String restriction) {
+        boolean passwordResetResult = mDevicePolicyManager.resetPasswordWithToken(
+                ADMIN_RECEIVER_COMPONENT, password, TOKEN0, /* flags= */0);
+        assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
+        assertPasswordSufficiency(true);
+    }
+
+    private void resetComplexPasswordRestrictions() {
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 0);
+        mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 0);
+        mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 0);
+        mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 0);
+        mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 0);
+        mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, 0);
+        mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 0);
+    }
 }
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java
index 4ea8f3b..54246a2 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ResetPasswordWithTokenTest.java
@@ -21,6 +21,8 @@
 import android.content.Intent;
 import android.os.UserManager;
 
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
 public class ResetPasswordWithTokenTest extends BaseManagedProfileTest {
 
     private static final String PASSWORD0 = "1234";
@@ -49,10 +51,6 @@
         // Reset password on the work profile will enable separate work challenge for it.
         assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, PASSWORD0,
                 token, 0));
-
-        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
-                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
-        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
     }
 
     public void testResetPasswordBeforeUnlock() {
@@ -61,12 +59,37 @@
         assertTrue(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
         assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, PASSWORD1,
                 token, 0));
+
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 6);
         try {
             mDevicePolicyManager.isActivePasswordSufficient();
             fail("Did not throw expected exception.");
         } catch (IllegalStateException expected) {}
     }
 
+    public void testClearPasswordBeforeUnlock() {
+        UserManager um = mContext.getSystemService(UserManager.class);
+        assertFalse(um.isUserUnlocked());
+        assertTrue(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
+        assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, null,
+                token, 0));
+
+        // When password is cleared, the system should automatically unlock the user.
+        final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+                Intent.ACTION_USER_UNLOCKED);
+        receiver.register();
+        try {
+            // Give the broadcast long enough time, as unlocking user could be slow.
+            assertNotNull(receiver.awaitForBroadcast(90_000));
+        } finally {
+            receiver.unregisterQuietly();
+        }
+        assertTrue(um.isUserUnlocked());
+        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+    }
+
     public void testSetResetPasswordToken() {
         assertTrue(mDevicePolicyManager.setResetPasswordToken(ADMIN_RECEIVER_COMPONENT, token));
         assertTrue(mDevicePolicyManager.isResetPasswordTokenActive(ADMIN_RECEIVER_COMPONENT));
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
index 9456a6a..2e8d9f4 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
@@ -32,3 +32,45 @@
     ],
     sdk_version: "current",
 }
+
+// Build for no component app
+android_test_helper_app {
+    name: "CtsNoComponentApp",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    srcs: ["src/**/*.java"],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ],
+    manifest: "no_component_AndroidManifest.xml",
+    sdk_version: "current",
+}
+
+// Build for no permission app
+android_test_helper_app {
+    name: "CtsNoPermissionApp",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    srcs: ["src/**/*.java"],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ],
+    manifest: "no_permission_AndroidManifest.xml",
+    sdk_version: "current",
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 3100832..1d5de7d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -1325,6 +1325,20 @@
         verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
     }
 
+    public void testClearPasswordWithTokenBeforeUnlock() throws Exception {
+        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+            return;
+        }
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testSetupWorkProfile", mProfileUserId);
+        lockProfile();
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testClearPasswordBeforeUnlock", mProfileUserId);
+        // Make sure profile has no password
+        verifyUserCredential("", mProfileUserId);
+    }
+
     /**
      * Test password reset token is still functional after the primary user clears and
      * re-adds back its device lock. This is to detect a regression where the work profile
diff --git a/hostsidetests/incident/AndroidTest.xml b/hostsidetests/incident/AndroidTest.xml
index 18a73fa..65a347b 100644
--- a/hostsidetests/incident/AndroidTest.xml
+++ b/hostsidetests/incident/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Config for CTS Incident host test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="metrics" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
         <option name="user-type" value="system" />
     </target_preparer>
diff --git a/hostsidetests/net/AndroidTest.xml b/hostsidetests/net/AndroidTest.xml
index 0656cae..c7cab7b 100644
--- a/hostsidetests/net/AndroidTest.xml
+++ b/hostsidetests/net/AndroidTest.xml
@@ -16,6 +16,9 @@
 <configuration description="Config for CTS net host test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
     <target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index 53b7e48..ac79c0e 100755
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsSecurityHostTestCases.jar" />
diff --git a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
index 525a3b4..fdc71ae 100644
--- a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
@@ -178,20 +178,45 @@
         }
     }
 
+    private static final String QUALCOMM_SOC_FILE = "/sys/devices/soc0/chip_name";
+
+    private String getHardware() throws Exception {
+        /* lookup for Qualcomm devices */
+        if (mDevice.doesFileExist(QUALCOMM_SOC_FILE)) {
+            return mDevice.pullFileContents(QUALCOMM_SOC_FILE).trim();
+        }
+        /* TODO lookup for other non-vulnerable devices. */
+        return "DEFAULT";
+    }
+
+    private Map<String, String[]> hardwareMitigations = new HashMap<String, String[]>() {
+    {
+        put("SM8150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
+            "CONFIG_UNMAP_KERNEL_AT_EL0=y"});
+    }};
+
+    private String[] lookupMitigations() throws Exception {
+        return hardwareMitigations.get(getHardware());
+    }
+
     /**
-     * Test that the kernel has KTPI enabled for architectures and kernel versions that support it.
+     * Test that the kernel has Spectre/Meltdown mitigations for architectures and kernel versions
+     * that support it. Exempt platforms which are known to not be vulnerable.
      *
      * @throws Exception
      */
     @CddTest(requirement="9.7")
-    public void testConfigKTPI() throws Exception {
+    public void testConfigHardwareMitigations() throws Exception {
         if (PropertyUtil.getFirstApiLevel(mDevice) < 28) {
             return;
         }
 
         if (CpuFeatures.isArm64(mDevice) && !CpuFeatures.kernelVersionLessThan(mDevice, 4, 4)) {
-            assertTrue("Linux kernel must have KPTI enabled: CONFIG_UNMAP_KERNEL_AT_EL0=y",
-                    configSet.contains("CONFIG_UNMAP_KERNEL_AT_EL0=y"));
+            for (String mitigation : lookupMitigations()) {
+                assertTrue("Linux kernel must have " + mitigation + " enabled.",
+                        configSet.contains(mitigation));
+            }
         } else if (CpuFeatures.isX86(mDevice)) {
             assertTrue("Linux kernel must have KPTI enabled: CONFIG_PAGE_TABLE_ISOLATION=y",
                     configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y"));
diff --git a/hostsidetests/stagedinstall/Android.bp b/hostsidetests/stagedinstall/Android.bp
index d22c170..01bb8be 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -51,9 +51,11 @@
         ":StagedInstallTestApexV2_WithPostInstallHook",
         ":StagedInstallTestApexV2_WithPreInstallHook",
         ":StagedInstallTestApexV2_WrongSha",
+        ":StagedInstallTestApexV3",
         ":StagedInstallTestAppAv1",
         ":StagedInstallTestAppAv2",
         ":StagedInstallTestAppBv1",
+        ":StagedInstallTestAppSamePackageNameAsApex",
     ],
     static_libs: [
         "androidx.test.runner",
@@ -89,6 +91,14 @@
     manifest:   "testdata/apk/Bv1.xml",
 }
 
+android_test_helper_app {
+    name: "StagedInstallTestAppSamePackageNameAsApex",
+
+    srcs:   ["testdata/apk/src/**/*java"],
+
+    manifest:   "testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml",
+}
+
 prebuilt_apex {
     name: "StagedInstallTestApexV2",
     src: "testdata/apex/com.android.apex.cts.shim.v2.apex",
@@ -97,6 +107,13 @@
 }
 
 prebuilt_apex {
+    name: "StagedInstallTestApexV3",
+    src: "testdata/apex/com.android.apex.cts.shim.v3.apex",
+    filename: "com.android.apex.cts.shim.v3.apex",
+    installable: false,
+}
+
+prebuilt_apex {
     name: "StagedInstallTestApexV2_AdditionalFile",
     src: "testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex",
     filename: "com.android.apex.cts.shim.v2_additional_file.apex",
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index 95e6505..5540f10 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -416,6 +416,38 @@
         assertThat(sessionInfo).isStagedSessionFailed();
     }
 
+    @Test
+    public void testStageApkWithSameNameAsApexShouldFail_Commit() throws Exception {
+        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+        int sessionId = stageSingleApk(
+                "StagedInstallTestAppSamePackageNameAsApex.apk").assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+    }
+
+    @Test
+    public void testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot() throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionFailed(sessionId);
+        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+    }
+
+    @Test
+    public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception {
+        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+        PackageInstaller packageInstaller = getPackageInstaller();
+        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        int sessionId = packageInstaller.createSession(sessionParams);
+        PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+        writeApk(session, "StagedInstallTestAppSamePackageNameAsApex.apk");
+        session.commit(LocalIntentSender.getIntentSender());
+        final String errorMessage = extractErrorMessage(LocalIntentSender.getIntentSenderResult());
+        assertThat(errorMessage).contains("is an APEX package and can't be installed as an APK");
+        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+    }
+
     private static PackageInstaller getPackageInstaller() {
         return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
                 .getPackageInstaller();
@@ -589,15 +621,7 @@
         }
 
         public String getErrorMessage() {
-            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageInstaller.STATUS_FAILURE);
-            if (status == -1) {
-                throw new AssertionError("PENDING USER ACTION");
-            }
-            if (status == 0) {
-                throw new AssertionError("Result was successful");
-            }
-            return result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+            return extractErrorMessage(result);
         }
 
         public StageSessionResult assertSuccessful() {
@@ -606,6 +630,18 @@
         }
     }
 
+    private static String extractErrorMessage(Intent result) {
+        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == -1) {
+            throw new AssertionError("PENDING USER ACTION");
+        }
+        if (status == 0) {
+            throw new AssertionError("Result was successful");
+        }
+        return result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+    }
+
     private static void abandonSession(int sessionId) {
         getPackageInstaller().abandonSession(sessionId);
     }
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index 6dc1aac..330bfc2 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -194,6 +194,20 @@
         runPhase("testInstallStagedNonPreInstalledApex_UserBuild_Fails");
     }
 
+    @Test
+    public void testStageApkWithSameNameAsApexShouldFail() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+        runPhase("testStageApkWithSameNameAsApexShouldFail_Commit");
+        getDevice().reboot();
+        runPhase("testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot");
+    }
+
+    @Test
+    public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+        runPhase("testNonStagedInstallApkWithSameNameAsApexShouldFail");
+    }
+
     private boolean isUpdatingApexSupported() throws Exception {
         final String updatable = getDevice().getProperty("ro.apex.updatable");
         return updatable != null && updatable.equals("true");
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
index 19fb348..6b950a4 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
index a52331b..ff7f550 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
index 556b783..1597fa4 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
index ea09f4d..be3a0b7 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
index 29ea5db..7caa3c0 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
index 05cd974..8db2afd 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
new file mode 100644
index 0000000..8b3e214
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
index d3bfbaf..2036ce5 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml b/hostsidetests/stagedinstall/testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml
new file mode 100644
index 0000000..2954538
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apk/StagedInstallTestAppSamePackageNameAsApex.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.apex.cts.shim"
+          android:versionCode="2"
+          android:versionName="2.0" >
+
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="StagedInstall Test App With Same Package Name As Apex">
+        <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/statsd/AndroidTest.xml b/hostsidetests/statsd/AndroidTest.xml
index a1beb7c..764e80f 100644
--- a/hostsidetests/statsd/AndroidTest.xml
+++ b/hostsidetests/statsd/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Statsd host test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="statsd" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsStatsdHostTestCases.jar" />
diff --git a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
index 8aa6e18..225e12b 100644
--- a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
+++ b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
@@ -16,6 +16,7 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.server.cts.device.statsd" >
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
diff --git a/hostsidetests/sustainedperf/AndroidTest.xml b/hostsidetests/sustainedperf/AndroidTest.xml
index dd49674..2377fa4 100644
--- a/hostsidetests/sustainedperf/AndroidTest.xml
+++ b/hostsidetests/sustainedperf/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Config for CTS Sustained Performance host test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsSustainedPerformanceTestCases.apk" />
diff --git a/tests/accessibility/Android.bp b/tests/accessibility/Android.bp
index c72a2ba..fba6008 100644
--- a/tests/accessibility/Android.bp
+++ b/tests/accessibility/Android.bp
@@ -12,14 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+java_library_static {
+    name: "CtsAccessibilityCommon",
+    sdk_version: "test_current",
+    static_libs: [
+        "compatibility-device-util-axt",
+    ],
+    srcs: ["common/src/**/*.java"],
+}
+
 android_test {
     name: "CtsAccessibilityTestCases",
     defaults: ["cts_defaults"],
     srcs: ["src/**/*.java"],
     static_libs: [
-        "compatibility-device-util-axt",
         "ctstestrunner-axt",
         "hamcrest-library",
+        "CtsAccessibilityCommon",
     ],
     libs: ["android.test.base.stubs"],
     // Tag this module as a cts test artifact
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
similarity index 85%
rename from tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
rename to tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
index 0ff067f..3f9a344 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
@@ -14,40 +14,44 @@
  * limitations under the License.
  */
 
-package android.accessibilityservice.cts;
+package android.accessibility.cts.common;
+
+import static com.android.compatibility.common.util.TestUtils.waitOn;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
 
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Instrumentation;
+import android.app.UiAutomation;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.provider.Settings;
-import androidx.annotation.CallSuper;
-import android.test.InstrumentationTestCase;
+import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.annotation.CallSuper;
+
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 public class InstrumentedAccessibilityService extends AccessibilityService {
+    private static final String LOG_TAG = "InstrumentedA11yService";
 
     private static final boolean DEBUG = false;
 
     // Match com.android.server.accessibility.AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
     private static final String COMPONENT_NAME_SEPARATOR = ":";
-
-    // Timeout disabled in #DEBUG mode to prevent breakpoint-related failures
-    private static final int TIMEOUT_SERVICE_ENABLE = DEBUG ? Integer.MAX_VALUE : 10000;
     private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 5000;
 
     private static final HashMap<Class, WeakReference<InstrumentedAccessibilityService>>
@@ -55,8 +59,11 @@
 
     private final Handler mHandler = new Handler();
     final Object mInterruptWaitObject = new Object();
+
     public boolean mOnInterruptCalled;
 
+    // Timeout disabled in #DEBUG mode to prevent breakpoint-related failures
+    public static final int TIMEOUT_SERVICE_ENABLE = DEBUG ? Integer.MAX_VALUE : 10000;
 
     @Override
     @CallSuper
@@ -65,6 +72,13 @@
             sInstances.put(getClass(), new WeakReference<>(this));
             sInstances.notifyAll();
         }
+        Log.v(LOG_TAG, "onServiceConnected ["  + this + "]");
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.v(LOG_TAG, "onUnbind [" + this + "]");
+        return false;
     }
 
     @Override
@@ -72,6 +86,7 @@
         synchronized (sInstances) {
             sInstances.remove(getClass());
         }
+        Log.v(LOG_TAG, "onDestroy ["  + this + "]");
     }
 
     @Override
@@ -151,7 +166,7 @@
         }
     }
 
-    protected static <T extends InstrumentedAccessibilityService> T enableService(
+    public static <T extends InstrumentedAccessibilityService> T enableService(
             Instrumentation instrumentation, Class<T> clazz) {
         final String serviceName = clazz.getSimpleName();
         final Context context = instrumentation.getContext();
@@ -189,7 +204,7 @@
         throw new RuntimeException("Accessibility service " + serviceName + " not found");
     }
 
-    private static <T extends InstrumentedAccessibilityService> T getInstanceForClass(Class clazz,
+    public static <T extends InstrumentedAccessibilityService> T getInstanceForClass(Class clazz,
             long timeoutMillis) {
         final long timeoutTimeMillis = SystemClock.uptimeMillis() + timeoutMillis;
         while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
@@ -218,32 +233,23 @@
         final Context context = instrumentation.getContext();
         final AccessibilityManager manager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        // Updates to manager.isEnabled() aren't synchronized
+        final AtomicBoolean accessibilityEnabled = new AtomicBoolean(manager.isEnabled());
         manager.addAccessibilityStateChangeListener(b -> {
             synchronized (waitLockForA11yOff) {
                 waitLockForA11yOff.notifyAll();
+                accessibilityEnabled.set(b);
             }
         });
-
-        ShellCommandBuilder.create(instrumentation)
+        final UiAutomation uiAutomation = instrumentation.getUiAutomation(
+                UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        ShellCommandBuilder.create(uiAutomation)
                 .deleteSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
                 .deleteSecureSetting(Settings.Secure.ACCESSIBILITY_ENABLED)
                 .run();
+        uiAutomation.destroy();
 
-        final long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_SERVICE_ENABLE;
-        while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
-            synchronized (waitLockForA11yOff) {
-                if (manager.getEnabledAccessibilityServiceList(
-                        AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
-                    return;
-                }
-                try {
-                    waitLockForA11yOff.wait(timeoutTimeMillis - SystemClock.uptimeMillis());
-                } catch (InterruptedException e) {
-                    // Ignored; loop again
-                }
-            }
-        }
-        throw new RuntimeException("Disabling all accessibility services took longer than "
-                + TIMEOUT_SERVICE_ENABLE + "ms");
+        waitOn(waitLockForA11yOff, () -> !accessibilityEnabled.get(), TIMEOUT_SERVICE_ENABLE,
+                "Accessibility turns off");
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
similarity index 77%
rename from tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
rename to tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
index 109c3f8..c305ceb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/ShellCommandBuilder.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package android.accessibilityservice.cts;
+package android.accessibility.cts.common;
 
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.os.ParcelFileDescriptor;
+import android.util.Log;
 
 import java.io.BufferedReader;
 import java.io.FileInputStream;
@@ -28,20 +29,33 @@
 import java.nio.charset.StandardCharsets;
 import java.util.LinkedList;
 
+/**
+ * A helper class to execute batch of shell commands.
+ */
 public class ShellCommandBuilder {
-    private final LinkedList<Runnable> mCommands = new LinkedList<>();
+    private static final String LOG_TAG = "ShellCommandBuilder";
 
-    private final Instrumentation mInstrumentation;
+    private final LinkedList<Runnable> mCommands = new LinkedList<>();
     private final UiAutomation mUiAutomation;
 
+    /**
+     * Returns a {@link ShellCommandBuilder} with an {@link UiAutomation} which doesn't suppress
+     * accessibility service.
+     */
     public static ShellCommandBuilder create(Instrumentation instrumentation) {
-        return new ShellCommandBuilder(instrumentation);
+        return new ShellCommandBuilder(instrumentation.getUiAutomation(
+                UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES));
     }
 
-    private ShellCommandBuilder(Instrumentation instrumentation) {
-        mInstrumentation = instrumentation;
-        mUiAutomation = mInstrumentation.getUiAutomation(
-                UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+    /**
+     * Returns a {@link ShellCommandBuilder} with {@code uiAutomation}.
+     */
+    public static ShellCommandBuilder create(UiAutomation uiAutomation) {
+        return new ShellCommandBuilder(uiAutomation);
+    }
+
+    private ShellCommandBuilder(UiAutomation uiAutomation) {
+        mUiAutomation = uiAutomation;
     }
 
     public void run() {
@@ -75,6 +89,7 @@
     }
 
     public static void execShellCommand(UiAutomation automation, String command) {
+        Log.i(LOG_TAG, "command [" + command + "]");
         try (ParcelFileDescriptor fd = automation.executeShellCommand(command)) {
             try (InputStream inputStream = new FileInputStream(fd.getFileDescriptor())) {
                 try (BufferedReader reader = new BufferedReader(
@@ -85,7 +100,7 @@
                 }
             }
         } catch (IOException e) {
-            throw new RuntimeException("Failed to exec shell command", e);
+            throw new RuntimeException("Failed to exec shell command [" + command + "]", e);
         }
     }
 }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index d42e52b..a681baf 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -16,12 +16,11 @@
 
 package android.view.accessibility.cts;
 
-import static android.view.accessibility.cts.ServiceControlUtils.TIMEOUT_FOR_SERVICE_ENABLE;
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE;
 import static android.view.accessibility.cts.ServiceControlUtils.getEnabledServices;
 import static android.view.accessibility.cts.ServiceControlUtils.waitForConditionWithServiceStateChange;
 
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-import static com.android.compatibility.common.util.TestUtils.waitOn;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -29,6 +28,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Instrumentation;
 import android.app.Service;
@@ -97,12 +97,12 @@
         mHandler = new Handler(mTargetContext.getMainLooper());
         // In case the test runner started a UiAutomation, destroy it to start with a clean slate.
         sInstrumentation.getUiAutomation().destroy();
-        ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
     }
 
     @After
     public void tearDown() throws Exception {
-        ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
     }
 
     @Test
@@ -127,7 +127,8 @@
 
     @Test
     public void testIsTouchExplorationEnabled() throws Exception {
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         new PollingCheck() {
             @Override
             protected boolean check() {
@@ -162,7 +163,8 @@
 
     @Test
     public void testGetEnabledAccessibilityServiceList() throws Exception {
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         List<AccessibilityServiceInfo> enabledServices =
             mAccessibilityManager.getEnabledAccessibilityServiceList(
                     AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
@@ -187,7 +189,8 @@
 
     @Test
     public void testGetEnabledAccessibilityServiceListForType() throws Exception {
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         List<AccessibilityServiceInfo> enabledServices =
             mAccessibilityManager.getEnabledAccessibilityServiceList(
                     AccessibilityServiceInfo.FEEDBACK_SPOKEN);
@@ -206,9 +209,10 @@
 
     @Test
     public void testGetEnabledAccessibilityServiceListForTypes() throws Exception {
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         // For this test, also enable a service with multiple feedback types
-        ServiceControlUtils.enableMultipleFeedbackTypesService(sInstrumentation);
+        SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
 
         List<AccessibilityServiceInfo> enabledServices =
                 mAccessibilityManager.getEnabledAccessibilityServiceList(
@@ -268,7 +272,8 @@
     public void testInterrupt() throws Exception {
         // The APIs are heavily tested in the android.accessibilityservice package.
         // This just makes sure the call does not throw an exception.
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         waitForAccessibilityEnabled();
         mAccessibilityManager.interrupt();
     }
@@ -277,7 +282,8 @@
     public void testSendAccessibilityEvent() throws Exception {
         // The APIs are heavily tested in the android.accessibilityservice package.
         // This just makes sure the call does not throw an exception.
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         waitForAccessibilityEnabled();
         mAccessibilityManager.sendAccessibilityEvent(AccessibilityEvent.obtain(
                 AccessibilityEvent.TYPE_VIEW_CLICKED));
@@ -295,12 +301,13 @@
             }
         };
         mAccessibilityManager.addTouchExplorationStateChangeListener(listener);
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Touch exploration state listener not called when services enabled");
         assertTrue("Listener told that touch exploration is enabled, but manager says disabled",
                 mAccessibilityManager.isTouchExplorationEnabled());
-        ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Touch exploration state listener not called when services disabled");
         assertFalse("Listener told that touch exploration is disabled, but manager says it enabled",
@@ -320,12 +327,13 @@
             }
         };
         mAccessibilityManager.addTouchExplorationStateChangeListener(listener, mHandler);
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Touch exploration state listener not called when services enabled");
         assertTrue("Listener told that touch exploration is enabled, but manager says disabled",
                 mAccessibilityManager.isTouchExplorationEnabled());
-        ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Touch exploration state listener not called when services disabled");
         assertFalse("Listener told that touch exploration is disabled, but manager says it enabled",
@@ -345,12 +353,12 @@
             }
         };
         mAccessibilityManager.addAccessibilityStateChangeListener(listener);
-        ServiceControlUtils.enableMultipleFeedbackTypesService(sInstrumentation);
+        SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Accessibility state listener not called when services enabled");
         assertTrue("Listener told that accessibility is enabled, but manager says disabled",
                 mAccessibilityManager.isEnabled());
-        ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Accessibility state listener not called when services disabled");
         assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -370,12 +378,12 @@
             }
         };
         mAccessibilityManager.addAccessibilityStateChangeListener(listener, mHandler);
-        ServiceControlUtils.enableMultipleFeedbackTypesService(sInstrumentation);
+        SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Accessibility state listener not called when services enabled");
         assertTrue("Listener told that accessibility is enabled, but manager says disabled",
                 mAccessibilityManager.isEnabled());
-        ServiceControlUtils.turnAccessibilityOff(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Accessibility state listener not called when services disabled");
         assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -385,7 +393,8 @@
 
     @Test
     public void testGetRecommendedTimeoutMillis() throws Exception {
-        ServiceControlUtils.enableSpeakingAndVibratingServices(sInstrumentation);
+        SpeakingAccessibilityService.enableSelf(sInstrumentation);
+        VibratingAccessibilityService.enableSelf(sInstrumentation);
         waitForAccessibilityEnabled();
         UiAutomation automan = sInstrumentation.getUiAutomation(
                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
@@ -445,9 +454,10 @@
             runWithShellPermissionIdentity(uiAutomation,
                     () -> mAccessibilityManager.performAccessibilityShortcut());
             // Make sure the service starts up
-            waitOn(SpeakingAccessibilityService.sWaitObjectForConnecting,
-                    () -> SpeakingAccessibilityService.sConnectedInstance != null,
-                    TIMEOUT_FOR_SERVICE_ENABLE, "Speaking accessibility service starts up");
+            final SpeakingAccessibilityService service =
+                    SpeakingAccessibilityService.getInstanceForClass(
+                    SpeakingAccessibilityService.class, TIMEOUT_SERVICE_ENABLE);
+            assertTrue("Speaking accessibility service starts up", service != null);
         } finally {
             configureShortcut(uiAutomation, originalShortcut);
             uiAutomation.destroy();
@@ -464,7 +474,7 @@
                     waitForConditionWithServiceStateChange(mTargetContext, () -> TextUtils.equals(
                             mAccessibilityManager.getAccessibilityShortcutService(),
                             shortcutService),
-                            TIMEOUT_FOR_SERVICE_ENABLE,
+                            TIMEOUT_SERVICE_ENABLE,
                             "accessibility shortcut set to test service"));
         }
         return currentService;
@@ -474,7 +484,7 @@
             boolean expectedValue, Object waitObject, String message)
             throws Exception {
         long timeoutTime =
-                System.currentTimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
+                System.currentTimeMillis() + TIMEOUT_SERVICE_ENABLE;
         synchronized (waitObject) {
             while ((atomicBoolean.get() != expectedValue)
                     && (System.currentTimeMillis() < timeoutTime)) {
@@ -494,7 +504,7 @@
         };
         mAccessibilityManager.addAccessibilityStateChangeListener(listener);
         long timeoutTime =
-                System.currentTimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
+                System.currentTimeMillis() + TIMEOUT_SERVICE_ENABLE;
         synchronized (waitObject) {
             while (!mAccessibilityManager.isEnabled()
                     && (System.currentTimeMillis() < timeoutTime)) {
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index cf3fb15..0029b53 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -16,6 +16,7 @@
 
 package android.view.accessibility.cts;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Service;
 import android.test.InstrumentationTestCase;
@@ -36,12 +37,13 @@
 
     @Override
     public void setUp() throws Exception {
-        ServiceControlUtils.enableSpeakingAndVibratingServices(getInstrumentation());
+        SpeakingAccessibilityService.enableSelf(getInstrumentation());
+        VibratingAccessibilityService.enableSelf(getInstrumentation());
     }
 
     @Override
     public void tearDown() {
-        ServiceControlUtils.turnAccessibilityOff(getInstrumentation());
+        InstrumentedAccessibilityService.disableAllServices(getInstrumentation());
     }
 
     /**
diff --git a/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java b/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
index 437c35e..9b1fae1 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/ServiceControlUtils.java
@@ -18,134 +18,17 @@
 
 import static com.android.compatibility.common.util.TestUtils.waitOn;
 
-import android.app.Instrumentation;
-import android.app.UiAutomation;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.os.ParcelFileDescriptor;
 import android.provider.Settings;
 import android.view.accessibility.AccessibilityManager;
 
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BooleanSupplier;
 
 /**
  * Utility methods for enabling and disabling the services used in this package
  */
 public class ServiceControlUtils {
-    public static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s
-
-    private static final String SETTING_ENABLE_SPEAKING_AND_VIBRATING_SERVICES =
-            "android.view.accessibility.cts/.SpeakingAccessibilityService:"
-            + "android.view.accessibility.cts/.VibratingAccessibilityService";
-
-    private static final String SETTING_ENABLE_MULTIPLE_FEEDBACK_TYPES_SERVICE =
-            "android.view.accessibility.cts/.SpeakingAndVibratingAccessibilityService";
-
-    /**
-     * Enable {@code SpeakingAccessibilityService} and {@code VibratingAccessibilityService}
-     *
-     * @param instrumentation A valid instrumentation
-     */
-    public static void enableSpeakingAndVibratingServices(Instrumentation instrumentation)
-            throws IOException {
-        Context context = instrumentation.getContext();
-
-        // Get permission to enable accessibility
-        UiAutomation uiAutomation = instrumentation.getUiAutomation();
-
-        // Change the settings to enable the two services
-        String alreadyEnabledServices = getEnabledServices(context.getContentResolver());
-        ParcelFileDescriptor fd = uiAutomation.executeShellCommand("settings --user cur put secure "
-                + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + " "
-                + alreadyEnabledServices + ":"
-                + SETTING_ENABLE_SPEAKING_AND_VIBRATING_SERVICES);
-        InputStream in = new FileInputStream(fd.getFileDescriptor());
-        byte[] buffer = new byte[4096];
-        while (in.read(buffer) > 0);
-        uiAutomation.destroy();
-
-        // Wait for speaking service to be connected
-        waitOn(SpeakingAccessibilityService.sWaitObjectForConnecting,
-                () -> SpeakingAccessibilityService.sConnectedInstance != null,
-                TIMEOUT_FOR_SERVICE_ENABLE, "Speaking accessibility service starts up");
-
-        // Wait for vibrating service to be connected
-        waitOn(VibratingAccessibilityService.sWaitObjectForConnecting,
-                () -> VibratingAccessibilityService.sConnectedInstance != null,
-                TIMEOUT_FOR_SERVICE_ENABLE, "Vibrating accessibility service starts up");
-    }
-
-    /**
-     * Enable {@link SpeakingAndVibratingAccessibilityService} for tests requiring a service with
-     * multiple feedback types
-     *
-     * @param instrumentation A valid instrumentation
-     */
-    public static void enableMultipleFeedbackTypesService(Instrumentation instrumentation)
-            throws IOException {
-        Context context = instrumentation.getContext();
-
-        // Get permission to enable accessibility
-        UiAutomation uiAutomation = instrumentation.getUiAutomation();
-
-        // Change the settings to enable the services
-        String alreadyEnabledServices = getEnabledServices(context.getContentResolver());
-        ParcelFileDescriptor fd = uiAutomation.executeShellCommand("settings --user cur put secure "
-                + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + " "
-                + alreadyEnabledServices + ":"
-                + SETTING_ENABLE_MULTIPLE_FEEDBACK_TYPES_SERVICE);
-        InputStream in = new FileInputStream(fd.getFileDescriptor());
-        byte[] buffer = new byte[4096];
-        while (in.read(buffer) > 0);
-        uiAutomation.destroy();
-
-        // Wait for the service to be connected
-        waitOn(SpeakingAndVibratingAccessibilityService.sWaitObjectForConnecting,
-                () -> SpeakingAndVibratingAccessibilityService.sConnectedInstance != null,
-                TIMEOUT_FOR_SERVICE_ENABLE,
-                "Multiple feedback types accessibility service starts up");
-    }
-
-    /**
-     * Turn off all accessibility services. Assumes permissions to write settings are already
-     * set, which they are in
-     * {@link ServiceControlUtils#enableSpeakingAndVibratingServices(Instrumentation)}.
-     *
-     * @param instrumentation A valid instrumentation
-     */
-    public static void turnAccessibilityOff(Instrumentation instrumentation) {
-        if (SpeakingAccessibilityService.sConnectedInstance != null) {
-            SpeakingAccessibilityService.sConnectedInstance.disableSelf();
-            SpeakingAccessibilityService.sConnectedInstance = null;
-        }
-        if (VibratingAccessibilityService.sConnectedInstance != null) {
-            VibratingAccessibilityService.sConnectedInstance.disableSelf();
-            VibratingAccessibilityService.sConnectedInstance = null;
-        }
-        if (SpeakingAndVibratingAccessibilityService.sConnectedInstance != null) {
-            SpeakingAndVibratingAccessibilityService.sConnectedInstance.disableSelf();
-            SpeakingAndVibratingAccessibilityService.sConnectedInstance = null;
-        }
-
-        final Object waitLockForA11yOff = new Object();
-        AccessibilityManager manager = (AccessibilityManager) instrumentation
-                .getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        // Updates to manager.isEnabled() aren't synchronized
-        AtomicBoolean accessibilityEnabled = new AtomicBoolean(manager.isEnabled());
-        AccessibilityManager.AccessibilityStateChangeListener listener = (boolean b) -> {
-            synchronized (waitLockForA11yOff) {
-                waitLockForA11yOff.notifyAll();
-                accessibilityEnabled.set(b);
-            }
-        };
-        manager.addAccessibilityStateChangeListener(listener);
-        waitOn(waitLockForA11yOff, () -> !accessibilityEnabled.get(), TIMEOUT_FOR_SERVICE_ENABLE,
-                "Accessibility turns off");
-    }
 
     public static String getEnabledServices(ContentResolver cr) {
         return Settings.Secure.getString(cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
index 19daf4b..d05232a 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
@@ -16,46 +16,20 @@
 
 package android.view.accessibility.cts;
 
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.app.Instrumentation;
 import android.content.ComponentName;
-import android.view.accessibility.AccessibilityEvent;
 
 /**
  * Stub accessibility service that reports itself as providing spoken feedback.
  */
-public class SpeakingAccessibilityService extends AccessibilityService {
+public class SpeakingAccessibilityService extends InstrumentedAccessibilityService {
     public static final ComponentName COMPONENT_NAME = new ComponentName(
             "android.view.accessibility.cts",
             "android.view.accessibility.cts.SpeakingAccessibilityService");
-    public static Object sWaitObjectForConnecting = new Object();
 
-    public static SpeakingAccessibilityService sConnectedInstance;
-
-    @Override
-    public void onDestroy() {
-        sConnectedInstance = null;
-    }
-
-    @Override
-    protected void onServiceConnected() {
-        final AccessibilityServiceInfo info = getServiceInfo();
-        info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
-        setServiceInfo(info);
-
-        synchronized (sWaitObjectForConnecting) {
-            sConnectedInstance = this;
-            sWaitObjectForConnecting.notifyAll();
-        }
-    }
-
-    @Override
-    public void onAccessibilityEvent(AccessibilityEvent event) {
-        /* do nothing */
-    }
-
-    @Override
-    public void onInterrupt() {
-        /* do nothing */
+    public static SpeakingAccessibilityService enableSelf(Instrumentation instrumentation) {
+        return InstrumentedAccessibilityService.enableService(
+                instrumentation, SpeakingAccessibilityService.class);
     }
 }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
index 4a591f4..cb8126d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
@@ -16,37 +16,16 @@
 
 package android.view.accessibility.cts;
 
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.view.accessibility.AccessibilityEvent;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.app.Instrumentation;
 
 /**
  * Stub accessibility service that reports itself as providing multiple feedback types.
  */
-public class SpeakingAndVibratingAccessibilityService extends AccessibilityService {
-    public static Object sWaitObjectForConnecting = new Object();
-
-    public static SpeakingAndVibratingAccessibilityService sConnectedInstance;
-
-    @Override
-    public void onDestroy() {
-        sConnectedInstance = null;
-    }
-
-
-    @Override
-    protected void onServiceConnected() {
-        synchronized (sWaitObjectForConnecting) {
-            sConnectedInstance = this;
-            sWaitObjectForConnecting.notifyAll();
-        }
-    }
-
-    @Override
-    public void onAccessibilityEvent(AccessibilityEvent event) {
-    }
-
-    @Override
-    public void onInterrupt() {
+public class SpeakingAndVibratingAccessibilityService extends InstrumentedAccessibilityService {
+    public static SpeakingAndVibratingAccessibilityService enableSelf(
+            Instrumentation instrumentation) {
+        return InstrumentedAccessibilityService.enableService(instrumentation,
+                SpeakingAndVibratingAccessibilityService.class);
     }
 }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
index 41edf9f..6b866aa 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
@@ -16,42 +16,15 @@
 
 package android.view.accessibility.cts;
 
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.view.accessibility.AccessibilityEvent;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.app.Instrumentation;
 
 /**
  * Stub accessibility service that reports itself as providing haptic feedback.
  */
-public class VibratingAccessibilityService extends AccessibilityService {
-    public static Object sWaitObjectForConnecting = new Object();
-
-    public static VibratingAccessibilityService sConnectedInstance;
-
-    @Override
-    public void onDestroy() {
-        sConnectedInstance = null;
-    }
-
-    @Override
-    protected void onServiceConnected() {
-        final AccessibilityServiceInfo info = getServiceInfo();
-        info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
-        setServiceInfo(info);
-
-        synchronized (sWaitObjectForConnecting) {
-            sConnectedInstance = this;
-            sWaitObjectForConnecting.notifyAll();
-        }
-    }
-
-    @Override
-    public void onAccessibilityEvent(AccessibilityEvent event) {
-        /* do nothing */
-    }
-
-    @Override
-    public void onInterrupt() {
-        /* do nothing */
+public class VibratingAccessibilityService extends InstrumentedAccessibilityService {
+    public static VibratingAccessibilityService enableSelf(Instrumentation instrumentation) {
+        return InstrumentedAccessibilityService.enableService(instrumentation,
+                VibratingAccessibilityService.class);
     }
 }
diff --git a/tests/accessibilityservice/Android.mk b/tests/accessibilityservice/Android.mk
index 2bf06d9..c4480d7 100644
--- a/tests/accessibilityservice/Android.mk
+++ b/tests/accessibilityservice/Android.mk
@@ -22,8 +22,8 @@
     ctstestrunner-axt \
     hamcrest-library \
     mockito-target-minus-junit4 \
-    compatibility-device-util-axt \
-    platform-test-annotations
+    platform-test-annotations \
+    CtsAccessibilityCommon
 
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index e7e313e..fc0b2e6 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -93,7 +93,7 @@
         </service>
 
         <service
-            android:name=".InstrumentedAccessibilityService"
+            android:name="android.accessibility.cts.common.InstrumentedAccessibilityService"
             android:label="@string/title_soft_keyboard_modes_accessibility_service"
             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
             <intent-filter>
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 586a9d7..3b70810 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -45,6 +45,8 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.ShellCommandBuilder;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
 import android.app.Activity;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
index bffdf96..2774457 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
@@ -25,6 +25,8 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.ShellCommandBuilder;
 import android.accessibilityservice.AccessibilityService.MagnificationController;
 import android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener;
 import android.accessibilityservice.AccessibilityServiceInfo;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
index bd053f3..e461bdf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertTrue;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.cts.utils.AsyncUtils;
 import android.app.Instrumentation;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index c683a8d..8bb1bfb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener;
 import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index e8c529d..16e0b68 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.app.Instrumentation;
 import android.content.pm.PackageManager;
 import android.media.AudioManager;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
index 63601cd..042904b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
@@ -14,6 +14,8 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+
 import android.app.Instrumentation;
 import android.view.accessibility.AccessibilityEvent;
 import java.util.ArrayList;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
index df17442..72bbd97 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -43,6 +43,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
 import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
@@ -66,8 +69,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
index ad3050a..c0ce5b6 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
@@ -14,7 +14,9 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.app.Instrumentation;
+import android.app.UiAutomation;
 
 /**
  * A stub accessibility service to install for testing accessibility button APIs
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
index 10776c8..87539f8 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
@@ -14,6 +14,7 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.app.Instrumentation;
 
 /**
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
index e1699e8..68c4e7b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
@@ -14,6 +14,7 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.GestureDescription;
 import android.app.Instrumentation;
 import android.os.Handler;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
index 669ebda..195ce89 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
@@ -14,6 +14,7 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.app.Instrumentation;
 
 /**
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
index 076a6da..99fa727 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
@@ -16,10 +16,10 @@
 
 package android.accessibilityservice.cts.utils;
 
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityService.GestureResultCallback;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
-import android.accessibilityservice.cts.InstrumentedAccessibilityService;
 import android.graphics.Path;
 import android.graphics.PointF;
 import android.view.ViewConfiguration;
diff --git a/tests/app/DownloadManagerApi28Test/Android.mk b/tests/app/DownloadManagerApi28Test/Android.mk
new file mode 100644
index 0000000..51f4dde
--- /dev/null
+++ b/tests/app/DownloadManagerApi28Test/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2019 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs \
+    org.apache.http.legacy \
+    android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util-axt \
+    ctstestrunner-axt \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    androidx.test.rules \
+    platform-test-annotations \
+    androidx.test.rules \
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    ../src/android/app/cts/DownloadManagerTestBase.java
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../app/res
+
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../app/assets
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDownloadManagerApi28
+
+LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 11
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/app/DownloadManagerApi28Test/AndroidManifest.xml b/tests/app/DownloadManagerApi28Test/AndroidManifest.xml
new file mode 100644
index 0000000..f6eb1c4
--- /dev/null
+++ b/tests/app/DownloadManagerApi28Test/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.app.cts.downloads.api28">
+
+    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:usesCleartextTraffic="true"
+                 android:networkSecurityConfig="@xml/network_security_config">
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="org.apache.http.legacy" android:required="false" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.app.cts.downloads.api28" />
+
+</manifest>
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml b/tests/app/DownloadManagerApi28Test/AndroidTest.xml
similarity index 64%
copy from tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
copy to tests/app/DownloadManagerApi28Test/AndroidTest.xml
index 0c3cc4a..8d93448 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
+++ b/tests/app/DownloadManagerApi28Test/AndroidTest.xml
@@ -13,22 +13,18 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
-         CtsWindowManagerSdk28TestCases -->
-<configuration description="Config for CTS WindowManager dummy SDK 28 compatibility test cases">
+<configuration description="Config for DownloadManagerApi28Test">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
-    <!-- These tests require targeting API 28 which does not support instant apps -->
-    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsActivityManagerDeviceSdk28TestCases.apk" />
+        <option name="test-file-name" value="CtsDownloadManagerApi28.apk" />
     </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="android.server.wm.cts.dummy28" />
-        <option name="runtime-hint" value="1m" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="package" value="android.app.cts.downloads.api28" />
     </test>
-</configuration>
+
+</configuration>
\ No newline at end of file
diff --git a/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java b/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
new file mode 100644
index 0000000..fd4cea7
--- /dev/null
+++ b/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2019 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 android.app.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.DownloadManager;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class DownloadManagerApi28Test extends DownloadManagerTestBase {
+
+    @Test
+    public void testSetDestinationUri_publicDir() throws Exception {
+        File publicLocation = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+                "publicFile.bin");
+        if (publicLocation.exists()) {
+            assertTrue(publicLocation.delete());
+        }
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+            requestPublic.setDestinationUri(Uri.fromFile(publicLocation));
+            long id = mDownloadManager.enqueue(requestPublic);
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(1, allDownloads);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+            assertSuccessfulDownload(id, publicLocation);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    @Test
+    public void testSetDestinationUri_sdcardPath() throws Exception {
+        File path = new File("/sdcard/publicFile.bin");
+        if (path.exists()) {
+            assertTrue(path.delete());
+        }
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+            requestPublic.setDestinationUri(Uri.fromFile(path));
+            long id = mDownloadManager.enqueue(requestPublic);
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(1, allDownloads);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+            assertSuccessfulDownload(id, path);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    @Test
+    public void testDestinationInExternalPublicDir() throws Exception {
+        File publicLocation = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+                "publicFile.bin");
+        if (publicLocation.exists()) {
+            assertTrue(publicLocation.delete());
+        }
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+            requestPublic.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOCUMENTS,
+                    "publicFile.bin");
+            long id = mDownloadManager.enqueue(requestPublic);
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(1, allDownloads);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+            assertSuccessfulDownload(id, publicLocation);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    @Test
+    public void testAddCompletedDownload_publicDirs() throws Exception {
+        final String[] filePaths = new String[] {
+                createFile(Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
+                createFile(Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_DOCUMENTS), "file2.txt").getPath(),
+        };
+
+        for (String path : filePaths) {
+            final String fileContents = path + "_" + System.nanoTime();
+
+            writeToFile(new File(path), fileContents);
+
+            final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+                    "text/plain", path, fileContents.getBytes().length, true);
+            final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
+            assertEquals(fileContents, actualContents);
+
+            final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
+            mContext.grantUriPermission("com.android.shell", downloadUri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            final String rawFilePath = getRawFilePath(downloadUri);
+            final String rawFileContents = readFromRawFile(rawFilePath);
+            assertEquals(fileContents, rawFileContents);
+            assertRemoveDownload(id, 0);
+        }
+    }
+
+
+    @Test
+    public void testDownloadManager_mediaStoreEntry() throws Exception {
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            mContext.registerReceiver(receiver,
+                    new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+
+            final String fileName = "noiseandchirps.mp3";
+            final DownloadManager.Request request
+                    = new DownloadManager.Request(getAssetUrl(fileName));
+            final Uri[] downloadPathUris = new Uri[] {
+                    Uri.fromFile(new File(Environment.getExternalStoragePublicDirectory(
+                            Environment.DIRECTORY_DOWNLOADS), "testfile1.mp3")),
+                    Uri.parse("file:///sdcard/testfile2.mp3"),
+            };
+            for (Uri downloadLocation : downloadPathUris) {
+                request.setDestinationUri(downloadLocation);
+                final long downloadId = mDownloadManager.enqueue(request);
+                receiver.waitForDownloadComplete(SHORT_TIMEOUT, downloadId);
+                assertSuccessfulDownload(downloadId, new File(downloadLocation.getPath()));
+                final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
+                mContext.grantUriPermission("com.android.shell", downloadUri,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+                final ContentResolver contentResolver = mContext.getContentResolver();
+                assertArrayEquals(hash(contentResolver.openInputStream(downloadUri)),
+                        hash(contentResolver.openInputStream(mediaStoreUri)));
+
+                // Delete entry in DownloadProvider and verify it's deleted from MediaProvider
+                // as well.
+                assertRemoveDownload(downloadId, 0);
+                try (Cursor cursor = mContext.getContentResolver().query(
+                        mediaStoreUri, null, null, null)) {
+                    assertEquals(0, cursor.getCount());
+                }
+            }
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    /**
+     * Add a file using DownloadManager.addCompleted and verify that the file has been added
+     * to MediaStore as well.
+     */
+    @Test
+    public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
+        final String[] downloadPath = new String[] {
+                new File(Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
+                new File(Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_MUSIC), "file2.mp3").getPath(),
+                "/sdcard/file3.mp3",
+        };
+        for (String downloadLocation : downloadPath) {
+            final String fileContents = downloadLocation + "_" + System.nanoTime();
+            final File file = new File(Uri.parse(downloadLocation).getPath());
+            writeToFile(file, fileContents);
+
+            final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
+                    true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
+            assertTrue(downloadId >= 0);
+            final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
+            mContext.grantUriPermission("com.android.shell", downloadUri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+            assertArrayEquals(hash(new FileInputStream(file)),
+                    hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+            // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
+            assertRemoveDownload(downloadId, 0);
+            try (Cursor cursor = mContext.getContentResolver().query(
+                    mediaStoreUri, null, null, null)) {
+                assertEquals(0, cursor.getCount());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/app/DownloadManagerLegacyTest/Android.mk b/tests/app/DownloadManagerLegacyTest/Android.mk
new file mode 100644
index 0000000..8f1bcba
--- /dev/null
+++ b/tests/app/DownloadManagerLegacyTest/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2019 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs \
+    org.apache.http.legacy \
+    android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util-axt \
+    ctstestrunner-axt \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    androidx.test.rules \
+    platform-test-annotations \
+    androidx.test.rules \
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    ../src/android/app/cts/DownloadManagerTestBase.java
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../app/res
+
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../app/assets
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDownloadManagerLegacy
+
+LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 11
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/app/DownloadManagerLegacyTest/AndroidManifest.xml b/tests/app/DownloadManagerLegacyTest/AndroidManifest.xml
new file mode 100644
index 0000000..eae3b6a
--- /dev/null
+++ b/tests/app/DownloadManagerLegacyTest/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.app.cts.downloads.legacy">
+
+    <uses-sdk android:minSdkVersion="11" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:usesCleartextTraffic="true"
+                 android:networkSecurityConfig="@xml/network_security_config"
+                 android:requestLegacyExternalStorage="true">
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="org.apache.http.legacy" android:required="false" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.app.cts.downloads.legacy" />
+
+</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml b/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
similarity index 64%
rename from tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
rename to tests/app/DownloadManagerLegacyTest/AndroidTest.xml
index 0c3cc4a..d225952 100644
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidTest.xml
+++ b/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
@@ -13,22 +13,20 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
-         CtsWindowManagerSdk28TestCases -->
-<configuration description="Config for CTS WindowManager dummy SDK 28 compatibility test cases">
+<configuration description="Config for DownloadManagerLegacyTest">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
-    <!-- These tests require targeting API 28 which does not support instant apps -->
-    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsActivityManagerDeviceSdk28TestCases.apk" />
+        <option name="test-file-name" value="CtsDownloadManagerLegacy.apk" />
     </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="android.server.wm.cts.dummy28" />
-        <option name="runtime-hint" value="1m" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="package" value="android.app.cts.downloads.legacy" />
+        <!--TODO: Remove-->
+        <!--<option name="isolated-storage" value="false" />-->
     </test>
+
 </configuration>
diff --git a/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
new file mode 100644
index 0000000..43efb20
--- /dev/null
+++ b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 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 android.app.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class DownloadManagerLegacyTest extends DownloadManagerTestBase {
+    @Test
+    public void testAddCompletedDownload() throws Exception {
+        final String[] filePaths = new String[] {
+                createFile(Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
+                "/sdcard/Download/file2.txt",
+        };
+
+        for (String path : filePaths) {
+            final String fileContents = path + "_" + System.nanoTime();
+
+            writeToFile(new File(path), fileContents);
+
+            final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+                    "text/plain", path, fileContents.getBytes().length, true);
+            final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
+            assertEquals(fileContents, actualContents);
+
+            final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
+            mContext.grantUriPermission("com.android.shell", downloadUri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            final String rawFilePath = getRawFilePath(downloadUri);
+            final String rawFileContents = readFromRawFile(rawFilePath);
+            assertEquals(fileContents, rawFileContents);
+            assertRemoveDownload(id, 0);
+        }
+    }
+
+    @Test
+    public void testAddCompletedDownload_invalidPublicDir() throws Exception {
+        final File file = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+                "colors.txt");
+        final String fileContents = "RED;GREEN;BLUE";
+        writeToFileFromShell(file, fileContents);
+        try {
+            mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+                    "text/plain", file.getPath(), fileContents.getBytes().length, true);
+            fail(file + " is not valid for addCompletedDownload()");
+        } catch (Exception e) {
+            // expected
+        }
+    }
+
+    /**
+     * Add a file using DownloadManager.addCompleteDownload
+     * and verify that the file has been added to MediaStore as well.
+     */
+    @Test
+    public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
+        final String[] downloadPath = new String[] {
+                new File(Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
+                "/sdcard/Download/file2.mp3",
+        };
+        for (String downloadLocation : downloadPath) {
+            final String fileContents = downloadLocation + "_" + System.nanoTime();
+            final File file = new File(downloadLocation);
+            writeToFile(file, fileContents);
+
+            final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
+                    true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
+            assertTrue(downloadId >= 0);
+            final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
+            mContext.grantUriPermission("com.android.shell", downloadUri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+            assertArrayEquals(hash(new FileInputStream(file)),
+                    hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+            // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
+            assertRemoveDownload(downloadId, 0);
+            try (Cursor cursor = mContext.getContentResolver().query(
+                    mediaStoreUri, null, null, null)) {
+                assertEquals(0, cursor.getCount());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/app/TEST_MAPPING b/tests/app/TEST_MAPPING
index 94f8345..60d71d3 100644
--- a/tests/app/TEST_MAPPING
+++ b/tests/app/TEST_MAPPING
@@ -4,6 +4,9 @@
       "name": "CtsAppTestCases",
       "options": [
         {
+          "exclude-annotation": "androidx.test.filters.LargeTest"
+        },
+        {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
diff --git a/tests/app/src/android/app/cts/AlarmManagerTest.java b/tests/app/src/android/app/cts/AlarmManagerTest.java
index cf3662c..559bb8a 100644
--- a/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -31,7 +31,7 @@
 import android.test.MoreAsserts;
 import android.util.Log;
 
-import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
 
 import com.android.compatibility.common.util.PollingCheck;
 
@@ -228,7 +228,7 @@
         }
     }
 
-    @FlakyTest
+    @LargeTest
     public void testSetRepeating() throws Exception {
         mMockAlarmReceiver.setAlarmedFalse();
         mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
diff --git a/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java b/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
index f0ff28d..fceae14 100644
--- a/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
+++ b/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
@@ -16,6 +16,15 @@
 
 package android.app.cts;
 
+import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
 import android.app.Instrumentation;
@@ -30,8 +39,7 @@
 import android.content.DialogInterface.OnMultiChoiceClickListener;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.os.SystemClock;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -40,16 +48,24 @@
 import android.widget.Button;
 import android.widget.ListAdapter;
 import android.widget.ListView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 
 import com.android.compatibility.common.util.PollingCheck;
 
-import static org.mockito.Mockito.*;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
 
 @SmallTest
-public class AlertDialog_BuilderTest extends ActivityInstrumentationTestCase2<DialogStubActivity> {
+@RunWith(JUnit4.class)
+public class AlertDialog_BuilderTest  {
     private Builder mBuilder;
-    private Context mContext;
     private Instrumentation mInstrumentation;
     private final CharSequence mTitle = "title";
     private Drawable mDrawable;
@@ -70,33 +86,29 @@
 
     private OnItemSelectedListener mOnItemSelectedListener = mock(OnItemSelectedListener.class);
 
+    @Rule
+    public ActivityTestRule<DialogStubActivity> mActivityRule =
+            new ActivityTestRule<>(DialogStubActivity.class);
+
+    private Context mContext;
+
     private OnMultiChoiceClickListener mOnMultiChoiceClickListener =
             mock(OnMultiChoiceClickListener.class);
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mBuilder = null;
-        mInstrumentation = getInstrumentation();
-        mContext = getActivity();
-
-        PollingCheck.waitFor(() -> getActivity().hasWindowFocus());
-
-        mButton = null;
-        mView = null;
-        mListView = null;
-        mDialog = null;
-        mSelectedItem = null;
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        Activity activity = mActivityRule.getActivity();
+        mContext = activity;
+        PollingCheck.waitFor(activity::hasWindowFocus);
     }
 
-    public AlertDialog_BuilderTest() {
-        super("android.app.stubs", DialogStubActivity.class);
-    }
-
+    @Test
     public void testConstructor() {
         new AlertDialog.Builder(mContext);
     }
 
+    @Test
     public void testConstructorWithThemeId() {
         mBuilder = new AlertDialog.Builder(mContext, R.style.DialogTheme_Test);
 
@@ -109,8 +121,9 @@
         assertEquals(20, ta.getInt(0, 0));
     }
 
+    @Test
     public void testSetIconWithParamInt() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mDrawable = mContext.getResources().getDrawable(android.R.drawable.btn_default);
                 mBuilder = new AlertDialog.Builder(mContext);
@@ -121,8 +134,9 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @Test
     public void testSetIconWithParamDrawable() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mDrawable = mContext.getResources().getDrawable(android.R.drawable.btn_default);
                 mBuilder = new AlertDialog.Builder(mContext);
@@ -133,8 +147,9 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @Test
     public void testSetIconAttribute() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mDrawable = mContext.getResources().getDrawable(android.R.drawable.btn_default);
                 mBuilder = new AlertDialog.Builder(mContext);
@@ -145,8 +160,9 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @Test
     public void testSetPositiveButtonWithParamInt() throws Throwable {
-       runTestOnUiThread(new Runnable() {
+       runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setPositiveButton(android.R.string.yes, mOnClickListener);
@@ -166,8 +182,9 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @Test
     public void testSetPositiveButtonWithParamCharSequence() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setPositiveButton(android.R.string.yes, mOnClickListener);
@@ -186,8 +203,9 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @Test
     public void testSetNegativeButtonWithParamCharSequence() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setNegativeButton(mTitle, mOnClickListener);
@@ -206,8 +224,9 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @Test
     public void testSetNegativeButtonWithParamInt() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setNegativeButton(R.string.notify, mOnClickListener);
@@ -226,8 +245,9 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @Test
     public void testSetNeutralButtonWithParamInt() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setNeutralButton(R.string.notify, mOnClickListener);
@@ -246,8 +266,9 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @Test
     public void testSetNeutralButtonWithParamCharSequence() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setNeutralButton(mTitle, mOnClickListener);
@@ -267,7 +288,7 @@
     }
 
     private void testCancelable(final boolean cancelable) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setCancelable(cancelable);
@@ -275,13 +296,8 @@
             }
         });
         mInstrumentation.waitForIdleSync();
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                return mDialog.isShowing();
-            }
-        }.run();
-        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(mDialog::isShowing);
+        sendKeySync(KeyEvent.KEYCODE_BACK);
         mInstrumentation.waitForIdleSync();
         new PollingCheck() {
             @Override
@@ -300,16 +316,19 @@
         }.run();
     }
 
+    @Test
     public void testSetCancelable() throws Throwable {
         testCancelable(true);
     }
 
+    @Test
     public void testDisableCancelable() throws Throwable {
         testCancelable(false);
     }
 
+    @Test
     public void testSetOnCancelListener() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setOnCancelListener(mOnCancelListener);
@@ -322,8 +341,9 @@
         verifyNoMoreInteractions(mOnCancelListener);
     }
 
+    @Test
     public void testSetOnDismissListener() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setOnDismissListener(mOnDismissListener);
@@ -336,8 +356,9 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @Test
     public void testSetOnKeyListener() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setOnKeyListener(mOnKeyListener);
@@ -345,7 +366,9 @@
             }
         });
         mInstrumentation.waitForIdleSync();
-        sendKeys(KeyEvent.KEYCODE_0, KeyEvent.KEYCODE_1);
+        sendKeySync(KeyEvent.KEYCODE_0);
+        sendKeySync(KeyEvent.KEYCODE_1);
+        mInstrumentation.waitForIdleSync();
         // Use Mockito captures so that we can verify that each "sent" key code resulted
         // in one DOWN event and one UP event.
         ArgumentCaptor<KeyEvent> keyEvent0Captor = ArgumentCaptor.forClass(KeyEvent.class);
@@ -361,8 +384,9 @@
         assertEquals(KeyEvent.ACTION_UP, keyEvent1Captor.getAllValues().get(1).getAction());
     }
 
+    @Test
     public void testSetItemsWithParamInt() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setItems(R.array.difficultyLevel, mOnClickListener);
@@ -377,11 +401,12 @@
         assertEquals(levels[0], mListView.getItemAtPosition(0));
     }
 
+    @Test
     public void testSetItemsWithParamCharSequence() throws Throwable {
         final CharSequence[] expect = mContext.getResources().getTextArray(
                 R.array.difficultyLevel);
 
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setItems(expect, mOnClickListener);
@@ -393,9 +418,10 @@
         assertEquals(expect[0], mListView.getItemAtPosition(0));
     }
 
+    @Test
     public void testSetAdapter() throws Throwable {
         final ListAdapter adapter = new AdapterTest();
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setAdapter(adapter, mOnClickListener);
@@ -407,12 +433,13 @@
         assertEquals(adapter, mListView.getAdapter());
     }
 
+    @Test
     public void testSetMultiChoiceItemsWithParamInt() throws Throwable {
 
         final CharSequence[] items = mContext.getResources().getTextArray(
                 R.array.difficultyLevel);
 
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setMultiChoiceItems(R.array.difficultyLevel, null,
@@ -432,11 +459,12 @@
         assertEquals(items[0], mListView.getItemAtPosition(0));
     }
 
+    @Test
     public void testSetMultiChoiceItemsWithParamCharSequence() throws Throwable {
         final CharSequence[] items = mContext.getResources().getTextArray(
                 R.array.difficultyLevel);
 
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setMultiChoiceItems(items, null, mOnMultiChoiceClickListener);
@@ -455,11 +483,12 @@
         assertEquals(items[0], mListView.getItemAtPosition(0));
     }
 
+    @Test
     public void testSetSingleChoiceItemsWithParamInt() throws Throwable {
         final CharSequence[] items = mContext.getResources().getTextArray(
                 R.array.difficultyLevel);
 
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setSingleChoiceItems(R.array.difficultyLevel, 0,
@@ -477,11 +506,12 @@
         verifyNoMoreInteractions(mOnClickListener);
     }
 
+    @Test
     public void testSetSingleChoiceItemsWithParamCharSequence() throws Throwable {
         final CharSequence[] items = mContext.getResources().getTextArray(
                 R.array.difficultyLevel);
 
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setSingleChoiceItems(items, 0, mOnClickListener);
@@ -498,11 +528,12 @@
         verifyNoMoreInteractions(mOnClickListener);
     }
 
+    @Test
     public void testSetSingleChoiceItems() throws Throwable {
         final CharSequence[] items = mContext.getResources().getTextArray(
                 R.array.difficultyLevel);
 
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setSingleChoiceItems(new ArrayAdapter<CharSequence>(mContext,
@@ -521,8 +552,9 @@
         verifyNoMoreInteractions(mOnClickListener);
     }
 
+    @Test
     public void testSetOnItemSelectedListener() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setOnItemSelectedListener(mOnItemSelectedListener);
@@ -538,10 +570,11 @@
         verifyNoMoreInteractions(mOnItemSelectedListener);
     }
 
+    @Test
     public void testSetView() throws Throwable {
         final View view = new View(mContext);
         view.setId(100);
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setView(view);
@@ -553,8 +586,9 @@
         assertEquals(view, mView);
     }
 
+    @Test
     public void testSetViewFromInflater() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setView(LayoutInflater.from(mBuilder.getContext()).inflate(
@@ -569,8 +603,9 @@
         assertNotNull(mView.findViewById(R.id.username_edit));
     }
 
+    @Test
     public void testSetViewById() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setView(R.layout.alert_dialog_text_entry_2);
@@ -584,8 +619,9 @@
         assertNotNull(mView.findViewById(R.id.username_edit));
     }
 
+    @Test
     public void testSetCustomTitle() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setCustomTitle(LayoutInflater.from(mBuilder.getContext()).inflate(
@@ -596,8 +632,9 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @Test
     public void testSetInverseBackgroundForced() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mBuilder.setInverseBackgroundForced(true);
@@ -608,8 +645,9 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @Test
     public void testCreate() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mDialog = mBuilder.create();
@@ -621,8 +659,9 @@
         assertTrue(mDialog.isShowing());
     }
 
+    @Test
     public void testShow() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        runOnUiThread(new Runnable() {
             public void run() {
                 mBuilder = new AlertDialog.Builder(mContext);
                 mDialog = mBuilder.show();
@@ -632,6 +671,17 @@
         assertTrue(mDialog.isShowing());
     }
 
+    private void sendKeySync(int keyCode) {
+        final long downTime = SystemClock.uptimeMillis();
+        final KeyEvent downEvent =
+                new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0);
+        mInstrumentation.getUiAutomation().injectInputEvent(downEvent, true /*sync*/);
+
+        final KeyEvent upEvent =
+                new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keyCode, 0);
+        mInstrumentation.getUiAutomation().injectInputEvent(upEvent, true /*sync*/);
+    }
+
     private static class AdapterTest implements android.widget.ListAdapter {
         public boolean areAllItemsEnabled() {
             return true;
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index eaf3876..ced5ed3 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -15,8 +15,6 @@
  */
 package android.app.cts;
 
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -27,10 +25,7 @@
 import android.app.DownloadManager;
 import android.app.DownloadManager.Query;
 import android.app.DownloadManager.Request;
-import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
@@ -40,72 +35,23 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Environment;
-import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.provider.MediaStore;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
 import android.util.Log;
-import android.webkit.cts.CtsTestServer;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.PollingCheck;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.util.Arrays;
-import java.util.HashSet;
 
 @RunWith(AndroidJUnit4.class)
-public class DownloadManagerTest {
-    private static final String TAG = "DownloadManagerTest";
-
-    /**
-     * According to the CDD Section 7.6.1, the DownloadManager implementation must be able to
-     * download individual files of 100 MB.
-     */
-    private static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
-
-    private static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
-    private static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
-
-    private Context mContext;
-    private DownloadManager mDownloadManager;
-
-    private CtsTestServer mWebServer;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
-        mWebServer = new CtsTestServer(mContext);
-        clearDownloads();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mWebServer.shutdown();
-    }
+public class DownloadManagerTest extends DownloadManagerTestBase {
 
     @FlakyTest
     @Test
@@ -368,6 +314,129 @@
         }
     }
 
+    @Test
+    public void testSetDestinationUri() throws Exception {
+        final File documentsFile = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+                "uriFile.bin");
+        if (documentsFile.exists()) {
+            assertTrue(documentsFile.delete());
+        }
+
+        final Request badRequest = new Request(getGoodUrl());
+        badRequest.setDestinationUri(Uri.fromFile(documentsFile));
+        try {
+            mDownloadManager.enqueue(badRequest);
+            fail(documentsFile + " is not valid for setDestinationUri()");
+        } catch (Exception e) {
+            // Expected
+        }
+
+        final Uri badUri = Uri.parse("file:///sdcard/Android/data/"
+                + mContext.getPackageName() + "/../uriFile.bin");
+        final Request badRequest2 = new Request(getGoodUrl());
+        badRequest2.setDestinationUri(badUri);
+        try {
+            mDownloadManager.enqueue(badRequest2);
+            fail(badUri + " is not valid for setDestinationUri()");
+        } catch (Exception e) {
+            // Expected
+        }
+
+        final File sdcardPath = new File("/sdcard/uriFile.bin");
+        final Request badRequest3 = new Request(getGoodUrl());
+        badRequest3.setDestinationUri(Uri.fromFile(sdcardPath));
+        try {
+            mDownloadManager.enqueue(badRequest2);
+            fail(sdcardPath + " is not valid for setDestinationUri()");
+        } catch (Exception e) {
+            // Expected
+        }
+
+        File downloadsFile = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+                "uriFile.bin");
+        if (downloadsFile.exists()) {
+            assertTrue(downloadsFile.delete());
+        }
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            DownloadManager.Request request = new DownloadManager.Request(getGoodUrl());
+            request.setDestinationUri(Uri.fromFile(downloadsFile));
+            long id = mDownloadManager.enqueue(request);
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(1, allDownloads);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+            assertSuccessfulDownload(id, downloadsFile);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    @Test
+    public void testSetDestinationInExternalPublicDir() throws Exception {
+        File publicLocation = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+                "testing/publicFile.bin");
+        if (publicLocation.exists()) {
+            assertTrue(publicLocation.delete());
+        }
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
+            requestPublic.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,
+                    "testing/publicFile.bin");
+            long id = mDownloadManager.enqueue(requestPublic);
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(1, allDownloads);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+            assertSuccessfulDownload(id, publicLocation);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    @Test
+    public void testSetDestinationInExternalPublicDir_invalidRequests() {
+        final Request badRequest = new Request(getGoodUrl());
+        try {
+            badRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOCUMENTS,
+                    "uriFile.bin");
+            mDownloadManager.enqueue(badRequest);
+            fail(new File(Environment.getExternalStoragePublicDirectory(
+                    Environment.DIRECTORY_DOCUMENTS), "uriFile.bin")
+                    + " is not valid for setDestinationInExternalPublicDir()");
+        } catch (Exception e) {
+            // Expected
+        }
+
+        final Request badRequest2 = new Request(getGoodUrl());
+        try {
+            badRequest2.setDestinationInExternalPublicDir("TestDir", "uriFile.bin");
+            mDownloadManager.enqueue(badRequest2);
+            fail("/sdcard/TestDir/uriFile.bin"
+                    + " is not valid for setDestinationInExternalPublicDir()");
+        } catch (Exception e) {
+            // Expected
+        }
+    }
+
     private String cannonicalizeProcessName(ApplicationInfo ai) {
         return cannonicalizeProcessName(ai.processName, ai);
     }
@@ -442,8 +511,8 @@
     @Test
     public void testAddCompletedDownload() throws Exception {
         final String fileContents = "RED;GREEN;BLUE";
-        final File file = createFile(Environment.getExternalStoragePublicDirectory(
-                Environment.DIRECTORY_DOCUMENTS), "colors.txt");
+        final File file = createFile(mContext.getExternalFilesDir(null), "colors.txt");
+
         writeToFile(file, fileContents);
 
         final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
@@ -463,10 +532,10 @@
     @Test
     public void testAddCompletedDownload_invalidPaths() throws Exception {
         final String fileContents = "RED;GREEN;BLUE";
-        final File file = createFile(mContext.getFilesDir(), "colors.txt");
-        writeToFile(file, fileContents);
 
         // Try adding internal path
+        File file = createFile(mContext.getFilesDir(), "colors.txt");
+        writeToFile(file, fileContents);
         try {
             mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
                     "text/plain", file.getPath(), fileContents.getBytes().length, true);
@@ -475,12 +544,47 @@
             // expected
         }
 
-        // Try adding non-existent path
+        // Try adding path in top-level download dir
+        file = new File(
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+                "colors.txt");
+        writeToFileFromShell(file, fileContents);
         try {
             mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
-                    "text/plain", new File(mContext.getFilesDir(), "test_file.mp4").getPath(),
+                    "text/plain", file.getPath(), fileContents.getBytes().length, true);
+            fail("addCompletedDownload should have failed for top-level download dir");
+        } catch (Exception e) {
+            // expected
+        }
+
+        // Try adding top-level sdcard path
+        final String path = "/sdcard/test-download.txt";
+        writeToFileFromShell(new File(path), fileContents);
+        try {
+            mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+                    "text/plain", path, fileContents.getBytes().length, true);
+            fail("addCompletedDownload should have failed for top-level sdcard path");
+        } catch (Exception e) {
+            // expected
+        }
+
+
+        final String path2 = "/sdcard/Android/data/" + mContext.getPackageName()
+                + "/../uriFile.bin";
+        try {
+            mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+                    "text/plain", path2, fileContents.getBytes().length, true);
+            fail(path2 + " is not valid for addCompleteDownload()");
+        } catch (Exception e) {
+            // Expected
+        }
+
+        // Try adding non-existent path
+        try {
+            mDownloadManager.addCompletedDownload("Test title", "Test desc", true, "text/plain",
+                    new File(mContext.getExternalFilesDir(null), "test_file.mp4").getPath(),
                     fileContents.getBytes().length, true);
-            fail("addCompletedDownload should have failed for adding internal path");
+            fail("addCompletedDownload should have failed for adding non-existent path");
         } catch (Exception e) {
             // expected
         }
@@ -495,27 +599,6 @@
         }
     }
 
-    @FlakyTest
-    @Test
-    public void testAddCompletedDownload_sdcardPath() throws Exception {
-        final String fileContents = "RED;GREEN;BLUE";
-        final File file = new File("/sdcard", "colors.txt");
-        writeToFile(file, fileContents);
-
-        final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
-                "text/plain", file.getPath(), fileContents.getBytes().length, true);
-        final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
-        assertEquals(fileContents, actualContents);
-
-        final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
-        mContext.grantUriPermission("com.android.shell", downloadUri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        final String rawFilePath = getRawFilePath(downloadUri);
-        final String rawFileContents = readFromRawFile(rawFilePath);
-        assertEquals(fileContents, rawFileContents);
-        assertRemoveDownload(id, 0);
-    }
-
     /**
      * Download a file using DownloadManager and verify that the file has been added
      * to MediaStore as well.
@@ -532,7 +615,7 @@
             final Request request = new Request(getAssetUrl(fileName));
             final File[] downloadLocations = new File[] {
                     new File(mContext.getExternalFilesDir(null), "file1.mp3"),
-                    new File("/sdcard", "file2.mp3"),
+                    new File(mContext.getExternalCacheDir(), "file2.mp3"),
             };
             for (File downloadLocation : downloadLocations) {
                 request.setDestinationUri(Uri.fromFile(downloadLocation));
@@ -547,7 +630,12 @@
                 assertArrayEquals(hash(contentResolver.openInputStream(downloadUri)),
                         hash(contentResolver.openInputStream(mediaStoreUri)));
 
+                // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
                 assertRemoveDownload(downloadId, 0);
+                try (Cursor cursor = mContext.getContentResolver().query(
+                        mediaStoreUri, null, null, null)) {
+                    assertEquals(0, cursor.getCount());
+                }
             }
         } finally {
             mContext.unregisterReceiver(receiver);
@@ -561,18 +649,18 @@
     @FlakyTest
     @Test
     public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
-        final File[] files = new File[] {
-                createFile(Environment.getExternalStoragePublicDirectory(
-                        Environment.DIRECTORY_DOCUMENTS), "file1.txt"),
-                createFile(new File("/sdcard"), "file2.txt"),
+        final String[] filePaths = new String[] {
+                new File(mContext.getExternalFilesDir(null), "file1.txt").getPath(),
+                "/sdcard/Android/data/" + mContext.getPackageName() + "/file2.txt",
         };
 
-        for (File file : files) {
-            final String fileContents = file.getName() + "_" + System.nanoTime();
+        for (String path : filePaths) {
+            final String fileContents = path + "_" + System.nanoTime();
+            final File file = new File(path);
             writeToFile(file, fileContents);
 
             final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
-                    true, "text/plain", file.getPath(), fileContents.getBytes().length, true);
+                    true, "text/plain", path, fileContents.getBytes().length, true);
             assertTrue(downloadId >= 0);
             final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
             mContext.grantUriPermission("com.android.shell", downloadUri,
@@ -580,341 +668,13 @@
             final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
             assertArrayEquals(hash(new FileInputStream(file)),
                     hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+            // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
             assertRemoveDownload(downloadId, 0);
-        }
-    }
-
-    /**
-     * Add a file to DownloadProvider using DownloadManager.addCompletedDownload and verify
-     * updates to this entry in DownlaodProvider are reflected in MediaProvider as well.
-     */
-    @FlakyTest
-    @Test
-    public void testDownloadManagerUpdates() throws Exception {
-        final File dataDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
-        dataDir.mkdir();
-        final File downloadFile = new File(dataDir, "colors.txt");
-        downloadFile.createNewFile();
-        final String fileContents = "RED;GREEN;BLUE";
-        try (final PrintWriter pw = new PrintWriter(downloadFile)) {
-            pw.print(fileContents);
-        }
-
-        // Insert into DownloadProvider and verify it's added to MediaProvider as well
-        final String testTitle = "Test title";
-        final long downloadId = mDownloadManager.addCompletedDownload(testTitle, "Test desc", true,
-                "text/plain", downloadFile.getPath(), fileContents.getBytes().length, true);
-        assertTrue(downloadId >= 0);
-        final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
-        mContext.grantUriPermission("com.android.shell", downloadUri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
-        assertArrayEquals(hash(new FileInputStream(downloadFile)),
-                hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
-        try (Cursor cursor = mContext.getContentResolver().query(mediaStoreUri,
-                new String[] { MediaStore.DownloadColumns.DISPLAY_NAME }, null, null)) {
-            cursor.moveToNext();
-            assertEquals(testTitle, cursor.getString(0));
-        }
-
-        // Update title in DownloadProvider and verify the change took effect in MediaProvider
-        // as well.
-        final String newTitle = "New title";
-        final ContentValues updateValues = new ContentValues();
-        updateValues.put(DownloadManager.COLUMN_TITLE, newTitle);
-        assertEquals(1, mContext.getContentResolver().update(
-                downloadUri, updateValues, null, null));
-        try (Cursor cursor = mContext.getContentResolver().query(mediaStoreUri,
-                new String[] { MediaStore.DownloadColumns.DISPLAY_NAME }, null, null)) {
-            cursor.moveToNext();
-            assertEquals(newTitle, cursor.getString(0));
-        }
-
-        // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
-        assertRemoveDownload(downloadId, 0);
-        try (Cursor cursor = mContext.getContentResolver().query(
-                mediaStoreUri, null, null, null)) {
-            assertEquals(0, cursor.getCount());
-        }
-    }
-
-    @FlakyTest
-    @Test
-    public void testDownloadManager_mediaStoreUpdates() throws Exception {
-        final File dataDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
-        dataDir.mkdir();
-        final File downloadFile = new File(dataDir, "colors.txt");
-        downloadFile.createNewFile();
-        final String fileContents = "RED;GREEN;BLUE";
-        try (final PrintWriter pw = new PrintWriter(downloadFile)) {
-            pw.print(fileContents);
-        }
-
-        // Insert into DownloadProvider and verify it's added to MediaProvider as well
-        final String testTitle = "Test_title";
-        final long downloadId = mDownloadManager.addCompletedDownload(testTitle, "Test desc", true,
-                "text/plain", downloadFile.getPath(), fileContents.getBytes().length, true);
-        assertTrue(downloadId >= 0);
-        final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
-        try (Cursor cursor = mContext.getContentResolver().query(downloadUri,
-                new String[] { DownloadManager.COLUMN_TITLE }, null, null)) {
-            cursor.moveToNext();
-            assertEquals(testTitle, cursor.getString(0));
-        }
-
-        mContext.grantUriPermission("com.android.shell", downloadUri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
-        final String newTitle = "New_title";
-        updateUri(mediaStoreUri, "_display_name", newTitle);
-        try (Cursor cursor = mContext.getContentResolver().query(downloadUri,
-                new String[] { DownloadManager.COLUMN_TITLE }, null, null)) {
-            cursor.moveToNext();
-            assertEquals(newTitle, cursor.getString(0));
-        }
-
-        assertRemoveDownload(downloadId, 0);
-    }
-
-    private void updateUri(Uri uri, String column, String value) throws Exception {
-        final String cmd = String.format("content update --uri %s --bind %s:s:%s",
-                uri, column, value);
-        final String res = runShellCommand(cmd).trim();
-        assertTrue(res, TextUtils.isEmpty(res));
-    }
-
-    private static byte[] hash(InputStream in) throws Exception {
-        try (DigestInputStream digestIn = new DigestInputStream(in,
-                MessageDigest.getInstance("SHA-1"));
-                OutputStream out = new FileOutputStream(new File("/dev/null"))) {
-            FileUtils.copy(digestIn, out);
-            return digestIn.getMessageDigest().digest();
-        } finally {
-            FileUtils.closeQuietly(in);
-        }
-    }
-
-    private Uri getMediaStoreUri(Uri downloadUri) throws Exception {
-        final String cmd = String.format("content query --uri %s --projection %s",
-                downloadUri, DownloadManager.COLUMN_MEDIASTORE_URI);
-        final String res = runShellCommand(cmd).trim();
-        final String str = DownloadManager.COLUMN_MEDIASTORE_URI + "=";
-        final int i = res.indexOf(str);
-        if (i >= 0) {
-            return Uri.parse(res.substring(i + str.length()));
-        } else {
-            throw new FileNotFoundException("Failed to find "
-                    + DownloadManager.COLUMN_MEDIASTORE_URI + " for "
-                    + downloadUri + "; found " + res);
-        }
-    }
-
-    private static String getRawFilePath(Uri uri) throws Exception {
-        final String res = runShellCommand("content query --uri " + uri + " --projection _data");
-        final int i = res.indexOf("_data=");
-        if (i >= 0) {
-            return res.substring(i + 6);
-        } else {
-            throw new FileNotFoundException("Failed to find _data for " + uri + "; found " + res);
-        }
-    }
-
-    private static String readFromRawFile(String filePath) throws Exception {
-        Log.d(TAG, "Reading form file: " + filePath);
-        return runShellCommand("cat " + filePath);
-    }
-
-    private static String readFromFile(ParcelFileDescriptor pfd) throws Exception {
-        BufferedReader br = null;
-        try (final InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
-            br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
-            String str;
-            StringBuilder out = new StringBuilder();
-            while ((str = br.readLine()) != null) {
-                out.append(str);
-            }
-            return out.toString();
-        } finally {
-            if (br != null) {
-                br.close();
+            try (Cursor cursor = mContext.getContentResolver().query(
+                    mediaStoreUri, null, null, null)) {
+                assertEquals(0, cursor.getCount());
             }
         }
     }
-
-    private static File createFile(File baseDir, String fileName) {
-        if (!baseDir.exists()) {
-            baseDir.mkdirs();
-        }
-        return new File(baseDir, fileName);
-    }
-
-    private static void writeToFile(File file, String contents) throws Exception {
-        try (final PrintWriter out = new PrintWriter(file)) {
-            out.print(contents);
-        }
-    }
-
-    private class DownloadCompleteReceiver extends BroadcastReceiver {
-        private HashSet<Long> mCompleteIds = new HashSet<>();
-
-        public DownloadCompleteReceiver() {
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mCompleteIds) {
-                mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
-                mCompleteIds.notifyAll();
-            }
-        }
-
-        private boolean isCompleteLocked(long... ids) {
-            for (long id : ids) {
-                if (!mCompleteIds.contains(id)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        public void waitForDownloadComplete(long timeoutMillis, long... waitForIds)
-                throws InterruptedException {
-            if (waitForIds.length == 0) {
-                throw new IllegalArgumentException("Missing IDs to wait for");
-            }
-
-            final long startTime = SystemClock.elapsedRealtime();
-            do {
-                synchronized (mCompleteIds) {
-                    mCompleteIds.wait(timeoutMillis);
-                    if (isCompleteLocked(waitForIds)) return;
-                }
-            } while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis);
-
-            throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds)
-                    + "; received " + mCompleteIds.toString()
-                    + ".  Make sure you have WiFi or some other connectivity for this test.");
-        }
-    }
-
-    private void clearDownloads() {
-        if (getTotalNumberDownloads() > 0) {
-            Cursor cursor = null;
-            try {
-                Query query = new Query();
-                cursor = mDownloadManager.query(query);
-                int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
-                long[] removeIds = new long[cursor.getCount()];
-                for (int i = 0; cursor.moveToNext(); i++) {
-                    removeIds[i] = cursor.getLong(columnIndex);
-                }
-                assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
-                assertEquals(0, getTotalNumberDownloads());
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-    }
-
-    private Uri getGoodUrl() {
-        return Uri.parse(mWebServer.getTestDownloadUrl("cts-good-download", 0));
-    }
-
-    private Uri getBadUrl() {
-        return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
-    }
-
-    private Uri getMinimumDownloadUrl() {
-        return Uri.parse(mWebServer.getTestDownloadUrl("cts-minimum-download",
-                MINIMUM_DOWNLOAD_BYTES));
-    }
-
-    private Uri getAssetUrl(String asset) {
-        return Uri.parse(mWebServer.getAssetUrl(asset));
-    }
-
-    private int getTotalNumberDownloads() {
-        Cursor cursor = null;
-        try {
-            Query query = new Query();
-            cursor = mDownloadManager.query(query);
-            return cursor.getCount();
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-    }
-
-    private void assertDownloadQueryableById(long downloadId) {
-        Cursor cursor = null;
-        try {
-            Query query = new Query().setFilterById(downloadId);
-            cursor = mDownloadManager.query(query);
-            assertEquals(1, cursor.getCount());
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-    }
-
-    private void assertDownloadQueryableByStatus(final int status) {
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                Cursor cursor= null;
-                try {
-                    Query query = new Query().setFilterByStatus(status);
-                    cursor = mDownloadManager.query(query);
-                    return 1 == cursor.getCount();
-                } finally {
-                    if (cursor != null) {
-                        cursor.close();
-                    }
-                }
-            }
-        }.run();
-    }
-
-    private void assertSuccessfulDownload(long id, File location) throws Exception {
-        Cursor cursor = null;
-        try {
-            final File expectedLocation = location.getCanonicalFile();
-            cursor = mDownloadManager.query(new Query().setFilterById(id));
-            assertTrue(cursor.moveToNext());
-            assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
-                    cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
-            assertEquals(Uri.fromFile(expectedLocation).toString(),
-                    cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
-            assertTrue(expectedLocation.exists());
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-    }
-
-    private void assertRemoveDownload(long removeId, int expectedNumDownloads) {
-        Cursor cursor = null;
-        try {
-            assertEquals(1, mDownloadManager.remove(removeId));
-            Query query = new Query();
-            cursor = mDownloadManager.query(query);
-            assertEquals(expectedNumDownloads, cursor.getCount());
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-    }
-
-    private boolean hasInternetConnection() {
-        final PackageManager pm = mContext.getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
-                || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
-                || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
-    }
 }
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
new file mode 100644
index 0000000..71eb4e9a
--- /dev/null
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2019 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 android.app.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.webkit.cts.CtsTestServer;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class DownloadManagerTestBase {
+    protected static final String TAG = "DownloadManagerTest";
+
+    /**
+     * According to the CDD Section 7.6.1, the DownloadManager implementation must be able to
+     * download individual files of 100 MB.
+     */
+    protected static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
+
+    protected static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
+    protected static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
+
+    protected Context mContext;
+    protected DownloadManager mDownloadManager;
+
+    private CtsTestServer mWebServer;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+        mWebServer = new CtsTestServer(mContext);
+        clearDownloads();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mWebServer.shutdown();
+        clearDownloads();
+    }
+
+    protected void updateUri(Uri uri, String column, String value) throws Exception {
+        final String cmd = String.format("content update --uri %s --bind %s:s:%s",
+                uri, column, value);
+        final String res = runShellCommand(cmd).trim();
+        assertTrue(res, TextUtils.isEmpty(res));
+    }
+
+    protected static byte[] hash(InputStream in) throws Exception {
+        try (DigestInputStream digestIn = new DigestInputStream(in,
+                MessageDigest.getInstance("SHA-1"));
+             OutputStream out = new FileOutputStream(new File("/dev/null"))) {
+            FileUtils.copy(digestIn, out);
+            return digestIn.getMessageDigest().digest();
+        } finally {
+            FileUtils.closeQuietly(in);
+        }
+    }
+
+    protected Uri getMediaStoreUri(Uri downloadUri) throws Exception {
+        final String cmd = String.format("content query --uri %s --projection %s",
+                downloadUri, DownloadManager.COLUMN_MEDIASTORE_URI);
+        final String res = runShellCommand(cmd).trim();
+        final String str = DownloadManager.COLUMN_MEDIASTORE_URI + "=";
+        final int i = res.indexOf(str);
+        if (i >= 0) {
+            return Uri.parse(res.substring(i + str.length()));
+        } else {
+            throw new FileNotFoundException("Failed to find "
+                    + DownloadManager.COLUMN_MEDIASTORE_URI + " for "
+                    + downloadUri + "; found " + res);
+        }
+    }
+
+    protected static String getRawFilePath(Uri uri) throws Exception {
+        final String res = runShellCommand("content query --uri " + uri + " --projection _data");
+        final int i = res.indexOf("_data=");
+        if (i >= 0) {
+            return res.substring(i + 6);
+        } else {
+            throw new FileNotFoundException("Failed to find _data for " + uri + "; found " + res);
+        }
+    }
+
+    protected static String readFromRawFile(String filePath) throws Exception {
+        Log.d(TAG, "Reading form file: " + filePath);
+        return runShellCommand("cat " + filePath);
+    }
+
+    protected static String readFromFile(ParcelFileDescriptor pfd) throws Exception {
+        BufferedReader br = null;
+        try (final InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
+            br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+            String str;
+            StringBuilder out = new StringBuilder();
+            while ((str = br.readLine()) != null) {
+                out.append(str);
+            }
+            return out.toString();
+        } finally {
+            if (br != null) {
+                br.close();
+            }
+        }
+    }
+
+    protected static File createFile(File baseDir, String fileName) {
+        if (!baseDir.exists()) {
+            baseDir.mkdirs();
+        }
+        return new File(baseDir, fileName);
+    }
+
+    protected static void writeToFile(File file, String contents) throws Exception {
+        try (final PrintWriter out = new PrintWriter(file)) {
+            out.print(contents);
+        }
+    }
+
+    protected static void writeToFileFromShell(File file, String contents) throws Exception {
+        final String cmd = "echo \"" + contents + "\" > " + file;
+        final String res = runShellCommand(cmd);
+        Log.d(TAG, "Output of '" + cmd + "': '" + res + "'");
+    }
+
+    protected void clearDownloads() {
+        if (getTotalNumberDownloads() > 0) {
+            Cursor cursor = null;
+            try {
+                DownloadManager.Query query = new DownloadManager.Query();
+                cursor = mDownloadManager.query(query);
+                int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+                long[] removeIds = new long[cursor.getCount()];
+                for (int i = 0; cursor.moveToNext(); i++) {
+                    removeIds[i] = cursor.getLong(columnIndex);
+                }
+                assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
+                assertEquals(0, getTotalNumberDownloads());
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    protected Uri getGoodUrl() {
+        return Uri.parse(mWebServer.getTestDownloadUrl("cts-good-download", 0));
+    }
+
+    protected Uri getBadUrl() {
+        return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
+    }
+
+    protected Uri getMinimumDownloadUrl() {
+        return Uri.parse(mWebServer.getTestDownloadUrl("cts-minimum-download",
+                MINIMUM_DOWNLOAD_BYTES));
+    }
+
+    protected Uri getAssetUrl(String asset) {
+        return Uri.parse(mWebServer.getAssetUrl(asset));
+    }
+
+    protected int getTotalNumberDownloads() {
+        Cursor cursor = null;
+        try {
+            DownloadManager.Query query = new DownloadManager.Query();
+            cursor = mDownloadManager.query(query);
+            return cursor.getCount();
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    protected void assertDownloadQueryableById(long downloadId) {
+        Cursor cursor = null;
+        try {
+            DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
+            cursor = mDownloadManager.query(query);
+            assertEquals(1, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    protected void assertDownloadQueryableByStatus(final int status) {
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                Cursor cursor= null;
+                try {
+                    DownloadManager.Query query = new DownloadManager.Query().setFilterByStatus(status);
+                    cursor = mDownloadManager.query(query);
+                    return 1 == cursor.getCount();
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
+            }
+        }.run();
+    }
+
+    protected void assertSuccessfulDownload(long id, File location) throws Exception {
+        Cursor cursor = null;
+        try {
+            final File expectedLocation = location.getCanonicalFile();
+            cursor = mDownloadManager.query(new DownloadManager.Query().setFilterById(id));
+            assertTrue(cursor.moveToNext());
+            assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
+                    cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
+            assertEquals(Uri.fromFile(expectedLocation).toString(),
+                    cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
+            assertTrue(expectedLocation.exists());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    protected void assertRemoveDownload(long removeId, int expectedNumDownloads) {
+        Cursor cursor = null;
+        try {
+            assertEquals(1, mDownloadManager.remove(removeId));
+            DownloadManager.Query query = new DownloadManager.Query();
+            cursor = mDownloadManager.query(query);
+            assertEquals(expectedNumDownloads, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    protected boolean hasInternetConnection() {
+        final PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+                || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
+                || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
+    }
+
+    public static class DownloadCompleteReceiver extends BroadcastReceiver {
+        private HashSet<Long> mCompleteIds = new HashSet<>();
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mCompleteIds) {
+                mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
+                mCompleteIds.notifyAll();
+            }
+        }
+
+        private boolean isCompleteLocked(long... ids) {
+            for (long id : ids) {
+                if (!mCompleteIds.contains(id)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public void waitForDownloadComplete(long timeoutMillis, long... waitForIds)
+                throws InterruptedException {
+            if (waitForIds.length == 0) {
+                throw new IllegalArgumentException("Missing IDs to wait for");
+            }
+
+            final long startTime = SystemClock.elapsedRealtime();
+            do {
+                synchronized (mCompleteIds) {
+                    mCompleteIds.wait(timeoutMillis);
+                    if (isCompleteLocked(waitForIds)) return;
+                }
+            } while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis);
+
+            throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds)
+                    + "; received " + mCompleteIds.toString()
+                    + ".  Make sure you have WiFi or some other connectivity for this test.");
+        }
+    }
+}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 8b94334..8abd57e 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -2404,7 +2404,8 @@
                                 SystemClock.currentThreadTimeMillis(), person)
                 )
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
-        sendAndVerifyBubble(1, nb, null /* use default metadata */, true /* shouldBeBubble */);
+        boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+        sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble);
     }
 
     public void testNotificationManagerBubblePolicy_flagForPhonecall() {
@@ -2412,8 +2413,9 @@
         serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_SUCCESS);
         mContext.startService(serviceIntent);
 
+        boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
         if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, true /* shouldBeBubble */)) {
+                true /* shouldExist */, shouldBeBubble)) {
             fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
         }
     }
@@ -2515,8 +2517,10 @@
         // The notif should be allowed to update as a bubble
         a.sendBubble(2);
 
+        boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+
         if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, true /* shouldBeBubble */)) {
+                true /* shouldExist */, shouldBeBubble)) {
             fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
         }
 
diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java
index 6bea944..0809d38 100644
--- a/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/app/src/android/app/cts/ServiceTest.java
@@ -1160,6 +1160,31 @@
     }
 
     /**
+     * Verify that certain characters are prohibited in instanceName.
+     */
+    public void testFailBindIsoaltedServiceWithInvalidInstanceName() throws Exception {
+        String[] badNames = {
+            "t\rest",
+            "test\n",
+            "test-three",
+            "test four",
+            "escape\u00a9seq",
+            "\u0164est",
+        };
+        for (String instanceName : badNames) {
+            EmptyConnection conn = new EmptyConnection();
+            try {
+                mContext.bindIsolatedService(mIsolatedService, Context.BIND_AUTO_CREATE,
+                        instanceName, mContextMainExecutor, conn);
+                mContext.unbindService(conn);
+                fail("Didn't get IllegalArgumentException: " + instanceName);
+            } catch (IllegalArgumentException e) {
+                // This is expected.
+            }
+        }
+    }
+
+    /**
      * Verify that bindIsolatedService() correctly makes different instances when given
      * different instance names.
      */
diff --git a/tests/apppredictionservice/AndroidManifest.xml b/tests/apppredictionservice/AndroidManifest.xml
index fa470fd..8ee464a 100644
--- a/tests/apppredictionservice/AndroidManifest.xml
+++ b/tests/apppredictionservice/AndroidManifest.xml
@@ -18,6 +18,8 @@
     package="android.apppredictionservice.cts"
     android:targetSandboxVersion="2">
 
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
index 890cbd4..eae6016 100644
--- a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
+++ b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
@@ -216,6 +216,25 @@
         client.destroy();
     }
 
+    @Test
+    public void testFailureWithoutPermission() {
+        setService(null);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().revokeRuntimePermission(
+                InstrumentationRegistry.getTargetContext().getPackageName(),
+                android.Manifest.permission.PACKAGE_USAGE_STATS);
+        assertFails(() -> createTestPredictor(createTestPredictionContext()));
+    }
+
+    @Test
+    public void testSuccessWithPermission() {
+        setService(null);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+                InstrumentationRegistry.getTargetContext().getPackageName(),
+                android.Manifest.permission.PACKAGE_USAGE_STATS);
+        AppPredictor predictor = createTestPredictor(createTestPredictionContext());
+        predictor.destroy();
+    }
+
     private void assertFails(Runnable r) {
         try {
             r.run();
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 6021e650..fe21919 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -40,6 +40,8 @@
                   android:theme="@style/MyAutofilledHighlight"/>
         <activity android:name=".LoginWithStringsActivity" />
         <activity android:name=".LoginNotImportantForAutofillActivity" />
+        <activity android:name=".LoginNotImportantForAutofillWrappedActivityContextActivity" />
+        <activity android:name=".LoginNotImportantForAutofillWrappedApplicationContextActivity" />
         <activity android:name=".WelcomeActivity" android:taskAffinity=".WelcomeActivity"/>
         <activity android:name=".ViewAttributesTestActivity" />
         <activity android:name=".AuthenticationActivity" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
index 63df568..a955587 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -42,6 +42,7 @@
 public abstract class AbstractAutoFillActivity extends Activity {
 
     private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
+    protected final String mTag = getClass().getSimpleName();
     private MyAutofillCallback mCallback;
 
     /**
@@ -109,7 +110,7 @@
      * <p>Note: caller doesn't need to call {@link #unregisterCallback()}, it will be automatically
      * unregistered on {@link #finish()}.
      */
-    protected MyAutofillCallback registerCallback() {
+    public MyAutofillCallback registerCallback() {
         assertWithMessage("already registered").that(mCallback).isNull();
         mCallback = new MyAutofillCallback();
         getAutofillManager().registerCallback(mCallback);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedActivityContextActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedActivityContextActivity.java
new file mode 100644
index 0000000..035cea6
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedActivityContextActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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 android.autofillservice.cts;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.util.Log;
+
+/**
+ * Same as {@link LoginNotImportantForAutofillActivity}, but using a context wrapper of itself
+ * as the base context.
+ */
+public class LoginNotImportantForAutofillWrappedActivityContextActivity
+        extends LoginNotImportantForAutofillActivity {
+
+    private Context mMyBaseContext;
+
+    @Override
+    public Context getBaseContext() {
+        if (mMyBaseContext == null) {
+            mMyBaseContext = new ContextWrapper(super.getBaseContext());
+            Log.d(mTag, "getBaseContext(): set to " + mMyBaseContext + " (instead of "
+                    + super.getBaseContext() + ")");
+        }
+        return mMyBaseContext;
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedApplicationContextActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedApplicationContextActivity.java
new file mode 100644
index 0000000..b47cfc6
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginNotImportantForAutofillWrappedApplicationContextActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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 android.autofillservice.cts;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.util.Log;
+
+/**
+ * Same as {@link LoginNotImportantForAutofillActivity}, but using a context wrapper of itself
+ * as the base context.
+ */
+public class LoginNotImportantForAutofillWrappedApplicationContextActivity
+        extends LoginNotImportantForAutofillActivity {
+
+    private Context mMyBaseContext;
+
+    @Override
+    public Context getBaseContext() {
+        if (mMyBaseContext == null) {
+            mMyBaseContext = new ContextWrapper(getApplicationContext());
+            Log.d(mTag, "getBaseContext(): set to " + mMyBaseContext + " (instead of "
+                    + super.getBaseContext() + ")");
+        }
+        return mMyBaseContext;
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 65ce0e3..7a09dbf 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -38,7 +38,7 @@
 /**
  * Custom {@link AutofillCallback} used to recover events during tests.
  */
-final class MyAutofillCallback extends AutofillCallback {
+public final class MyAutofillCallback extends AutofillCallback {
 
     private static final String TAG = "MyAutofillCallback";
     private final BlockingQueue<MyEvent> mEvents = new LinkedBlockingQueue<>();
@@ -77,7 +77,7 @@
     /**
      * Gets the next available event or fail if it times out.
      */
-    MyEvent getEvent() throws InterruptedException {
+    public MyEvent getEvent() throws InterruptedException {
         final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
         if (event == null) {
             throw new RetryableException(CONNECTION_TIMEOUT, "no event");
@@ -88,7 +88,7 @@
     /**
      * Assert no more events were received.
      */
-    void assertNotCalled() throws InterruptedException {
+    public void assertNotCalled() throws InterruptedException {
         final MyEvent event = mEvents.poll(CALLBACK_NOT_CALLED_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         if (event != null) {
             // Not retryable.
@@ -99,7 +99,7 @@
     /**
      * Used to assert there is no event left behind.
      */
-    void assertNumberUnhandledEvents(int expected) {
+    public void assertNumberUnhandledEvents(int expected) {
         assertWithMessage("Invalid number of events left: %s", mEvents).that(mEvents.size())
                 .isEqualTo(expected);
     }
@@ -107,7 +107,7 @@
     /**
      * Convenience method to assert an UI shown event for the given view was received.
      */
-    MyEvent assertUiShownEvent(View expectedView) throws InterruptedException {
+    public MyEvent assertUiShownEvent(View expectedView) throws InterruptedException {
         final MyEvent event = getEvent();
         assertWithMessage("Invalid type on event %s", event).that(event.event)
                 .isEqualTo(EVENT_INPUT_SHOWN);
@@ -119,7 +119,8 @@
     /**
      * Convenience method to assert an UI shown event for the given virtual view was received.
      */
-    void assertUiShownEvent(View expectedView, int expectedChildId) throws InterruptedException {
+    public void assertUiShownEvent(View expectedView, int expectedChildId)
+            throws InterruptedException {
         final MyEvent event = assertUiShownEvent(expectedView);
         assertWithMessage("Invalid child on event %s", event).that(event.childId)
             .isEqualTo(expectedChildId);
@@ -130,7 +131,7 @@
      *
      * @return virtual child id
      */
-    int assertUiShownEventForVirtualChild(View expectedView) throws InterruptedException {
+    public int assertUiShownEventForVirtualChild(View expectedView) throws InterruptedException {
         final MyEvent event = assertUiShownEvent(expectedView);
         return event.childId;
     }
@@ -138,7 +139,7 @@
     /**
      * Convenience method to assert an UI hidden event for the given view was received.
      */
-    MyEvent assertUiHiddenEvent(View expectedView) throws InterruptedException {
+    public MyEvent assertUiHiddenEvent(View expectedView) throws InterruptedException {
         final MyEvent event = getEvent();
         assertWithMessage("Invalid type on event %s", event).that(event.event)
                 .isEqualTo(EVENT_INPUT_HIDDEN);
@@ -150,7 +151,8 @@
     /**
      * Convenience method to assert an UI hidden event for the given view was received.
      */
-    void assertUiHiddenEvent(View expectedView, int expectedChildId) throws InterruptedException {
+    public void assertUiHiddenEvent(View expectedView, int expectedChildId)
+            throws InterruptedException {
         final MyEvent event = assertUiHiddenEvent(expectedView);
         assertWithMessage("Invalid child on event %s", event).that(event.childId)
                 .isEqualTo(expectedChildId);
@@ -159,7 +161,7 @@
     /**
      * Convenience method to assert an UI unavailable event for the given view was received.
      */
-    MyEvent assertUiUnavailableEvent(View expectedView) throws InterruptedException {
+    public MyEvent assertUiUnavailableEvent(View expectedView) throws InterruptedException {
         final MyEvent event = getEvent();
         assertWithMessage("Invalid type on event %s", event).that(event.event)
                 .isEqualTo(EVENT_INPUT_UNAVAILABLE);
@@ -171,7 +173,7 @@
     /**
      * Convenience method to assert an UI unavailable event for the given view was received.
      */
-    void assertUiUnavailableEvent(View expectedView, int expectedChildId)
+    public void assertUiUnavailableEvent(View expectedView, int expectedChildId)
             throws InterruptedException {
         final MyEvent event = assertUiUnavailableEvent(expectedView);
         assertWithMessage("Invalid child on event %s", event).that(event.childId)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AbstractLoginNotImportantForAutofillTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AbstractLoginNotImportantForAutofillTestCase.java
new file mode 100644
index 0000000..859b1e2
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AbstractLoginNotImportantForAutofillTestCase.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 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 android.autofillservice.cts.augmented;
+
+import static android.autofillservice.cts.augmented.AugmentedHelper.assertBasicRequestInfo;
+import static android.autofillservice.cts.augmented.CannedAugmentedFillResponse.NO_AUGMENTED_RESPONSE;
+
+import android.autofillservice.cts.LoginNotImportantForAutofillActivity;
+import android.autofillservice.cts.augmented.CtsAugmentedAutofillService.AugmentedFillRequest;
+import android.support.test.uiautomator.UiObject2;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.EditText;
+
+import org.junit.Test;
+
+abstract class AbstractLoginNotImportantForAutofillTestCase<A extends
+        LoginNotImportantForAutofillActivity> extends
+        AugmentedAutofillAutoActivityLaunchTestCase<A> {
+
+    protected A mActivity;
+
+    @Test
+    public void testAutofill_none() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText username = mActivity.getUsername();
+        final AutofillValue expectedFocusedValue = username.getAutofillValue();
+        final AutofillId expectedFocusedId = username.getAutofillId();
+        sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request, mActivity, expectedFocusedId, expectedFocusedValue);
+
+        // Make sure standard Autofill UI is not shown.
+        mUiBot.assertNoDatasetsEver();
+
+        // Make sure Augmented Autofill UI is not shown.
+        mAugmentedUiBot.assertUiNeverShown();
+    }
+
+    @Test
+    public void testAutofill_oneField() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText username = mActivity.getUsername();
+        final AutofillId usernameId = username.getAutofillId();
+        final AutofillValue expectedFocusedValue = username.getAutofillValue();
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude")
+                        .build(), usernameId)
+                .build());
+        mActivity.expectAutoFill("dude");
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
+
+        // Make sure standard Autofill UI is not shown.
+        mUiBot.assertNoDatasetsEver();
+
+        // Make sure Augmented Autofill UI is shown.
+        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+        // Autofill
+        ui.click();
+        mActivity.assertAutoFilled();
+        mAugmentedUiBot.assertUiGone();
+    }
+
+    @Test
+    public void testAutofill_twoFields() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText username = mActivity.getUsername();
+        final AutofillId usernameId = username.getAutofillId();
+        final AutofillValue expectedFocusedValue = username.getAutofillValue();
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude")
+                        .setField(mActivity.getPassword().getAutofillId(), "sweet")
+                        .build(), usernameId)
+                .build());
+        mActivity.expectAutoFill("dude", "sweet");
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
+
+        // Make sure standard Autofill UI is not shown.
+        mUiBot.assertNoDatasetsEver();
+
+        // Make sure Augmented Autofill UI is shown.
+        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+        // Autofill
+        ui.click();
+        mActivity.assertAutoFilled();
+        mAugmentedUiBot.assertUiGone();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
index e620969..ea61007 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
@@ -32,6 +32,7 @@
 import android.autofillservice.cts.AutofillActivityTestRule;
 import android.autofillservice.cts.Helper;
 import android.autofillservice.cts.LoginActivity;
+import android.autofillservice.cts.MyAutofillCallback;
 import android.autofillservice.cts.OneTimeCancellationSignalListener;
 import android.autofillservice.cts.augmented.CtsAugmentedAutofillService.AugmentedFillRequest;
 import android.content.ComponentName;
@@ -307,6 +308,80 @@
     }
 
     @Test
+    public void testAugmentedAutoFill_callback() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText username = mActivity.getUsername();
+        final AutofillId usernameId = username.getAutofillId();
+        final AutofillValue usernameValue = username.getAutofillValue();
+        sReplier.addResponse(NO_RESPONSE);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req1")
+                        .build(), usernameId)
+                .build());
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        final AugmentedFillRequest request1 = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request1, mActivity, usernameId, usernameValue);
+
+        // Make sure standard Autofill UI is not shown.
+        mUiBot.assertNoDatasetsEver();
+
+        // Make sure Augmented Autofill UI is shown.
+        callback.assertUiShownEvent(username);
+        mAugmentedUiBot.assertUiShown(usernameId, "req1");
+
+        // Move focus away to make sure Augmented Autofill UI is gone.
+        mActivity.onLogin(View::requestFocus);
+        mAugmentedUiBot.assertUiGone();
+        callback.assertUiHiddenEvent(username);
+
+        // Tap on password field
+        final EditText password = mActivity.getPassword();
+        final AutofillId passwordId = password.getAutofillId();
+        final AutofillValue passwordValue = password.getAutofillValue();
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req2")
+                        .build(), passwordId)
+                .build());
+        mActivity.onPassword(View::requestFocus);
+        mUiBot.assertNoDatasetsEver();
+        final AugmentedFillRequest request2 = sAugmentedReplier.getNextFillRequest();
+        assertBasicRequestInfo(request2, mActivity, passwordId, passwordValue);
+
+        callback.assertUiShownEvent(password);
+        mAugmentedUiBot.assertUiShown(passwordId, "req2");
+
+        // Tap on username again...
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude")
+                        .setField(passwordId, "sweet")
+                        .build(), usernameId)
+                .build());
+
+        mActivity.onUsername(View::requestFocus);
+        final AugmentedFillRequest request3 = sAugmentedReplier.getNextFillRequest();
+        assertBasicRequestInfo(request3, mActivity, usernameId, usernameValue);
+        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+        // ...and autofill this time
+        mActivity.expectAutoFill("dude", "sweet");
+        ui.click();
+        mActivity.assertAutoFilled();
+        mAugmentedUiBot.assertUiGone();
+        callback.assertUiHiddenEvent(password);
+    }
+
+    @Test
     public void testAugmentedAutoFill_rotateDevice() throws Exception {
         assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java
index 6d88b0b..3875561 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginNotImportantForAutofillActivityTest.java
@@ -16,26 +16,13 @@
 
 package android.autofillservice.cts.augmented;
 
-import static android.autofillservice.cts.augmented.AugmentedHelper.assertBasicRequestInfo;
-import static android.autofillservice.cts.augmented.CannedAugmentedFillResponse.NO_AUGMENTED_RESPONSE;
-
 import android.autofillservice.cts.AutofillActivityTestRule;
 import android.autofillservice.cts.LoginNotImportantForAutofillActivity;
-import android.autofillservice.cts.augmented.CtsAugmentedAutofillService.AugmentedFillRequest;
 import android.platform.test.annotations.AppModeFull;
-import android.support.test.uiautomator.UiObject2;
-import android.view.View;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
-import android.widget.EditText;
-
-import org.junit.Test;
 
 @AppModeFull(reason = "AugmentedLoginActivityTest is enough")
 public class AugmentedLoginNotImportantForAutofillActivityTest
-        extends AugmentedAutofillAutoActivityLaunchTestCase<LoginNotImportantForAutofillActivity> {
-
-    protected LoginNotImportantForAutofillActivity mActivity;
+        extends AbstractLoginNotImportantForAutofillTestCase<LoginNotImportantForAutofillActivity> {
 
     @Override
     protected AutofillActivityTestRule<LoginNotImportantForAutofillActivity> getActivityRule() {
@@ -47,103 +34,4 @@
             }
         };
     }
-
-    @Test
-    public void testAutofill_none() throws Exception {
-        // Set services
-        enableService();
-        enableAugmentedService();
-
-        // Set expectations
-        final EditText username = mActivity.getUsername();
-        final AutofillValue expectedFocusedValue = username.getAutofillValue();
-        final AutofillId expectedFocusedId = username.getAutofillId();
-        sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
-
-        // Trigger autofill
-        mActivity.onUsername(View::requestFocus);
-        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
-
-        // Assert request
-        assertBasicRequestInfo(request, mActivity, expectedFocusedId, expectedFocusedValue);
-
-        // Make sure standard Autofill UI is not shown.
-        mUiBot.assertNoDatasetsEver();
-
-        // Make sure Augmented Autofill UI is not shown.
-        mAugmentedUiBot.assertUiNeverShown();
-    }
-
-    @Test
-    public void testAutofill_oneField() throws Exception {
-        // Set services
-        enableService();
-        enableAugmentedService();
-
-        // Set expectations
-        final EditText username = mActivity.getUsername();
-        final AutofillId usernameId = username.getAutofillId();
-        final AutofillValue expectedFocusedValue = username.getAutofillValue();
-        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
-                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
-                        .setField(usernameId, "dude")
-                        .build(), usernameId)
-                .build());
-        mActivity.expectAutoFill("dude");
-
-        // Trigger autofill
-        mActivity.onUsername(View::requestFocus);
-        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
-
-        // Assert request
-        assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
-
-        // Make sure standard Autofill UI is not shown.
-        mUiBot.assertNoDatasetsEver();
-
-        // Make sure Augmented Autofill UI is shown.
-        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
-
-        // Autofill
-        ui.click();
-        mActivity.assertAutoFilled();
-        mAugmentedUiBot.assertUiGone();
-    }
-
-    @Test
-    public void testAutofill_twoFields() throws Exception {
-        // Set services
-        enableService();
-        enableAugmentedService();
-
-        // Set expectations
-        final EditText username = mActivity.getUsername();
-        final AutofillId usernameId = username.getAutofillId();
-        final AutofillValue expectedFocusedValue = username.getAutofillValue();
-        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
-                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
-                        .setField(usernameId, "dude")
-                        .setField(mActivity.getPassword().getAutofillId(), "sweet")
-                        .build(), usernameId)
-                .build());
-        mActivity.expectAutoFill("dude", "sweet");
-
-        // Trigger autofill
-        mActivity.onUsername(View::requestFocus);
-        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
-
-        // Assert request
-        assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
-
-        // Make sure standard Autofill UI is not shown.
-        mUiBot.assertNoDatasetsEver();
-
-        // Make sure Augmented Autofill UI is shown.
-        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
-
-        // Autofill
-        ui.click();
-        mActivity.assertAutoFilled();
-        mAugmentedUiBot.assertUiGone();
-    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedActivityContextTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedActivityContextTest.java
new file mode 100644
index 0000000..e4300ac
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedActivityContextTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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 android.autofillservice.cts.augmented;
+
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.LoginNotImportantForAutofillWrappedActivityContextActivity;
+import android.platform.test.annotations.AppModeFull;
+
+@AppModeFull(reason = "AugmentedLoginActivityTest is enough")
+public class AugmentedNotImportantForAutofillWrappedActivityContextTest extends
+        AbstractLoginNotImportantForAutofillTestCase<
+        LoginNotImportantForAutofillWrappedActivityContextActivity> {
+    @Override
+    protected AutofillActivityTestRule<LoginNotImportantForAutofillWrappedActivityContextActivity>
+            getActivityRule() {
+        return new AutofillActivityTestRule<
+                LoginNotImportantForAutofillWrappedActivityContextActivity>(
+                LoginNotImportantForAutofillWrappedActivityContextActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedApplicationContextTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedApplicationContextTest.java
new file mode 100644
index 0000000..efdb036
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedNotImportantForAutofillWrappedApplicationContextTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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 android.autofillservice.cts.augmented;
+
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.LoginNotImportantForAutofillWrappedApplicationContextActivity;
+import android.platform.test.annotations.AppModeFull;
+
+@AppModeFull(reason = "AugmentedLoginActivityTest is enough")
+public class AugmentedNotImportantForAutofillWrappedApplicationContextTest extends
+        AbstractLoginNotImportantForAutofillTestCase<
+        LoginNotImportantForAutofillWrappedApplicationContextActivity> {
+    @Override
+    protected AutofillActivityTestRule<
+            LoginNotImportantForAutofillWrappedApplicationContextActivity> getActivityRule() {
+        return new AutofillActivityTestRule<
+                LoginNotImportantForAutofillWrappedApplicationContextActivity>(
+                LoginNotImportantForAutofillWrappedApplicationContextActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
+    }
+}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index caae73f..8723f7e 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -156,6 +156,12 @@
         <activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivity2" />
         <activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivityWithBrokenContextWrapper" />
 
+        <activity android:name="android.server.wm.MultiDisplayClientTests$ClientTestActivity" />
+        <activity android:name="android.server.wm.MultiDisplayClientTests$NoRelaunchActivity"
+                  android:resizeableActivity="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+        />
+
         <activity android:name="android.server.wm.KeyguardLockedTests$ShowWhenLockedImeActivity" />
 
         <activity android:name="android.server.wm.lifecycle.ActivityStarterTests$StandardActivity"
@@ -277,7 +283,8 @@
         <activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
                   android:theme="@style/no_starting_window" />
         <activity android:name="android.server.wm.LocationInWindowTests$TestActivity" />
-        <activity android:name="android.server.wm.EnsureBarContrastTest$TestActivity" />
+        <activity android:name="android.server.wm.EnsureBarContrastTest$TestActivity"
+                  android:theme="@style/no_starting_window" />
         <activity android:name="android.server.wm.WindowFocusTests$PrimaryActivity" />
         <activity android:name="android.server.wm.WindowFocusTests$SecondaryActivity"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density" />
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 9ab7e8e..851943e 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -61,7 +61,7 @@
                 android:resizeableActivity="true"
                 android:allowEmbedded="true"
                 android:exported="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
         />
         <activity android:name=".NonResizeableActivity"
                 android:resizeableActivity="false"
@@ -84,7 +84,7 @@
         />
         <activity android:name=".NoRelaunchActivity"
                 android:resizeableActivity="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale|colorMode|density|touchscreen"
                 android:exported="true"
                 android:taskAffinity="nobody.but.NoRelaunchActivity"
         />
@@ -289,6 +289,8 @@
             android:exported="true"
             android:theme="@style/WallpaperTheme"
         />
+        <activity android:name=".InputMethodTestActivity"
+                android:exported="true" />
         <activity android:name=".KeyguardLockActivity"
                   android:exported="true"
         />
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 7300411..29420bf 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -185,6 +185,15 @@
     public static final ComponentName SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3 =
             component("SingleTaskInstanceDisplayActivity3");
 
+    public static final ComponentName INPUT_METHOD_TEST_ACTIVITY =
+            component("InputMethodTestActivity");
+
+    /**
+     * Action and extra key constants for {@link #INPUT_METHOD_TEST_ACTIVITY}.
+     */
+    public static class InputMethodTestActivity {
+        public static final String EXTRA_PRIVATE_IME_OPTIONS = "private_ime_options";
+    }
 
     /**
      * The keys are used for {@link TestJournalProvider} when testing wallpaper
@@ -363,6 +372,8 @@
         public static final String EXTRA_START_ACTIVITY = "start_activity";
         // Adds a click listener to finish this activity when it is clicked
         public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+        // Dismiss keyguard when activity show.
+        public static final String EXTRA_DISMISS_KEYGUARD = "dismiss_keyguard";
     }
 
     /**
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/InputMethodTestActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/InputMethodTestActivity.java
new file mode 100644
index 0000000..12d82c2
--- /dev/null
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/InputMethodTestActivity.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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 android.server.wm.app;
+
+import static android.server.wm.app.Components.InputMethodTestActivity.EXTRA_PRIVATE_IME_OPTIONS;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+import com.android.compatibility.common.util.ImeAwareEditText;
+
+public final class InputMethodTestActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final Intent intent = getIntent();
+
+        final LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+        final ImeAwareEditText editText = new ImeAwareEditText(this);
+
+        final String privateImeOption = intent.getStringExtra(EXTRA_PRIVATE_IME_OPTIONS);
+        if (privateImeOption != null) {
+            editText.setPrivateImeOptions(privateImeOption);
+        }
+        layout.addView(editText);
+        editText.scheduleShowSoftInput();
+        editText.requestFocus();
+
+        setContentView(layout);
+    }
+}
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java
index c9800fa..5f9dcc1 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PipActivity.java
@@ -25,6 +25,7 @@
 import static android.server.wm.app.Components.PipActivity.ACTION_MOVE_TO_BACK;
 import static android.server.wm.app.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP;
+import static android.server.wm.app.Components.PipActivity.EXTRA_DISMISS_KEYGUARD;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR;
@@ -39,6 +40,7 @@
 import static android.server.wm.app.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
 import static android.server.wm.app.Components.PipActivity.EXTRA_START_ACTIVITY;
 import static android.server.wm.app.Components.PipActivity.EXTRA_TAP_TO_FINISH;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 
 import android.app.Activity;
 import android.app.ActivityOptions;
@@ -119,6 +121,11 @@
             setShowWhenLocked(true);
         }
 
+        // Set the window flag to dismiss the keyguard
+        if (getIntent().hasExtra(EXTRA_DISMISS_KEYGUARD)) {
+            getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
+        }
+
         boolean enteringPip = false;
         // Enter picture in picture with the given aspect ratio if provided
         if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml b/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml
index cee8aad..75a53e3 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/backgroundactivity/AndroidTest.xml
@@ -18,7 +18,7 @@
 <configuration description="Config for CTS starting background activity test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/framework/base/windowmanager/dummyTests/Android.mk b/tests/framework/base/windowmanager/dummyTests/Android.mk
deleted file mode 100644
index 16b36e7..0000000
--- a/tests/framework/base/windowmanager/dummyTests/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2015 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests optional
-
-# TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the test APK.
-LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceTestCases
-LOCAL_SDK_VERSION := test_current
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/framework/base/windowmanager/dummyTests/AndroidManifest.xml b/tests/framework/base/windowmanager/dummyTests/AndroidManifest.xml
deleted file mode 100644
index 0ae7842..0000000
--- a/tests/framework/base/windowmanager/dummyTests/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2017 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.
- -->
-
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.server.am.cts">
-
-    <!--
-     TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the test APK.
-      -->
-    <application android:label="CtsActivityManagerDeviceTestCases">
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:label="CTS dummy tests of ActivityManager"
-        android:targetPackage="android.server.am.cts" />
-
-</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/AndroidTest.xml b/tests/framework/base/windowmanager/dummyTests/AndroidTest.xml
deleted file mode 100644
index e7efca8..0000000
--- a/tests/framework/base/windowmanager/dummyTests/AndroidTest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<configuration description="Config for dummy CTS ActivityManager test cases">
-    <option name="test-suite-tag" value="cts" />
-    <option name="config-descriptor:metadata" key="component" value="framework" />
-    <!-- This module needs a permission not available to instant apps -->
-    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
-    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck"/>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <!--
-             TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the
-             test APK.
-        -->
-        <option name="test-file-name" value="CtsActivityManagerDeviceTestCases.apk" />
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="android.server.am.cts"/>
-        <option name="runtime-hint" value="1m"/>
-    </test>
-</configuration>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/Android.mk b/tests/framework/base/windowmanager/dummyTests/dummySdk25/Android.mk
deleted file mode 100644
index 510ef5a..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Copyright (C) 2019 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests optional
-
-# TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk25TestCases is renamed to
-# CtsWindowManagerSdk25TestCases
-LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk25TestCases
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 25
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidManifest.xml b/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidManifest.xml
deleted file mode 100644
index 3ca9d97..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.server.wm.cts.dummy25">
-
-    <uses-sdk android:targetSdkVersion="25" />
-
-    <!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk25TestCases is renamed to
-         CtsWindowManagerSdk25TestCases -->
-    <application android:label="CtsActivityManagerDeviceSdk25TestCases">
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="android.server.wm.cts.dummy25" />
-
-</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidTest.xml b/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidTest.xml
deleted file mode 100644
index 2d9e0fe..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 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.
--->
-
-<!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk25TestCases is renamed to
-         CtsWindowManagerSdk25TestCases -->
-<configuration description="Config for CTS WindowManager dummy SDK 25 compatibility test cases">
-    <option name="test-suite-tag" value="cts" />
-    <option name="config-descriptor:metadata" key="component" value="framework" />
-    <!-- These tests require targeting API 25 which does not support instant apps -->
-    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
-    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsActivityManagerDeviceSdk25TestCases.apk" />
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="android.server.wm.cts.dummy25" />
-        <option name="runtime-hint" value="1m" />
-    </test>
-</configuration>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk25/src/android/server/wm/DummySdk25Test.java b/tests/framework/base/windowmanager/dummyTests/dummySdk25/src/android/server/wm/DummySdk25Test.java
deleted file mode 100644
index 6d960f1..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk25/src/android/server/wm/DummySdk25Test.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 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 android.server.wm;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class DummySdk25Test {
-
-    @Test
-    @Ignore
-    public void dummyTest() {
-        // TODO(b/129909356): Rename CtsActivityManagerDeviceSdk25TestCases to
-        // CtsWindowManagerSdk25TestCases.
-    }
-}
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/Android.mk b/tests/framework/base/windowmanager/dummyTests/dummySdk28/Android.mk
deleted file mode 100644
index 5042de3..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2019 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests optional
-
-# TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
-# CtsWindowManagerSdk28TestCases
-LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk28TestCases
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 28
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidManifest.xml b/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidManifest.xml
deleted file mode 100644
index 7579028..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.server.wm.cts.dummy28">
-
-    <uses-sdk android:targetSdkVersion="28" />
-
-    <!-- TODO(b/129909356): Remove this once CtsActivityManagerDeviceSdk28TestCases is renamed to
-         CtsWindowManagerSdk28TestCases -->
-    <application android:label="CtsActivityManagerDeviceSdk28TestCases">
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="android.server.wm.cts.dummy28" />
-
-</manifest>
diff --git a/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java b/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java
deleted file mode 100644
index 991d082..0000000
--- a/tests/framework/base/windowmanager/dummyTests/dummySdk28/src/android/server/wm/DummySdk28Test.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 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 android.server.wm;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class DummySdk28Test {
-
-    @Test
-    @Ignore
-    public void dummyTest() {
-        // TODO(b/129909356): Rename CtsActivityManagerDeviceSdk28TestCases to
-        // CtsWindowManagerSdk28TestCases.
-    }
-}
diff --git a/tests/framework/base/windowmanager/dummyTests/src/android/server/am/DummyTest.java b/tests/framework/base/windowmanager/dummyTests/src/android/server/am/DummyTest.java
deleted file mode 100644
index 73f138c..0000000
--- a/tests/framework/base/windowmanager/dummyTests/src/android/server/am/DummyTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package android.server.am;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class DummyTest {
-
-    @Test
-    @Ignore
-    public void dummyTest() {
-        // TODO(b/130526034): Cleanup CtsActivityManagerDeviceTestCases, once no one refer the
-        // test APK.
-    }
-}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
index 714bc84..f39422c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
@@ -18,11 +18,16 @@
 
 import static android.server.wm.ActivityManagerState.STATE_RESUMED;
 import static android.server.wm.ActivityManagerState.STATE_STOPPED;
+import static android.server.wm.app.Components.INPUT_METHOD_TEST_ACTIVITY;
+import static android.server.wm.app.Components.InputMethodTestActivity.EXTRA_PRIVATE_IME_OPTIONS;
 import static android.server.wm.app.Components.TEST_ACTIVITY;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
@@ -31,6 +36,7 @@
 import android.app.Instrumentation;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -42,17 +48,23 @@
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Build/Install/Run:
  *      atest CtsWindowManagerDeviceTestCases:ActivityViewTest
  */
 @Presubmit
 public class ActivityViewTest extends ActivityManagerTestBase {
+    private static final long IME_EVENT_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
 
     private Instrumentation mInstrumentation;
     private ActivityView mActivityView;
@@ -173,10 +185,41 @@
         assertLifecycleCounts(TEST_ACTIVITY, 0, 1, 1, 0, 0, 0, CountSpec.DONT_CARE);
     }
 
+    @Test
+    public void testInputMethod() throws Exception {
+        assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+                mInstrumentation.getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_INPUT_METHODS));
+
+        final String uniqueKey =
+                ActivityViewTest.class.getSimpleName() + "/" + SystemClock.elapsedRealtimeNanos();
+
+        final String privateImeOptions = uniqueKey + "/privateImeOptions";
+
+        final Bundle extras = new Bundle();
+        extras.putString(EXTRA_PRIVATE_IME_OPTIONS, privateImeOptions);
+
+        try (final MockImeSession imeSession = MockImeSession.create(mContext,
+                mInstrumentation.getUiAutomation(), new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+            launchActivityInActivityView(INPUT_METHOD_TEST_ACTIVITY, extras);
+
+            // IME's seeing uniqueStringValue means that a valid connection is successfully
+            // established from INPUT_METHOD_TEST_ACTIVITY the MockIme
+            expectEvent(stream, editorMatcher("onStartInput", privateImeOptions),
+                    IME_EVENT_TIMEOUT);
+        }
+    }
+
     private void launchActivityInActivityView(ComponentName activity) {
+        launchActivityInActivityView(activity, new Bundle());
+    }
+
+    private void launchActivityInActivityView(ComponentName activity, Bundle extras) {
         Intent intent = new Intent();
         intent.setComponent(activity);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        intent.putExtras(extras);
         SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.startActivity(intent));
         mAmWmState.waitForValidState(activity);
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
index e4408f5..f0e0b9e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
@@ -23,6 +23,7 @@
 import static android.server.wm.app.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
 import static android.server.wm.app.Components.PIP_ACTIVITY;
 import static android.server.wm.app.Components.PipActivity.ACTION_ENTER_PIP;
+import static android.server.wm.app.Components.PipActivity.EXTRA_DISMISS_KEYGUARD;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
 import static android.server.wm.app.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
 import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ACTIVITY;
@@ -30,11 +31,8 @@
 import static android.server.wm.app.Components.TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
-
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
-
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
@@ -47,16 +45,13 @@
 import android.platform.test.annotations.Presubmit;
 import android.widget.EditText;
 import android.widget.LinearLayout;
-
 import com.android.cts.mockime.ImeEventStream;
 import com.android.cts.mockime.ImeSettings;
 import com.android.cts.mockime.MockImeSession;
-
+import java.util.concurrent.TimeUnit;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.concurrent.TimeUnit;
-
 /**
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:KeyguardLockedTests
@@ -295,6 +290,27 @@
     }
 
     @Test
+    public void testDismissKeyguardPipActivity() throws Exception {
+        assumeTrue(supportsPip());
+
+        try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
+            // Show an activity in PIP
+            launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true", EXTRA_DISMISS_KEYGUARD, "true");
+            waitForEnterPip(PIP_ACTIVITY);
+            mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD);
+            mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+
+            // Lock the screen and ensure the PiP activity is not visible on the lockscreen even
+            // though it's marked as dismiss keyguard.
+            lockScreenSession.gotoKeyguard();
+            mAmWmState.computeState(true);
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+        }
+    }
+
+    @Test
     public void testShowWhenLockedAttrImeActivityAndShowSoftInput() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession();
              // Leverage MockImeSession to ensure at least an IME exists as default.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
new file mode 100644
index 0000000..cbb8d5c
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 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 android.server.wm;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.wm.CommandSession.ActivityCallback.ON_CONFIGURATION_CHANGED;
+import static android.server.wm.CommandSession.ActivityCallback.ON_RESUME;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:MultiDisplayClientTests
+ */
+@Presubmit
+public class MultiDisplayClientTests extends MultiDisplayTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        assumeTrue(supportsMultiDisplay());
+    }
+
+    @Test
+    @FlakyTest(bugId = 130260102, detail = "Promote to presubmit once proved stable")
+    public void testDisplayIdUpdateOnMove_RelaunchActivity() throws Exception {
+        testDisplayIdUpdateOnMove(ClientTestActivity.class, false /* handlesConfigChange */);
+    }
+
+    @Test
+    @FlakyTest(bugId = 130260102, detail = "Promote to presubmit once proved stable")
+    public void testDisplayIdUpdateOnMove_NoRelaunchActivity() throws Exception {
+        testDisplayIdUpdateOnMove(NoRelaunchActivity.class, true /* handlesConfigChange */);
+    }
+
+    private void testDisplayIdUpdateOnMove(Class<? extends Activity> activityClass,
+            boolean handlesConfigChange) throws Exception {
+        final ActivityTestRule activityTestRule = new ActivityTestRule(
+                activityClass, true /* initialTouchMode */, false /* launchActivity */);
+
+        // Launch activity display.
+        separateTestJournal();
+        Activity activity = activityTestRule.launchActivity(new Intent());
+        final ComponentName activityName = getComponentName(activityClass);
+        waitAndAssertResume(activityName);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display
+            final ActivityManagerState.ActivityDisplay newDisplay =
+                    virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+            // Move the activity to the new secondary display.
+            separateTestJournal();
+            final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+            launchOptions.setLaunchDisplayId(newDisplay.mId);
+            final Intent newDisplayIntent = new Intent(mContext, activityClass);
+            newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+            getInstrumentation().getTargetContext().startActivity(newDisplayIntent,
+                    launchOptions.toBundle());
+            waitAndAssertTopResumedActivity(activityName, newDisplay.mId,
+                    "Activity moved to secondary display must be focused");
+
+            if (handlesConfigChange) {
+                // Wait for activity to receive the configuration change after move
+                waitAndAssertConfigurationChange(activityName);
+            } else {
+                // Activity will be re-created, wait for resumed state
+                waitAndAssertResume(activityName);
+                activity = activityTestRule.getActivity();
+            }
+            final String message = "Display id must be updated";
+            assertEquals(message, newDisplay.mId, activity.getDisplayId());
+            assertEquals(message, newDisplay.mId, activity.getDisplay().getDisplayId());
+            final WindowManager wm = activity.getWindowManager();
+            assertEquals(message, newDisplay.mId, wm.getDefaultDisplay().getDisplayId());
+        }
+    }
+
+    private static ComponentName getComponentName(Class<? extends Activity> activity) {
+        return new ComponentName(getInstrumentation().getContext(), activity);
+    }
+
+    private void waitAndAssertConfigurationChange(ComponentName activityName) {
+        mAmWmState.waitForWithAmState((state) ->
+                        getCallbackCount(activityName, ON_CONFIGURATION_CHANGED) == 1,
+                "waitForConfigurationChange");
+        assertEquals("Must receive a single configuration change", 1,
+                getCallbackCount(activityName, ON_CONFIGURATION_CHANGED));
+    }
+
+    private void waitAndAssertResume(ComponentName activityName) {
+        mAmWmState.waitForWithAmState((state) ->
+                getCallbackCount(activityName, ON_RESUME) == 1, "waitForResume");
+        assertEquals("Must be resumed once", 1, getCallbackCount(activityName, ON_RESUME));
+    }
+
+    private int getCallbackCount(ComponentName activityName,
+            CommandSession.ActivityCallback callback) {
+        final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(activityName);
+        return lifecycles.getCount(callback);
+    }
+
+    public static class ClientTestActivity extends CommandSession.BasicTestActivity { }
+
+    public static class NoRelaunchActivity extends CommandSession.BasicTestActivity { }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index d988c903..317f298 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -57,6 +57,7 @@
 import android.text.TextUtils;
 import android.view.WindowManager;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 import android.widget.LinearLayout;
@@ -64,6 +65,7 @@
 import com.android.compatibility.common.util.ImeAwareEditText;
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.compatibility.common.util.TestUtils;
+import com.android.cts.mockime.ImeCommand;
 import com.android.cts.mockime.ImeEvent;
 import com.android.cts.mockime.ImeEventStream;
 import com.android.cts.mockime.ImeSettings;
@@ -211,11 +213,13 @@
     @Test
     public void testNavBarNotShowingOnDisplayWithoutDecor() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Wait navigation bar show on default display and record the states.
+            mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
+            final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
+
             virtualDisplaySession.setPublicDisplay(true)
                     .setShowSystemDecorations(false).createDisplay();
 
-            final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
-
             waitAndAssertNavBarStatesAreTheSame(expected);
         }
     }
@@ -227,11 +231,13 @@
     @Test
     public void testNavBarNotShowingOnPrivateDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Wait navigation bar show on default display and record the states.
+            mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
+            final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
+
             virtualDisplaySession.setPublicDisplay(false)
                     .setShowSystemDecorations(true).createDisplay();
 
-            final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
-
             waitAndAssertNavBarStatesAreTheSame(expected);
         }
     }
@@ -252,6 +258,7 @@
             // This display has finished his task. Just close it.
         }
 
+        mAmWmState.computeState(true);
         final List<WindowState> result = mAmWmState.getWmState().getAllNavigationBarStates();
 
         assertEquals("The number of nav bars should be the same", expected.size(), result.size());
@@ -516,11 +523,12 @@
     }
 
     /**
-     * Test that the IME should be shown in default display and verify committed texts can deliver
-     * to target display which does not support system decoration.
+     * Test that the IME can be shown in a different display (actually the default display) than
+     * the display on which the target IME application is shown.  Then test several basic operations
+     * in {@link InputConnection}.
      */
     @Test
-    public void testImeShowAndCommitTextsInDefaultDisplayWhenNoSysDecor() throws Exception {
+    public void testCrossDisplayBasicImeOperations() throws Exception {
         final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
 
         try (final VirtualDisplaySession virtualDisplaySession  = new VirtualDisplaySession();
@@ -559,6 +567,13 @@
             imeTestActivitySession.runOnMainAndAssertWithTimeout(
                     () -> TextUtils.equals(commitText, editText.getText()), TIMEOUT,
                     "The input text should be delivered");
+
+            // Since the IME and the IME target app are running in different displays,
+            // InputConnection#requestCursorUpdates() is not supported and it should return false.
+            // See InputMethodServiceTest#testOnUpdateCursorAnchorInfo() for the normal scenario.
+            final ImeCommand callCursorUpdates = mockImeSession.callRequestCursorUpdates(
+                    InputConnection.CURSOR_UPDATE_IMMEDIATE);
+            assertFalse(expectCommand(stream, callCursorUpdates, TIMEOUT).getReturnBooleanValue());
         }
     }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index acfc048..6caf3be 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -236,6 +236,10 @@
     protected void tapOnDisplayCenter(int displayId) {
         final Rect bounds = mAmWmState.getWmState().getDisplay(displayId).getDisplayRect();
         tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId);
+        // This is needed after a tap in multi-display to ensure that the display focus has really
+        // changed, if needed. The call to syncInputTransaction will wait until focus change has
+        // propagated from WMS to native input before returning.
+        getInstrumentation().getUiAutomation().syncInputTransactions();
     }
 
     public class VirtualDisplaySession implements AutoCloseable {
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 878890d..5c40207 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -44,6 +44,7 @@
 import android.view.WindowInsets;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputBinding;
@@ -518,6 +519,12 @@
         return getTracer().onKeyDown(keyCode, event, () -> super.onKeyDown(keyCode, event));
     }
 
+    @Override
+    public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+        getTracer().onUpdateCursorAnchorInfo(cursorAnchorInfo,
+                () -> super.onUpdateCursorAnchorInfo(cursorAnchorInfo));
+    }
+
     @CallSuper
     public boolean onEvaluateInputViewShown() {
         return getTracer().onEvaluateInputViewShown(() -> {
@@ -735,6 +742,13 @@
             return recordEventInternal("onKeyDown", supplier::getAsBoolean, arguments);
         }
 
+        public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo,
+                @NonNull Runnable runnable) {
+            final Bundle arguments = new Bundle();
+            arguments.putParcelable("cursorAnchorInfo", cursorAnchorInfo);
+            recordEventInternal("onUpdateCursorAnchorInfo", runnable, arguments);
+        }
+
         public boolean onShowInputRequested(int flags, boolean configChange,
                 @NonNull BooleanSupplier supplier) {
             final Bundle arguments = new Bundle();
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/CursorAnchorInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/CursorAnchorInfoTest.java
new file mode 100644
index 0000000..466a7cd
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/CursorAnchorInfoTest.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2019 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 android.view.inputmethod.cts;
+
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.CursorAnchorInfo.Builder;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CursorAnchorInfoTest {
+    private static final float EPSILON = 0.0000001f;
+
+    private static final RectF[] MANY_BOUNDS = new RectF[] {
+            new RectF(101.0f, 201.0f, 301.0f, 401.0f),
+            new RectF(102.0f, 202.0f, 302.0f, 402.0f),
+            new RectF(103.0f, 203.0f, 303.0f, 403.0f),
+            new RectF(104.0f, 204.0f, 304.0f, 404.0f),
+            new RectF(105.0f, 205.0f, 305.0f, 405.0f),
+            new RectF(106.0f, 206.0f, 306.0f, 406.0f),
+            new RectF(107.0f, 207.0f, 307.0f, 407.0f),
+            new RectF(108.0f, 208.0f, 308.0f, 408.0f),
+            new RectF(109.0f, 209.0f, 309.0f, 409.0f),
+            new RectF(110.0f, 210.0f, 310.0f, 410.0f),
+            new RectF(111.0f, 211.0f, 311.0f, 411.0f),
+            new RectF(112.0f, 212.0f, 312.0f, 412.0f),
+            new RectF(113.0f, 213.0f, 313.0f, 413.0f),
+            new RectF(114.0f, 214.0f, 314.0f, 414.0f),
+            new RectF(115.0f, 215.0f, 315.0f, 415.0f),
+            new RectF(116.0f, 216.0f, 316.0f, 416.0f),
+            new RectF(117.0f, 217.0f, 317.0f, 417.0f),
+            new RectF(118.0f, 218.0f, 318.0f, 418.0f),
+            new RectF(119.0f, 219.0f, 319.0f, 419.0f),
+    };
+    private static final int[] MANY_FLAGS_ARRAY = new int[] {
+        FLAG_HAS_INVISIBLE_REGION,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_INVISIBLE_REGION,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
+    };
+
+    @Test
+    public void testBuilder() {
+        final int selectionStart = 30;
+        final int selectionEnd = 40;
+        final int composingTextStart = 32;
+        final String composingText = "test";
+        final int insertionMarkerFlags =
+                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
+        final float insertionMarkerHorizontal = 10.5f;
+        final float insertionMarkerTop = 100.1f;
+        final float insertionMarkerBaseline = 110.4f;
+        final float insertionMarkerBottom = 111.0f;
+
+        Matrix transformMatrix = new Matrix();
+        transformMatrix.setScale(10.0f, 20.0f);
+
+        final Builder builder = new Builder();
+        builder.setSelectionRange(selectionStart, selectionEnd)
+                .setComposingText(composingTextStart, composingText)
+                .setInsertionMarkerLocation(insertionMarkerHorizontal, insertionMarkerTop,
+                        insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags)
+                .setMatrix(transformMatrix);
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF bounds = MANY_BOUNDS[i];
+            final int flags = MANY_FLAGS_ARRAY[i];
+            builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
+                    flags);
+        }
+
+        final CursorAnchorInfo info = builder.build();
+        assertEquals(selectionStart, info.getSelectionStart());
+        assertEquals(selectionEnd, info.getSelectionEnd());
+        assertEquals(composingTextStart, info.getComposingTextStart());
+        assertTrue(TextUtils.equals(composingText, info.getComposingText()));
+        assertEquals(insertionMarkerFlags, info.getInsertionMarkerFlags());
+        assertEquals(insertionMarkerHorizontal, info.getInsertionMarkerHorizontal(), EPSILON);
+        assertEquals(insertionMarkerTop, info.getInsertionMarkerTop(), EPSILON);
+        assertEquals(insertionMarkerBaseline, info.getInsertionMarkerBaseline(), EPSILON);
+        assertEquals(insertionMarkerBottom, info.getInsertionMarkerBottom(), EPSILON);
+        assertEquals(transformMatrix, info.getMatrix());
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF expectedBounds = MANY_BOUNDS[i];
+            assertEquals(expectedBounds, info.getCharacterBounds(i));
+        }
+        assertNull(info.getCharacterBounds(-1));
+        assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1));
+        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
+            final int expectedFlags = MANY_FLAGS_ARRAY[i];
+            assertEquals(expectedFlags, info.getCharacterBoundsFlags(i));
+        }
+        assertEquals(0, info.getCharacterBoundsFlags(-1));
+        assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+
+        // Make sure that the builder can reproduce the same object.
+        final CursorAnchorInfo info2 = builder.build();
+        assertEquals(selectionStart, info2.getSelectionStart());
+        assertEquals(selectionEnd, info2.getSelectionEnd());
+        assertEquals(composingTextStart, info2.getComposingTextStart());
+        assertTrue(TextUtils.equals(composingText, info2.getComposingText()));
+        assertEquals(insertionMarkerFlags, info2.getInsertionMarkerFlags());
+        assertEquals(insertionMarkerHorizontal, info2.getInsertionMarkerHorizontal(), EPSILON);
+        assertEquals(insertionMarkerTop, info2.getInsertionMarkerTop(), EPSILON);
+        assertEquals(insertionMarkerBaseline, info2.getInsertionMarkerBaseline(), EPSILON);
+        assertEquals(insertionMarkerBottom, info2.getInsertionMarkerBottom(), EPSILON);
+        assertEquals(transformMatrix, info2.getMatrix());
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF expectedBounds = MANY_BOUNDS[i];
+            assertEquals(expectedBounds, info2.getCharacterBounds(i));
+        }
+        assertNull(info2.getCharacterBounds(-1));
+        assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1));
+        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
+            final int expectedFlags = MANY_FLAGS_ARRAY[i];
+            assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i));
+        }
+        assertEquals(0, info2.getCharacterBoundsFlags(-1));
+        assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+        assertEquals(info, info2);
+        assertEquals(info.hashCode(), info2.hashCode());
+
+        // Make sure that object can be marshaled via Parcel.
+        final CursorAnchorInfo info3 = cloneViaParcel(info2);
+        assertEquals(selectionStart, info3.getSelectionStart());
+        assertEquals(selectionEnd, info3.getSelectionEnd());
+        assertEquals(composingTextStart, info3.getComposingTextStart());
+        assertTrue(TextUtils.equals(composingText, info3.getComposingText()));
+        assertEquals(insertionMarkerFlags, info3.getInsertionMarkerFlags());
+        assertEquals(insertionMarkerHorizontal, info3.getInsertionMarkerHorizontal(), EPSILON);
+        assertEquals(insertionMarkerTop, info3.getInsertionMarkerTop(), EPSILON);
+        assertEquals(insertionMarkerBaseline, info3.getInsertionMarkerBaseline(), EPSILON);
+        assertEquals(insertionMarkerBottom, info3.getInsertionMarkerBottom(), EPSILON);
+        assertEquals(transformMatrix, info3.getMatrix());
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF expectedBounds = MANY_BOUNDS[i];
+            assertEquals(expectedBounds, info3.getCharacterBounds(i));
+        }
+        assertNull(info3.getCharacterBounds(-1));
+        assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1));
+        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
+            final int expectedFlags = MANY_FLAGS_ARRAY[i];
+            assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i));
+        }
+        assertEquals(0, info3.getCharacterBoundsFlags(-1));
+        assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+        assertEquals(info.hashCode(), info3.hashCode());
+
+        builder.reset();
+        final CursorAnchorInfo uninitializedInfo = builder.build();
+        assertEquals(-1, uninitializedInfo.getSelectionStart());
+        assertEquals(-1, uninitializedInfo.getSelectionEnd());
+        assertEquals(-1, uninitializedInfo.getComposingTextStart());
+        assertNull(uninitializedInfo.getComposingText());
+        assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
+        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
+        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
+        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
+        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
+        assertEquals(new Matrix(), uninitializedInfo.getMatrix());
+    }
+
+    @Test
+    public void testEquality() {
+        final Matrix matrix1 = new Matrix();
+        matrix1.setTranslate(10.0f, 20.0f);
+        final Matrix matrix2 = new Matrix();
+        matrix2.setTranslate(110.0f, 120.0f);
+        final Matrix nanMatrix = new Matrix();
+        nanMatrix.setValues(new float[]{
+                Float.NaN, Float.NaN, Float.NaN,
+                Float.NaN, Float.NaN, Float.NaN,
+                Float.NaN, Float.NaN, Float.NaN});
+        final int selectionStart1 = 2;
+        final int selectionEnd1 = 7;
+        final String composingText1 = "0123456789";
+        final int composingTextStart1 = 0;
+        final int insertionMarkerFlags1 = FLAG_HAS_VISIBLE_REGION;
+        final float insertionMarkerHorizontal1 = 10.5f;
+        final float insertionMarkerTop1 = 100.1f;
+        final float insertionMarkerBaseline1 = 110.4f;
+        final float insertionMarkerBottom1 = 111.0f;
+        final int selectionStart2 = 4;
+        final int selectionEnd2 = 8;
+        final String composingText2 = "9876543210";
+        final int composingTextStart2 = 3;
+        final int insertionMarkerFlags2 =
+                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
+        final float insertionMarkerHorizontal2 = 14.5f;
+        final float insertionMarkerTop2 = 200.1f;
+        final float insertionMarkerBaseline2 = 210.4f;
+        final float insertionMarkerBottom2 = 211.0f;
+
+        // Default instance should be equal.
+        assertEquals(new Builder().build(), new Builder().build());
+
+        assertEquals(
+                new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+                new Builder().setSelectionRange(selectionStart1, selectionEnd1).build());
+        assertNotEquals(
+                new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+                new Builder().setSelectionRange(selectionStart1, selectionEnd2).build());
+        assertNotEquals(
+                new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+                new Builder().setSelectionRange(selectionStart2, selectionEnd1).build());
+        assertNotEquals(
+                new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(),
+                new Builder().setSelectionRange(selectionStart2, selectionEnd2).build());
+        assertEquals(
+                new Builder().setComposingText(composingTextStart1, composingText1).build(),
+                new Builder().setComposingText(composingTextStart1, composingText1).build());
+        assertNotEquals(
+                new Builder().setComposingText(composingTextStart1, composingText1).build(),
+                new Builder().setComposingText(composingTextStart2, composingText1).build());
+        assertNotEquals(
+                new Builder().setComposingText(composingTextStart1, composingText1).build(),
+                new Builder().setComposingText(composingTextStart1, composingText2).build());
+        assertNotEquals(
+                new Builder().setComposingText(composingTextStart1, composingText1).build(),
+                new Builder().setComposingText(composingTextStart2, composingText2).build());
+
+        // For insertion marker locations, Float#NaN is treated as if it was a number.
+        assertEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        Float.NaN, Float.NaN, Float.NaN, Float.NaN,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        Float.NaN, Float.NaN, Float.NaN, Float.NaN,
+                        insertionMarkerFlags1).build());
+
+        // Check Matrix.
+        assertEquals(
+                new Builder().setMatrix(matrix1).build(),
+                new Builder().setMatrix(matrix1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).build(),
+                new Builder().setMatrix(matrix2).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).build(),
+                new Builder().setMatrix(nanMatrix).build());
+        // Unlike insertion marker locations, Float#NaN in the matrix is treated as just a NaN as
+        // usual (NaN == NaN -> false).
+        assertNotEquals(
+                new Builder().setMatrix(nanMatrix).build(),
+                new Builder().setMatrix(nanMatrix).build());
+
+        assertEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        Float.NaN, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal2, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop2,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline2, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal2, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom2,
+                        insertionMarkerFlags1).build());
+        assertNotEquals(
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags1).build(),
+                new Builder().setMatrix(matrix1).setInsertionMarkerLocation(
+                        insertionMarkerHorizontal1, insertionMarkerTop1,
+                        insertionMarkerBaseline1, insertionMarkerBottom1,
+                        insertionMarkerFlags2).build());
+    }
+
+    @Test
+    public void testMatrixIsCopied() {
+        final Matrix matrix1 = new Matrix();
+        matrix1.setTranslate(10.0f, 20.0f);
+        final Matrix matrix2 = new Matrix();
+        matrix2.setTranslate(110.0f, 120.0f);
+        final Matrix matrix3 = new Matrix();
+        matrix3.setTranslate(210.0f, 220.0f);
+        final Matrix matrix = new Matrix();
+        final Builder builder = new Builder();
+
+        matrix.set(matrix1);
+        builder.setMatrix(matrix);
+        matrix.postRotate(90.0f);
+
+        final CursorAnchorInfo firstInstance = builder.build();
+        assertEquals(matrix1, firstInstance.getMatrix());
+        matrix.set(matrix2);
+        builder.setMatrix(matrix);
+        final CursorAnchorInfo secondInstance = builder.build();
+        assertEquals(matrix1, firstInstance.getMatrix());
+        assertEquals(matrix2, secondInstance.getMatrix());
+
+        matrix.set(matrix3);
+        assertEquals(matrix1, firstInstance.getMatrix());
+        assertEquals(matrix2, secondInstance.getMatrix());
+    }
+
+    @Test
+    public void testMatrixIsRequired() {
+        final int selectionStart = 30;
+        final int selectionEnd = 40;
+        final int composingTextStart = 32;
+        final String composingText = "test";
+        final int insertionMarkerFlags = FLAG_HAS_VISIBLE_REGION;
+        final float insertionMarkerHorizontal = 10.5f;
+        final float insertionMarkerTop = 100.1f;
+        final float insertionMarkerBaseline = 110.4f;
+        final float insertionMarkerBottom = 111.0f;
+        Matrix transformMatrix = new Matrix();
+        transformMatrix.setScale(10.0f, 20.0f);
+
+        final Builder builder = new Builder();
+        // Check twice to make sure if Builder#reset() works as expected.
+        for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
+            builder.setSelectionRange(selectionStart, selectionEnd)
+                    .setComposingText(composingTextStart, composingText);
+            try {
+                // Should succeed as coordinate transformation matrix is not required if no
+                // positional information is specified.
+                builder.build();
+            } catch (IllegalArgumentException ex) {
+                fail();
+            }
+
+            builder.setInsertionMarkerLocation(insertionMarkerHorizontal, insertionMarkerTop,
+                    insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
+            try {
+                // Coordinate transformation matrix is required if no positional information is
+                // specified.
+                builder.build();
+                fail();
+            } catch (IllegalArgumentException ex) {
+            }
+
+            builder.setMatrix(transformMatrix);
+            try {
+                // Should succeed as coordinate transformation matrix is required.
+                builder.build();
+            } catch (IllegalArgumentException ex) {
+                fail();
+            }
+
+            builder.reset();
+        }
+    }
+
+    @Test
+    public void testBuilderAddCharacterBounds() {
+        // A negative index should be rejected.
+        try {
+            new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
+            fail();
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
+    private static CursorAnchorInfo cloneViaParcel(CursorAnchorInfo src) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            src.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            return new CursorAnchorInfo(parcel);
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java
index b9103ef..0b20b85 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceTest.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
 import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
+import static android.view.inputmethod.cts.util.TestUtils.runOnMainSync;
 import static android.view.inputmethod.cts.util.TestUtils.waitOnMainUntil;
 
 import static com.android.cts.mockime.ImeEventStreamTestUtils.EventFilterMode.CHECK_EXIT_EVENT_ONLY;
@@ -33,14 +34,17 @@
 import static org.junit.Assert.fail;
 
 import android.app.Instrumentation;
+import android.graphics.Matrix;
 import android.inputmethodservice.InputMethodService;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.cts.util.EndToEndImeTestBase;
 import android.view.inputmethod.cts.util.TestActivity;
 import android.view.inputmethod.cts.util.TestUtils;
@@ -64,6 +68,7 @@
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Predicate;
 
@@ -287,4 +292,76 @@
                     expectedKeyCode, uptimeStart, uptimeEnd);
         }
     }
+
+    /**
+     * Ensure that {@link InputConnection#requestCursorUpdates(int)} works for the built-in
+     * {@link EditText} and {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}
+     * will be called back.
+     */
+    @Test
+    public void testOnUpdateCursorAnchorInfo() throws Exception {
+        try (MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final String marker =
+                    "testOnUpdateCursorAnchorInfo()/" + SystemClock.elapsedRealtimeNanos();
+
+            final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+            final AtomicInteger requestCursorUpdatesCallCount = new AtomicInteger();
+            TestActivity.startSync(activity -> {
+                final LinearLayout layout = new LinearLayout(activity);
+                layout.setOrientation(LinearLayout.VERTICAL);
+
+                final EditText editText = new EditText(activity) {
+                    @Override
+                    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+                        final InputConnection original = super.onCreateInputConnection(outAttrs);
+                        return new InputConnectionWrapper(original, false) {
+                            @Override
+                            public boolean requestCursorUpdates(int cursorUpdateMode) {
+                                if (cursorUpdateMode == InputConnection.CURSOR_UPDATE_IMMEDIATE) {
+                                    requestCursorUpdatesCallCount.incrementAndGet();
+                                    return true;
+                                }
+                                return false;
+                            }
+                        };
+                    }
+                };
+                editTextRef.set(editText);
+                editText.setPrivateImeOptions(marker);
+                layout.addView(editText);
+                editText.requestFocus();
+                return layout;
+            });
+            final EditText editText = editTextRef.get();
+
+            final ImeEventStream stream = imeSession.openEventStream();
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+
+            // Make sure that InputConnection#requestCursorUpdates() returns true.
+            assertTrue(expectCommand(stream,
+                    imeSession.callRequestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE),
+                    TIMEOUT).getReturnBooleanValue());
+
+            // Also make sure that requestCursorUpdates() actually gets called only once.
+            assertEquals(1, requestCursorUpdatesCallCount.get());
+
+            final CursorAnchorInfo originalCursorAnchorInfo = new CursorAnchorInfo.Builder()
+                    .setMatrix(new Matrix())
+                    .setInsertionMarkerLocation(3.0f, 4.0f, 5.0f, 6.0f, 0)
+                    .setSelectionRange(7, 8)
+                    .build();
+
+            runOnMainSync(() -> editText.getContext().getSystemService(InputMethodManager.class)
+                    .updateCursorAnchorInfo(editText, originalCursorAnchorInfo));
+
+            final CursorAnchorInfo receivedCursorAnchorInfo = expectEvent(stream,
+                    event -> "onUpdateCursorAnchorInfo".equals(event.getEventName()),
+                    TIMEOUT).getArguments().getParcelable("cursorAnchorInfo");
+            assertNotNull(receivedCursorAnchorInfo);
+            assertEquals(receivedCursorAnchorInfo, originalCursorAnchorInfo);
+        }
+    }
 }
diff --git a/tests/sensor/src/android/hardware/cts/SensorTest.java b/tests/sensor/src/android/hardware/cts/SensorTest.java
index 4c70cd9..debf8ce 100644
--- a/tests/sensor/src/android/hardware/cts/SensorTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorTest.java
@@ -181,6 +181,17 @@
             assertNull(sensor);
         }
 
+        sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
+        boolean hasPressure = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SENSOR_BAROMETER);
+        // pressure sensor is optional
+        if (hasPressure) {
+            assertEquals(Sensor.TYPE_PRESSURE, sensor.getType());
+            assertSensorValues(sensor);
+        } else {
+            assertNull(sensor);
+        }
+
         sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
         // Note: orientation sensor is deprecated.
         if (sensor != null) {
@@ -527,7 +538,9 @@
                 sensor.getName(), sensor.getPower() >= 0);
         assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
                 " " + sensor.getName(), sensor.getResolution() >= 0);
-        if (SensorCtsHelper.hasResolutionRequirement(sensor)) {
+        boolean hasHifiSensors = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_HIFI_SENSORS);
+        if (SensorCtsHelper.hasResolutionRequirement(sensor, hasHifiSensors)) {
             float requiredResolution = SensorCtsHelper.getRequiredResolutionForSensor(sensor);
             assertTrue("Resolution must be <= " + requiredResolution + ". Resolution=" +
                     sensor.getResolution() + " " + sensor.getName(),
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 5ad0c43..a4909c5 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -328,7 +328,7 @@
         return "";
     }
 
-    public static boolean hasResolutionRequirement(Sensor sensor) {
+    public static boolean hasResolutionRequirement(Sensor sensor, boolean hasHifiSensors) {
         switch (sensor.getType()) {
             case Sensor.TYPE_ACCELEROMETER:
             case Sensor.TYPE_ACCELEROMETER_UNCALIBRATED:
@@ -337,6 +337,10 @@
             case Sensor.TYPE_MAGNETIC_FIELD:
             case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
                 return true;
+
+            case Sensor.TYPE_PRESSURE:
+                // Pressure sensor only has a resolution requirement when there are HiFi sensors
+                return hasHifiSensors;
         }
         return false;
     }
@@ -359,6 +363,11 @@
                 // Magnetometer must have a resolution equal to or denser
                 // than 0.6 uT
                 return 0.6f;
+            case Sensor.TYPE_PRESSURE:
+                // Pressure sensor must have at least 80 LSB / hPa which is
+                // equivalent to 0.0125 hPa / LSB. Allow for a small margin of
+                // error due to rounding errors.
+                return 1.01f * (1.0f / 80.0f);
         }
         return 0.0f;
     }
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
index 4387f9d..f0294d1 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
@@ -148,21 +148,25 @@
     /**
      * Utility method to log the stats to a file. Will overwrite the file if it already exists.
      */
-    public void logToFile(String fileName) throws IOException {
+    public void logToFile(String fileName) {
         // Check that external storage is mounted before attempting to write the recorded sensor
         // data to file. This is necessary since Instant Apps do not have access to external
         // storage.
-        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
-            File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
-            File logFile = new File(statsDirectory, fileName);
-            final Map<String, Object> flattened = flatten();
-            FileWriter fileWriter = new FileWriter(logFile, false /* append */);
-            try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
-                for (String key : getSortedKeys(flattened)) {
-                    Object value = flattened.get(key);
-                    writer.write(String.format("%s: %s\n", key, getValueString(value)));
+        try {
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
+                File logFile = new File(statsDirectory, fileName);
+                final Map<String, Object> flattened = flatten();
+                FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+                try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
+                    for (String key : getSortedKeys(flattened)) {
+                        Object value = flattened.get(key);
+                        writer.write(String.format("%s: %s\n", key, getValueString(value)));
+                    }
                 }
             }
+        } catch (IOException e) {
+            Log.e("SensorStats", "Failed to log stats to file\n" + e.getMessage());
         }
     }
 
diff --git a/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml
index 2760a6c..49ac0a7 100644
--- a/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-base-current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for CTS Android Test Base Current API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
index f5e841860..e90984d 100644
--- a/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-mock-current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for CTS Android Test Mock Current API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml b/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
index 5280d2f..e2cb9e0 100644
--- a/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-runner-current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for CTS Android Test Runner Current API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml b/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml
index c0f39d7..8674d5b 100644
--- a/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml
+++ b/tests/signature/api-check/apache-http-legacy-uses-library-api/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for CTS Apache Http Legacy UsesLibrary API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
diff --git a/tests/signature/api-check/current-api/AndroidTest.xml b/tests/signature/api-check/current-api/AndroidTest.xml
index 64dd957..dc6ded5 100644
--- a/tests/signature/api-check/current-api/AndroidTest.xml
+++ b/tests/signature/api-check/current-api/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for CTS Current API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 8d7041b..03d8aa0 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -21,9 +21,9 @@
 import static android.carrierapi.cts.IccUtils.hexStringToBytes;
 import static android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL;
 import static android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
-import static android.telephony.IccOpenLogicalChannelResponse.STATUS_UNKNOWN_ERROR;
 
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentProviderClient;
@@ -458,13 +458,13 @@
         verifyValidIccOpenLogicalChannelResponse(response);
         mTelephonyManager.iccCloseLogicalChannel(response.getChannel());
 
-        // Only values 0x00, 0x04, 0x08, and 0x0C are allowed for p2. Any p2 values that produce
-        // non '9000'/'62xx'/'63xx' status words are treated as an error and the channel is not
-        // opened.
-        p2 = 0x02;
+        // Valid p2 values are defined in TS 102 221 Table 11.2. Per Table 11.2, 0xF0 should be
+        // invalid. Any p2 values that produce non '9000'/'62xx'/'63xx' status words are treated as
+        // an error and the channel is not opened.
+        p2 = 0xF0;
         response = mTelephonyManager.iccOpenLogicalChannel("", p2);
         assertEquals(INVALID_CHANNEL, response.getChannel());
-        assertEquals(STATUS_UNKNOWN_ERROR, response.getStatus());
+        assertNotEquals(STATUS_NO_ERROR, response.getStatus());
     }
 
     /**
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 54e1c4b..3658d04 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -27,6 +27,7 @@
 import static android.content.pm.PackageManager.GET_SERVICES;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.content.ComponentName;
 import android.content.Intent;
@@ -44,14 +45,18 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.os.SystemProperties;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * This test is based on the declarations in AndroidManifest.xml. We create mock declarations
@@ -60,6 +65,8 @@
  */
 @AppModeFull // TODO(Instant) Figure out which APIs should work.
 public class PackageManagerTest extends AndroidTestCase {
+    private static final String TAG = "PackageManagerTest";
+
     private PackageManager mPackageManager;
     private static final String PACKAGE_NAME = "android.content.cts";
     private static final String CONTENT_PKG_NAME = "android.content.cts";
@@ -83,6 +90,8 @@
     // There are 11 activities/activity-alias in AndroidManifest
     private static final int NUM_OF_ACTIVITIES_IN_MANIFEST = 11;
 
+    private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -791,4 +800,123 @@
         assertEquals(PACKAGE_NAME, savedInfo.packageName);
         assertEquals(PermissionInfo.PROTECTION_NORMAL, savedInfo.protectionLevel);
     }
+
+    public void testGetPackageInfo_ApexSupported_ApexPackage_MatchesApex() throws Exception {
+        // This really should be a assumeTrue(isUpdatingApexSupported()), but JUnit3 doesn't support
+        // assumptions framework.
+        // TODO: change to assumeTrue after migrating tests to JUnit4.
+        if (!isUpdatingApexSupported()) {
+            Log.i(TAG, "Device doesn't support updating APEX");
+            return;
+        }
+        PackageInfo packageInfo = mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME,
+                PackageManager.MATCH_APEX);
+        assertShimApexInfoIsCorrect(packageInfo);
+    }
+
+    public void testGetPackageInfo_ApexSupported_ApexPackage_DoesNotMatchApex() {
+        // This really should be a assumeTrue(isUpdatingApexSupported()), but JUnit3 doesn't support
+        // assumptions framework.
+        // TODO: change to assumeTrue after migrating tests to JUnit4.
+        if (!isUpdatingApexSupported()) {
+            Log.i(TAG, "Device doesn't support updating APEX");
+            return;
+        }
+        try {
+            mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME, 0 /* flags */);
+            fail("NameNotFoundException expected");
+        } catch (NameNotFoundException expected) {
+        }
+    }
+
+    public void testGetPackageInfo_ApexNotSupported_ApexPackage_MatchesApex() {
+        if (isUpdatingApexSupported()) {
+            Log.i(TAG, "Device supports updating APEX");
+            return;
+        }
+        try {
+            mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX);
+            fail("NameNotFoundException expected");
+        } catch (NameNotFoundException expected) {
+        }
+    }
+
+    public void testGetPackageInfo_ApexNotSupported_ApexPackage_DoesNotMatchApex() {
+        if (isUpdatingApexSupported()) {
+            Log.i(TAG, "Device supports updating APEX");
+            return;
+        }
+        try {
+            mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME, 0);
+            fail("NameNotFoundException expected");
+        } catch (NameNotFoundException expected) {
+        }
+    }
+
+    public void testGetInstalledPackages_ApexSupported_MatchesApex() {
+        if (!isUpdatingApexSupported()) {
+            Log.i(TAG, "Device doesn't support updating APEX");
+            return;
+        }
+        List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(
+                PackageManager.MATCH_APEX);
+        List<PackageInfo> shimApex = installedPackages.stream().filter(
+                packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+                Collectors.toList());
+        assertWithMessage("More than one shim apex found").that(shimApex).hasSize(1);
+        assertShimApexInfoIsCorrect(shimApex.get(0));
+    }
+
+    public void testGetInstalledPackages_ApexSupported_DoesNotMatchApex() {
+        if (!isUpdatingApexSupported()) {
+            Log.i(TAG, "Device doesn't support updating APEX");
+            return;
+        }
+        List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(0);
+        List<PackageInfo> shimApex = installedPackages.stream().filter(
+                packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+                Collectors.toList());
+        assertWithMessage("Shim apex wasn't supposed to be found").that(shimApex).isEmpty();
+    }
+
+    public void testGetInstalledPackages_ApexNotSupported_MatchesApex() {
+        if (isUpdatingApexSupported()) {
+            Log.i(TAG, "Device supports updating APEX");
+            return;
+        }
+        List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(
+                PackageManager.MATCH_APEX);
+        List<PackageInfo> shimApex = installedPackages.stream().filter(
+                packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+                Collectors.toList());
+        assertWithMessage("Shim apex wasn't supposed to be found").that(shimApex).isEmpty();
+    }
+
+    public void testGetInstalledPackages_ApexNotSupported_DoesNotMatchApex() {
+        if (isUpdatingApexSupported()) {
+            Log.i(TAG, "Device supports updating APEX");
+            return;
+        }
+        List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(0);
+        List<PackageInfo> shimApex = installedPackages.stream().filter(
+                packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
+                Collectors.toList());
+        assertWithMessage("Shim apex wasn't supposed to be found").that(shimApex).isEmpty();
+    }
+
+    private boolean isUpdatingApexSupported() {
+        return SystemProperties.getBoolean("ro.apex.updatable", false);
+    }
+
+    private static void assertShimApexInfoIsCorrect(PackageInfo packageInfo) {
+        assertThat(packageInfo.packageName).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+        assertThat(packageInfo.getLongVersionCode()).isEqualTo(1);
+        assertThat(packageInfo.isApex).isTrue();
+        assertThat(packageInfo.applicationInfo.sourceDir).isEqualTo(
+                "/system/apex/com.android.apex.cts.shim.apex");
+        // Verify that legacy mechanism for handling signatures is supported.
+        Signature[] pastSigningCertificates =
+                packageInfo.signingInfo.getSigningCertificateHistory();
+        assertThat(packageInfo.signatures).asList().containsExactly(pastSigningCertificates);
+    }
 }
diff --git a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
index ab8088a..77806f0 100644
--- a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
+++ b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
@@ -16,7 +16,7 @@
 
 package android.deviceconfig.cts;
 
-import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.provider.DeviceConfig;
@@ -194,7 +194,8 @@
 
         try {
             String property = DeviceConfig.getProperty(NAMESPACE, KEY);
-            assertThat(property).isEqualTo(VALUE);
+            assertEquals("Value read from DeviceConfig API does not match written value.",
+                    property, VALUE);
         } catch (SecurityException e) {
             violations.append("DeviceConfig.getProperty() must be accessible with"
                     + " READ_DEVICE_CONFIG permission\n");
@@ -226,14 +227,25 @@
 
         try {
             DeviceConfig.setProperty(PUBLIC_NAMESPACE, KEY, VALUE, /*makeDefault=*/ false);
-            violations.append("DeviceConfig.setProperty() must not be accessible with"
-                    + " WRITE_DEVICE_CONFIG permission\n");
+            violations.append("DeviceConfig.setProperty() for public namespaces must not be "
+                    + "accessible without WRITE_DEVICE_CONFIG permission\n");
         } catch (SecurityException e) {
         }
 
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG_PERMISSION);
+
+        try {
+            DeviceConfig.setProperty(PUBLIC_NAMESPACE, KEY, VALUE, /*makeDefault=*/ false);
+        } catch (SecurityException e) {
+            violations.append("DeviceConfig.setProperty() must be accessible with"
+                    + " WRITE_DEVICE_CONFIG permission\n");
+        }
+
         try {
             String property = DeviceConfig.getProperty(PUBLIC_NAMESPACE, KEY);
-            assertThat(property).isEqualTo(VALUE);
+            assertEquals("Value read from DeviceConfig API public namespace does not match written"
+                    + " value.", property, VALUE);
         } catch (SecurityException e) {
             violations.append("DeviceConfig.getProperty() for public namespaces must be accessible"
                     + "without READ_DEVICE_CONFIG permission\n");
diff --git a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
index b08f384..d44c14f 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
@@ -67,6 +67,18 @@
     AHardwareBuffer_unlock(hardware_buffer, nullptr);
 }
 
+static jint getFormat(JNIEnv* env, jclass, jobject jbitmap) {
+    AndroidBitmapInfo info;
+    info.format = ANDROID_BITMAP_FORMAT_NONE;
+    int err = 0;
+    err = AndroidBitmap_getInfo(env, jbitmap, &info);
+    if (err != ANDROID_BITMAP_RESULT_SUCCESS) {
+        fail(env, "AndroidBitmap_getInfo failed, err=%d", err);
+        return ANDROID_BITMAP_FORMAT_NONE;
+    }
+    return info.format;
+}
+
 static JNINativeMethod gMethods[] = {
     { "nValidateBitmapInfo", "(Landroid/graphics/Bitmap;IIZ)V",
         (void*) validateBitmapInfo },
@@ -74,6 +86,7 @@
         (void*) validateNdkAccessAfterRecycle },
     { "nFillRgbaHwBuffer", "(Landroid/hardware/HardwareBuffer;)V",
         (void*) fillRgbaHardwareBuffer },
+    { "nGetFormat", "(Landroid/graphics/Bitmap;)I", (void*) getFormat },
 };
 
 int register_android_graphics_cts_BitmapTest(JNIEnv* env) {
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 5a05e5f..bbba7a1 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -239,6 +239,7 @@
         assertEquals(10, ret.getWidth());
         assertEquals(10, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
+        assertEquals(ANDROID_BITMAP_FORMAT_RGB_565, nGetFormat(ret));
     }
 
     @Test(expected=IllegalArgumentException.class)
@@ -261,6 +262,7 @@
         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50);
         assertNotNull(ret);
         assertFalse(mBitmap.equals(ret));
+        assertEquals(ANDROID_BITMAP_FORMAT_RGBA_8888, nGetFormat(mBitmap));
     }
 
     @Test(expected=IllegalArgumentException.class)
@@ -2179,6 +2181,10 @@
 
     private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
 
+    private static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
+    private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
+    private static native int nGetFormat(Bitmap bitmap);
+
     private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
         long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
         if (cpuAccess) {
diff --git a/tests/tests/media/res/raw/loudsoftoggmkv.mkv b/tests/tests/media/res/raw/loudsoftoggmkv.mkv
new file mode 100644
index 0000000..d4d62dc
--- /dev/null
+++ b/tests/tests/media/res/raw/loudsoftoggmkv.mkv
Binary files differ
diff --git a/tests/tests/media/res/raw/loudsoftoggmp4.mp4 b/tests/tests/media/res/raw/loudsoftoggmp4.mp4
new file mode 100644
index 0000000..8e1154e
--- /dev/null
+++ b/tests/tests/media/res/raw/loudsoftoggmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/monotestoggmkv.mkv b/tests/tests/media/res/raw/monotestoggmkv.mkv
new file mode 100644
index 0000000..0d1c6d0
--- /dev/null
+++ b/tests/tests/media/res/raw/monotestoggmkv.mkv
Binary files differ
diff --git a/tests/tests/media/res/raw/monotestoggmp4.mp4 b/tests/tests/media/res/raw/monotestoggmp4.mp4
new file mode 100644
index 0000000..bed86e9
--- /dev/null
+++ b/tests/tests/media/res/raw/monotestoggmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/segment000001_m2ts.mp4 b/tests/tests/media/res/raw/segment000001_m2ts.mp4
new file mode 100644
index 0000000..fc8daba
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001_m2ts.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepflacmkv.mkv b/tests/tests/media/res/raw/sinesweepflacmkv.mkv
new file mode 100644
index 0000000..2f622cd
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepflacmkv.mkv
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepflacmp4.mp4 b/tests/tests/media/res/raw/sinesweepflacmp4.mp4
new file mode 100644
index 0000000..f397afa
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepflacmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepoggmp4.mp4 b/tests/tests/media/res/raw/sinesweepoggmp4.mp4
new file mode 100644
index 0000000..8bfbbf7
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepoggmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepopusmp4.mp4 b/tests/tests/media/res/raw/sinesweepopusmp4.mp4
new file mode 100644
index 0000000..6894908
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepopusmp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sinesweepvorbismp4.mp4 b/tests/tests/media/res/raw/sinesweepvorbismp4.mp4
new file mode 100644
index 0000000..660ca02
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweepvorbismp4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_av1_hdr_static_3mbps.webm b/tests/tests/media/res/raw/video_1280x720_av1_hdr_static_3mbps.webm
new file mode 100644
index 0000000..0e332c2
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_av1_hdr_static_3mbps.webm
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 3391aa3..068273b 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -1369,17 +1369,19 @@
     public CodecCapabilities capabilities;
     public Media[] mediaList;
     public boolean adaptive;
-    public Codec(String n, CodecCapabilities c, Media[] m) {
-        name = n;
+    public boolean vendor;
+    public Codec(MediaCodecInfo info, CodecCapabilities c, Media[] m) {
+        name = info.getName();
         capabilities = c;
         List<Media> medias = new ArrayList<Media>();
 
         if (capabilities == null) {
             adaptive = false;
+            vendor = true;
         } else {
             Log.w(TAG, "checking capabilities of " + name + " for " + m[0].getMime());
             adaptive = capabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback);
-
+            vendor = info.isVendor();
             for (Media media : m) {
                 if (media.getHeight() >= 720 &&
                         !capabilities.isFormatSupported(media.getFormat())) {
@@ -1430,13 +1432,16 @@
             /* enumerate codecs */
             MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
             for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) {
+                if (codecInfo.isAlias()) {
+                    continue;
+                }
                 if (codecInfo.isEncoder()) {
                     continue;
                 }
                 for (String type : codecInfo.getSupportedTypes()) {
                     if (type.equals(mime)) {
                         add(new Codec(
-                                codecInfo.getName(),
+                                codecInfo,
                                 codecInfo.getCapabilitiesForType(mime),
                                 mediaList));
                         break;
@@ -1455,7 +1460,7 @@
     public CodecFamilySpecific(
             Context context, String mime, boolean isGoogle, int ... resources) {
         for (Codec c: new CodecFamily(context, mime, resources)) {
-            if (MediaUtils.isGoogle(c.name) == isGoogle) {
+            if (!c.vendor == isGoogle) {
                 add(c);
             }
         }
diff --git a/tests/tests/media/src/android/media/cts/AudioHelper.java b/tests/tests/media/src/android/media/cts/AudioHelper.java
index 7add051..7208e51 100644
--- a/tests/tests/media/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/src/android/media/cts/AudioHelper.java
@@ -18,18 +18,29 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
 import java.nio.ByteBuffer;
 
 import org.junit.Assert;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioRecord;
+import android.media.AudioTimestamp;
 import android.media.AudioTrack;
 import android.os.Looper;
 import android.os.PersistableBundle;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
 
 // Used for statistics and loopers in listener tests.
 // See AudioRecordTest.java and AudioTrack_ListenerTest.java.
@@ -240,6 +251,151 @@
         }
     }
 
+    public static class TimestampVerifier {
+
+        // CDD 5.6 1ms timestamp accuracy
+        private static final double TEST_MAX_JITTER_MS_ALLOWED = 6.; // a sanity check
+        private static final double TEST_STD_JITTER_MS_ALLOWED = 3.; // flaky tolerance 3x
+        private static final double TEST_STD_JITTER_MS_WARN = 1.;    // CDD requirement warning
+
+        // CDD 5.6 100ms track startup latency
+        private static final double TEST_STARTUP_TIME_MS_ALLOWED = 500.; // flaky tolerance 5x
+        private static final double TEST_STARTUP_TIME_MS_WARN = 100.;    // CDD requirement warning
+
+        private static final int MILLIS_PER_SECOND = 1000;
+        private static final long NANOS_PER_MILLISECOND = 1000000;
+        private static final long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND;
+        private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+
+        private final String mTag;
+        private final int mSampleRate;
+
+        // Running statistics
+        private int mCount = 0;
+        private long mLastFrames = 0;
+        private long mLastTimeNs = 0;
+        private int mJitterCount = 0;
+        private double mMeanJitterMs = 0.;
+        private double mSecondMomentJitterMs = 0.;
+        private double mMaxAbsJitterMs = 0.;
+        private int mWarmupCount = 0;
+
+        public TimestampVerifier(@Nullable String tag, @IntRange(from=4000) int sampleRate) {
+            mTag = tag;  // Log accepts null
+            mSampleRate = sampleRate;
+        }
+
+        public int getJitterCount() { return mJitterCount; }
+        public double getMeanJitterMs() { return mMeanJitterMs; }
+        public double getStdJitterMs() { return Math.sqrt(mSecondMomentJitterMs / mJitterCount); }
+        public double getMaxAbsJitterMs() { return mMaxAbsJitterMs; }
+        public double getStartTimeNs() {
+            return mLastTimeNs - (mLastFrames * NANOS_PER_SECOND / mSampleRate);
+        }
+
+        public void add(@NonNull AudioTimestamp ts) {
+            final long frames = ts.framePosition;
+            final long timeNs = ts.nanoTime;
+
+            assertTrue("timestamps must have causal time", System.nanoTime() >= timeNs);
+
+            if (mCount > 0) { // need delta info from previous iteration (skipping first)
+                final long deltaFrames = frames - mLastFrames;
+                final long deltaTimeNs = timeNs - mLastTimeNs;
+
+                if (deltaFrames == 0 && deltaTimeNs == 0) return;
+
+                final double deltaFramesNs = (double)deltaFrames * NANOS_PER_SECOND / mSampleRate;
+                final double jitterMs = (deltaTimeNs - deltaFramesNs)  // actual - expected
+                        * (1. / NANOS_PER_MILLISECOND);
+
+                Log.d(mTag, "frames(" + frames
+                        + ") timeNs(" + timeNs
+                        + ") lastframes(" + mLastFrames
+                        + ") lastTimeNs(" + mLastTimeNs
+                        + ") deltaFrames(" + deltaFrames
+                        + ") deltaTimeNs(" + deltaTimeNs
+                        + ") jitterMs(" + jitterMs + ")");
+                assertTrue("timestamp time should be increasing", deltaTimeNs >= 0);
+                assertTrue("timestamp frames should be increasing", deltaFrames >= 0);
+
+                if (mLastFrames != 0) {
+                    if (mWarmupCount++ > 1) { // ensure device is warmed up
+                        // Welford's algorithm
+                        // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+                        ++mJitterCount;
+                        final double delta = jitterMs - mMeanJitterMs;
+                        mMeanJitterMs += delta / mJitterCount;
+                        final double delta2 = jitterMs - mMeanJitterMs;
+                        mSecondMomentJitterMs += delta * delta2;
+
+                        // jitterMs is signed, so max uses abs() here.
+                        final double absJitterMs = Math.abs(jitterMs);
+                        if (absJitterMs > mMaxAbsJitterMs) {
+                            mMaxAbsJitterMs = absJitterMs;
+                        }
+                    }
+                }
+            }
+            ++mCount;
+            mLastFrames = frames;
+            mLastTimeNs = timeNs;
+        }
+
+        public void verifyAndLog(long trackStartTimeNs, @Nullable String logName) {
+            // enough timestamps?
+            assertTrue("need at least 2 jitter measurements", mJitterCount >= 2);
+
+            // Compute startup time and std jitter.
+            final int startupTimeMs =
+                    (int) ((getStartTimeNs() - trackStartTimeNs) / NANOS_PER_MILLISECOND);
+            final double stdJitterMs = getStdJitterMs();
+
+            // Check startup time
+            if (startupTimeMs > TEST_STARTUP_TIME_MS_WARN) {
+                Log.w(mTag, "CDD warning: startup time " + startupTimeMs
+                        + " > " + TEST_STARTUP_TIME_MS_WARN);
+            }
+            assertTrue("expect startupTimeMs " + startupTimeMs
+                            + " < " + TEST_STARTUP_TIME_MS_ALLOWED,
+                    startupTimeMs < TEST_STARTUP_TIME_MS_ALLOWED);
+
+            // Check maximum jitter
+            assertTrue("expect maxAbsJitterMs(" + mMaxAbsJitterMs + ") < "
+                            + TEST_MAX_JITTER_MS_ALLOWED,
+                    mMaxAbsJitterMs < TEST_MAX_JITTER_MS_ALLOWED);
+
+            // Check std jitter
+            if (stdJitterMs > TEST_STD_JITTER_MS_WARN) {
+                Log.w(mTag, "CDD warning: std timestamp jitter " + stdJitterMs
+                        + " > " + TEST_STD_JITTER_MS_WARN);
+            }
+            assertTrue("expect stdJitterMs " + stdJitterMs + " < " + TEST_STD_JITTER_MS_ALLOWED,
+                    stdJitterMs < TEST_STD_JITTER_MS_ALLOWED);
+
+            Log.d(mTag, "startupTimeMs(" + startupTimeMs
+                    + ") meanJitterMs(" + mMeanJitterMs
+                    + ") maxAbsJitterMs(" + mMaxAbsJitterMs
+                    + ") stdJitterMs(" + stdJitterMs
+                    + ")");
+
+            // Log results if logName is provided
+            if (logName != null) {
+                DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, logName);
+                // ReportLog needs at least one Value and Summary.
+                log.addValue("startup_time_ms", startupTimeMs,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                log.addValue("maximum_abs_jitter_ms", mMaxAbsJitterMs,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                log.addValue("mean_jitter_ms", mMeanJitterMs,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                log.setSummary("std_jitter_ms", stdJitterMs,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                log.submit(InstrumentationRegistry.getInstrumentation());
+            }
+        }
+    }
+
     /* AudioRecordAudit extends AudioRecord to allow concurrent playback
      * of read content to an AudioTrack.  This is for testing only.
      * For general applications, it is NOT recommended to extend AudioRecord.
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index 207c2a2..6256175 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -16,8 +16,13 @@
 
 package android.media.cts;
 
+import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
+import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
+import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM;
+
 import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
+import android.media.AudioAttributes.CapturePolicy;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.media.SoundPool;
@@ -77,6 +82,7 @@
         final AudioAttributes aa = (new AudioAttributes.Builder())
                 .setUsage(TEST_USAGE)
                 .setContentType(TEST_CONTENT)
+                .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_NONE)
                 .build();
         mMp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong,
                 aa, am.generateAudioSessionId());
@@ -120,6 +126,7 @@
         final AudioAttributes aa = (new AudioAttributes.Builder())
                 .setUsage(TEST_USAGE)
                 .setContentType(TEST_CONTENT)
+                .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_ALL)
                 .build();
 
         List<AudioPlaybackConfiguration> configs = am.getActivePlaybackConfigurations();
@@ -308,6 +315,7 @@
         final AudioAttributes aa = (new AudioAttributes.Builder())
                 .setUsage(TEST_USAGE)
                 .setContentType(TEST_CONTENT)
+                .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_SYSTEM)
                 .build();
 
         mSp = new SoundPool.Builder()
@@ -398,17 +406,27 @@
     }
 
     private static boolean hasAttr(List<AudioPlaybackConfiguration> configs, AudioAttributes aa) {
-        Iterator<AudioPlaybackConfiguration> it = configs.iterator();
-        while (it.hasNext()) {
-            final AudioPlaybackConfiguration apc = it.next();
+        for (AudioPlaybackConfiguration apc : configs) {
             if (apc.getAudioAttributes().getContentType() == aa.getContentType()
-                    && apc.getAudioAttributes().getUsage() == aa.getUsage()) {
+                && apc.getAudioAttributes().getUsage() == aa.getUsage()
+                && apc.getAudioAttributes().getFlags() == aa.getFlags()
+                && anonymizeCapturePolicy(apc.getAudioAttributes().getAllowedCapturePolicy())
+                    == aa.getAllowedCapturePolicy()) {
                 return true;
             }
         }
         return false;
     }
 
+    /** ALLOW_CAPTURE_BY_SYSTEM is anonymized to ALLOW_CAPTURE_BY_NONE. */
+    @CapturePolicy
+    private static int anonymizeCapturePolicy(@CapturePolicy int policy) {
+        if (policy == ALLOW_CAPTURE_BY_SYSTEM) {
+            return ALLOW_CAPTURE_BY_NONE;
+        }
+        return policy;
+    }
+
     private boolean isValidPlatform(String testName) {
         if (!getContext().getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index f8e0e64..b7e565d 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -532,9 +532,9 @@
         AudioRecord record = null;
 
         try {
-            final int NANOS_PER_MILLIS = 1000000;
-            final long RECORD_TIME_IN_MS = 2000;
-            final long RECORD_TIME_IN_NANOS = RECORD_TIME_IN_MS * NANOS_PER_MILLIS;
+            final int NANOS_PER_MILLISECOND = 1000000;
+            final long RECORD_TIME_MS = 2000;
+            final long RECORD_TIME_NS = RECORD_TIME_MS * NANOS_PER_MILLISECOND;
             final int RECORD_ENCODING = AudioFormat.ENCODING_PCM_16BIT; // fixed at this time.
             final int RECORD_CHANNEL_MASK = AudioFormat.CHANNEL_IN_STEREO;
             final int RECORD_SAMPLE_RATE = 23456;  // requires resampling
@@ -555,34 +555,36 @@
             final int bytesPerFrame = numChannels * bytesPerSample;
             // careful about integer overflow in the formula below:
             final int targetFrames =
-                    (int)((long)RECORD_TIME_IN_MS * RECORD_SAMPLE_RATE / 1000);
+                    (int)((long)RECORD_TIME_MS * RECORD_SAMPLE_RATE / 1000);
             final int targetSamples = targetFrames * numChannels;
             final int BUFFER_FRAMES = 512;
             final int BUFFER_SAMPLES = BUFFER_FRAMES * numChannels;
 
             final int tries = 2;
             for (int i = 0; i < tries; ++i) {
-                long startTime = System.nanoTime();
-                long startTimeBoot = android.os.SystemClock.elapsedRealtimeNanos();
+                final long trackStartTimeNs = System.nanoTime();
+                final long trackStartTimeBootNs = android.os.SystemClock.elapsedRealtimeNanos();
 
                 record.startRecording();
 
-                AudioTimestamp startTs = new AudioTimestamp();
+                final AudioTimestamp ts = new AudioTimestamp();
                 int samplesRead = 0;
-                boolean timestampRead = false;
                 // For 16 bit data, use shorts
-                short[] shortData = new short[BUFFER_SAMPLES];
+                final short[] shortData = new short[BUFFER_SAMPLES];
+                final AudioHelper.TimestampVerifier tsVerifier =
+                        new AudioHelper.TimestampVerifier(TAG, RECORD_SAMPLE_RATE);
+
                 while (samplesRead < targetSamples) {
-                    int amount = samplesRead == 0 ? numChannels :
-                        Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
-                    int ret = record.read(shortData, 0, amount);
-                    assertEquals(TEST_NAME, amount, ret);
+                    final int amount = samplesRead == 0 ? numChannels :
+                            Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
+                    final int ret = record.read(shortData, 0, amount);
+                    assertEquals("read incorrect amount", amount, ret);
                     // timestamps follow a different path than data, so it is conceivable
                     // that first data arrives before the first timestamp is ready.
-                    if (!timestampRead) {
-                        timestampRead =
-                                record.getTimestamp(startTs, AudioTimestamp.TIMEBASE_MONOTONIC)
-                                    == AudioRecord.SUCCESS;
+
+                    if (record.getTimestamp(ts, AudioTimestamp.TIMEBASE_MONOTONIC)
+                            == AudioRecord.SUCCESS) {
+                        tsVerifier.add(ts);
                     }
                     samplesRead += ret;
                 }
@@ -607,9 +609,10 @@
 
                 assertEquals(stopTs.framePosition, stopTsBoot.framePosition);
                 assertTrue(stopTs.framePosition >= targetFrames);
-                assertTrue(stopTs.nanoTime - startTime > RECORD_TIME_IN_NANOS);
-                assertTrue(stopTsBoot.nanoTime - startTimeBoot > RECORD_TIME_IN_NANOS);
-                verifyContinuousTimestamps(startTs, stopTs, RECORD_SAMPLE_RATE);
+                assertTrue(stopTs.nanoTime - trackStartTimeNs > RECORD_TIME_NS);
+                assertTrue(stopTsBoot.nanoTime - trackStartTimeBootNs > RECORD_TIME_NS);
+
+                tsVerifier.verifyAndLog(trackStartTimeNs, "test_timestamp" /* logName */);
             }
         } finally {
             if (record != null) {
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 01c7919..d52dc16 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -58,7 +58,6 @@
     private final long WAIT_MSEC = 200;
     private final int OFFSET_DEFAULT = 0;
     private final int OFFSET_NEGATIVE = -10;
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
 
     private void log(String testName, String message) {
         Log.v(TAG, "[" + testName + "] " + message);
@@ -2126,13 +2125,11 @@
     private void doTestTimestamp(int sampleRate, int channelMask, int encoding, int transferMode,
             String streamName) throws Exception {
         // constants for test
-        final String TEST_NAME = "testGetTimestamp";
         final int TEST_LOOP_CNT = 10;
         final int TEST_BUFFER_MS = 100;
         final int TEST_USAGE = AudioAttributes.USAGE_MEDIA;
-        // For jitter we allow 30 msec in frames.  This is a large margin.
-        // Often this is just 0 or 1 frames, but that can depend on hardware.
-        final int TEST_JITTER_FRAMES_ALLOWED = sampleRate * 30 / 1000;
+
+        final int MILLIS_PER_SECOND = 1000;
 
         // -------- initialization --------------
         final int frameSize =
@@ -2161,124 +2158,74 @@
                 .setTransferMode(transferMode)
                 .build();
         assertEquals(AudioTrack.STATE_INITIALIZED, track.getState());
-        // We generally use a transfer size of 100ms for testing, but in rare cases
-        // (e.g. Bluetooth) this needs to be larger to exceed the internal track buffer.
-        final int frameCount =
-                Math.max(track.getBufferCapacityInFrames(), sampleRate * TEST_BUFFER_MS / 1000);
-        track.play();
 
-        ByteBuffer data = ByteBuffer.allocate(frameCount * frameSize);
-        data.order(java.nio.ByteOrder.nativeOrder()).limit(frameCount * frameSize);
-        AudioTimestamp timestamp = new AudioTimestamp();
-        long framesWritten = 0, lastFramesPresented = 0, lastFramesPresentedAt = 0;
-        int cumulativeJitterCount = 0;
-        int differentials = 0;
-        float cumulativeJitter = 0;
-        float maxJitter = 0;
-        for (int i = 0; i < TEST_LOOP_CNT; i++) {
-            final long writeTime = System.nanoTime();
+        try {
+            // We generally use a transfer size of 100ms for testing, but in rare cases
+            // (e.g. Bluetooth) this needs to be larger to exceed the internal track buffer.
+            final int frameCount =
+                    Math.max(track.getBufferCapacityInFrames(),
+                            sampleRate * TEST_BUFFER_MS / MILLIS_PER_SECOND);
+            track.play();
 
-            data.position(0);
-            assertEquals(data.limit(),
-                    track.write(data, data.limit(), AudioTrack.WRITE_BLOCKING));
-            assertEquals(data.position(), data.limit());
-            framesWritten += data.limit() / frameSize;
+            // Android nanoTime implements MONOTONIC, same as our audio timestamps.
+            final long trackStartTimeNs = System.nanoTime();
 
-            // track.getTimestamp may return false if there are no physical HAL outputs.
-            // This may occur on TV devices without connecting an HDMI monitor.
-            // It may also be true immediately after start-up, as the mixing thread could
-            // be idle, but since we've already pushed much more than the minimum buffer size,
-            // that is unlikely.
-            // Nevertheless, we don't want to have unnecessary failures, so we ignore the
-            // first iteration if we don't get a timestamp.
-            final boolean result = track.getTimestamp(timestamp);
-            assertTrue("timestamp could not be read", result || i == 0);
-            if (!result) {
-                continue;
-            }
+            final ByteBuffer data = ByteBuffer.allocate(frameCount * frameSize);
+            data.order(java.nio.ByteOrder.nativeOrder()).limit(frameCount * frameSize);
+            final AudioTimestamp timestamp = new AudioTimestamp();
 
-            final long framesPresented = timestamp.framePosition;
-            final long framesPresentedAt = timestamp.nanoTime;
+            long framesWritten = 0;
+            final AudioHelper.TimestampVerifier tsVerifier =
+                    new AudioHelper.TimestampVerifier(TAG, sampleRate);
+            for (int i = 0; i < TEST_LOOP_CNT; ++i) {
+                final long trackWriteTimeNs = System.nanoTime();
 
-            // We read timestamp here to ensure that seen is greater than presented.
-            // This is an "on-the-fly" read without pausing because pausing may cause the
-            // timestamp to become stale and affect our jitter measurements.
-            final int framesSeen = track.getPlaybackHeadPosition();
-            assertTrue("server frames ahead of client frames", framesWritten >= framesSeen);
-            assertTrue("presented frames ahead of server frames", framesSeen >= framesPresented);
+                data.position(0);
+                assertEquals("write did not complete",
+                        data.limit(), track.write(data, data.limit(), AudioTrack.WRITE_BLOCKING));
+                assertEquals("write did not fill buffer",
+                        data.position(), data.limit());
+                framesWritten += data.limit() / frameSize;
 
-            if (i > 0) { // need delta info from previous iteration (skipping first)
-                final long deltaFrames = framesPresented - lastFramesPresented;
-                final long deltaTime = framesPresentedAt - lastFramesPresentedAt;
-                final long NANOSECONDS_PER_SECOND = 1000000000;
-                final long expectedFrames = deltaTime * sampleRate / NANOSECONDS_PER_SECOND;
-                final long jitterFrames = Math.abs(deltaFrames - expectedFrames);
-
-                Log.d(TAG, "framesWritten(" + framesWritten
-                        + ") framesSeen(" + framesSeen
-                        + ") framesPresented(" + framesPresented
-                        + ") framesPresentedAt(" + framesPresentedAt
-                        + ") lastframesPresented(" + lastFramesPresented
-                        + ") lastFramesPresentedAt(" + lastFramesPresentedAt
-                        + ") deltaFrames(" + deltaFrames
-                        + ") deltaTime(" + deltaTime
-                        + ") expectedFrames(" + expectedFrames
-                        + ") writeTime(" + writeTime
-                        + ") jitter(" + jitterFrames + ")");
-                assertTrue("timestamp time should be increasing", deltaTime >= 0);
-                assertTrue("timestamp frames should be increasing", deltaFrames >= 0);
-
-                // the first nonzero value may have a jump, wait for the second.
-                if (lastFramesPresented != 0) {
-                    if (differentials++ > 1) {
-                        // We check that the timestamp position is reasonably accurate.
-                        assertTrue("jitterFrames(" + jitterFrames + ") < "
-                                + TEST_JITTER_FRAMES_ALLOWED,
-                                jitterFrames < TEST_JITTER_FRAMES_ALLOWED);
-                        cumulativeJitter += jitterFrames;
-                        cumulativeJitterCount++;
-                        if (jitterFrames > maxJitter) {
-                            maxJitter = jitterFrames;
-                        }
-                    }
-                    final long NANOS_PER_SECOND = 1000000000;
-                    final long closeTimeNs = frameCount * 2 * NANOS_PER_SECOND / sampleRate;
-                    // We check that the timestamp time is reasonably current.
-                    assertTrue("framesPresentedAt(" + framesPresentedAt
-                            + ") close to writeTime(" + writeTime
-                            + ") tolerance(" + closeTimeNs
-                            + ")", Math.abs(framesPresentedAt - writeTime) <= closeTimeNs);
-                    assertTrue("timestamps must have causal time",
-                            writeTime >= lastFramesPresentedAt);
+                // track.getTimestamp may return false if there are no physical HAL outputs.
+                // This may occur on TV devices without connecting an HDMI monitor.
+                // It may also be true immediately after start-up, as the mixing thread could
+                // be idle, but since we've already pushed much more than the minimum buffer size,
+                // that is unlikely.
+                // Nevertheless, we don't want to have unnecessary failures, so we ignore the
+                // first iteration if we don't get a timestamp.
+                final boolean result = track.getTimestamp(timestamp);
+                assertTrue("timestamp could not be read", result || i == 0);
+                if (!result) {
+                    continue;
                 }
-            }
-            lastFramesPresented = framesPresented;
-            lastFramesPresentedAt = framesPresentedAt;
-        }
-        // Full drain.
-        Thread.sleep(1000 /* millis */);
-        // check that we are really at the end of playback.
-        assertTrue("timestamp should be valid while draining", track.getTimestamp(timestamp));
-        // fast tracks and sw emulated tracks may not fully drain.  we log the status here.
-        if (framesWritten != timestamp.framePosition) {
-            Log.d(TAG, "timestamp should fully drain.  written: "
-                    + framesWritten + " position: " + timestamp.framePosition);
-        }
-        assertTrue("sufficient nonzero timestamps", differentials > 2);
 
-        track.release();
-        // Log the average jitter
-        if (cumulativeJitterCount > 0) {
-            DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, streamName);
-            final float averageJitterInFrames = cumulativeJitter / cumulativeJitterCount;
-            final float averageJitterInMs = averageJitterInFrames * 1000 / sampleRate;
-            final float maxJitterInMs = maxJitter * 1000 / sampleRate;
-            // ReportLog needs at least one Value and Summary.
-            log.addValue("maximum_jitter", maxJitterInMs,
-                    ResultType.LOWER_BETTER, ResultUnit.MS);
-            log.setSummary("average_jitter", averageJitterInMs,
-                    ResultType.LOWER_BETTER, ResultUnit.MS);
-            log.submit(InstrumentationRegistry.getInstrumentation());
+                tsVerifier.add(timestamp);
+
+                // Ensure that seen is greater than presented.
+                // This is an "on-the-fly" read without pausing because pausing may cause the
+                // timestamp to become stale and affect our jitter measurements.
+                final long framesPresented = timestamp.framePosition;
+                final int framesSeen = track.getPlaybackHeadPosition();
+                assertTrue("server frames ahead of client frames",
+                        framesWritten >= framesSeen);
+                assertTrue("presented frames ahead of server frames",
+                        framesSeen >= framesPresented);
+            }
+            // Full drain.
+            Thread.sleep(1000 /* millis */);
+            // check that we are really at the end of playback.
+            assertTrue("timestamp should be valid while draining", track.getTimestamp(timestamp));
+            // fast tracks and sw emulated tracks may not fully drain.  we log the status here.
+            if (framesWritten != timestamp.framePosition) {
+                Log.d(TAG, "timestamp should fully drain.  written: "
+                        + framesWritten + " position: " + timestamp.framePosition);
+            }
+
+            tsVerifier.verifyAndLog(trackStartTimeNs, streamName);
+
+        } finally {
+            track.release();
         }
     }
 
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
old mode 100755
new mode 100644
index f1655a0..9a20670
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -156,14 +156,30 @@
         decode(R.raw.sinesweepogg, 168.f);
         testTimeStampOrdering(R.raw.sinesweepogg);
     }
+    public void testDecodeOggMkv() throws Exception {
+        decode(R.raw.sinesweepoggmkv, 168.f);
+        testTimeStampOrdering(R.raw.sinesweepoggmkv);
+    }
+    public void testDecodeOggMp4() throws Exception {
+        decode(R.raw.sinesweepoggmp4, 168.f);
+        testTimeStampOrdering(R.raw.sinesweepoggmp4);
+    }
     public void testDecodeWav() throws Exception {
         decode(R.raw.sinesweepwav, 0.0f);
         testTimeStampOrdering(R.raw.sinesweepwav);
     }
+    public void testDecodeFlacMkv() throws Exception {
+        decode(R.raw.sinesweepflacmkv, 0.0f);
+        testTimeStampOrdering(R.raw.sinesweepflacmkv);
+    }
     public void testDecodeFlac() throws Exception {
         decode(R.raw.sinesweepflac, 0.0f);
         testTimeStampOrdering(R.raw.sinesweepflac);
     }
+    public void testDecodeFlacMp4() throws Exception {
+        decode(R.raw.sinesweepflacmp4, 0.0f);
+        testTimeStampOrdering(R.raw.sinesweepflacmp4);
+    }
 
     public void testDecodeMonoMp3() throws Exception {
         monoTest(R.raw.monotestmp3, 44100);
@@ -179,6 +195,14 @@
         monoTest(R.raw.monotestogg, 44100);
         testTimeStampOrdering(R.raw.monotestogg);
     }
+    public void testDecodeMonoOggMkv() throws Exception {
+        monoTest(R.raw.monotestoggmkv, 44100);
+        testTimeStampOrdering(R.raw.monotestoggmkv);
+    }
+    public void testDecodeMonoOggMp4() throws Exception {
+        monoTest(R.raw.monotestoggmp4, 44100);
+        testTimeStampOrdering(R.raw.monotestoggmp4);
+    }
 
     public void testDecodeMonoGsm() throws Exception {
         if (MediaUtils.hasCodecsForResource(mContext, R.raw.monotestgsm)) {
@@ -196,10 +220,16 @@
     public void testDecodeVorbis() throws Exception {
         testTimeStampOrdering(R.raw.sinesweepvorbis);
     }
+    public void testDecodeVorbisMp4() throws Exception {
+        testTimeStampOrdering(R.raw.sinesweepvorbismp4);
+    }
 
     public void testDecodeOpus() throws Exception {
         testTimeStampOrdering(R.raw.sinesweepopus);
     }
+    public void testDecodeOpusMp4() throws Exception {
+        testTimeStampOrdering(R.raw.sinesweepopusmp4);
+    }
 
     public void testDecode51M4a() throws Exception {
         decodeToMemory(R.raw.sinesweep51m4a, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
@@ -721,6 +751,14 @@
                 staticInfo, true /*metadataInContainer*/);
     }
 
+    public void testAV1HdrStaticMetadata() throws Exception {
+        final String staticInfo =
+                "00 d0 84 80 3e c2 33 c4  86 4c 1d b8 0b 13 3d 42" +
+                "40 e8 03 64 00 e8 03 2c  01                     " ;
+        testHdrStaticMetadata(R.raw.video_1280x720_av1_hdr_static_3mbps,
+                staticInfo, false /*metadataInContainer*/);
+    }
+
     public void testH265HDR10StaticMetadata() throws Exception {
         // Expected value of MediaFormat.KEY_HDR_STATIC_INFO key.
         // The associated value is a ByteBuffer. This buffer contains the raw contents of the
@@ -776,6 +814,11 @@
                 // it here so that we only test HDR when decoder supports it.
                 format.setInteger(MediaFormat.KEY_PROFILE,
                         MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR);
+            } else if (MediaFormat.MIMETYPE_VIDEO_AV1.equals(mime)) {
+                // The muxer might not have put AV1 CSD in the webm, we manually patch
+                // it here so that we only test HDR when decoder supports it.
+                format.setInteger(MediaFormat.KEY_PROFILE,
+                        MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10);
             } else {
                 fail("Codec " + mime + " shouldn't be tested with this test!");
             }
@@ -1757,9 +1800,15 @@
         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a);
         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame);
         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopus);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopusmp4);
         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmkv);
         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmp4);
         testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmkv);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmp4);
     }
 
     /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty
@@ -2854,6 +2903,8 @@
     public void testFlush() throws Exception {
         testFlush(R.raw.loudsoftwav);
         testFlush(R.raw.loudsoftogg);
+        testFlush(R.raw.loudsoftoggmkv);
+        testFlush(R.raw.loudsoftoggmp4);
         testFlush(R.raw.loudsoftmp3);
         testFlush(R.raw.loudsoftaac);
         testFlush(R.raw.loudsoftfaac);
@@ -3048,6 +3099,9 @@
         List<CodecCapabilities> caps = new ArrayList<CodecCapabilities>();
         for (int i = 0; i < numCodecs; i++) {
             MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+            if (codecInfo.isAlias()) {
+                continue;
+            }
             if (codecInfo.isEncoder()) {
                 continue;
             }
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
index 7da538a..4a2b9db 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
@@ -71,6 +71,9 @@
         final MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         final MediaCodecInfo[] mediaCodecInfos = mediaCodecList.getCodecInfos();
         for (MediaCodecInfo mediaCodecInfo : mediaCodecInfos) {
+            if (mediaCodecInfo.isAlias()) {
+                continue;
+            }
             if (mediaCodecInfo.isEncoder()) {
                 continue;
             }
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 6acd5b6..dd7131b 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -1216,6 +1216,9 @@
         for (int i = 0; i < numCodecs; i++) {
             MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
 
+            if (codecInfo.isAlias()) {
+                continue;
+            }
             if (!codecInfo.isEncoder()) {
                 continue;
             }
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index d31a73b..cecb4d0 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -323,7 +323,7 @@
         ArrayList<Decoder> result = new ArrayList<Decoder>();
 
         for (MediaCodecInfo info : mcl.getCodecInfos()) {
-            if (info.isEncoder() || MediaUtils.isGoogle(info.getName()) != goog) {
+            if (info.isEncoder() || info.isAlias() || !info.isVendor() != goog) {
                 continue;
             }
             CodecCapabilities caps = null;
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index ad0aa36..25829c5 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -735,6 +735,7 @@
             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS     ) ||
             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW      ) ||
             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS   ) ||
+            type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1      ) ||
             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC      ) ||
             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263     ) ||
             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC     ) ||
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 9723684..e3ef811 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -437,7 +437,11 @@
         return format.containsKey(key) ? format.getInteger(key) : 0;
     }
 
+    // Find first secure decoder for media type. If none found, return
+    // the name of the first regular codec with ".secure" suffix added.
+    // If all else fails, return null.
     protected String getSecureDecoderNameForMime(String mime) {
+        String firstDecoderName = null;
         int n = MediaCodecList.getCodecCount();
         for (int i = 0; i < n; ++i) {
             MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
@@ -450,10 +454,18 @@
 
             for (int j = 0; j < supportedTypes.length; ++j) {
                 if (supportedTypes[j].equalsIgnoreCase(mime)) {
-                    return info.getName() + ".secure";
+                    if (info.getCapabilitiesForType(mime).isFeatureSupported(
+                            MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback)) {
+                        return info.getName();
+                    } else if (firstDecoderName == null) {
+                        firstDecoderName = info.getName();
+                    }
                 }
             }
         }
+        if (firstDecoderName != null) {
+            return firstDecoderName + ".secure";
+        }
         return null;
     }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 3c4f6fd..0ce4803 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -19,6 +19,8 @@
 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback;
 
+import com.android.compatibility.common.util.PropertyUtil;
+
 import android.content.pm.PackageManager;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
@@ -31,6 +33,7 @@
 import android.platform.test.annotations.RequiresDevice;
 import android.test.AndroidTestCase;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Size;
 
 import androidx.test.filters.SmallTest;
@@ -101,6 +104,10 @@
         }
     }
 
+    public static boolean hasExpandedCodecInfo() {
+        return PropertyUtil.isVendorApiLevelNewerThan(29);
+    }
+
     public static void testMediaCodecXmlFileExist() {
         File file = new File(MEDIA_CODEC_XML_FILE);
         File vendorFile = new File(VENDOR_MEDIA_CODEC_XML_FILE);
@@ -712,15 +719,82 @@
                    .covers(new VideoCapabilities.PerformancePoint(1280, 720, 68)));
     }
 
-    public void verifyPerformancePoints(
+    private void verifyPerformancePoints(
             MediaCodecInfo info, String mediaType,
             List<VideoCapabilities.PerformancePoint> points) {
+        List<VideoCapabilities.PerformancePoint> standardPoints = Arrays.asList(
+                VideoCapabilities.PerformancePoint.UHD_240,
+                VideoCapabilities.PerformancePoint.UHD_200,
+                VideoCapabilities.PerformancePoint.UHD_120,
+                VideoCapabilities.PerformancePoint.UHD_100,
+                VideoCapabilities.PerformancePoint.UHD_60,
+                VideoCapabilities.PerformancePoint.UHD_50,
+                VideoCapabilities.PerformancePoint.UHD_30,
+                VideoCapabilities.PerformancePoint.UHD_25,
+                VideoCapabilities.PerformancePoint.UHD_24,
+                VideoCapabilities.PerformancePoint.FHD_240,
+                VideoCapabilities.PerformancePoint.FHD_200,
+                VideoCapabilities.PerformancePoint.FHD_120,
+                VideoCapabilities.PerformancePoint.FHD_100,
+                VideoCapabilities.PerformancePoint.FHD_60,
+                VideoCapabilities.PerformancePoint.FHD_50,
+                VideoCapabilities.PerformancePoint.FHD_30,
+                VideoCapabilities.PerformancePoint.FHD_25,
+                VideoCapabilities.PerformancePoint.FHD_24,
+                VideoCapabilities.PerformancePoint.HD_240,
+                VideoCapabilities.PerformancePoint.HD_200,
+                VideoCapabilities.PerformancePoint.HD_120,
+                VideoCapabilities.PerformancePoint.HD_100,
+                VideoCapabilities.PerformancePoint.HD_60,
+                VideoCapabilities.PerformancePoint.HD_50,
+                VideoCapabilities.PerformancePoint.HD_30,
+                VideoCapabilities.PerformancePoint.HD_25,
+                VideoCapabilities.PerformancePoint.HD_24,
+                VideoCapabilities.PerformancePoint.SD_60,
+                VideoCapabilities.PerformancePoint.SD_50,
+                VideoCapabilities.PerformancePoint.SD_48,
+                VideoCapabilities.PerformancePoint.SD_30,
+                VideoCapabilities.PerformancePoint.SD_25,
+                VideoCapabilities.PerformancePoint.SD_24);
+
         // Components must list all supported standard performance points unless those performance
         // points are covered by other listed standard performance points.
-
-
-        // TODO: verify performance points listed conform to the requirements ... once those
-        // requirements are agreed upon.
+        for (VideoCapabilities.PerformancePoint pp : points) {
+            if (standardPoints.contains(pp)) {
+                // standard points must not be covered by other listed standard points
+                for (VideoCapabilities.PerformancePoint pp2 : points) {
+                    if (!standardPoints.contains(pp2)) {
+                        continue;
+                    }
+                    // using object equality to determine otherness
+                    assertFalse("standard " + pp2 + " for " + info.getCanonicalName()
+                            + " for media type " + mediaType + " covers standard " + pp,
+                            pp2 != pp && pp2.covers(pp));
+                }
+            } else {
+                // non-standard points must list all covered standard point not covered by another
+                // listed standard point
+                for (VideoCapabilities.PerformancePoint spp : standardPoints) {
+                    if (pp.covers(spp)) {
+                        // Must be either listed or covered by another standard. Since a point
+                        // covers itself, it is sufficient to check that it is covered by a listed
+                        // standard point.
+                        boolean covered = false;
+                        for (VideoCapabilities.PerformancePoint pp2 : points) {
+                            // using object equality to determine otherness
+                            if (standardPoints.contains(pp2) && pp2.covers(spp)) {
+                                covered = true;
+                                break;
+                            }
+                        }
+                        assertTrue(pp + " for " + info.getCanonicalName() + " for media type "
+                                + mediaType + " covers standard " + spp
+                                + " that is not covered by a listed standard point",
+                                covered);
+                    }
+                }
+            }
+        }
     }
 
     public void testAllHardwareAcceleratedVideoCodecsPublishPerformancePoints() {
@@ -737,20 +811,46 @@
             FEATURE_TunneledPlayback,
         };
 
+        Set<Pair<String, Integer>> describedTypes = new HashSet<>(); // mediaType - featureIndex
+        Set<Pair<String, Integer>> supportedTypes = new HashSet<>(); // mediaType - featureIndex
+
+        // Once any hardware codec performance is described, we assume that all hardware codecs
+        // must be described, even if we cannot confirm expanded codec info support.
+        boolean hasPerformancePoints = hasExpandedCodecInfo();
+        if (!hasPerformancePoints) {
+            for (MediaCodecInfo info : mAllInfos) {
+                String[] types = info.getSupportedTypes();
+                for (int j = 0; j < types.length; ++j) {
+                    String mediaType = types[j];
+                    CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
+                    VideoCapabilities videoCap = cap.getVideoCapabilities();
+                    if (videoCap != null
+                            && videoCap.getSupportedPerformancePoints() != null) {
+                        hasPerformancePoints = true;
+                        break;
+                    }
+                }
+                if (hasPerformancePoints) {
+                    break;
+                }
+            }
+        }
+
         for (MediaCodecInfo info : mAllInfos) {
             String[] types = info.getSupportedTypes();
             for (int j = 0; j < types.length; ++j) {
-                CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
+                String mediaType = types[j];
+                CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
                 MediaFormat defaultFormat = cap.getDefaultFormat();
                 VideoCapabilities videoCap = cap.getVideoCapabilities();
 
                 Log.d(TAG, "codec: " + info.getName() + " canonical: " + info.getCanonicalName()
-                        + " type: " + types[j]);
+                        + " type: " + mediaType);
 
                 if (videoCap == null) {
-                    assertFalse("no video capabilities for video media type " + types[j] + " of "
+                    assertFalse("no video capabilities for video media type " + mediaType + " of "
                                     + info.getName(),
-                                types[j].toLowerCase().startsWith("video/"));
+                                mediaType.toLowerCase().startsWith("video/"));
                     continue;
                 }
 
@@ -775,16 +875,29 @@
                         supportedFeatureConfigs.add(cfg_ix);
                     }
                 }
-                int[] supportedFeatureConfigsArray =
-                    supportedFeatureConfigs.stream().mapToInt(Integer::intValue).toArray();
 
-                Log.d(TAG, "codec supports configs "
-                        + Arrays.toString(supportedFeatureConfigsArray));
+                Log.d(TAG, "codec supports configs " + Arrays.toString(
+                        supportedFeatureConfigs.stream().mapToInt(Integer::intValue).toArray()));
+                boolean isMandatory = mandatoryTypes.contains(mediaType);
+                if (info.isHardwareAccelerated()) {
+                    for (Integer cfg_ix : supportedFeatureConfigs) {
+                        Pair<String, Integer> type = Pair.create(mediaType, cfg_ix);
+                        if (hasPerformancePoints && isMandatory) {
+                            supportedTypes.add(type);
+                        }
+                        if (pps != null && pps.size() > 0) {
+                            describedTypes.add(type);
+                        }
+                    }
+                }
+
                 if (pps == null) {
-                    // Hardware-accelerated video components must publish performance points,
-                    // even if it is an empty list.
-                    assertFalse("HW-accelerated codec '" + info.getName()
-                            + "' must publish performance points", info.isHardwareAccelerated());
+                    if (hasExpandedCodecInfo()) {
+                        // Hardware-accelerated video components must publish performance points,
+                        // even if it is an empty list.
+                        assertFalse("HW-accelerated codec '" + info.getName()
+                                + "' must publish performance points", info.isHardwareAccelerated());
+                    }
 
                     continue;
                 }
@@ -792,7 +905,7 @@
                 // At least one hardware accelerated codec for each media type (including secure
                 // codecs) must publish valid performance points for AVC/VP8/VP9/HEVC/AV1.
                 if (pps.size() == 0) {
-                    if (mandatoryTypes.contains(types[j])) {
+                    if (isMandatory) {
                         Log.d(TAG, "empty performance points list published by HW accelerated" +
                                    "component " + info.getName() + " for " + types[j]);
                     }
@@ -800,8 +913,15 @@
                     for (VideoCapabilities.PerformancePoint p : pps) {
                         Log.d(TAG, "got performance point " + p);
                     }
+                    verifyPerformancePoints(info, types[j], pps);
                 }
             }
         }
+
+        for (Pair<String, Integer> type : supportedTypes) {
+            assertTrue("codecs for media type " + type.first + " in configuration " + type.second
+                    + " do not have substantial performance point data",
+                    describedTypes.contains(type));
+        }
     }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index c61e5d7..3802110 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -1750,10 +1750,7 @@
     private static boolean supportsCodec(String mimeType, boolean encoder) {
         MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
         for (MediaCodecInfo info : list.getCodecInfos()) {
-            if (encoder && !info.isEncoder()) {
-                continue;
-            }
-            if (!encoder && info.isEncoder()) {
+            if (encoder != info.isEncoder()) {
                 continue;
             }
 
@@ -2051,13 +2048,10 @@
             // check if float
             final MediaFormat actualFormat =
                     encode ? mCodec.getInputFormat() : mCodec.getOutputFormat();
-            Integer actualEncoding = null;
-            try {
-                actualEncoding = actualFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);
-            } catch (Exception e) {
-                ; // trying to get a non-existent key throws exception
-            }
-            mIsFloat = actualEncoding != null && actualEncoding == AudioFormat.ENCODING_PCM_FLOAT;
+
+            mIsFloat = actualFormat.getInteger(
+                    MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT)
+                            == AudioFormat.ENCODING_PCM_FLOAT;
 
             mCodec.start();
         }
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index e82ffcb..5af69a3 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -416,13 +416,10 @@
                 }
             }
             assertNotNull("MediaExtractor cannot find mime type " + mime, mFormat);
-            Integer actualEncoding = null;
-            try {
-                actualEncoding = mFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);
-            } catch (Exception e) {
-                ; // trying to get a non-existent key throws exception
-            }
-            mIsFloat = actualEncoding != null && actualEncoding == AudioFormat.ENCODING_PCM_FLOAT;
+            mIsFloat = mFormat.getInteger(
+                    MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT)
+                            == AudioFormat.ENCODING_PCM_FLOAT;
+
         }
 
         public MediaExtractorStream(
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 721d39a..6a10e40 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -1694,6 +1694,17 @@
         playVideoTest(R.raw.mkv_audio_pcms16le, -1, -1);
     }
 
+    public void testLocalVideo_segment000001_m2ts()
+            throws Exception {
+        if (checkLoadResource(R.raw.segment000001)) {
+            mMediaPlayer.stop();
+            assertTrue(checkLoadResource(R.raw.segment000001_m2ts));
+            playLoadedVideo(320, 240, 0);
+        } else {
+            MediaUtils.skipTest("no mp2 support, skipping m2ts");
+        }
+    }
+
     private void readSubtitleTracks() throws Exception {
         mSubtitleTrackIndex.clear();
         MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
index 96cf427..f0e63ef 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
@@ -300,6 +300,53 @@
         }
     }
 
+    public void testGetSession2TokensWithTwoSessions() throws Exception {
+        final Context context = getInstrumentation().getTargetContext();
+        Handler handler = createHandler();
+        Executor handlerExecutor = (runnable) -> {
+            if (handler != null) {
+                handler.post(() -> {
+                    runnable.run();
+                });
+            }
+        };
+
+        Session2TokenListener listener = new Session2TokenListener();
+        mSessionManager.addOnSession2TokensChangedListener(listener, handler);
+
+        try (MediaSession2 session1 = new MediaSession2.Builder(context)
+                .setSessionCallback(handlerExecutor, new Session2Callback())
+                .setId("testGetSession2TokensWithTwoSessions_session1")
+                .build()) {
+
+            assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            Session2Token session1Token = session1.getToken();
+            assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session1Token));
+
+            // Create another session and check the result of getSession2Token().
+            listener.resetCountDownLatch();
+            Session2Token session2Token = null;
+            try (MediaSession2 session2 = new MediaSession2.Builder(context)
+                    .setSessionCallback(handlerExecutor, new Session2Callback())
+                    .setId("testGetSession2TokensWithTwoSessions_session2")
+                    .build()) {
+
+                assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+                session2Token = session2.getToken();
+                assertNotNull(session2Token);
+                assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session1Token));
+                assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session2Token));
+
+                listener.resetCountDownLatch();
+            }
+
+            // Since the session2 is closed, getSession2Tokens() shouldn't include session2's token.
+            assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            assertTrue(listContainsToken(mSessionManager.getSession2Tokens(), session1Token));
+            assertFalse(listContainsToken(mSessionManager.getSession2Tokens(), session2Token));
+        }
+    }
+
     public void testAddAndRemoveSession2TokensListener() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
         Handler handler = createHandler();
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index c2d1392..9e85858 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -116,10 +116,16 @@
 
     public void testExtractor() throws Exception {
         testExtractor(R.raw.sinesweepogg);
+        testExtractor(R.raw.sinesweepoggmkv);
+        testExtractor(R.raw.sinesweepoggmp4);
         testExtractor(R.raw.sinesweepmp3lame);
         testExtractor(R.raw.sinesweepmp3smpb);
+        testExtractor(R.raw.sinesweepopus);
+        testExtractor(R.raw.sinesweepopusmp4);
         testExtractor(R.raw.sinesweepm4a);
+        testExtractor(R.raw.sinesweepflacmkv);
         testExtractor(R.raw.sinesweepflac);
+        testExtractor(R.raw.sinesweepflacmp4);
         testExtractor(R.raw.sinesweepwav);
 
         testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
@@ -309,10 +315,16 @@
     public void testDecoder() throws Exception {
         int testsRun =
             testDecoder(R.raw.sinesweepogg) +
+            testDecoder(R.raw.sinesweepoggmkv) +
+            testDecoder(R.raw.sinesweepoggmp4) +
             testDecoder(R.raw.sinesweepmp3lame) +
             testDecoder(R.raw.sinesweepmp3smpb) +
+            testDecoder(R.raw.sinesweepopus) +
+            testDecoder(R.raw.sinesweepopusmp4) +
             testDecoder(R.raw.sinesweepm4a) +
+            testDecoder(R.raw.sinesweepflacmkv) +
             testDecoder(R.raw.sinesweepflac) +
+            testDecoder(R.raw.sinesweepflacmp4) +
             testDecoder(R.raw.sinesweepwav) +
 
             testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
index 57ff9d3..cd035e7 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
@@ -25,6 +25,7 @@
 import android.media.MediaCodecInfo.VideoCapabilities;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.os.Build;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Pair;
@@ -83,6 +84,15 @@
         super.tearDown();
     }
 
+    // Performance numbers only make sense on real devices, so skip on non-real devices
+    public static boolean frankenDevice() {
+        if (("Android".equals(Build.BRAND) || "generic".equals(Build.BRAND)) &&
+            (Build.MODEL.startsWith("AOSP on ") || Build.PRODUCT.startsWith("aosp_"))) {
+            return true;
+        }
+        return false;
+    }
+
     private void decode(String name, int resourceId, MediaFormat format) throws Exception {
         int width = format.getInteger(MediaFormat.KEY_WIDTH);
         int height = format.getInteger(MediaFormat.KEY_HEIGHT);
@@ -91,6 +101,10 @@
         // Ensure we can finish this test within the test timeout. Allow 25% slack (4/5).
         long maxTimeMs = Math.min(
                 MAX_TEST_TIMEOUT_MS * 4 / 5 / NUMBER_OF_REPEATS, MAX_TIME_MS);
+        // reduce test run on non-real device
+        if (frankenDevice()) {
+            maxTimeMs /= 10;
+        }
         double measuredFps[] = new double[NUMBER_OF_REPEATS];
 
         for (int i = 0; i < NUMBER_OF_REPEATS; ++i) {
@@ -107,7 +121,12 @@
 
         String error =
             MediaPerfUtils.verifyAchievableFrameRates(name, mime, width, height, measuredFps);
-        assertNull(error, error);
+        if (frankenDevice() && error != null) {
+            // ensure there is data, but don't insist that it is correct
+            assertFalse(error, error.startsWith("Failed to get "));
+        } else {
+            assertNull(error, error);
+        }
         mSamplesInMemory.clear();
     }
 
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index 55f1608..c7018a2 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -1195,7 +1195,7 @@
         ArrayList<Encoder> result = new ArrayList<Encoder>();
 
         for (MediaCodecInfo info : mcl.getCodecInfos()) {
-            if (!info.isEncoder() || MediaUtils.isGoogle(info.getName()) != goog) {
+            if (!info.isEncoder() || !info.isVendor() != goog) {
                 continue;
             }
             CodecCapabilities caps = null;
diff --git a/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
index 48faf49..a18a9d5 100644
--- a/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
@@ -99,10 +99,6 @@
             this.codecName = codecName;
             this.colorFormat = colorFormat;
         }
-        public boolean  isGoogleCodec() {
-            return MediaUtils.isGoogle(codecName);
-        }
-
         public final String codecName; // OpenMax component name for VPx codec.
         public final int colorFormat;  // Color format supported by codec.
     }
@@ -128,7 +124,7 @@
         CodecProperties codecProperties = null;
         String mime = format.getString(MediaFormat.KEY_MIME);
 
-        // Loop through the list of omx components in case platform specific codec
+        // Loop through the list of codec components in case platform specific codec
         // is requested.
         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) {
@@ -138,8 +134,7 @@
             Log.v(TAG, codecInfo.getName());
             // TODO: remove dependence of Google from the test
             // Check if this is Google codec - we should ignore it.
-            boolean isGoogleCodec = MediaUtils.isGoogle(codecInfo.getName());
-            if (!isGoogleCodec && forceGoogleCodec) {
+            if (codecInfo.isVendor() && forceGoogleCodec) {
                 continue;
             }
 
@@ -166,8 +161,8 @@
                                     codecColorFormat);
                             Log.v(TAG, "Found target codec " + codecProperties.codecName +
                                     ". Color: 0x" + Integer.toHexString(codecColorFormat));
-                            // return first HW codec found
-                            if (!isGoogleCodec) {
+                            // return first vendor codec (hopefully HW) found
+                            if (!codecInfo.isVendor()) {
                                 return codecProperties;
                             }
                         }
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index 2b941d2..c841216 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -36,7 +36,7 @@
 LOCAL_C_INCLUDES += frameworks/ml/nn/common/include
 LOCAL_C_INCLUDES += frameworks/ml/nn/tools/test_generator/include
 
-LOCAL_CFLAGS := -Werror -Wall -DNNTEST_ONLY_PUBLIC_API
+LOCAL_CFLAGS := -Werror -Wall -DNNTEST_ONLY_PUBLIC_API -DNNTEST_CTS
 
 LOCAL_SHARED_LIBRARIES := libandroid liblog libneuralnetworks
 LOCAL_STATIC_LIBRARIES := libgtest_ndk_c++ libgmock_ndk
diff --git a/tests/tests/neuralnetworks/benchmark/AndroidTest.xml b/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
index dd309c7..2492614 100644
--- a/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Configuration for CTS NNAPI Benchmark Tests">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="neuralnetworks" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsNNAPIBenchmarkTestCases.apk" />
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index 57a8716..2dba4fd 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -51,6 +51,8 @@
         <option name="push" value="CtsAppThatAccessesStorageOnCommand28v3.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesStorageOnCommand28v3.apk" />
         <option name="push" value="CtsAppWithSharedUidThatRequestsPermissions.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsPermissions.apk" />
         <option name="push" value="CtsAppWithSharedUidThatRequestsNoPermissions.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsNoPermissions.apk" />
+        <option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission28.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsLocationPermission28.apk" />
+        <option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission29.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsLocationPermission29.apk" />
     </target_preparer>
 
     <!-- Remove additional apps if installed -->
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
new file mode 100644
index 0000000..15da653
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsAppWithSharedUidThatRequestsLocationPermission28",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ],
+}
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml
new file mode 100644
index 0000000..ec69d15
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.appthatrequestpermission"
+    android:versionCode="2"
+    android:sharedUserId="android.permission.cts.appthatrequestpermission">
+
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+    <application />
+</manifest>
+
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
new file mode 100644
index 0000000..a369df7
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsAppWithSharedUidThatRequestsLocationPermission29",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ],
+}
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml
new file mode 100644
index 0000000..2341922
--- /dev/null
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.appthatrequestpermission"
+    android:versionCode="1"
+    android:sharedUserId="android.permission.cts.appthatrequestpermission">
+
+    <!-- STOPSHIP: Set to apk level that shipped the location tristate -->
+    <!-- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> -->
+
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+    <application />
+</manifest>
+
diff --git a/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java b/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
index 3ae5edb..adac0be 100644
--- a/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
@@ -25,12 +25,13 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.runner.RunWith;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public class MainlineNetworkStackPermissionTest{
@@ -41,6 +42,7 @@
      * and is a system package
      */
     @Test
+    @AppModeFull(reason = "Instant apps cannot access PackageManager#getPermissionInfo")
     public void testPackageWithMainlineNetworkStackPermission() throws Exception {
         final PackageManager packageManager = mContext.getPackageManager();
         assertNotNull("Unable to find PackageManager.", packageManager);
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionUtils.java b/tests/tests/permission/src/android/permission/cts/PermissionUtils.java
index 3163fe8..7e0d441 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionUtils.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUtils.java
@@ -39,6 +39,7 @@
 import android.app.UiAutomation;
 import android.content.Context;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
 import android.os.Process;
 import android.os.UserHandle;
@@ -128,8 +129,10 @@
      *
      * @return {@code true} iff the permission is granted
      */
-    static boolean isPermissionGranted(@NonNull String packageName, @NonNull String permission) {
-        return sContext.getPackageManager().checkPermission(permission, packageName)
+    static boolean isPermissionGranted(@NonNull String packageName, @NonNull String permission)
+            throws Exception {
+        return sContext.checkPermission(permission, Process.myPid(),
+                sContext.getPackageManager().getPackageUid(packageName, 0))
                 == PERMISSION_GRANTED;
     }
 
@@ -170,7 +173,8 @@
      * @param packageName The app that should have the permission granted
      * @param permission The permission to grant
      */
-    static void grantPermission(@NonNull String packageName, @NonNull String permission) {
+    static void grantPermission(@NonNull String packageName, @NonNull String permission)
+            throws Exception {
         sUiAutomation.grantRuntimePermission(packageName, permission);
 
         if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
@@ -194,6 +198,31 @@
     }
 
     /**
+     * Revoke a permission from an app.
+     *
+     * <p>This correctly handles pre-M apps by setting the app-ops.
+     * <p>This also correctly handles the location background permission, but does not handle any
+     * other background permission
+     *
+     * @param packageName The app that should have the permission revoked
+     * @param permission The permission to revoke
+     */
+    static void revokePermission(@NonNull String packageName, @NonNull String permission)
+            throws Exception {
+        sUiAutomation.revokeRuntimePermission(packageName, permission);
+
+        if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
+            // The app-op for background location is encoded into the mode of the foreground
+            // location
+            if (isGranted(packageName, ACCESS_COARSE_LOCATION)) {
+                setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
+            }
+        } else {
+            setAppOp(packageName, permission, MODE_IGNORED);
+        }
+    }
+
+    /**
      * Clear permission state (not app-op state) of package.
      *
      * @param packageName Package to clear
diff --git a/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java b/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java
index aa0e2c9..2f8d9e6 100644
--- a/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SharedUidPermissionsTest.java
@@ -16,30 +16,34 @@
 
 package android.permission.cts;
 
-import static android.Manifest.permission.READ_CALENDAR;
+import static android.Manifest.permission.INTERNET;
 import static android.Manifest.permission.READ_CONTACTS;
 import static android.permission.cts.PermissionUtils.grantPermission;
 import static android.permission.cts.PermissionUtils.install;
 import static android.permission.cts.PermissionUtils.isPermissionGranted;
+import static android.permission.cts.PermissionUtils.revokePermission;
 import static android.permission.cts.PermissionUtils.uninstallApp;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import android.platform.test.annotations.AppModeFull;
-import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+        + "to grant permissions to them.")
 public class SharedUidPermissionsTest {
-    private static final String LOG_TAG = SharedUidPermissionsTest.class.getSimpleName();
-
     /** The package name of all apps used in the test */
-    private static final String PKG_THAT_REQUESTS_PERMISSIONS = "android.permission.cts.appthatrequestpermission";
-    private static final String PKG_THAT_REQUESTS_NO_PERMISSIONS = "android.permission.cts.appthatrequestnopermission";
+    private static final String PKG_THAT_REQUESTS_PERMISSIONS =
+            "android.permission.cts.appthatrequestpermission";
+    private static final String PKG_THAT_REQUESTS_NO_PERMISSIONS =
+            "android.permission.cts.appthatrequestnopermission";
 
     private static final String TMP_DIR = "/data/local/tmp/cts/permissions/";
     private static final String APK_THAT_REQUESTS_PERMISSIONS =
@@ -47,30 +51,90 @@
     private static final String APK_THAT_REQUESTS_NO_PERMISSIONS =
             TMP_DIR + "CtsAppWithSharedUidThatRequestsNoPermissions.apk";
 
+    @Before
+    @After
+    public void uninstallTestApps() {
+        uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+        uninstallApp(PKG_THAT_REQUESTS_NO_PERMISSIONS);
+    }
+
     @Test
-    @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
-            + "to grant permissions to them.")
-    public void appsWithSharedUidsSharePermissions() throws Exception {
+    public void packageGainsRuntimePermissionsWhenJoiningSharedUid() throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+    }
+
+    @Test
+    public void packageGainsNormalPermissionsWhenJoiningSharedUid() throws Exception {
         install(APK_THAT_REQUESTS_PERMISSIONS);
         install(APK_THAT_REQUESTS_NO_PERMISSIONS);
 
-        try {
-            grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
-            grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CALENDAR);
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, INTERNET)).isTrue();
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, INTERNET)).isTrue();
+    }
 
-            // Permissions are shared
-            assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
-            assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CALENDAR)).isTrue();
+    @Test
+    public void grantingRuntimePermissionAffectsAllPackageInSharedUid() throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+        grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
 
-            uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+    }
 
-            // When the app requesting the permissions is uninstalled, the other apps are no longer
-            // granted the permissions it requested.
-            assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
-            assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CALENDAR)).isFalse();
-        } finally {
-            uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
-            uninstallApp(PKG_THAT_REQUESTS_NO_PERMISSIONS);
-        }
+    @Test
+    public void revokingRuntimePermissionAffectsAllPackageInSharedUid() throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+        grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+        revokePermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isFalse();
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+    }
+
+    @Test(expected = SecurityException.class)
+    public void runtimePermissionsCannotBeRevokedOnPackageThatDoesNotDeclarePermission()
+            throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+        grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+
+        revokePermission(APK_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void runtimePermissionsCannotBeGrantedOnPackageThatDoesNotDeclarePermission()
+            throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+
+        grantPermission(APK_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS);
+    }
+
+    @Test
+    public void sharedUidLoosesRuntimePermissionWhenLastAppDeclaringItGetsUninstalled()
+            throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+        grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+        uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+    }
+
+    @Test
+    public void sharedUidLoosesNormalPermissionWhenLastAppDeclaringItGetsUninstalled()
+            throws Exception {
+        install(APK_THAT_REQUESTS_PERMISSIONS);
+        install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+        uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+
+        assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, INTERNET)).isFalse();
     }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
index 5809cfd..7731a11 100644
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
@@ -29,6 +29,7 @@
 import static android.permission.cts.PermissionUtils.getPermissions;
 import static android.permission.cts.PermissionUtils.grantPermission;
 import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.revokePermission;
 import static android.permission.cts.PermissionUtils.setAppOp;
 import static android.permission.cts.PermissionUtils.setPermissionFlags;
 import static android.permission.cts.PermissionUtils.uninstallApp;
@@ -78,36 +79,15 @@
             TMP_DIR + "CtsAppThatRequestsLocationPermission22.apk";
     private static final String APK_LOCATION_BACKGROUND_29 =
             TMP_DIR + "CtsAppThatRequestsLocationAndBackgroundPermission29.apk";
+    private static final String APK_SHARED_UID_LOCATION_29 =
+            TMP_DIR + "CtsAppWithSharedUidThatRequestsLocationPermission29.apk";
+    private static final String APK_SHARED_UID_LOCATION_28 =
+            TMP_DIR + "CtsAppWithSharedUidThatRequestsLocationPermission28.apk";
 
     private static final UiAutomation sUiAutomation =
             InstrumentationRegistry.getInstrumentation().getUiAutomation();
 
     /**
-     * Revoke a permission from an app.
-     *
-     * <p>This correctly handles pre-M apps by setting the app-ops.
-     * <p>This also correctly handles the location background permission, but does not handle any
-     * other background permission
-     *
-     * @param packageName The app that should have the permission revoked
-     * @param permission The permission to revoke
-     */
-    private void revokePermission(@NonNull String packageName, @NonNull String permission)
-            throws Exception {
-        sUiAutomation.revokeRuntimePermission(packageName, permission);
-
-        if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
-            // The app-op for background location is encoded into the mode of the foreground
-            // location
-            if (isGranted(packageName, ACCESS_COARSE_LOCATION)) {
-                setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
-            }
-        } else {
-            setAppOp(packageName, permission, MODE_IGNORED);
-        }
-    }
-
-    /**
      * Assert that {@link #APP_PKG} requests a certain permission.
      *
      * @param permName The permission that needs to be requested
@@ -277,6 +257,22 @@
     }
 
     /**
+     * If a permission was granted before the split happens, the new permission should inherit the
+     * granted state.
+     *
+     * <p>App using a shared uid
+     */
+    @Test
+    public void inheritGrantedPermissionStateSharedUidApp() throws Exception {
+        install(APK_SHARED_UID_LOCATION_29);
+        grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+        install(APK_SHARED_UID_LOCATION_28);
+
+        assertPermissionGranted(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    /**
      * If a permission has flags before the split happens, the new permission should inherit the
      * flags.
      *
diff --git a/tests/tests/permission2/res/raw/OWNERS b/tests/tests/permission2/res/raw/OWNERS
index 17e828d..f46dbfe 100644
--- a/tests/tests/permission2/res/raw/OWNERS
+++ b/tests/tests/permission2/res/raw/OWNERS
@@ -6,3 +6,4 @@
 yamasani@google.com
 michaelwr@google.com
 narayan@google.com
+per-file automotive_android_manifest.xml = sgurun@google.com
\ No newline at end of file
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
index 975796d..fac0789 100644
--- a/tests/tests/permission2/res/raw/automotive_android_manifest.xml
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -21,48 +21,22 @@
         android:sharedUserId="android.uid.system">
 
     <original-package android:name="com.android.car" />
-
-    <permission-group
+     <permission-group
         android:name="android.car.permission-group.CAR_MONITORING"
-        android:icon="@drawable/car_ic_mode"
+        android:icon="@drawable/perm_group_car"
         android:description="@string/car_permission_desc"
         android:label="@string/car_permission_label" />
     <permission
-        android:name="android.car.permission.ADJUST_CAR_CABIN"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_cabin"
-        android:description="@string/car_permission_desc_cabin" />
-    <permission
         android:name="android.car.permission.CAR_ENERGY"
         android:permissionGroup="android.car.permission-group.CAR_MONITORING"
         android:protectionLevel="dangerous"
         android:label="@string/car_permission_label_energy"
         android:description="@string/car_permission_desc_energy" />
     <permission
-        android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
-        android:protectionLevel="normal"
-        android:label="@string/car_permission_label_car_exterior_environment"
-        android:description="@string/car_permission_desc_car_exterior_environment" />
-    <permission
-        android:name="android.car.permission.CAR_ENERGY_PORTS"
-        android:protectionLevel="normal"
-        android:label="@string/car_permission_label_car_energy_ports"
-        android:description="@string/car_permission_desc_car_energy_ports" />
-    <permission
-        android:name="android.car.permission.CAR_POWERTRAIN"
-        android:protectionLevel="normal"
-        android:label="@string/car_permission_label_car_powertrain"
-        android:description="@string/car_permission_desc_car_powertrain" />
-    <permission
-        android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
+        android:name="android.car.permission.CAR_IDENTIFICATION"
         android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_car_exterior_lights"
-        android:description="@string/car_permission_desc_car_exterior_lights" />
-    <permission
-        android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_control_car_exterior_lights"
-        android:description="@string/car_permission_desc_control_car_exterior_lights" />
+        android:label="@string/car_permission_label_car_identification"
+        android:description="@string/car_permission_desc_car_identification" />
     <permission
         android:name="android.car.permission.CONTROL_CAR_CLIMATE"
         android:protectionLevel="system|signature"
@@ -94,32 +68,42 @@
         android:label="@string/car_permission_label_mileage"
         android:description="@string/car_permission_desc_mileage" />
     <permission
+        android:name="android.car.permission.CAR_TIRES"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_tires"
+        android:description="@string/car_permission_desc_car_tires" />
+    <permission
+        android:name="android.car.permission.READ_CAR_STEERING"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_steering"
+        android:description="@string/car_permission_desc_car_steering" />
+    <permission
+        android:name="android.car.permission.READ_CAR_DISPLAY_UNITS"
+        android:protectionLevel="normal"
+        android:label="@string/car_permission_label_read_car_display_units"
+        android:description="@string/car_permission_desc_read_car_display_units" />
+    <permission
+        android:name="android.car.permission.CONTROL_CAR_DISPLAY_UNITS"
+        android:protectionLevel="normal"
+        android:label="@string/car_permission_label_control_car_display_units"
+        android:description="@string/car_permission_desc_control_car_display_units" />
+    <permission
         android:name="android.car.permission.CAR_SPEED"
         android:permissionGroup="android.permission-group.LOCATION"
         android:protectionLevel="dangerous"
         android:label="@string/car_permission_label_speed"
         android:description="@string/car_permission_desc_speed" />
     <permission
+        android:name="android.car.permission.CAR_ENERGY_PORTS"
+        android:protectionLevel="normal"
+        android:label="@string/car_permission_label_car_energy_ports"
+        android:description="@string/car_permission_desc_car_energy_ports" />
+    <permission
         android:name="android.car.permission.CAR_ENGINE_DETAILED"
         android:protectionLevel="system|signature"
         android:label="@string/car_permission_label_car_engine_detailed"
         android:description="@string/car_permission_desc_car_engine_detailed" />
     <permission
-        android:name="android.car.permission.CAR_TIRES"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_car_tires"
-        android:description="@string/car_permission_desc_car_tires" />
-    <permission
-        android:name="android.car.permission.CAR_IDENTIFICATION"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_car_identification"
-        android:description="@string/car_permission_desc_car_identification" />
-    <permission
-        android:name="android.car.permission.CAR_INFO"
-        android:protectionLevel="normal"
-        android:label="@string/car_permission_label_car_info"
-        android:description="@string/car_permission_desc_car_info" />
-    <permission
         android:name="android.car.permission.CAR_DYNAMICS_STATE"
         android:protectionLevel="system|signature"
         android:label="@string/car_permission_label_vehicle_dynamics_state"
@@ -135,11 +119,61 @@
         android:label="@string/car_permission_label_projection"
         android:description="@string/car_permission_desc_projection" />
     <permission
+            android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"
+            android:protectionLevel="system|signature"
+            android:label="@string/car_permission_label_access_projection_status"
+            android:description="@string/car_permission_desc_access_projection_status" />
+    <permission
+            android:name="android.car.permission.BIND_PROJECTION_SERVICE"
+            android:protectionLevel="signature"
+            android:label="@string/car_permission_label_bind_projection_service"
+            android:description="@string/car_permission_desc_bind_projection_service" />
+    <permission
         android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL"
         android:protectionLevel="system|signature"
         android:label="@string/car_permission_label_mock_vehicle_hal"
         android:description="@string/car_permission_desc_mock_vehicle_hal" />
     <permission
+        android:name="android.car.permission.CAR_INFO"
+        android:protectionLevel="normal"
+        android:label="@string/car_permission_label_car_info"
+        android:description="@string/car_permission_desc_car_info" />
+    <permission
+        android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
+        android:protectionLevel="normal"
+        android:label="@string/car_permission_label_car_exterior_environment"
+        android:description="@string/car_permission_desc_car_exterior_environment" />
+    <permission
+        android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_exterior_lights"
+        android:description="@string/car_permission_desc_car_exterior_lights" />
+    <permission
+        android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_control_car_exterior_lights"
+        android:description="@string/car_permission_desc_control_car_exterior_lights" />
+    <permission
+        android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_interior_lights"
+        android:description="@string/car_permission_desc_car_interior_lights" />
+    <permission
+        android:name="android.car.permission.CONTROL_CAR_INTERIOR_LIGHTS"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_control_car_interior_lights"
+        android:description="@string/car_permission_desc_control_car_interior_lights" />
+    <permission
+        android:name="android.car.permission.CAR_POWER"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_power"
+        android:description="@string/car_permission_desc_car_power" />
+    <permission
+        android:name="android.car.permission.CAR_POWERTRAIN"
+        android:protectionLevel="normal"
+        android:label="@string/car_permission_label_car_powertrain"
+        android:description="@string/car_permission_desc_car_powertrain" />
+    <permission
         android:name="android.car.permission.CAR_NAVIGATION_MANAGER"
         android:protectionLevel="system|signature"
         android:label="@string/car_permission_car_navigation_manager"
@@ -150,10 +184,15 @@
         android:label="@string/car_permission_label_diag_read"
         android:description="@string/car_permission_desc_diag_read" />
     <permission
-        android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
-        android:protectionLevel="system|signature"
-        android:label="@string/car_permission_label_diag_clear"
-        android:description="@string/car_permission_desc_diag_clear" />
+      android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
+      android:protectionLevel="system|signature"
+      android:label="@string/car_permission_label_diag_clear"
+      android:description="@string/car_permission_desc_diag_clear" />
+    <permission
+        android:name="android.car.permission.BIND_VMS_CLIENT"
+        android:protectionLevel="signature"
+        android:label="@string/car_permission_label_bind_vms_client"
+        android:description="@string/car_permission_desc_bind_vms_client" />
     <permission
         android:name="android.car.permission.VMS_PUBLISHER"
         android:protectionLevel="system|signature"
@@ -189,33 +228,59 @@
         android:description="@string/car_permission_desc_audio_settings" />
 
     <permission
-            android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
-            android:protectionLevel="signature"
-            android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
-            android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
+        android:name="android.car.permission.RECEIVE_CAR_AUDIO_DUCKING_EVENTS"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_receive_ducking"
+        android:description="@string/car_permission_desc_receive_ducking" />
 
     <permission
-            android:name="android.car.permission.BIND_CAR_INPUT_SERVICE"
-            android:protectionLevel="signature"
-            android:label="@string/car_permission_label_bind_input_service"
-            android:description="@string/car_permission_desc_bind_input_service"/>
+        android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
+        android:protectionLevel="signature"
+        android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
+        android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
 
     <permission
-            android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
-            android:protectionLevel="system|signature"
-            android:label="@string/car_permission_car_display_in_cluster"
-            android:description="@string/car_permission_desc_car_display_in_cluster" />
+        android:name="android.car.permission.BIND_CAR_INPUT_SERVICE"
+        android:protectionLevel="signature"
+        android:label="@string/car_permission_label_bind_input_service"
+        android:description="@string/car_permission_desc_bind_input_service"/>
 
-    <permission android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
-                android:protectionLevel="system|signature"
-                android:label="@string/car_permission_car_cluster_control"
-                android:description="@string/car_permission_desc_car_cluster_control" />
+    <permission
+        android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_car_display_in_cluster"
+        android:description="@string/car_permission_desc_car_display_in_cluster" />
 
-    <permission android:name="android.car.permission.STORAGE_MONITORING"
+    <permission
+        android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_car_cluster_control"
+        android:description="@string/car_permission_desc_car_cluster_control" />
+
+    <permission
+        android:name="android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_handle_usb_aoap_device"
+        android:description="@string/car_permission_desc_car_handle_usb_aoap_device" />
+
+    <permission
+        android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_car_ux_restrictions_configuration"
+        android:description="@string/car_permission_desc_car_ux_restrictions_configuration" />
+
+    <permission
+        android:name="android.car.permission.STORAGE_MONITORING"
         android:protectionLevel="system|signature"
         android:label="@string/car_permission_label_storage_monitoring"
         android:description="@string/car_permission_desc_storage_monitoring" />
 
+    <permission
+        android:name="android.car.permission.CAR_ENROLL_TRUST"
+        android:protectionLevel="system|signature"
+        android:label="@string/car_permission_label_enroll_trust"
+        android:description="@string/car_permission_desc_enroll_trust" />
+
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
@@ -236,8 +301,9 @@
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.LOCATION_HARDWARE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
 
-    <application android:label="Car service"
+    <application android:label="@string/app_title"
                  android:directBootAware="true"
                  android:allowBackup="false"
                  android:persistent="true">
@@ -250,9 +316,26 @@
             </intent-filter>
         </service>
         <service android:name=".PerUserCarService" android:exported="false" />
+
+        <service
+            android:name="com.android.car.trust.CarBleTrustAgent"
+            android:permission="android.permission.BIND_TRUST_AGENT"
+            android:singleUser="true">
+            <intent-filter>
+                <action android:name="android.service.trust.TrustAgentService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <!-- Warning: the meta data must be included if the service is direct boot aware.
+                If not included, the device will crash before boot completes. Rendering the
+                device unusable. -->
+            <meta-data android:name="android.service.trust.trustagent"
+                       android:resource="@xml/car_trust_agent"/>
+        </service>
         <activity android:name="com.android.car.pm.ActivityBlockingActivity"
                   android:excludeFromRecents="true"
-                  android:exported="false">
+                  android:theme="@android:style/Theme.Translucent.NoTitleBar"
+                  android:exported="false"
+                  android:launchMode="singleTask">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
diff --git a/tests/tests/proto/AndroidTest.xml b/tests/tests/proto/AndroidTest.xml
index 05df543..6237e8f 100644
--- a/tests/tests/proto/AndroidTest.xml
+++ b/tests/tests/proto/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Configuration for Proto Tests">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="metrics" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsProtoTestCases.apk" />
diff --git a/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java b/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
index 5f4ae7b..89b231d 100644
--- a/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
@@ -69,6 +69,9 @@
 import android.provider.DocumentsContract.Path;
 import android.provider.DocumentsProvider;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -78,9 +81,6 @@
 import java.util.Arrays;
 import java.util.List;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
 @RunWith(AndroidJUnit4.class)
 public class DocumentsContractTest {
     private static final String AUTHORITY = "com.example";
@@ -142,6 +142,23 @@
     }
 
     @Test
+    public void testRootUri_returnFalse() {
+        final String auth = "com.example";
+        final String rootId = "rootId";
+        final PackageManager pm = mock(PackageManager.class);
+        final List<ResolveInfo> infoList = new ArrayList<>();
+
+        doReturn(pm).when(mContext).getPackageManager();
+        doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt());
+
+        final Uri uri = DocumentsContract.buildRootUri(auth, rootId);
+
+        assertEquals(auth, uri.getAuthority());
+        assertEquals(rootId, DocumentsContract.getRootId(uri));
+        assertFalse(DocumentsContract.isRootUri(mContext, uri));
+    }
+
+    @Test
     public void testRootsUri() {
         final String auth = "com.example";
         final PackageManager pm = mock(PackageManager.class);
@@ -162,6 +179,20 @@
     }
 
     @Test
+    public void testRootsUri_returnsFalse() {
+        final String auth = "com.example";
+        final PackageManager pm = mock(PackageManager.class);
+        final List<ResolveInfo> infoList = new ArrayList<>();
+
+        doReturn(pm).when(mContext).getPackageManager();
+        doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt());
+
+        final Uri uri = DocumentsContract.buildRootsUri(auth);
+        assertEquals(auth, uri.getAuthority());
+        assertFalse(DocumentsContract.isRootsUri(mContext, uri));
+    }
+
+    @Test
     public void testDocumentUri() {
         final String auth = "com.example";
         final String docId = "doc:12";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
index 51b9907..1c45687 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
@@ -43,6 +43,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -105,6 +106,9 @@
 
     @Test
     public void testScannedDownload() throws Exception {
+        Assume.assumeTrue(MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)
+                || MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(mVolumeName));
+
         final File downloadFile = new File(mDownloadsDir, "colors.txt");
         downloadFile.createNewFile();
         final String fileContents = "RED;GREEN;BLUE";
@@ -116,6 +120,9 @@
 
     @Test
     public void testScannedMediaDownload() throws Exception {
+        Assume.assumeTrue(MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)
+                || MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(mVolumeName));
+
         final File downloadFile = new File(mDownloadsDir, "scenery.png");
         downloadFile.createNewFile();
         try (InputStream in = mContext.getResources().openRawResource(R.raw.scenery);
@@ -135,6 +142,9 @@
 
     @Test
     public void testMediaInDownloadsDir() throws Exception {
+        Assume.assumeTrue(MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)
+                || MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(mVolumeName));
+
         final String displayName = "cts" + System.nanoTime();
         final Uri insertUri = insertImage(displayName, "test image",
                 new File(mDownloadsDir, displayName + ".jpg"), "image/jpeg", R.raw.scenery);
diff --git a/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp b/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
index 47f760a..9d255bd 100644
--- a/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
@@ -33,43 +33,6 @@
 #define TEST_ITERATIONS     100 /* MiB */
 #define TEST_THRESHOLD      2000 /* ms */
 
-/*
- * Detect if filesystem is already encrypted looking at the file
- * system type. It should be possible to check this first but fall
- * back to checking a property value if this is not possible to
- * verify.
- */
-static jboolean checkEncryptedFileSystem() {
-    struct statfs buf;
-    if ((-1 != statfs("/data", &buf)) &&
-        (buf.f_type == 0xf15f /* ecryptfs */)) {
-        return true;
-    }
-    return false;
-}
-
-/*
- * Function: deviceIsEncrypted
- * Purpose: Check the device is encrypted
- * Parameters: none
- * Returns: boolean: (true) if encrypted, (false) otherwise
- * Exceptions: none
- */
-static jboolean android_security_cts_EncryptionTest_deviceIsEncrypted(JNIEnv *, jobject)
-{
-    if (checkEncryptedFileSystem()) {
-        return true;
-    }
-
-    char prop_value[PROP_VALUE_MAX];
-    property_get("ro.crypto.state", prop_value, "");
-
-    jboolean rc = !strcmp(prop_value, "encrypted");
-    ALOGE("EncryptionTest::deviceIsEncrypted: %d", rc);
-
-    return rc;
-}
-
 static inline uint64_t ns()
 {
     struct timespec ts;
@@ -78,12 +41,7 @@
 }
 
 /*
- * Function: aesIsFast
- * Purpose: Test if AES performance is sufficient to require encryption
- * Parameters: none
- * Returns: boolean: (true) if AES performance is acceptable, (false) otherwise
- * Exceptions: InvalidKeyException if EVP_DecryptInit fails, OutOfMemoryError
- *             if memory allocation fails.
+ * Test if AES performance is sufficient to require encryption
  */
 static jboolean android_security_cts_EncryptionTest_aesIsFast(JNIEnv *env, jobject)
 {
@@ -136,8 +94,6 @@
 }
 
 static JNINativeMethod gMethods[] = {
-    { "deviceIsEncrypted", "()Z",
-            (void *) android_security_cts_EncryptionTest_deviceIsEncrypted },
     { "aesIsFast", "()Z",
             (void *) android_security_cts_EncryptionTest_aesIsFast }
 };
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
index 07b39de..f021033a 100644
--- a/tests/tests/security/src/android/security/cts/EncryptionTest.java
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -22,39 +22,65 @@
 import android.test.AndroidTestCase;
 import junit.framework.TestCase;
 
-import android.content.Context;
+import android.os.Build;
 import android.util.Log;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 @SecurityTest
 public class EncryptionTest extends AndroidTestCase {
-
     static {
         System.loadLibrary("ctssecurity_jni");
     }
 
-    private static final int MIN_API_LEVEL = 23;
+    private static final int MIN_ENCRYPTION_REQUIRED_API_LEVEL = 23;
+
+    // First API level where there are no speed exemptions.
+    private static final int MIN_ALL_SPEEDS_API_LEVEL = Build.VERSION_CODES.Q;
+
+    // First API level at which file based encryption must be used.
+    private static final int MIN_FBE_REQUIRED_API_LEVEL = Build.VERSION_CODES.Q;
 
     private static final String TAG = "EncryptionTest";
 
-    private static native boolean deviceIsEncrypted();
-
     private static native boolean aesIsFast();
 
-    private boolean isRequired() {
-        // Optional before MIN_API_LEVEL
-        return PropertyUtil.getFirstApiLevel() >= MIN_API_LEVEL;
+    private void handleUnencryptedDevice() {
+        if (PropertyUtil.getFirstApiLevel() < MIN_ENCRYPTION_REQUIRED_API_LEVEL) {
+            Log.d(TAG, "Exempt from encryption due to an old starting API level.");
+            return;
+        }
+        // In older API levels, we grant an exemption if AES is not fast enough.
+        if (PropertyUtil.getFirstApiLevel() < MIN_ALL_SPEEDS_API_LEVEL) {
+            // Note: aesIsFast() takes ~2 second to run, so it's worth rearranging
+            //     test logic to delay calling this.
+            if (!aesIsFast()) {
+                Log.d(TAG, "Exempt from encryption because AES performance is too low.");
+                return;
+            }
+        }
+        fail("Device encryption is required");
+    }
+
+    private void handleEncryptedDevice() {
+        if ("file".equals(PropertyUtil.getProperty("ro.crypto.type"))) {
+            Log.d(TAG, "Device is encrypted with file-based encryption.");
+            // TODO(b/111311698): If we're able to determine if the hardware
+            //     has AES instructions, confirm that AES, and only AES,
+            //     is in use.  If the hardware does not have AES instructions,
+            //     confirm that either AES or Adiantum is in use.
+            return;
+        }
+        if (PropertyUtil.getFirstApiLevel() < MIN_FBE_REQUIRED_API_LEVEL) {
+            Log.d(TAG, "Device is encrypted.");
+            return;
+        }
+        fail("File-based encryption is required");
     }
 
     public void testEncryption() throws Exception {
-        if (!isRequired() || deviceIsEncrypted()) {
-            return;
+        if ("encrypted".equals(PropertyUtil.getProperty("ro.crypto.state"))) {
+            handleEncryptedDevice();
+        } else {
+            handleUnencryptedDevice();
         }
-
-        // Required if performance is sufficient
-        assertFalse("Device encryption is required", aesIsFast());
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml
index f5a5f28..ae9576e 100644
--- a/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk25/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml
index 8cb520a..78e5ad4 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk27/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml
index c587bf7..71453c9 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk28/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml
index 3ba1f45..f3f7d84 100644
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/simpleperf/AndroidTest.xml b/tests/tests/simpleperf/AndroidTest.xml
index ad94b98..bf6cb9e 100644
--- a/tests/tests/simpleperf/AndroidTest.xml
+++ b/tests/tests/simpleperf/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Config for CTS Simpleperf test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="bionic" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsSimpleperfTestCases->/data/local/tmp/CtsSimpleperfTestCases" />
diff --git a/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java b/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
index 04e5b62..2d4a90c 100644
--- a/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
+++ b/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
@@ -246,8 +246,9 @@
         addAccountAndLetInitialSyncRun(ACCOUNT_1_A, APP1_AUTHORITY);
 
         // App should be brought out of the NEVER bucket to handle the sync
-        assertEquals(UsageStatsManager.STANDBY_BUCKET_WORKING_SET,
-                AmUtils.getStandbyBucket(APP1_PACKAGE));
+        assertTrue("Standby bucket should be WORKING_SET or better",
+                AmUtils.getStandbyBucket(APP1_PACKAGE)
+                        <= UsageStatsManager.STANDBY_BUCKET_WORKING_SET);
 
         // Check the sync request parameters.
         Response res = mRpc.invoke(APP1_PACKAGE,
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 1946772..f61dd54 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -40,6 +40,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -905,8 +906,13 @@
                 TelephonyManager.RADIO_POWER_ON);
         assertThat(mRadioRebootTriggered).isFalse();
         assertThat(mHasRadioPowerOff).isFalse();
-        ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+        boolean success = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
                 (tm) -> tm.rebootRadio());
+        //skip this test if not supported or unsuccessful (success=false)
+        if(!success) {
+            return;
+        }
+
         t.start();
         synchronized (mLock) {
             // reboot takes longer time
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
index f79439b..05acf32 100644
--- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
@@ -51,4 +51,17 @@
             fail("No access to current APN");
         }
     }
+
+    public void testNoAccessToPassword() {
+        try {
+            String selection = Carriers.CURRENT + " IS NOT NULL AND "
+                    + Carriers.PASSWORD + " IS NOT NULL";
+            String[] selectionArgs = null;
+            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
+                    APN_PROJECTION, selection, selectionArgs, null);
+            fail("Expected SecurityExceptio");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
 }
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 1390ccf..828d9b5 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -435,7 +435,7 @@
             uiAutomation.destroy();
             assertTrue(UiAutomationTestA11yService.sConnectedInstance.isConnected());
             getInstrumentation().getUiAutomation(); // Should suppress
-            assertFalse(UiAutomationTestA11yService.sConnectedInstance.isConnected());
+            waitForAccessibilityServiceToUnbind();
         } finally {
             turnAccessibilityOff();
         }
@@ -454,7 +454,7 @@
             UiAutomation suppressingUiAutomation = getInstrumentation().getUiAutomation();
             // We verify above that the connection is broken here. Make sure we see a new one
             // after we destroy it
-            UiAutomationTestA11yService.sConnectedInstance = null;
+            waitForAccessibilityServiceToUnbind();
             suppressingUiAutomation.destroy();
             waitForAccessibilityServiceToStart();
         } finally {
@@ -474,7 +474,7 @@
             getInstrumentation().getUiAutomation();
             // We verify above that the connection is broken here. Make sure we see a new one
             // after we change the flags
-            UiAutomationTestA11yService.sConnectedInstance = null;
+            waitForAccessibilityServiceToUnbind();
             getInstrumentation()
                     .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
             waitForAccessibilityServiceToStart();
@@ -541,12 +541,12 @@
     private void waitForAccessibilityServiceToStart() {
         long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
         while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
-            synchronized(UiAutomationTestA11yService.sWaitObjectForConnecting) {
+            synchronized(UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind) {
                 if (UiAutomationTestA11yService.sConnectedInstance != null) {
                     return;
                 }
                 try {
-                    UiAutomationTestA11yService.sWaitObjectForConnecting.wait(
+                    UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind.wait(
                             timeoutTimeMillis - SystemClock.uptimeMillis());
                 } catch (InterruptedException e) {
                     // Ignored; loop again
@@ -556,6 +556,24 @@
         throw new RuntimeException("Test accessibility service not starting");
     }
 
+    private void waitForAccessibilityServiceToUnbind() {
+        long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
+        while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
+            synchronized(UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind) {
+                if (UiAutomationTestA11yService.sConnectedInstance == null) {
+                    return;
+                }
+                try {
+                    UiAutomationTestA11yService.sWaitObjectForConnectOrUnbind.wait(
+                            timeoutTimeMillis - SystemClock.uptimeMillis());
+                } catch (InterruptedException e) {
+                    // Ignored; loop again
+                }
+            }
+        }
+        throw new RuntimeException("Test accessibility service doesn't unbind");
+    }
+
     private void turnAccessibilityOff() {
         getInstrumentation().getUiAutomation().destroy();
         final Object waitLockForA11yOff = new Object();
@@ -574,7 +592,6 @@
         ContentResolver cr = context.getContentResolver();
         Settings.Secure.putString(
                 cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, null);
-        UiAutomationTestA11yService.sConnectedInstance = null;
         long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
         while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
             synchronized (waitLockForA11yOff) {
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
index 1808630..d062ed5 100644
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestA11yService.java
@@ -26,12 +26,16 @@
  */
 public class UiAutomationTestA11yService extends AccessibilityService {
     private static String LOG_TAG = "UiAutomationTest";
-    public static Object sWaitObjectForConnecting = new Object();
+    public static Object sWaitObjectForConnectOrUnbind = new Object();
 
     public static UiAutomationTestA11yService sConnectedInstance;
 
     @Override
     public boolean onUnbind(Intent intent) {
+        synchronized (sWaitObjectForConnectOrUnbind) {
+            sConnectedInstance = null;
+            sWaitObjectForConnectOrUnbind.notifyAll();
+        }
         Log.v(LOG_TAG, "onUnbind [" + this + "]");
         return false;
     }
@@ -52,9 +56,9 @@
 
     @Override
     protected void onServiceConnected() {
-        synchronized (sWaitObjectForConnecting) {
+        synchronized (sWaitObjectForConnectOrUnbind) {
             sConnectedInstance = this;
-            sWaitObjectForConnecting.notifyAll();
+            sWaitObjectForConnectOrUnbind.notifyAll();
         }
         Log.v(LOG_TAG, "onServiceConnected ["  + this + "]");
     }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java
index 0942ec8..8ea5834 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/WideColorGamutTests.java
@@ -151,11 +151,30 @@
                             .001f));
     }
 
+    private static Color plus(Color a, Color b) {
+        final ColorSpace cs = a.getColorSpace();
+        Assert.assertSame(cs, b.getColorSpace());
+
+        float[] ac = a.getComponents();
+        float[] bc = b.getComponents();
+        float[] result = new float[ac.length];
+        for (int i = 0; i < ac.length; ++i) {
+            // BlendMode.PLUS clamps to [0,1]
+            result[i] = Math.max(Math.min(ac[i] + bc[i], 1.0f), 0.0f);
+        }
+        return Color.valueOf(result, cs);
+    }
+
     @Test
     public void testCanvasDrawColorLongBlendMode() {
         final Color greenP3 = Color.valueOf(0, 1.0f, 0, 1.0f, DISPLAY_P3);
         final Color redP3 = Color.valueOf(1.0f, 0, 0, 1.0f, DISPLAY_P3);
-        final Color expected = Color.valueOf(1.0f, 1.0f, 0, 1.0f, DISPLAY_P3);
+
+        final ColorSpace displaySpace = displaySpace();
+        final Color greenDisplay = greenP3.convert(displaySpace);
+        final Color redDisplay = redP3.convert(displaySpace);
+
+        final Color expected = plus(greenDisplay, redDisplay);
         createTest()
                 .addCanvasClient((canvas, width, height) -> {
                     canvas.drawColor(greenP3.pack());
@@ -167,16 +186,19 @@
                             .002f));
     }
 
-    @Test
-    public void testProPhoto() {
-        Color blueProPhoto = Color.valueOf(0, 0, 1, 1,
-                ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
+    private ColorSpace displaySpace() {
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
         WindowManager window = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         Display defaultDisplay = window.getDefaultDisplay();
         ColorSpace displaySpace = defaultDisplay.getPreferredWideGamutColorSpace();
-        final Color blueDisplay = blueProPhoto.convert(displaySpace == null
-                ? ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) : displaySpace);
+        return displaySpace == null ? ColorSpace.get(ColorSpace.Named.SRGB) : displaySpace;
+    }
+
+    @Test
+    public void testProPhoto() {
+        Color blueProPhoto = Color.valueOf(0, 0, 1, 1,
+                ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
+        final Color blueDisplay = blueProPhoto.convert(displaySpace());
         createTest()
                 .addCanvasClient("RGBA16F_ProPhoto", (canvas, width, height) -> {
                     AssetManager assets = getActivity().getResources().getAssets();
diff --git a/tests/tests/view/AndroidTest.xml b/tests/tests/view/AndroidTest.xml
index 1ee295b..cedc664 100644
--- a/tests/tests/view/AndroidTest.xml
+++ b/tests/tests/view/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for CTS View test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsViewTestCases.apk" />
diff --git a/tests/tests/voiceinteraction/Android.mk b/tests/tests/voiceinteraction/Android.mk
index 2af5e9f..ee73595 100644
--- a/tests/tests/voiceinteraction/Android.mk
+++ b/tests/tests/voiceinteraction/Android.mk
@@ -33,7 +33,7 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index fc8916e..94bef39 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -15,12 +15,11 @@
  */
 package android.voiceinteraction.common;
 
-import android.app.VoiceInteractor;
 import android.app.VoiceInteractor.PickOptionRequest.Option;
+import android.content.LocusId;
 import android.os.Bundle;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 
 public class Utils {
     public enum TestCaseType {
@@ -34,8 +33,11 @@
         PICKOPTION_REQUEST_CANCEL_TEST,
         COMMANDREQUEST_TEST,
         COMMANDREQUEST_CANCEL_TEST,
-        SUPPORTS_COMMANDS_TEST,
+        SUPPORTS_COMMANDS_TEST
     }
+
+    public static final long OPERATION_TIMEOUT_MS = 5000;
+
     public static final String TESTCASE_TYPE = "testcase_type";
     public static final String TESTINFO = "testinfo";
     public static final String BROADCAST_INTENT = "android.intent.action.VOICE_TESTAPP";
@@ -64,6 +66,48 @@
     public static final String PRIVATE_OPTIONS_KEY = "private_key";
     public static final String PRIVATE_OPTIONS_VALUE = "private_value";
 
+    public static final String DIRECT_ACTION_EXTRA_KEY = "directActionExtraKey";
+    public static final String DIRECT_ACTION_EXTRA_VALUE = "directActionExtraValue";
+    public static final String DIRECT_ACTION_FILE_NAME = "directActionFileName";
+    public static final String DIRECT_ACTION_FILE_CONTENT = "directActionFileContent";
+    public static final String DIRECT_ACTION_AUTHORITY =
+            "android.voiceinteraction.testapp.fileprovider";
+
+    public static final String DIRECT_ACTIONS_KEY_CALLBACK = "callback";
+    public static final String DIRECT_ACTIONS_KEY_CANCEL_CALLBACK = "cancelCallback";
+    public static final String DIRECT_ACTIONS_KEY_CONTROL = "control";
+    public static final String DIRECT_ACTIONS_KEY_COMMAND = "command";
+    public static final String DIRECT_ACTIONS_KEY_RESULT = "result";
+    public static final String DIRECT_ACTIONS_KEY_ACTION = "action";
+    public static final String DIRECT_ACTIONS_KEY_ARGUMENTS = "arguments";
+    public static final String DIRECT_ACTIONS_KEY_CLASS = "class";
+
+    public static final String DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION = "performAction";
+    public static final String DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL =
+            "performActionCancel";
+    public static final String DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED =
+            "detectActionsChanged";
+    public static final String DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS = "getActions";
+    public static final String DIRECT_ACTIONS_SESSION_CMD_FINISH = "hide";
+
+    public static final String DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR =
+            "destroyedInteractor";
+    public static final String DIRECT_ACTIONS_ACTIVITY_CMD_FINISH = "finish";
+    public static final String DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS = "invalidateActions";
+
+    public static final String DIRECT_ACTIONS_RESULT_PERFORMED = "performed";
+    public static final String DIRECT_ACTIONS_RESULT_CANCELLED = "cancelled";
+    public static final String DIRECT_ACTIONS_RESULT_EXECUTING = "executing";
+
+
+    public static final String DIRECT_ACTIONS_ACTION_ID = "actionId";
+    public static final Bundle DIRECT_ACTIONS_ACTION_EXTRAS = new Bundle();
+    static {
+        DIRECT_ACTIONS_ACTION_EXTRAS.putString(DIRECT_ACTION_EXTRA_KEY,
+                DIRECT_ACTION_EXTRA_VALUE);
+    }
+    public static final LocusId DIRECT_ACTIONS_LOCUS_ID = new LocusId("locusId");
+
     public static final String toBundleString(Bundle bundle) {
         if (bundle == null) {
             return "*** Bundle is null ****";
diff --git a/tests/tests/voiceinteraction/service/Android.mk b/tests/tests/voiceinteraction/service/Android.mk
index f19cf74..abae526 100644
--- a/tests/tests/voiceinteraction/service/Android.mk
+++ b/tests/tests/voiceinteraction/service/Android.mk
@@ -30,6 +30,6 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
new file mode 100644
index 0000000..fa2e5e3
--- /dev/null
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2019 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 andf
+ * limitations under the License.
+ */
+
+package android.voiceinteraction.service;
+
+import android.app.DirectAction;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.RemoteCallback;
+import android.service.voice.VoiceInteractionSession;
+import android.voiceinteraction.common.Utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
+
+/**
+ * Sessions for testing direct action related functionality
+ */
+public class DirectActionsSession extends VoiceInteractionSession {
+    private final ReentrantLock mLock = new ReentrantLock();
+    private final Condition mCondition = mLock.newCondition();
+
+    // GuardedBy("mLock")
+    private @Nullable ActivityId mActivityId;
+
+    // GuardedBy("mLock")
+    private boolean mActionsInvalidated;
+
+    private static final int OPERATION_TIMEOUT_MS = 1000000;//5000;
+
+    public DirectActionsSession(@NonNull Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onShow(Bundle args, int showFlags) {
+        final RemoteCallback callback = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+
+        final RemoteCallback control = new RemoteCallback((cmdArgs) -> {
+            final String command = cmdArgs.getString(Utils.DIRECT_ACTIONS_KEY_COMMAND);
+            final RemoteCallback commandCallback = cmdArgs.getParcelable(
+                    Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+            switch (command) {
+                case Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION: {
+                    executeWithAssist((result) -> performDirectAction(cmdArgs, result),
+                            commandCallback);
+                } break;
+                case Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL: {
+                    executeWithAssist((result) -> performDirectActionAndCancel(cmdArgs, result),
+                            commandCallback);
+                } break;
+                case Utils.DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS: {
+                    executeWithAssist(this::getDirectActions, commandCallback);
+                } break;
+                case Utils.DIRECT_ACTIONS_SESSION_CMD_FINISH: {
+                    executeWithAssist(this::performHide, commandCallback);
+                } break;
+                case Utils.DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED: {
+                    executeWithAssist(this::detectDirectActionsInvalidated, commandCallback);
+                } break;
+            }
+        });
+
+        final Bundle result = new Bundle();
+        result.putParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL, control);
+        callback.sendResult(result);
+    }
+
+    @Override
+    public void onHandleAssist(AssistState state) {
+        if (state.getIndex() == 0) {
+            mLock.lock();
+            try {
+                mActivityId = state.getActivityId();
+                mCondition.signalAll();
+            } finally {
+                mLock.unlock();
+            }
+        }
+    }
+
+    @Override
+    public void onDirectActionsInvalidated(ActivityId activityId) {
+         mLock.lock();
+         try {
+            mActionsInvalidated = true;
+            mCondition.signalAll();
+        } finally {
+             mLock.unlock();
+         }
+    }
+
+    private void executeWithAssist(@Nullable Consumer<Bundle> command,
+            @NonNull RemoteCallback callback) {
+        mLock.lock();
+        try {
+            if (mActivityId == null) {
+                try {
+                    mCondition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                    /* ignore */
+                }
+            }
+            final Bundle result = new Bundle();
+            if (mActivityId != null) {
+                command.accept(result);
+                callback.sendResult(result);
+            } else {
+                callback.sendResult(result);
+            }
+        } finally {
+            mLock.unlock();
+        }
+    }
+
+    private void getDirectActions(@NonNull Bundle outResult) {
+        final ArrayList<DirectAction> actions = new ArrayList<>();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        mLock.lock();
+        try {
+            requestDirectActions(mActivityId,null, AsyncTask.THREAD_POOL_EXECUTOR, (b) -> {
+                actions.addAll(b);
+                latch.countDown();
+            });
+        } finally {
+            mLock.unlock();
+        }
+        try {
+            latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            /* ignore */
+        }
+
+        outResult.putParcelableArrayList(Utils.DIRECT_ACTIONS_KEY_RESULT, actions);
+    }
+
+    private void performDirectAction(@NonNull Bundle args, @NonNull Bundle outResult) {
+        final DirectAction action = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_ACTION);
+        final Bundle arguments = args.getBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+
+        final Bundle result = new Bundle();
+        final CountDownLatch latch = new CountDownLatch(1);
+        performDirectAction(action, arguments, null, AsyncTask.THREAD_POOL_EXECUTOR, (b) -> {
+            result.putAll(b);
+            latch.countDown();
+        });
+        try {
+            latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            /* ignore */
+        }
+
+        outResult.putBundle(Utils.DIRECT_ACTIONS_KEY_RESULT, result);
+    }
+
+    private void performDirectActionAndCancel(@NonNull Bundle args, @NonNull Bundle outResult) {
+        final DirectAction action = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_ACTION);
+        final Bundle arguments = args.getBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+        final Bundle result = new Bundle();
+
+        final CountDownLatch cancelLatch = new CountDownLatch(1);
+        final RemoteCallback cancelCallback = new RemoteCallback((b) -> {
+            result.clear();
+            result.putAll(b);
+            cancelLatch.countDown();
+        });
+        arguments.putParcelable(Utils.DIRECT_ACTIONS_KEY_CANCEL_CALLBACK, cancelCallback);
+
+        final CancellationSignal cancellationSignal = new CancellationSignal();
+
+        final CountDownLatch resultLatch = new CountDownLatch(1);
+
+        performDirectAction(action, arguments, cancellationSignal,
+                AsyncTask.THREAD_POOL_EXECUTOR, (b) ->
+            resultLatch.countDown()
+        );
+
+        try {
+            resultLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            /* ignore */
+        }
+
+        cancellationSignal.cancel();
+
+        try {
+            cancelLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            /* ignore */
+        }
+
+        outResult.putBundle(Utils.DIRECT_ACTIONS_KEY_RESULT, result);
+    }
+
+    private void detectDirectActionsInvalidated(@NonNull Bundle outResult) {
+        mLock.lock();
+        try {
+            if (!mActionsInvalidated) {
+                try {
+                    mCondition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                    /* ignore */
+                }
+            }
+            outResult.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, mActionsInvalidated);
+            mActionsInvalidated = false;
+        } finally {
+            mLock.unlock();
+        }
+    }
+
+    private void performHide(@NonNull Bundle outResult) {
+        finish();
+        outResult.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+    }
+}
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
index 1af7bd5..037540f 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -17,11 +17,12 @@
 package android.voiceinteraction.service;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionService;
+import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
+import android.voiceinteraction.common.Utils;
 
 public class MainInteractionService extends VoiceInteractionService {
     static final String TAG = "MainInteractionService";
@@ -43,21 +44,28 @@
     }
 
     private void maybeStart() {
-       if (mIntent == null || !mReady) {
+        if (mIntent == null || !mReady) {
             Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
                     + "is not called yet. mIntent = " + mIntent + ", mReady = " + mReady);
         } else {
-            Log.i(TAG, "Yay! about to start session with TestApp");
-            if (isActiveService(this, new ComponentName(this, getClass()))) {
-                Bundle args = new Bundle();
-                Intent intent = new Intent();
-                intent.setComponent(new ComponentName("android.voiceinteraction.testapp",
-                        "android.voiceinteraction.testapp.TestApp"));
-                args.putParcelable("intent", intent);
-                showSession(args, 0);
+            Bundle args = mIntent.getExtras();
+            final String className = (args != null)
+                    ? args.getString(Utils.DIRECT_ACTIONS_KEY_CLASS) : null;
+            if (className == null) {
+                Log.i(TAG, "Yay! about to start session with TestApp");
+                if (isActiveService(this, new ComponentName(this, getClass()))) {
+                    args = new Bundle();
+                    Intent intent = new Intent();
+                    intent.setComponent(new ComponentName("android.voiceinteraction.testapp",
+                            "android.voiceinteraction.testapp.TestApp"));
+                    args.putParcelable("intent", intent);
+                    showSession(args, 0);
+                } else {
+                    Log.wtf(TAG, "**** Not starting MainInteractionService because" +
+                            " it is not set as the current voice interaction service");
+                }
             } else {
-                Log.wtf(TAG, "**** Not starting MainInteractionService because" +
-                    " it is not set as the current voice interaction service");
+                showSession(args, VoiceInteractionSession.SHOW_WITH_ASSIST);
             }
         }
     }
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java
index 43bfc8d..feb8d62 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSessionService.java
@@ -16,13 +16,31 @@
 
 package android.voiceinteraction.service;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionSession;
 import android.service.voice.VoiceInteractionSessionService;
+import android.voiceinteraction.common.Utils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 
 public class MainInteractionSessionService extends VoiceInteractionSessionService {
     @Override
     public VoiceInteractionSession onNewSession(Bundle args) {
-        return new MainInteractionSession(this);
+        final String className = (args != null)
+                ? args.getString(Utils.DIRECT_ACTIONS_KEY_CLASS) : null;
+        if (className == null) {
+            return new MainInteractionSession(this);
+        } else {
+            try {
+                final Constructor<?> constructor = Class.forName(className)
+                        .getConstructor(Context.class);
+                return (VoiceInteractionSession) constructor.newInstance(this);
+            } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+                    | InstantiationException | InvocationTargetException e) {
+                throw new RuntimeException("Cannot instantiate class: " + className, e);
+            }
+        }
     }
 }
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java
index 388b47f..d28151c 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/VoiceInteractionMain.java
@@ -19,13 +19,9 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.content.ComponentName;
-import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-
 public class VoiceInteractionMain extends Activity {
     static final String TAG = "VoiceInteractionMain";
 
@@ -34,7 +30,12 @@
         super.onCreate(savedInstanceState);
         Intent intent = new Intent();
         intent.setComponent(new ComponentName(this, MainInteractionService.class));
+        final Bundle intentExtras = getIntent().getExtras();
+        if (intentExtras != null) {
+            intent.putExtras(intentExtras);
+        }
         ComponentName serviceName = startService(intent);
         Log.i(TAG, "Started service: " + serviceName);
+        finish();
     }
 }
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
new file mode 100644
index 0000000..d9108fd
--- /dev/null
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2019 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 andf
+ * limitations under the License.
+ */
+
+package android.voiceinteraction.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.DirectAction;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.voiceinteraction.common.Utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests for the direction action related functions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DirectActionsTest {
+    private static final long OPERATION_TIMEOUT_MS = 10000000;//5000;
+
+    private final @NonNull SessionControl mSessionControl = new SessionControl();
+    private final @NonNull ActivityControl mActivityControl = new ActivityControl();
+
+    @Test
+    public void testPerformDirectAction() throws Exception {
+        mActivityControl.startActivity();
+        mSessionControl.startVoiceInteractionSession();
+        try {
+            // Get the actions.
+            final List<DirectAction> actions = mSessionControl.getDirectActions();
+
+            // Only the expected action should be reported
+            final DirectAction action = getExpectedDirectActionAssertively(actions);
+
+            // Perform the expected action.
+            final Bundle result = mSessionControl.performDirectAction(action,
+                    createActionArguments());
+
+            // Assert the action completed successfully.
+            assertActionSucceeded(result);
+        } finally {
+            mSessionControl.stopVoiceInteractionSession();
+            mActivityControl.finishActivity();
+        }
+    }
+
+    @Test
+    public void testCancelPerformedDirectAction() throws Exception {
+        mActivityControl.startActivity();
+        mSessionControl.startVoiceInteractionSession();
+        try {
+            // Get the actions.
+            final List<DirectAction> actions = mSessionControl.getDirectActions();
+
+            // Only the expected action should be reported
+            final DirectAction action = getExpectedDirectActionAssertively(actions);
+
+            // Perform the expected action.
+            final Bundle result = mSessionControl.performDirectActionAndCancel(action,
+                    createActionArguments());
+
+            // Assert the action was cancelled.
+            assertActionCancelled(result);
+        } finally {
+            mSessionControl.stopVoiceInteractionSession();
+            mActivityControl.finishActivity();
+        }
+    }
+
+    @Test
+    public void testVoiceInteractorDestroy() throws Exception {
+        mActivityControl.startActivity();
+        mSessionControl.startVoiceInteractionSession();
+        try {
+            // Get the actions to set up the VoiceInteractor
+            mSessionControl.getDirectActions();
+
+            assertThat(mActivityControl.detectInteractorDestroyed(() -> {
+                try {
+                    mSessionControl.stopVoiceInteractionSession();
+                } catch (TimeoutException e) {
+                    /* ignore */
+                }
+            })).isTrue();
+        } finally {
+            mSessionControl.stopVoiceInteractionSession();
+            mActivityControl.finishActivity();
+        }
+    }
+
+    @Test
+    public void testNotifyDirectActionsChanged() throws Exception {
+        mActivityControl.startActivity();
+        mSessionControl.startVoiceInteractionSession();
+        try {
+            // Get the actions to set up the VoiceInteractor
+            mSessionControl.getDirectActions();
+
+            assertThat(mSessionControl.detectDirectActionsInvalidated(() -> {
+                try {
+                    mActivityControl.invalidateDirectActions();
+                } catch (TimeoutException e) {
+                    /* ignore */
+                }
+            })).isTrue();
+        } finally {
+            mSessionControl.stopVoiceInteractionSession();
+            mActivityControl.finishActivity();
+        }
+    }
+    
+    private class SessionControl {
+        private @Nullable RemoteCallback mControl;
+
+        private void startVoiceInteractionSession() throws TimeoutException {
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            final RemoteCallback callback = new RemoteCallback((result) -> {
+                mControl = result.getParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL);
+                latch.countDown();
+            });
+
+            final Intent intent = new Intent();
+            intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CLASS,
+                    "android.voiceinteraction.service.DirectActionsSession");
+            intent.setClassName("android.voiceinteraction.service",
+                    "android.voiceinteraction.service.VoiceInteractionMain");
+            intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            getContext().startActivity(intent);
+
+            try {
+                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    throw new TimeoutException();
+                }
+            } catch (InterruptedException e) {
+                /* cannot happen */
+            }
+        }
+
+        private void stopVoiceInteractionSession() throws TimeoutException {
+            executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_FINISH,
+                    null /*directAction*/, null /*arguments*/, null /*postActionCommand*/);
+        }
+
+        @Nullable List<DirectAction> getDirectActions() throws TimeoutException {
+            final ArrayList<DirectAction> actions = new ArrayList<>();
+            final Bundle result = executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS,
+                    null /*directAction*/, null /*arguments*/, null /*postActionCommand*/);
+            actions.addAll(result.getParcelableArrayList(Utils.DIRECT_ACTIONS_KEY_RESULT));
+            return actions;
+        }
+
+        @Nullable Bundle performDirectAction(@NonNull DirectAction directAction,
+                @NonNull Bundle arguments) throws TimeoutException {
+            return executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION,
+                    directAction, arguments, null /*postActionCommand*/);
+        }
+
+        @Nullable Bundle performDirectActionAndCancel(@NonNull DirectAction directAction,
+                @NonNull Bundle arguments) throws TimeoutException {
+            return executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL,
+                    directAction, arguments, null /*postActionCommand*/);
+        }
+
+        @Nullable boolean detectDirectActionsInvalidated(@NonNull Runnable postActionCommand)
+                throws TimeoutException {
+            final Bundle result = executeCommand(
+                    Utils.DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED,
+                    null /*directAction*/, null /*arguments*/, postActionCommand);
+            return result.getBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT);
+        }
+
+        @Nullable Bundle executeCommand(@NonNull String action, @Nullable DirectAction directAction,
+                @Nullable Bundle arguments, @Nullable Runnable postActionCommand)
+                throws TimeoutException {
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            final Bundle result = new Bundle();
+
+            final RemoteCallback callback = new RemoteCallback((b) -> {
+                result.putAll(b);
+                latch.countDown();
+            });
+
+            final Bundle command = new Bundle();
+            command.putString(Utils.DIRECT_ACTIONS_KEY_COMMAND, action);
+            command.putParcelable(Utils.DIRECT_ACTIONS_KEY_ACTION, directAction);
+            command.putBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS, arguments);
+            command.putParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+
+            mControl.sendResult(command);
+
+            if (postActionCommand != null) {
+                postActionCommand.run();
+            }
+
+            try {
+                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    throw new TimeoutException();
+                }
+            } catch (InterruptedException e) {
+                /* cannot happen */
+            }
+
+            return result;
+        }
+    }
+
+    private final class ActivityControl {
+        private @Nullable RemoteCallback mControl;
+
+        void startActivity() throws TimeoutException {
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            final RemoteCallback callback = new RemoteCallback((result) -> {
+                mControl = result.getParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL);
+                latch.countDown();
+            });
+
+            final Intent intent = new Intent();
+            intent.setClassName("android.voiceinteraction.testapp",
+                    "android.voiceinteraction.testapp.DirectActionsActivity");
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+
+            getContext().startActivity(intent);
+
+            try {
+                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    throw new TimeoutException();
+                }
+            } catch (InterruptedException e) {
+                /* cannot happen */
+            }
+        }
+
+        private boolean detectInteractorDestroyed(Runnable destroyTrigger) throws TimeoutException {
+            final Bundle result = executeRemoteCommand(
+                    Utils.DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR,
+                    destroyTrigger);
+            return result.getBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT);
+        }
+
+        void finishActivity() throws TimeoutException {
+            executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_FINISH,
+                    null /*postActionCommand*/);
+        }
+
+        void invalidateDirectActions() throws TimeoutException {
+            executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS,
+                    null /*postActionCommand*/);
+        }
+
+        Bundle executeRemoteCommand(@NonNull String action,
+                @Nullable Runnable postActionCommand) throws TimeoutException {
+            final Bundle result = new Bundle();
+
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            final RemoteCallback callback = new RemoteCallback((b) -> {
+                if (b != null) {
+                    result.putAll(b);
+                }
+                latch.countDown();
+            });
+
+            final Bundle command = new Bundle();
+            command.putString(Utils.DIRECT_ACTIONS_KEY_COMMAND, action);
+            command.putParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+
+            mControl.sendResult(command);
+
+            if (postActionCommand != null) {
+                postActionCommand.run();
+            }
+
+            try {
+                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    throw new TimeoutException();
+                }
+            } catch (InterruptedException e) {
+                /* cannot happen */
+            }
+            return result;
+        }
+    }
+
+    private @NonNull DirectAction getExpectedDirectActionAssertively(
+            @Nullable List<DirectAction> actions) {
+        final DirectAction action = actions.get(0);
+        assertThat(action.getId()).isEqualTo(Utils.DIRECT_ACTIONS_ACTION_ID);
+        assertThat(action.getExtras().getString(Utils.DIRECT_ACTION_EXTRA_KEY))
+                .isEqualTo(Utils.DIRECT_ACTION_EXTRA_VALUE);
+        assertThat(action.getLocusId().getId()).isEqualTo(Utils.DIRECT_ACTIONS_LOCUS_ID.getId());
+        return action;
+    }
+
+    private @NonNull Bundle createActionArguments() {
+        final Bundle args = new Bundle();
+        args.putString(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS, Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+        return args;
+    }
+
+    private void assertActionSucceeded(@NonNull Bundle result) {
+        final Bundle bundle = result.getBundle(Utils.DIRECT_ACTIONS_KEY_RESULT);
+        final String status = bundle.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
+        assertThat(Utils.DIRECT_ACTIONS_RESULT_PERFORMED).isEqualTo(status);
+    }
+
+    private void assertActionCancelled(@NonNull Bundle result) {
+        final Bundle bundle = result.getBundle(Utils.DIRECT_ACTIONS_KEY_RESULT);
+        final String status = bundle.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
+        assertThat(Utils.DIRECT_ACTIONS_RESULT_CANCELLED).isEqualTo(status);
+    }
+
+    private static @NonNull Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getContext();
+    }
+}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
index 66c945a..1cdd61e 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
@@ -16,24 +16,14 @@
 
 package android.voiceinteraction.cts;
 
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
 import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
 
-import junit.framework.Assert;
-
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import android.voiceinteraction.common.Utils;
-
 public class LocalVoiceInteractionTest
         extends ActivityInstrumentationTestCase2<TestLocalInteractionActivity> {
 
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
index 682667f..920d828 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
@@ -19,12 +19,9 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.content.ComponentName;
-import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
 
-import android.voiceinteraction.common.Utils;
-
 public class TestStartActivity extends Activity {
     static final String TAG = "TestStartActivity";
 
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 732cff2..48f334a 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -25,10 +25,6 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 
-import junit.framework.Assert;
-
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
diff --git a/tests/tests/voiceinteraction/testapp/Android.mk b/tests/tests/voiceinteraction/testapp/Android.mk
index 1c37e51..0191d61 100644
--- a/tests/tests/voiceinteraction/testapp/Android.mk
+++ b/tests/tests/voiceinteraction/testapp/Android.mk
@@ -21,13 +21,14 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceInteractionCommon
+LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceInteractionCommon \
+      androidx.core_core \
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsVoiceInteractionApp
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
index 15069ec..4e7156b 100644
--- a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
@@ -30,5 +30,11 @@
               <category android:name="android.intent.category.VOICE" />
           </intent-filter>
       </activity>
+
+       <activity android:name=".DirectActionsActivity"
+                android:label="Direct actions activity"
+                android:exported="true">
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/tests/voiceinteraction/testapp/res/xml/file_paths.xml b/tests/tests/voiceinteraction/testapp/res/xml/file_paths.xml
new file mode 100644
index 0000000..5d18332
--- /dev/null
+++ b/tests/tests/voiceinteraction/testapp/res/xml/file_paths.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <files-path name="files" path="/" />
+</paths>
+
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
new file mode 100644
index 0000000..3457507
--- /dev/null
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 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 android.voiceinteraction.testapp;
+
+import android.app.Activity;
+import android.app.DirectAction;
+import android.app.VoiceInteractor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.RemoteCallback;
+import android.voiceinteraction.common.Utils;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Activity to test direct action behaviors.
+ */
+public final class DirectActionsActivity extends Activity {
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        final Bundle args = getIntent().getExtras();
+        final RemoteCallback callback = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+
+        final RemoteCallback control = new RemoteCallback((cmdArgs) -> {
+            final String command = cmdArgs.getString(Utils.DIRECT_ACTIONS_KEY_COMMAND);
+            switch (command) {
+                case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR: {
+                    final RemoteCallback commandCallback = cmdArgs.getParcelable(
+                            Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+                    detectDestroyedInteractor(commandCallback);
+                } break;
+                case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_FINISH: {
+                    final RemoteCallback commandCallback = cmdArgs.getParcelable(
+                            Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+                    doFinish(commandCallback);
+                } break;
+                case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS: {
+                    final RemoteCallback commandCallback = cmdArgs.getParcelable(
+                            Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+                    invalidateDirectActions(commandCallback);
+                } break;
+            }
+        });
+
+        final Bundle result = new Bundle();
+        result.putParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL, control);
+        callback.sendResult(result);
+    }
+
+    @Override
+    public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal,
+            @NonNull Consumer<List<DirectAction>> callback) {
+        if (getVoiceInteractor() == null) {
+            callback.accept(Collections.emptyList());
+            return;
+        }
+        final DirectAction action = new DirectAction.Builder(Utils.DIRECT_ACTIONS_ACTION_ID)
+                .setExtras(Utils.DIRECT_ACTIONS_ACTION_EXTRAS)
+                .setLocusId(Utils.DIRECT_ACTIONS_LOCUS_ID)
+                .build();
+
+        final ArrayList<DirectAction> actions = new ArrayList<>();
+        actions.add(action);
+        callback.accept(actions);
+    }
+
+    @Override
+    public void onPerformDirectAction(String actionId, Bundle arguments,
+            CancellationSignal cancellationSignal, Consumer<Bundle> callback) {
+        if (arguments == null || !arguments.getString(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS)
+                .equals(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS)) {
+            reportActionFailed(callback);
+            return;
+        }
+        final RemoteCallback cancelCallback = arguments.getParcelable(
+                Utils.DIRECT_ACTIONS_KEY_CANCEL_CALLBACK);
+        if (cancelCallback != null) {
+            cancellationSignal.setOnCancelListener(() -> reportActionCancelled(
+                    cancelCallback::sendResult));
+            reportActionExecuting(callback);
+        } else {
+            reportActionPerformed(callback);
+        }
+    }
+
+    private void detectDestroyedInteractor(@NonNull RemoteCallback callback) {
+        final Bundle result = new Bundle();
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        final VoiceInteractor interactor = getVoiceInteractor();
+        interactor.registerOnDestroyedCallback(AsyncTask.THREAD_POOL_EXECUTOR, () -> {
+            if (interactor.isDestroyed() && getVoiceInteractor() == null) {
+                result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+            }
+            latch.countDown();
+        });
+
+        try {
+            latch.await(Utils.OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            /* ignore */
+        }
+
+        callback.sendResult(result);
+    }
+
+    private void invalidateDirectActions(@NonNull RemoteCallback callback) {
+        getVoiceInteractor().notifyDirectActionsChanged();
+        final Bundle result = new Bundle();
+        result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+        callback.sendResult(result);
+    }
+
+    private void doFinish(@NonNull RemoteCallback callback) {
+        finish();
+        final Bundle result = new Bundle();
+        result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+        callback.sendResult(result);
+    }
+
+    private static void reportActionPerformed(Consumer<Bundle> callback) {
+        final Bundle result = new Bundle();
+        result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
+                Utils.DIRECT_ACTIONS_RESULT_PERFORMED);
+        callback.accept(result);
+    }
+
+    private static void reportActionCancelled(Consumer<Bundle> callback) {
+        final Bundle result = new Bundle();
+        result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
+                Utils.DIRECT_ACTIONS_RESULT_CANCELLED);
+        callback.accept(result);
+    }
+
+    private static void reportActionExecuting(Consumer<Bundle> callback) {
+        final Bundle result = new Bundle();
+        result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
+                Utils.DIRECT_ACTIONS_RESULT_EXECUTING);
+        callback.accept(result);
+    }
+
+    private static void reportActionFailed(Consumer<Bundle> callback) {
+        callback.accept( new Bundle());
+    }
+}
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
index 11f330e..cb498b6 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -69,43 +69,43 @@
         mTestInProgress = (Utils.TestCaseType.values())[mIndex++];
         testSetup();
         switch (mTestInProgress) {
-          case ABORT_REQUEST_TEST:
-          case ABORT_REQUEST_CANCEL_TEST:
-              abortRequest();
-              break;
+            case ABORT_REQUEST_TEST:
+            case ABORT_REQUEST_CANCEL_TEST:
+                abortRequest();
+                break;
 
-          case COMPLETION_REQUEST_TEST:
-          case COMPLETION_REQUEST_CANCEL_TEST:
-              completionRequest();
-              break;
+            case COMPLETION_REQUEST_TEST:
+            case COMPLETION_REQUEST_CANCEL_TEST:
+                completionRequest();
+                break;
 
-          case CONFIRMATION_REQUEST_TEST:
-          case CONFIRMATION_REQUEST_CANCEL_TEST:
-              confirmationRequest();
-              break;
+            case CONFIRMATION_REQUEST_TEST:
+            case CONFIRMATION_REQUEST_CANCEL_TEST:
+                confirmationRequest();
+                break;
 
-          case PICKOPTION_REQUEST_TEST:
-          case PICKOPTION_REQUEST_CANCEL_TEST:
-              pickOptionRequest();
-              break;
+            case PICKOPTION_REQUEST_TEST:
+            case PICKOPTION_REQUEST_CANCEL_TEST:
+                pickOptionRequest();
+                break;
 
-          case COMMANDREQUEST_TEST:
-          case COMMANDREQUEST_CANCEL_TEST:
-              commandRequest();
-              break;
+            case COMMANDREQUEST_TEST:
+            case COMMANDREQUEST_CANCEL_TEST:
+                commandRequest();
+                break;
 
-          case SUPPORTS_COMMANDS_TEST:
-              String[] commands = {Utils.TEST_COMMAND};
-              boolean[] supported = mInteractor.supportsCommands(commands);
-              Log.i(TAG, "from supportsCommands: " + Arrays.toString(supported));
-              if (supported.length == 1 && supported[0]) {
-                addTestResult(Utils.SUPPORTS_COMMANDS_SUCCESS);
-              } else {
-                addTestResult(Utils.TEST_ERROR + " supported commands failure!");
-              }
-              saveTestResults();
-              continueTests();
-              break;
+            case SUPPORTS_COMMANDS_TEST:
+                String[] commands = {Utils.TEST_COMMAND};
+                boolean[] supported = mInteractor.supportsCommands(commands);
+                Log.i(TAG, "from supportsCommands: " + Arrays.toString(supported));
+                if (supported.length == 1 && supported[0]) {
+                    addTestResult(Utils.SUPPORTS_COMMANDS_SUCCESS);
+                } else {
+                    addTestResult(Utils.TEST_ERROR + " supported commands failure!");
+                }
+                saveTestResults();
+                continueTests();
+                break;
         }
     }
 
@@ -133,11 +133,11 @@
 
     private void broadcastResults() {
         Intent intent = new Intent(Utils.BROADCAST_INTENT)
-            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-            .putExtras(mTotalInfo);
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                .putExtras(mTotalInfo);
         Log.i(TAG, "broadcasting: " + intent + ", Bundle = " + mTotalInfo);
         sendOrderedBroadcast(intent, null, new DoneReceiver(),
-                             null, Activity.RESULT_OK, null, null);
+                null, Activity.RESULT_OK, null, null);
     }
 
     private void confirmationRequest() {
@@ -233,7 +233,7 @@
                         ", selections = " + Utils.toOptionsString(selections) +
                         ", recvd bundle =" + Utils.toBundleString(result));
                 if ((selections.length != 1) ||
-                    !selections[0].getLabel().toString().equals(Utils.PICKOPTON_3)) {
+                        !selections[0].getLabel().toString().equals(Utils.PICKOPTON_3)) {
                     Utils.addErrorResult(result,
                             "Pickoption Selections Not received correctly in TestApp.");
                 } else {
@@ -285,4 +285,4 @@
             Log.i(TAG, "Done_broadcast " + intent.getAction());
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
index c63c676..7c656ba 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -16,6 +16,8 @@
 
 package android.widget.cts;
 
+import static com.android.compatibility.common.util.CtsMockitoUtils.within;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -32,6 +34,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
+import android.os.SystemClock;
 import android.view.Gravity;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -45,20 +48,25 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.CtsTouchUtils;
 import com.android.compatibility.common.util.WidgetTestUtils;
 
+import junit.framework.Assert;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@MediumTest
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class PopupMenuTest {
     private Instrumentation mInstrumentation;
@@ -89,11 +97,23 @@
     @After
     public void teardown() throws Throwable {
         if (mPopupMenu != null) {
-            mActivityRule.runOnUiThread(() -> {
-                mPopupMenu.dismiss();
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            try {
+                mActivityRule.runOnUiThread(() -> {
+                    mPopupMenu.setOnDismissListener((PopupMenu menu) -> {
+                        latch.countDown();
+                    });
+                    mPopupMenu.dismiss();
+                });
+
+                Assert.assertTrue("Expected dismissal occurred within 5 seconds",
+                        latch.await(5, TimeUnit.SECONDS));
+            } catch (Throwable t) {
+                throw new RuntimeException(t);
+            } finally {
                 mPopupMenu = null;
-            });
-            mInstrumentation.waitForIdleSync();
+            }
         }
     }
 
@@ -116,18 +136,24 @@
 
     @Test
     public void testPopulateViaInflater() throws Throwable {
-        mBuilder = new Builder().inflateWithInflater(true);
+        mBuilder = new Builder().inflateWithInflater(true).withDismissListener();
+
         mActivityRule.runOnUiThread(mBuilder::show);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
+        // Verify that nothing has dismissed the popup menu since it was shown
+        verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
 
         verifyMenuContent();
     }
 
     @Test
     public void testDirectPopulate() throws Throwable {
-        mBuilder = new Builder().inflateWithInflater(false);
+        mBuilder = new Builder().inflateWithInflater(false).withDismissListener();
+
         mActivityRule.runOnUiThread(mBuilder::show);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
+        // Verify that nothing has dismissed the popup menu since it was shown
+        verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
 
         verifyMenuContent();
     }
@@ -153,20 +179,24 @@
     @Test
     public void testDismissalViaAPI() throws Throwable {
         mBuilder = new Builder().withDismissListener();
-        mActivityRule.runOnUiThread(mBuilder::show);
 
-        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(mBuilder::show);
+        SystemClock.sleep(2000);
+        // Verify that nothing has dismissed the popup menu since it was shown
         verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
 
         mActivityRule.runOnUiThread(mPopupMenu::dismiss);
-        mInstrumentation.waitForIdleSync();
+        // Verify that the popup menu has been dismissed exactly once
+        verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
         verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
 
         mActivityRule.runOnUiThread(mPopupMenu::dismiss);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
         // Shouldn't have any more interactions with our dismiss listener since the menu was
         // already dismissed when we called dismiss()
         verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+        mPopupMenu = null;
     }
 
     @Test
@@ -177,27 +207,31 @@
         mBuilder = new Builder().withDismissListener()
                 .withPopupStyleResource(R.style.PopupWindow_NullTransitions);
         mActivityRule.runOnUiThread(mBuilder::show);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
+        // Verify that nothing has dismissed the popup menu since it was shown
         verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
 
         mActivityRule.runOnUiThread(
                 () -> mPopupMenu.getMenu().performIdentifierAction(R.id.action_share, 0));
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(500);
 
         mActivityRule.runOnUiThread(
                 () -> mPopupMenu.getMenu().findItem(R.id.action_share).getSubMenu().
                         performIdentifierAction(R.id.action_share_email, 0));
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(500);
 
         mActivityRule.runOnUiThread(mPopupMenu::dismiss);
-        mInstrumentation.waitForIdleSync();
+        // Verify that the popup menu has been dismissed exactly once
+        verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
         verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
 
         mActivityRule.runOnUiThread(mPopupMenu::dismiss);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
         // Shouldn't have any more interactions with our dismiss listener since the menu was
         // already dismissed when we called dismiss()
         verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+        mPopupMenu = null;
     }
 
     @Test
@@ -209,7 +243,9 @@
                 .withPopupMenuContent(R.menu.popup_menu_single)
                 .withPopupStyleResource(R.style.PopupWindow_NullTransitions);
         mActivityRule.runOnUiThread(mBuilder::show);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
+        // Verify that nothing has dismissed the popup menu since it was shown
+        verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
 
         // The call below uses Instrumentation to emulate a tap outside the bounds of the
         // displayed popup menu. This tap is then treated by the framework to be "split" as
@@ -221,8 +257,11 @@
         // sequences as well.
         CtsTouchUtils.emulateTapOnView(mInstrumentation, mActivityRule, mBuilder.mAnchor, 10, -20);
 
-        // At this point our popup should have notified its dismiss listener
+        // Verify that the popup menu has been dismissed exactly once
+        verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
         verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
+
+        mPopupMenu = null;
     }
 
     @Test
@@ -241,8 +280,11 @@
                 mPopupMenu.getMenu().findItem(R.id.action_highlight));
 
         // Popup menu should be automatically dismissed on selecting an item
+        verify(mBuilder.mOnDismissListener, within(1000)).onDismiss(mPopupMenu);
         verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
         verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+        mPopupMenu = null;
     }
 
     @Test
@@ -253,7 +295,7 @@
         mBuilder = new Builder().withDismissListener().withMenuItemClickListener()
                 .withPopupStyleResource(R.style.PopupWindow_NullTransitions);
         mActivityRule.runOnUiThread(mBuilder::show);
-        mInstrumentation.waitForIdleSync();
+        SystemClock.sleep(2000);
 
         // Verify that our menu item click listener hasn't been called yet
         verify(mBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
@@ -279,12 +321,15 @@
         // Popup menu should be automatically dismissed on selecting an item
         verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
         verifyNoMoreInteractions(mBuilder.mOnDismissListener);
+
+        mPopupMenu = null;
     }
 
     @Test
     public void testItemViewAttributes() throws Throwable {
         mBuilder = new Builder().withDismissListener().withAnchorId(R.id.anchor_upper_left);
-        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
+        mActivityRule.runOnUiThread(mBuilder::show);
+        SystemClock.sleep(2000);
 
         Menu menu = mPopupMenu.getMenu();
         ListView menuItemList = mPopupMenu.getMenuListView();
@@ -322,7 +367,8 @@
     private void testGroupDivider(boolean groupDividerEnabled) throws Throwable {
         mBuilder = new Builder().withGroupDivider(groupDividerEnabled)
             .withAnchorId(R.id.anchor_upper_left);
-        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
+        mActivityRule.runOnUiThread(mBuilder::show);
+        SystemClock.sleep(2000);
 
         Menu menu = mPopupMenu.getMenu();
         ListView menuItemList = mPopupMenu.getMenuListView();
@@ -359,7 +405,8 @@
 
     private void testForceShowIcon(boolean forceShowIcon) throws Throwable {
         mBuilder = new Builder().withForceShowIcon(forceShowIcon);
-        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::configure, true);
+        mActivityRule.runOnUiThread(mBuilder::configure);
+
         final TestColorDrawable drawable = new TestColorDrawable(Color.BLUE);
         mPopupMenu.getMenu().getItem(0).setIcon(drawable);
         WidgetTestUtils.runOnMainAndDrawSync(
diff --git a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
index 08dd6a5..5b64009 100644
--- a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
@@ -28,19 +28,19 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
-import android.app.Instrumentation;
 import android.content.res.Resources;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.view.inputmethod.EditorInfo;
 import android.widget.SearchView;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.WidgetTestUtils;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -52,7 +52,6 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class SearchViewTest {
-    private Instrumentation mInstrumentation;
     private Activity mActivity;
     private SearchView mSearchView;
 
@@ -62,7 +61,6 @@
 
     @Before
     public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
         mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
     }
@@ -123,8 +121,8 @@
             mSearchView.setIconified(false);
         });
 
-        mActivityRule.runOnUiThread(() -> mSearchView.setIconified(true));
-        mInstrumentation.waitForIdleSync();
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+                () -> mSearchView.setIconified(true), true);
 
         // Since our search view is marked with iconifiedByDefault=false, call to setIconified
         // with true us going to be ignored, as detailed in the class-level documentation of
@@ -144,8 +142,8 @@
         when(mockDenyCloseListener.onClose()).thenReturn(Boolean.TRUE);
         mSearchView.setOnCloseListener(mockDenyCloseListener);
 
-        mActivityRule.runOnUiThread(() -> mSearchView.setIconified(true));
-        mInstrumentation.waitForIdleSync();
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+                () -> mSearchView.setIconified(true), true);
 
         // Our mock listener is configured to return true from its onClose, thereby preventing
         // the iconify request to be completed. Check that the listener was called and that the
@@ -166,8 +164,8 @@
         when(mockAllowCloseListener.onClose()).thenReturn(Boolean.FALSE);
         mSearchView.setOnCloseListener(mockAllowCloseListener);
 
-        mActivityRule.runOnUiThread(() -> mSearchView.setIconified(true));
-        mInstrumentation.waitForIdleSync();
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+                () -> mSearchView.setIconified(true), true);
 
         // Our mock listener is configured to return false from its onClose, thereby allowing
         // the iconify request to be completed. Check that the listener was called and that the
@@ -183,15 +181,16 @@
         final int maxWidth2 = res.getDimensionPixelSize(R.dimen.search_view_max_width2);
 
         // Set search view to not be iconified before running max-width tests
-        mActivityRule.runOnUiThread(() -> mSearchView.setIconified(false));
-
-        mActivityRule.runOnUiThread(() -> mSearchView.setMaxWidth(maxWidth1));
-        mInstrumentation.waitForIdleSync();
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+                () -> {
+                    mSearchView.setIconified(false);
+                    mSearchView.setMaxWidth(maxWidth1);
+                }, true);
         assertEquals(maxWidth1, mSearchView.getMaxWidth());
         assertTrue(mSearchView.getWidth() <= maxWidth1);
 
-        mActivityRule.runOnUiThread(() -> mSearchView.setMaxWidth(maxWidth2));
-        mInstrumentation.waitForIdleSync();
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
+                () -> mSearchView.setMaxWidth(maxWidth2), true);
         assertEquals(maxWidth2, mSearchView.getMaxWidth());
         assertTrue(mSearchView.getWidth() <= maxWidth2);
     }
diff --git a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
index 99852e8..d5d5428 100644
--- a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
@@ -49,6 +49,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.WidgetTestUtils;
 
 import org.junit.Before;
@@ -404,9 +405,8 @@
 
         // Dismiss the popup with the emulated back key
         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        mInstrumentation.waitForIdleSync();
         // Verify that we're not showing the popup
-        assertFalse(mSpinnerDropdownMode.isPopupShowing());
+        PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing());
 
         // Set yellow background on the popup
         mActivityRule.runOnUiThread(() ->
@@ -444,9 +444,8 @@
 
         // Dismiss the popup with the emulated back key
         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        mInstrumentation.waitForIdleSync();
         // Verify that we're not showing the popup
-        assertFalse(mSpinnerDialogMode.isPopupShowing());
+        PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing());
 
         // Set yellow background on the popup
         mActivityRule.runOnUiThread(() ->
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index efc5440..72b5af8 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
@@ -37,6 +36,8 @@
 import android.widget.ImageView;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.LargeTest;
@@ -47,11 +48,16 @@
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.compatibility.common.util.TestUtils;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class ToastTest {
@@ -63,7 +69,6 @@
     private static final long TIME_OUT = 5000L;
     private Toast mToast;
     private Context mContext;
-    private Instrumentation mInstrumentation;
     private boolean mLayoutDone;
     private ViewTreeObserver.OnGlobalLayoutListener mLayoutListener;
 
@@ -73,7 +78,6 @@
 
     @Before
     public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mContext = InstrumentationRegistry.getTargetContext();
         mLayoutListener = () -> mLayoutDone = true;
     }
@@ -118,7 +122,6 @@
     private void makeToast() throws Throwable {
         mActivityRule.runOnUiThread(
                 () -> mToast = Toast.makeText(mContext, TEST_TOAST_TEXT, Toast.LENGTH_LONG));
-        mInstrumentation.waitForIdleSync();
     }
 
     @Test
@@ -131,8 +134,7 @@
         assertNull(view.getParent());
         assertEquals(View.VISIBLE, view.getVisibility());
 
-        mActivityRule.runOnUiThread(mToast::show);
-        mInstrumentation.waitForIdleSync();
+        runOnMainAndDrawSync(view, mToast::show);
 
         // view will be attached to screen when show it
         assertEquals(View.VISIBLE, view.getVisibility());
@@ -160,7 +162,6 @@
             mToast.show();
             mToast.cancel();
         });
-        mInstrumentation.waitForIdleSync();
 
         assertNotShowToast(view);
     }
@@ -174,11 +175,10 @@
         Drawable drawable = mContext.getResources().getDrawable(R.drawable.pass);
         imageView.setImageDrawable(drawable);
 
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(imageView, () -> {
             mToast.setView(imageView);
             mToast.show();
         });
-        mInstrumentation.waitForIdleSync();
         assertSame(imageView, mToast.getView());
         assertShowAndHide(imageView);
     }
@@ -187,8 +187,7 @@
     public void testAccessDuration() throws Throwable {
         long start = SystemClock.uptimeMillis();
         makeToast();
-        mActivityRule.runOnUiThread(mToast::show);
-        mInstrumentation.waitForIdleSync();
+        runOnMainAndDrawSync(mToast.getView(), mToast::show);
         assertEquals(Toast.LENGTH_LONG, mToast.getDuration());
 
         View view = mToast.getView();
@@ -196,11 +195,10 @@
         long longDuration = SystemClock.uptimeMillis() - start;
 
         start = SystemClock.uptimeMillis();
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(mToast.getView(), () -> {
             mToast.setDuration(Toast.LENGTH_SHORT);
             mToast.show();
         });
-        mInstrumentation.waitForIdleSync();
         assertEquals(Toast.LENGTH_SHORT, mToast.getDuration());
 
         view = mToast.getView();
@@ -218,8 +216,7 @@
             mToast.show();
         };
         long start = SystemClock.uptimeMillis();
-        mActivityRule.runOnUiThread(showToast);
-        mInstrumentation.waitForIdleSync();
+        runOnMainAndDrawSync(mToast.getView(), showToast);
         assertShowAndHide(mToast.getView());
         final long shortDuration = SystemClock.uptimeMillis() - start;
 
@@ -232,8 +229,7 @@
             waitForA11yRecommendedTimeoutChanged(mContext,
                     ACCESSIBILITY_STATE_WAIT_TIMEOUT_MS, a11ySettingDuration);
             start = SystemClock.uptimeMillis();
-            mActivityRule.runOnUiThread(showToast);
-            mInstrumentation.waitForIdleSync();
+            runOnMainAndDrawSync(mToast.getView(), showToast);
             assertShowAndHide(mToast.getView());
             final long a11yDuration = SystemClock.uptimeMillis() - start;
             assertTrue(a11yDuration >= a11ySettingDuration);
@@ -284,12 +280,11 @@
 
         final float horizontal1 = 1.0f;
         final float vertical1 = 1.0f;
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(view, () -> {
             mToast.setMargin(horizontal1, vertical1);
             mToast.show();
             registerLayoutListener(mToast.getView());
         });
-        mInstrumentation.waitForIdleSync();
         assertShowToast(view);
 
         assertEquals(horizontal1, mToast.getHorizontalMargin(), 0.0f);
@@ -305,12 +300,11 @@
 
         final float horizontal2 = 0.1f;
         final float vertical2 = 0.1f;
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(view, () -> {
             mToast.setMargin(horizontal2, vertical2);
             mToast.show();
             registerLayoutListener(mToast.getView());
         });
-        mInstrumentation.waitForIdleSync();
         assertShowToast(view);
 
         assertEquals(horizontal2, mToast.getHorizontalMargin(), 0.0f);
@@ -342,12 +336,11 @@
     @Test
     public void testAccessGravity() throws Throwable {
         makeToast();
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(mToast.getView(), () -> {
             mToast.setGravity(Gravity.CENTER, 0, 0);
             mToast.show();
             registerLayoutListener(mToast.getView());
         });
-        mInstrumentation.waitForIdleSync();
         View view = mToast.getView();
         assertShowToast(view);
         assertEquals(Gravity.CENTER, mToast.getGravity());
@@ -358,12 +351,11 @@
         view.getLocationOnScreen(centerXY);
         assertShowAndHide(view);
 
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(mToast.getView(), () -> {
             mToast.setGravity(Gravity.BOTTOM, 0, 0);
             mToast.show();
             registerLayoutListener(mToast.getView());
         });
-        mInstrumentation.waitForIdleSync();
         view = mToast.getView();
         assertShowToast(view);
         assertEquals(Gravity.BOTTOM, mToast.getGravity());
@@ -381,12 +373,11 @@
 
         final int xOffset = 20;
         final int yOffset = 10;
-        mActivityRule.runOnUiThread(() -> {
+        runOnMainAndDrawSync(mToast.getView(), () -> {
             mToast.setGravity(Gravity.BOTTOM, xOffset, yOffset);
             mToast.show();
             registerLayoutListener(mToast.getView());
         });
-        mInstrumentation.waitForIdleSync();
         view = mToast.getView();
         assertShowToast(view);
         assertEquals(Gravity.BOTTOM, mToast.getGravity());
@@ -425,7 +416,7 @@
 
     @UiThreadTest
     @Test(expected=NullPointerException.class)
-    public void testMaketTextFromStringNullContext() {
+    public void testMakeTextFromStringNullContext() {
         Toast.makeText(null, "test", Toast.LENGTH_LONG);
     }
 
@@ -448,7 +439,7 @@
 
     @UiThreadTest
     @Test(expected=NullPointerException.class)
-    public void testMaketTextFromResourceNullContext() {
+    public void testMakeTextFromResourceNullContext() {
         Toast.makeText(null, R.string.hello_android, Toast.LENGTH_SHORT);
     }
 
@@ -490,4 +481,38 @@
         toast.setView(null);
         toast.setText(null);
     }
+
+    private void runOnMainAndDrawSync(@NonNull final View toastView,
+            @Nullable final Runnable runner) {
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        try {
+            mActivityRule.runOnUiThread(() -> {
+                final ViewTreeObserver.OnDrawListener listener =
+                        new ViewTreeObserver.OnDrawListener() {
+                            @Override
+                            public void onDraw() {
+                                // posting so that the sync happens after the draw that's about
+                                // to happen
+                                toastView.post(() -> {
+                                    toastView.getViewTreeObserver().removeOnDrawListener(this);
+                                    latch.countDown();
+                                });
+                            }
+                        };
+
+                toastView.getViewTreeObserver().addOnDrawListener(listener);
+
+                if (runner != null) {
+                    runner.run();
+                }
+                toastView.invalidate();
+            });
+
+            Assert.assertTrue("Expected toast draw pass occurred within 5 seconds",
+                    latch.await(5, TimeUnit.SECONDS));
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
index 227aeeb..2e3dca6 100644
--- a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
@@ -45,6 +45,7 @@
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.WidgetTestUtils;
 
 import org.junit.Before;
@@ -167,13 +168,11 @@
 
         // Ask to show overflow menu and check that it's showing
         mActivityRule.runOnUiThread(() -> mMainToolbar.showOverflowMenu());
-        mInstrumentation.waitForIdleSync();
-        assertTrue(mMainToolbar.isOverflowMenuShowing());
+        PollingCheck.waitFor(() -> mMainToolbar.isOverflowMenuShowing());
 
         // Ask to hide the overflow menu and check that it's not showing
         mActivityRule.runOnUiThread(() -> mMainToolbar.hideOverflowMenu());
-        mInstrumentation.waitForIdleSync();
-        assertFalse(mMainToolbar.isOverflowMenuShowing());
+        PollingCheck.waitFor(() -> !mMainToolbar.isOverflowMenuShowing());
     }
 
     @Test
@@ -185,8 +184,7 @@
 
         // Ask to show overflow menu and check that it's showing
         mActivityRule.runOnUiThread(mMainToolbar::showOverflowMenu);
-        mInstrumentation.waitForIdleSync();
-        assertTrue(mMainToolbar.isOverflowMenuShowing());
+        PollingCheck.waitFor(() -> mMainToolbar.isOverflowMenuShowing());
 
         // Register a mock menu item click listener on the toolbar
         Toolbar.OnMenuItemClickListener menuItemClickListener =
@@ -203,8 +201,7 @@
 
         // Ask to dismiss all the popups and check that we're not showing the overflow menu
         mActivityRule.runOnUiThread(mMainToolbar::dismissPopupMenus);
-        mInstrumentation.waitForIdleSync();
-        assertFalse(mMainToolbar.isOverflowMenuShowing());
+        PollingCheck.waitFor(() -> !mMainToolbar.isOverflowMenuShowing());
     }
 
     @Test
@@ -234,29 +231,25 @@
         // action view
         final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
         mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
-        mInstrumentation.waitForIdleSync();
-        assertTrue(searchMenuItem.isActionViewExpanded());
-        assertTrue(mMainToolbar.hasExpandedActionView());
+        PollingCheck.waitFor(() ->
+                searchMenuItem.isActionViewExpanded() && mMainToolbar.hasExpandedActionView());
 
         // Collapse search menu item's action view and verify that main toolbar doesn't have an
         // expanded action view
         mActivityRule.runOnUiThread(searchMenuItem::collapseActionView);
-        mInstrumentation.waitForIdleSync();
-        assertFalse(searchMenuItem.isActionViewExpanded());
-        assertFalse(mMainToolbar.hasExpandedActionView());
+        PollingCheck.waitFor(() ->
+                !searchMenuItem.isActionViewExpanded() && !mMainToolbar.hasExpandedActionView());
 
         // Expand search menu item's action view again
         mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
-        mInstrumentation.waitForIdleSync();
-        assertTrue(searchMenuItem.isActionViewExpanded());
-        assertTrue(mMainToolbar.hasExpandedActionView());
+        PollingCheck.waitFor(() ->
+                searchMenuItem.isActionViewExpanded() && mMainToolbar.hasExpandedActionView());
 
         // Now collapse search menu item's action view via toolbar's API and verify that main
         // toolbar doesn't have an expanded action view
         mActivityRule.runOnUiThread(mMainToolbar::collapseActionView);
-        mInstrumentation.waitForIdleSync();
-        assertFalse(searchMenuItem.isActionViewExpanded());
-        assertFalse(mMainToolbar.hasExpandedActionView());
+        PollingCheck.waitFor(() ->
+                !searchMenuItem.isActionViewExpanded() && !mMainToolbar.hasExpandedActionView());
     }
 
     @Test
@@ -294,7 +287,8 @@
                 () -> mMainToolbar.inflateMenu(R.menu.toolbar_menu_search));
         final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
         mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
-        mInstrumentation.waitForIdleSync();
+        PollingCheck.waitFor(() ->
+                searchMenuItem.isActionViewExpanded() && mMainToolbar.hasExpandedActionView());
 
         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
                 () -> mMainToolbar.setCollapseIcon(R.drawable.icon_green));
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
index 986e024..1e81ae0 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
@@ -29,7 +29,6 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.app.Activity;
-import android.app.Instrumentation;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
@@ -41,7 +40,6 @@
 import android.widget.MediaController;
 import android.widget.VideoView;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
@@ -84,7 +82,6 @@
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .build();
 
-    private Instrumentation mInstrumentation;
     private Activity mActivity;
     private VideoView mVideoView;
     private String mVideoPath;
@@ -95,7 +92,6 @@
 
     @Before
     public void setup() throws Throwable {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
         mVideoView = (VideoView) mActivity.findViewById(R.id.videoview);
 
@@ -124,7 +120,6 @@
             MediaController mediaController = new MediaController(mActivity);
             mVideoView.setMediaController(mediaController);
         });
-        mInstrumentation.waitForIdleSync();
     }
 
     @UiThreadTest
@@ -224,7 +219,6 @@
             mVideoView.setVideoPath(path);
             mVideoView.start();
         });
-        mInstrumentation.waitForIdleSync();
 
         verify(mockErrorListener, within(TIME_OUT)).onError(
                 any(MediaPlayer.class), anyInt(), anyInt());
@@ -245,7 +239,6 @@
         mVideoView.setOnPreparedListener(mockPreparedListener);
 
         mActivityRule.runOnUiThread(() -> mVideoView.setVideoPath(mVideoPath));
-        mInstrumentation.waitForIdleSync();
 
         verify(mockPreparedListener, within(TIME_OUT)).onPrepared(any(MediaPlayer.class));
         verify(mockPreparedListener, times(1)).onPrepared(any(MediaPlayer.class));
diff --git a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
index 1cd5c26..a223b97 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
-import android.app.Instrumentation;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Xml;
@@ -30,7 +29,6 @@
 import android.widget.TextView;
 import android.widget.ViewFlipper;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
@@ -49,7 +47,6 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class ViewFlipperTest {
-    private Instrumentation mInstrumentation;
     private Activity mActivity;
 
     @Rule
@@ -58,7 +55,6 @@
 
     @Before
     public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
     }
 
@@ -113,7 +109,6 @@
 
         // wait for a longer time to make sure the view flipping is completed.
         SystemClock.sleep(flipInterval + 200);
-        mInstrumentation.waitForIdleSync();
         mActivityRule.runOnUiThread(() -> {
             ViewFlipper viewFlipper =
                     (ViewFlipper) mActivity.findViewById(R.id.viewflipper_test);
@@ -127,7 +122,6 @@
         });
 
         SystemClock.sleep(flipInterval + 200);
-        mInstrumentation.waitForIdleSync();
         mActivityRule.runOnUiThread(() -> {
             ViewFlipper viewFlipper =
                     (ViewFlipper) mActivity.findViewById(R.id.viewflipper_test);
diff --git a/tests/tests/widget/src/android/widget/cts/util/ListUtil.java b/tests/tests/widget/src/android/widget/cts/util/ListUtil.java
index 8215b9f..4c1b4fd 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ListUtil.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ListUtil.java
@@ -38,33 +38,6 @@
     }
 
     /**
-     * Set the selected position of the list view.
-     * @param pos The desired position.
-     */
-    public final void setSelectedPosition(final int pos) {
-        mListView.post(new Runnable() {
-            public void run() {
-                mListView.setSelection(pos);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-    }
-
-    /**
-     * Get the top of the list.
-     */
-    public final int getListTop() {
-        return mListView.getListPaddingTop();
-    }
-
-    /**
-     * Get the bottom of the list.
-     */
-    public final int getListBottom() {
-        return mListView.getHeight() - mListView.getListPaddingBottom();
-    }
-
-    /**
      * Arrow (up or down as appropriate) to the desired position in the list.
      * @param desiredPos The desired position
      * @throws IllegalStateException if the position can't be reached within 20 presses.
diff --git a/tests/video/AndroidTest.xml b/tests/video/AndroidTest.xml
index 1c02dae..aa1b576 100644
--- a/tests/video/AndroidTest.xml
+++ b/tests/video/AndroidTest.xml
@@ -27,5 +27,7 @@
         <!-- test-timeout unit is ms, value = 10 min -->
         <option name="test-timeout" value="600000" />
         <option name="runtime-hint" value="42m45s" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
index 07e7c6c..9ce6ada 100644
--- a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
+++ b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
@@ -28,6 +28,7 @@
 import android.media.cts.CodecImage;
 import android.media.cts.CodecUtils;
 import android.media.cts.YUVImage;
+import android.os.Build;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -141,6 +142,15 @@
 
     private TestConfig mTestConfig;
 
+    // Performance numbers only make sense on real devices, so skip on non-real devices
+    public static boolean frankenDevice() {
+        if (("Android".equals(Build.BRAND) || "generic".equals(Build.BRAND)) &&
+            (Build.MODEL.startsWith("AOSP on ") || Build.PRODUCT.startsWith("aosp_"))) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     protected void setUp() throws Exception {
         mEncodedOutputBuffer = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
@@ -696,6 +706,10 @@
         mTestConfig.mMaxTimeMs = Math.min(
                 mTestConfig.mMaxTimeMs, MAX_TEST_TIMEOUT_MS / 5 * 4 / codingPasses
                         / mTestConfig.mNumberOfRepeat);
+        // reduce test-run on non-real devices
+        if (frankenDevice()) {
+            mTestConfig.mMaxTimeMs /= 10;
+        }
 
         mVideoWidth = w;
         mVideoHeight = h;
@@ -802,7 +816,12 @@
         if (isPerf) {
             String error = MediaPerfUtils.verifyAchievableFrameRates(
                     encoderName, mimeType, w, h, measuredFps);
-            assertNull(error, error);
+            if (frankenDevice() && error != null) {
+                // ensure there is data, but don't insist that it is correct
+                assertFalse(error, error.startsWith("Failed to get "));
+            } else {
+                assertNull(error, error);
+            }
         }
         assertTrue(success);
     }
diff --git a/tests/vr/AndroidTest.xml b/tests/vr/AndroidTest.xml
index ee9d9cb..cca1a02a 100644
--- a/tests/vr/AndroidTest.xml
+++ b/tests/vr/AndroidTest.xml
@@ -17,7 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="vr" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsVrTestCases.apk" />