Merge "Test NdkMediaDataSource"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ff340b4..7ed77ac 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3581,6 +3581,51 @@
     <string name="managed_user_check_managed_user_test">Check affiliated profile owner</string>
     <string name="managed_user_incorrect_managed_user">Missing or incorrect affiliated profile owner: CTSVerifier is not affilaited PO!</string>
 
+    <string name="device_owner_disallow_user_switch">Disallow user switch</string>
+    <string name="device_owner_disallow_user_switch_info">
+        Please press the Set restriction button to set the user restriction.
+        Then press Go to open the Settings, and manually find and open user settings section.
+        Confirm that:\n
+        \n
+        - Trying to switch to guest users triggers a support message.\n
+        - Trying to switch to secondary users triggers a support message.\n
+        \n
+        In additional, if quick settings is available, confirm that user switcher is hidden or
+        disabled.
+        Use the Back button to return to this page.
+    </string>
+
+    <string name="device_owner_user_switcher_message">User switcher message</string>
+    <string name="device_owner_user_switcher_message_info">
+        1. Please press the \'With user switcher message\' button to set the user switcher message.
+        You will then be automatically switched to a secondary user. If a user switcher dialog shows
+        up, it should read \'Start user session\'. Wait until you are automatically switched back to
+        primary, if a user switcher dialog shows up, it should read \'End user session\'.
+
+        \n
+        2. Please press the \'Without user switcher message\' button to clear the user switcher
+        message. You will then be automatically switched to a secondary user. If a user switcher
+        dialog shows up, it should read \'Switching to managed user\'. Wait until you are
+        automatically switched back to primary, if a user switcher dialog shows up, it should read
+        \'Switching to (name of primary user)\'.
+    </string>
+    <string name="device_owner_with_user_switcher_message">With user switcher message</string>
+    <string name="device_owner_without_user_switcher_message">Without user switcher message</string>
+
+    <string name="device_owner_enable_logout">Logout</string>
+    <string name="device_owner_enable_logout_info">
+        Please press the Go button to enable logout. You will then be switched to a newly created
+        user.
+        Look for a way to logout the current user without unlocking the lock screen. The control is
+        usually named \'End session\'.\n
+        The location may vary depending on manufacturer, typical locations are:\n
+        - In power button menu by long pressing power button.\n
+        - On the lock screen.\n
+        \n
+        When successfully logout and switched back to primary user, confirm that the logout control
+        is not available in primary user.
+    </string>
+
     <!-- Strings for JobScheduler Tests -->
     <string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index c62f111..7384f5b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -111,6 +111,10 @@
     public static final String COMMAND_SET_DEFAULT_IME = "set-default-ime";
     public static final String COMMAND_CLEAR_DEFAULT_IME = "clear-default-ime";
     public static final String COMMAND_CREATE_MANAGED_USER = "create-managed-user";
+    public static final String COMMAND_WITH_USER_SWITCHER_MESSAGE = "with-user-switcher-message";
+    public static final String COMMAND_WITHOUT_USER_SWITCHER_MESSAGE =
+            "without-user-switcher-message";
+    public static final String COMMAND_ENABLE_LOGOUT = "enable-logout";
 
     public static final String EXTRA_USER_RESTRICTION =
             "com.android.cts.verifier.managedprovisioning.extra.USER_RESTRICTION";
@@ -486,6 +490,22 @@
                             Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
                     mDpm.startUserInBackground(mAdmin, userHandle);
                 } break;
+                case COMMAND_WITH_USER_SWITCHER_MESSAGE: {
+                    createAndSwitchUserWithMessage("Start user session", "End user session");
+                } break;
+                case COMMAND_WITHOUT_USER_SWITCHER_MESSAGE: {
+                    createAndSwitchUserWithMessage(null, null);
+                } break;
+                case COMMAND_ENABLE_LOGOUT: {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                        return;
+                    }
+                    mDpm.addUserRestriction(mAdmin, UserManager.DISALLOW_USER_SWITCH);
+                    mDpm.setLogoutEnabled(mAdmin, true);
+                    UserHandle userHandle = mDpm.createAndManageUser(mAdmin, "managed user", mAdmin,
+                            null, SKIP_SETUP_WIZARD | MAKE_USER_EPHEMERAL);
+                    mDpm.switchUser(mAdmin, userHandle);
+                } break;
             }
         } catch (Exception e) {
             Log.e(TAG, "Failed to execute command: " + intent, e);
@@ -533,6 +553,7 @@
         mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_CONFIG_BLUETOOTH);
         mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_CONFIG_VPN);
         mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_DATA_ROAMING);
+        mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_USER_SWITCH);
 
         mDpm.setDeviceOwnerLockScreenInfo(mAdmin, null);
         mDpm.setKeyguardDisabled(mAdmin, false);
@@ -554,6 +575,10 @@
         mDpm.uninstallCaCert(mAdmin, TEST_CA.getBytes());
         mDpm.setMaximumFailedPasswordsForWipe(mAdmin, 0);
         mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD, null);
+        mDpm.setAffiliationIds(mAdmin, Collections.emptySet());
+        mDpm.setStartUserSessionMessage(mAdmin, null);
+        mDpm.setEndUserSessionMessage(mAdmin, null);
+        mDpm.setLogoutEnabled(mAdmin, false);
 
         uninstallHelperPackage();
         removeManagedProfile();
@@ -611,4 +636,22 @@
     private boolean isSystemInputMethodInfo(InputMethodInfo inputMethodInfo) {
         return inputMethodInfo.getServiceInfo().applicationInfo.isSystemApp();
     }
+
+    private void createAndSwitchUserWithMessage(String startUserSessionMessage,
+            String endUserSessionMessage) {
+        if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+            return;
+        }
+        mDpm.setStartUserSessionMessage(mAdmin, startUserSessionMessage);
+        mDpm.setEndUserSessionMessage(mAdmin, endUserSessionMessage);
+        mDpm.setAffiliationIds(mAdmin,
+                Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
+
+        PersistableBundle extras = new PersistableBundle();
+        extras.putBoolean(DeviceAdminTestReceiver.EXTRA_LOGOUT_ON_START, true);
+        UserHandle userHandle = mDpm.createAndManageUser(mAdmin, "managed user", mAdmin,
+                extras,
+                SKIP_SETUP_WIZARD | MAKE_USER_EPHEMERAL);
+        mDpm.switchUser(mAdmin, userHandle);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index 974b063..e9f62a1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -55,6 +55,8 @@
             DEVICE_OWNER_PKG, ADMIN_RECEIVER_TEST_CLASS);
     public static final String EXTRA_MANAGED_USER_TEST =
             "com.android.cts.verifier.managedprovisioning.extra.MANAGED_USER_TEST";
+    public static final String EXTRA_LOGOUT_ON_START =
+            "com.android.cts.verifier.managedprovisioning.extra.LOGOUT_ON_START";
     public static final String AFFILIATION_ID = "affiliationId";
 
     public static ComponentName getReceiverComponentName() {
@@ -120,6 +122,12 @@
                     Log.e(TAG, "Error when calling primary user", re);
                 }
             });
+        } else if (intent.getBooleanExtra(EXTRA_LOGOUT_ON_START, false)) {
+            DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+            ComponentName admin = getReceiverComponentName();
+            dpm.setAffiliationIds(admin,
+                    Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
+            dpm.logoutUser(admin);
         }
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 6c87b84..887d795 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -70,6 +70,9 @@
     private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
     private static final String ENTERPRISE_PRIVACY_TEST_ID = "ENTERPRISE_PRIVACY";
     private static final String NETWORK_LOGGING_UI_TEST_ID = "NETWORK_LOGGING_UI";
+    private static final String DISALLOW_USER_SWITCH_TEST_ID = "DISALLOW_USER_SWITCH";
+    private static final String USER_SWITCHER_MESSAGE_TEST_ID = "USER_SWITCHER_MESSAGE";
+    private static final String ENABLE_LOGOUT_TEST_ID = "ENABLE_LOGOUT";
     private static final String COMP_TEST_ID = "COMP_UI";
     private static final String MANAGED_USER_TEST_ID = "MANAGED_USER_UI";
     private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
@@ -333,9 +336,9 @@
                     compIntent));
         }
 
-        // Managed user
         if (packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)
                 && UserManager.supportsMultipleUsers()) {
+            // Managed user
             adapter.add(createInteractiveTestItem(this, MANAGED_USER_TEST_ID,
                     R.string.managed_user_test,
                     R.string.managed_user_positive_tests_instructions,
@@ -343,6 +346,40 @@
                             new ButtonInfo(
                                     R.string.device_owner_settings_go,
                                     createCreateManagedUserIntent())}));
+
+            // DISALLOW_USER_SWITCH
+            adapter.add(createInteractiveTestItem(this, DISALLOW_USER_SWITCH_TEST_ID,
+                    R.string.device_owner_disallow_user_switch,
+                    R.string.device_owner_disallow_user_switch_info,
+                    new ButtonInfo[]{
+                            new ButtonInfo(
+                                    R.string.device_owner_user_restriction_set,
+                                    CommandReceiverActivity.createSetUserRestrictionIntent(
+                                            UserManager.DISALLOW_USER_SWITCH, true)),
+                            new ButtonInfo(
+                                    R.string.device_owner_settings_go,
+                                    new Intent(Settings.ACTION_SETTINGS))}));
+
+            // User switcher message
+            adapter.add(createInteractiveTestItem(this, USER_SWITCHER_MESSAGE_TEST_ID,
+                    R.string.device_owner_user_switcher_message,
+                    R.string.device_owner_user_switcher_message_info,
+                    new ButtonInfo[]{
+                            new ButtonInfo(
+                                    R.string.device_owner_with_user_switcher_message,
+                                    createWithUserSwitcherMessageIntent()),
+                            new ButtonInfo(
+                                    R.string.device_owner_without_user_switcher_message,
+                                    createWithoutUserSwitcherMessageIntent())}));
+
+            // Enable logout
+            adapter.add(createInteractiveTestItem(this, ENABLE_LOGOUT_TEST_ID,
+                    R.string.device_owner_enable_logout,
+                    R.string.device_owner_enable_logout_info,
+                    new ButtonInfo[]{
+                            new ButtonInfo(
+                                    R.string.device_owner_settings_go,
+                                    createEnableLogoutIntent())}));
         }
 
         // Network logging UI
@@ -409,6 +446,24 @@
                         CommandReceiverActivity.COMMAND_CREATE_MANAGED_USER);
     }
 
+    private Intent createWithUserSwitcherMessageIntent() {
+        return new Intent(this, CommandReceiverActivity.class)
+                .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+                        CommandReceiverActivity.COMMAND_WITH_USER_SWITCHER_MESSAGE);
+    }
+
+    private Intent createWithoutUserSwitcherMessageIntent() {
+        return new Intent(this, CommandReceiverActivity.class)
+                .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+                        CommandReceiverActivity.COMMAND_WITHOUT_USER_SWITCHER_MESSAGE);
+    }
+
+    private Intent createEnableLogoutIntent() {
+        return new Intent(this, CommandReceiverActivity.class)
+                .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+                        CommandReceiverActivity.COMMAND_ENABLE_LOGOUT);
+    }
+
     private boolean isStatusBarEnabled() {
       // Watches don't support the status bar so this is an ok proxy, but this is not the most
       // general test for that. TODO: add a test API to do a real check for status bar support.
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
index 6ce71df..08c3665 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
@@ -24,6 +24,7 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.suite.BaseTestSuite;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -32,6 +33,7 @@
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.util.LinkedHashMap;
+import java.util.Set;
 
 /**
  * A Test for running Compatibility Test Suite with new suite system.
@@ -107,4 +109,23 @@
     public final void resetRetryId() {
         mRetrySessionId = null;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public LinkedHashMap<String, IConfiguration> loadingStrategy(
+            Set<IAbi> abis, File testsDir, String suitePrefix, String suiteTag) {
+        LinkedHashMap<String, IConfiguration> loadedConfigs = new LinkedHashMap<>();
+        // Load the configs that are part of the tests dir
+        loadedConfigs.putAll(
+                getModuleLoader().loadConfigsFromDirectory(testsDir, abis, suitePrefix, suiteTag));
+        // Add an extra check in CTS since we never expect the config folder to be empty.
+        if (loadedConfigs.size() == 0) {
+            throw new IllegalArgumentException(
+                    String.format("No config files found in %s or in resources.",
+                            testsDir.getAbsolutePath()));
+        }
+        return loadedConfigs;
+    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index 28c44fc..b797b32 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -16,6 +16,8 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.Presubmit;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -271,6 +273,7 @@
                 "testRequestNonExistentPermission");
     }
 
+    @Presubmit
     public void testRequestPermissionFromTwoGroups23() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
index b4ed09f..9e408b3 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
@@ -450,7 +450,7 @@
                 Manifest.permission.WRITE_CALENDAR
         };
 
-        // Request the permission and do nothing
+        // Request the permission and allow it
         BasePermissionActivity.Result result = requestPermissions(permissions,
                 REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, () -> {
             try {
@@ -463,9 +463,24 @@
             }
         });
 
-        // Expect the permission is not granted
+        // Expect the permission are reported as granted
         assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
                 permissions, new boolean[] {true, true});
+
+        // The permissions are granted
+        assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(Manifest.permission.WRITE_CONTACTS));
+        assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(Manifest.permission.WRITE_CALENDAR));
+
+        // In API < N_MR1 all permissions of a group are granted. I.e. the grant was "expanded"
+        assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(Manifest.permission.READ_CALENDAR));
+
+        // Even the contacts group was expanded, the read-calendar permission is not in the
+        // manifest, hence not granted.
+        assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getContext()
+                .checkSelfPermission(Manifest.permission.READ_CONTACTS));
     }
 
     @Test
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
index 40dfbb3..9b1ae92 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
@@ -26,19 +26,46 @@
  */
 public class ClearApplicationDataTest extends BaseDeviceAdminTest {
     private static final String TEST_PKG = "com.android.cts.intent.receiver";
+    private static final String DEVICE_ADMIN_PKG = "com.android.cts.deviceandprofileowner";
     private static final Semaphore mSemaphore = new Semaphore(0);
     private static final long CLEAR_APPLICATION_DATA_TIMEOUT_S = 10;
 
-    public void testClearApplicationData() throws Exception {
-        mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT, TEST_PKG,
-                AsyncTask.THREAD_POOL_EXECUTOR,
+    public void testClearApplicationData_testPkg() throws Exception {
+        clearApplicationDataTest(TEST_PKG, /* shouldSucceed */ true);
+    }
+
+    public void testClearApplicationData_deviceProvisioning() throws Exception {
+        String deviceProvisioningPackageName = getDeviceProvisioningPackageName();
+        if (deviceProvisioningPackageName == null) {
+            return;
+        }
+        clearApplicationDataTest(deviceProvisioningPackageName, /* shouldSucceed */ false);
+    }
+
+    public void testClearApplicationData_activeAdmin() throws Exception {
+        clearApplicationDataTest(DEVICE_ADMIN_PKG, /* shouldSucceed */ false);
+    }
+
+    private void clearApplicationDataTest(String packageName, boolean shouldSucceed)
+            throws Exception {
+        mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT,
+                packageName, AsyncTask.THREAD_POOL_EXECUTOR,
                 (String pkg, boolean succeeded) -> {
-                    assertEquals(TEST_PKG, pkg);
-                    assertTrue(succeeded);
+                    assertEquals(packageName, pkg);
+                    assertEquals(shouldSucceed, succeeded);
                     mSemaphore.release();
                 });
-
         assertTrue("Clearing application data took too long",
                 mSemaphore.tryAcquire(CLEAR_APPLICATION_DATA_TIMEOUT_S, TimeUnit.SECONDS));
     }
+
+    private String getDeviceProvisioningPackageName() {
+        final int provisioning_app_id = mContext.getResources().getIdentifier(
+                "config_deviceProvisioningPackage", "string", "android");
+        if (provisioning_app_id > 0) {
+            return mContext.getResources().getString(provisioning_app_id);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 42dce85..8609684 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -856,18 +856,36 @@
                 Collections.singletonMap(ARG_ALLOW_FAILURE, Boolean.toString(allowFailures)));
     }
 
-    public void testClearApplicationData() throws Exception {
+    public void testClearApplicationData_testPkg() throws Exception {
         if (!mHasFeature) {
             return;
         }
         installAppAsUser(INTENT_RECEIVER_APK, mUserId);
         runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
                 "testWriteToSharedPreference", mUserId);
-        executeDeviceTestMethod(".ClearApplicationDataTest", "testClearApplicationData");
+        executeDeviceTestMethod(".ClearApplicationDataTest", "testClearApplicationData_testPkg");
         runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
                 "testSharedPreferenceCleared", mUserId);
     }
 
+    public void testClearApplicationData_deviceProvisioning() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Clearing data of device configuration app should fail
+        executeDeviceTestMethod(".ClearApplicationDataTest",
+                "testClearApplicationData_deviceProvisioning");
+    }
+
+    public void testClearApplicationData_activeAdmin() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Clearing data of active admin should fail
+        executeDeviceTestMethod(".ClearApplicationDataTest",
+                "testClearApplicationData_activeAdmin");
+    }
+
     public void testPrintingPolicy() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index b1f16e5..5245612 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -58,14 +58,13 @@
     public void testCreateIme1() throws Throwable {
         final TestHelper helper = new TestHelper(getClass(), DeviceTestConstants.TEST_CREATE_IME1);
 
+        final long startActivityTime = SystemClock.uptimeMillis();
+        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
+
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
                         .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
                 TIMEOUT, "CtsInputMethod1.onCreate is called");
-
-        final long startActivityTime = SystemClock.uptimeMillis();
-        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(startActivityTime))
                         .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
@@ -78,14 +77,13 @@
         final TestHelper helper = new TestHelper(
                 getClass(), DeviceTestConstants.TEST_SWITCH_IME1_TO_IME2);
 
+        final long startActivityTime = SystemClock.uptimeMillis();
+        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
+
         pollingCheck(() -> helper.queryAllEvents()
                         .collect(startingFrom(helper.isStartOfTest()))
                         .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
                 TIMEOUT, "CtsInputMethod1.onCreate is called");
-
-        final long startActivityTime = SystemClock.uptimeMillis();
-        helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(startActivityTime))
                         .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index 1bba585..ef9581b 100644
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -86,7 +86,6 @@
         <!-- Bulletin 2017-11 -->
         <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
 
-
         <option name="append-bitness" value="true" />
     </target_preparer>
 
diff --git a/hostsidetests/security/src/android/security/cts/Poc16_10.java b/hostsidetests/security/src/android/security/cts/Poc16_10.java
index 5b54eea..0c9a5b3 100644
--- a/hostsidetests/security/src/android/security/cts/Poc16_10.java
+++ b/hostsidetests/security/src/android/security/cts/Poc16_10.java
@@ -16,21 +16,8 @@
 
 package android.security.cts;
 
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-
 import android.platform.test.annotations.SecurityTest;
 
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Scanner;
-
 @SecurityTest
 public class Poc16_10 extends SecurityTestCase {
 
diff --git a/hostsidetests/security/src/android/security/cts/Poc16_12.java b/hostsidetests/security/src/android/security/cts/Poc16_12.java
index b102d4e..8ae30d6 100644
--- a/hostsidetests/security/src/android/security/cts/Poc16_12.java
+++ b/hostsidetests/security/src/android/security/cts/Poc16_12.java
@@ -16,21 +16,8 @@
 
 package android.security.cts;
 
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-
 import android.platform.test.annotations.SecurityTest;
 
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Scanner;
-
 @SecurityTest
 public class Poc16_12 extends SecurityTestCase {
 
diff --git a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
index db7c886..03e3979 100644
--- a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
+++ b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
@@ -29,7 +29,6 @@
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.DUMP" /> <!-- must be granted via pm grant -->
 
-
     <application android:label="@string/app_name">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
@@ -72,6 +71,12 @@
         <service android:name=".StatsdJobService"
             android:permission="android.permission.BIND_JOB_SERVICE" />
 
+        <receiver android:name=".AlertTests$BroadcastSubscriberReceiver1"
+                  android:exported="false" >
+        </receiver>
+        <receiver android:name=".AlertTests$BroadcastSubscriberReceiver2"
+                  android:exported="false" >
+        </receiver>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AlertTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AlertTests.java
new file mode 100644
index 0000000..c303ad8
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AlertTests.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.cts.device.statsd;
+
+import android.app.PendingIntent;
+import android.app.StatsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.StatsDimensionsValue;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+import android.util.StatsLog;
+import com.android.internal.os.StatsdConfigProto.Alert;
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.BroadcastSubscriberDetails;
+import com.android.internal.os.StatsdConfigProto.CountMetric;
+import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.Subscription;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+import com.android.os.AtomsProto.AppHook;
+import com.android.os.AtomsProto.Atom;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AlertTests {
+    public static final String TAG = "statsd.AlertTests";
+
+    private Context mContext;
+    private StatsManager mStatsManager;
+
+    private PendingIntent mPendingIntent1;
+    private PendingIntent mPendingIntent2;
+    private static CountDownLatch sLatch1; // static since used by receiver
+    private static CountDownLatch sLatch2; // static since used by receiver
+    private static final int UID = android.os.Process.myUid();  // static since used by receiver
+
+    public static final int SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS = 2_000;
+
+    public static final int CONFIG_ID = 12;
+    public static final int ATOM_MATCHER_ID = 1;
+    public static final int METRIC_ID = 1;
+    public static final int ALERT_ID = 1;
+    public static final int SUBSCRIPTION_ID_1 = 1;
+    public static final int SUBSCRIPTION_ID_2 = 2;
+    public static final int SUBSCRIBER_ID_1 = 1;
+    public static final int SUBSCRIBER_ID_2 = 2;
+
+    private static final StatsdConfig CONFIG = StatsdConfig.newBuilder().setId(CONFIG_ID)
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                    .setId(ATOM_MATCHER_ID)
+                    .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                            .setAtomId(Atom.APP_HOOK_FIELD_NUMBER)
+                            // Event only when the uid is this app's uid.
+                            .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+                                    .setField(AppHook.UID_FIELD_NUMBER)
+                                    .setEqInt(UID)
+                            )
+                    )
+            )
+            .addCountMetric(CountMetric.newBuilder()
+                    .setId(METRIC_ID)
+                    .setWhat(ATOM_MATCHER_ID)
+                    .setBucket(TimeUnit.CTS)
+                    // Slice by uid (since that's the typical case)
+                    .setDimensionsInWhat(FieldMatcher.newBuilder()
+                        .setField(Atom.APP_HOOK_FIELD_NUMBER)
+                        .addChild(FieldMatcher.newBuilder().setField(AppHook.UID_FIELD_NUMBER))
+                    )
+            )
+            .addAlert(Alert.newBuilder()
+                    .setId(ALERT_ID)
+                    .setMetricId(METRIC_ID)
+                    .setNumBuckets(4)
+                    .setRefractoryPeriodSecs(0)
+                    .setTriggerIfSumGt(0) // even a single event triggers it
+            )
+            .addSubscription(Subscription.newBuilder()
+                    .setId(SUBSCRIPTION_ID_1)
+                    .setRuleType(Subscription.RuleType.ALERT)
+                    .setRuleId(ALERT_ID)
+                    .setBroadcastSubscriberDetails(BroadcastSubscriberDetails.newBuilder()
+                            .setSubscriberId(SUBSCRIBER_ID_1))
+            )
+            .addSubscription(Subscription.newBuilder()
+                    .setId(SUBSCRIPTION_ID_2)
+                    .setRuleType(Subscription.RuleType.ALERT)
+                    .setRuleId(ALERT_ID)
+                    .setBroadcastSubscriberDetails(BroadcastSubscriberDetails.newBuilder()
+                            .setSubscriberId(SUBSCRIBER_ID_2))
+            )
+            .build();
+
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mStatsManager = (StatsManager) mContext.getSystemService("stats");
+        mPendingIntent1 = PendingIntent.getBroadcast(mContext, 1,
+                new Intent(mContext, BroadcastSubscriberReceiver1.class), 0);
+        mPendingIntent2 = PendingIntent.getBroadcast(mContext, 2,
+                new Intent(mContext, BroadcastSubscriberReceiver2.class), 0);
+    }
+
+    /** Tests that setBroadcastSubscriber() works as intended. */
+    @Test
+    public void testBroadcastSubscriber() {
+        Log.d(TAG, "Running testBroadcastSubscriber under UID=" + UID);
+        try {
+            logPendingIntent(mPendingIntent1);
+            logPendingIntent(mPendingIntent2);
+            sLatch1 = new CountDownLatch(1);
+            sLatch2 = new CountDownLatch(1);
+            mStatsManager.removeConfiguration(CONFIG_ID);
+            mStatsManager.addConfiguration(CONFIG_ID, CONFIG.toByteArray(), "", "");
+
+            mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_1, mPendingIntent1);
+            mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, mPendingIntent2);
+            AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+            StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+
+            Assert.assertTrue(waitForLatch(sLatch1, 10_000));
+            Assert.assertTrue(waitForLatch(sLatch2, 10_000));
+        } finally {
+            mStatsManager.removeConfiguration(CONFIG_ID);
+        }
+    }
+
+    /** Tests that setBroadcastSubscriber(,, null) works as intended. */
+    @Test
+    public void testUnsetBroadcastSubscriber() {
+        Log.d(TAG, "Running testUnsetBroadcastSubscriber under UID=" + UID);
+        try {
+            logPendingIntent(mPendingIntent1);
+            logPendingIntent(mPendingIntent2);
+            sLatch1 = new CountDownLatch(2);
+            sLatch2 = new CountDownLatch(2);
+            mStatsManager.removeConfiguration(CONFIG_ID);
+            mStatsManager.addConfiguration(CONFIG_ID, CONFIG.toByteArray(), "", "");
+
+            mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_1, mPendingIntent1);
+            mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, mPendingIntent2);
+            AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+            StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+            AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+            // During sleep, both latches count down by 1, as tested in testBroadcastSubscriber.
+
+            // Now remove subscriber2 and make sure only subscriber1 receives broadcast.
+            mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, null);
+            AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+            StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+            Assert.assertTrue(waitForLatch(sLatch1, 10_000));
+            Log.d(TAG, "About to wait for a latch, expecting it to not finish");
+            Assert.assertFalse(waitForLatch(sLatch2, 2_000)); // should fail
+
+            // Now add subscriber2 back again and make sure it receives this time.
+            mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, mPendingIntent2);
+            AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+            StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+            Assert.assertTrue(waitForLatch(sLatch2, 10_000));
+        } finally {
+            mStatsManager.removeConfiguration(CONFIG_ID);
+        }
+    }
+
+    /** Prints some information about the PendingIntent to logcat. */
+    private static void logPendingIntent(PendingIntent pendingIntent) {
+        Log.d(TAG, String.format("Created PendingIntent with " +
+                        "CreatorPackage=%s, CreatorUid=%d, toString=%s",
+                pendingIntent.getCreatorPackage(),
+                pendingIntent.getCreatorUid(),
+                pendingIntent.toString()));
+    }
+
+    /** Checks that the intent (presumably received from a broadcast) is as expected. */
+    private static void checkIntent(Intent intent, long subscriptionId) {
+        Log.d(TAG, "Received " + (intent != null ? intent.toString() : "null intent")
+        + "\nwith extras " + (intent != null && intent.getExtras() != null ?
+                intent.getExtras().toString() : "(null intent)"));
+
+        Assert.assertNotNull(intent);
+
+        long intentConfigUid = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_UID, -1);
+        long intentConfigKey = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, -1);
+        long intentSubscrId = intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, -1);
+        long intentRuleId = intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, -1);
+        StatsDimensionsValue intentDimsValue =
+                intent.getParcelableExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE);
+        String intentDimsValueStr =
+                intentDimsValue != null ? intentDimsValue.toString() : "null";
+
+        Log.d(TAG, "Relevant extras are"
+                + " {ConfigUid=" + intentConfigUid
+                + ", ConfigKey=" + intentConfigKey
+                + ", SubscriptionId=" + intentSubscrId
+                + ", RuleId=" + intentRuleId
+                + ", DimensionsValue=" + intentDimsValueStr
+                + "}");
+
+        Assert.assertEquals(UID, intentConfigUid);
+        Assert.assertEquals(CONFIG_ID, intentConfigKey);
+        Assert.assertEquals(subscriptionId, intentSubscrId);
+        Assert.assertEquals(METRIC_ID, intentRuleId);
+
+        String expectedDimValue = String.format("%d:{%d:%d|}",
+                Atom.APP_HOOK_FIELD_NUMBER, AppHook.UID_FIELD_NUMBER, UID);
+        Assert.assertEquals(expectedDimValue, intentDimsValueStr);
+
+        Assert.assertEquals(Atom.APP_HOOK_FIELD_NUMBER, intentDimsValue.getField());
+        List<StatsDimensionsValue> intentTuple = intentDimsValue.getTupleValueList();
+        Assert.assertEquals(1, intentTuple.size());
+        StatsDimensionsValue intentTupleValue = intentTuple.get(0);
+        Assert.assertEquals(AppHook.UID_FIELD_NUMBER, intentTupleValue.getField());
+        Assert.assertTrue(intentTupleValue.isValueType(StatsDimensionsValue.INT_VALUE_TYPE));
+        Assert.assertEquals(UID, intentTupleValue.getIntValue());
+    }
+
+    public final static class BroadcastSubscriberReceiver1 extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context mContext, Intent intent) {
+            Log.d(TAG, "Broadcast received by BroadcastSubscriberReceiver1");
+            checkIntent(intent, SUBSCRIPTION_ID_1);
+            Assert.assertNotNull(sLatch1);
+            sLatch1.countDown();
+        }
+    }
+    public final static class BroadcastSubscriberReceiver2 extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context mContext, Intent intent) {
+            Log.d(TAG, "Broadcast received by BroadcastSubscriberReceiver2");
+            checkIntent(intent, SUBSCRIPTION_ID_2);
+            Assert.assertNotNull(sLatch2);
+            sLatch2.countDown();
+        }
+    }
+
+    /** Waits for up to maxWaitTimeMs for the latch to count down, and returns whether it did so. */
+    private static boolean waitForLatch(CountDownLatch latch, int maxWaitTimeMs) {
+        try {
+            boolean didFinish
+                    = latch.await(maxWaitTimeMs, java.util.concurrent.TimeUnit.MILLISECONDS);
+            if (didFinish) {
+                Log.v(TAG, "Latch finished");
+                return true;
+            } else {
+                Log.w(TAG, "Latch did not finish in specified time.");
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Interrupted exception while awaiting latch to finish", e);
+        }
+        return false;
+    }
+}
+
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 3cc2816..791fc86 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -61,11 +61,12 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-public class AtomTests{
+public class AtomTests {
     private static final String TAG = AtomTests.class.getSimpleName();
 
     @Test
     public void testAudioState() {
+        // TODO: This should surely be getTargetContext(), here and everywhere, but test first.
         Context context = InstrumentationRegistry.getContext();
         MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.good);
         mediaPlayer.start();
@@ -365,6 +366,7 @@
         BroadcastReceiver receiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
+                Log.d(TAG, "Received broadcast.");
                 onReceiveLatch.countDown();
             }
         };
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alert/BroadcastSubscriberTests.java b/hostsidetests/statsd/src/android/cts/statsd/alert/BroadcastSubscriberTests.java
new file mode 100644
index 0000000..f0e14fc
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/alert/BroadcastSubscriberTests.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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.cts.statsd.alert;
+
+import android.cts.statsd.atom.DeviceAtomTestCase;
+import com.android.tradefed.log.LogUtil.CLog;
+
+/**
+ * Statsd broadcast subscriber tests. Some may be done device-side.
+ */
+public class BroadcastSubscriberTests extends DeviceAtomTestCase {
+
+    private static final String TAG = "Statsd.BroadcastSubscriberTests";
+
+    // These tests currently require selinux policy to be disabled, so keep false!
+    // TODO: Fix this.
+    private static final boolean TESTS_ENABLED = false;
+
+    public static final int SUBSCRIBER_TESTS_CONFIG_ID = 12;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!TESTS_ENABLED) {
+            CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getDevice().executeShellCommand(
+                String.join(" ", REMOVE_CONFIG_CMD, String.valueOf(getUid()),
+                        String.valueOf(SUBSCRIBER_TESTS_CONFIG_ID)));
+        super.tearDown();
+    }
+
+    /** Tests that anomaly detection generic subscribers work. */
+    public void testBroadcastSubscriber() throws Exception {
+        runAlertTest("testBroadcastSubscriber");
+    }
+
+    /** Tests that unsetting generic subscribers work. */
+    public void testUnsetBroadcastSubscriber() throws Exception {
+        runAlertTest("testUnsetBroadcastSubscriber");
+    }
+
+    public void runAlertTest(String methodName) throws Exception {
+        if (!TESTS_ENABLED) return;
+        CLog.d("\nPerforming device-side test of " + methodName + " for uid " + getUid());
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AlertTests", methodName);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 8561614..06fd98e 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -54,9 +54,9 @@
  */
 public class AtomTestCase extends BaseTestCase {
 
-    private static final String UPDATE_CONFIG_CMD = "cmd stats config update";
-    private static final String DUMP_REPORT_CMD = "cmd stats dump-report";
-    private static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
+    protected static final String UPDATE_CONFIG_CMD = "cmd stats config update";
+    protected static final String DUMP_REPORT_CMD = "cmd stats dump-report";
+    protected static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
     protected static final String CONFIG_UID = "1000";
     /** ID of the config, which evaluates to -1572883457. */
     protected static final long CONFIG_ID = "cts_config".hashCode();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index ddb59d4..00995ae 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -36,6 +36,7 @@
 import com.android.os.AtomsProto.ScreenStateChanged;
 import com.android.os.AtomsProto.SubsystemSleepState;
 import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil.CLog;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -53,6 +54,17 @@
     // For tests that require incidentd. Keep as true until TESTS_ENABLED is permanently enabled.
     private static final boolean INCIDENTD_TESTS_ENABLED = true;
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!TESTS_ENABLED) {
+            CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+        }
+        if (!INCIDENTD_TESTS_ENABLED) {
+            CLog.w(TAG, TAG + " anomaly tests are disabled by a flag. Change flag to true to run");
+        }
+    }
+
     public void testScreenStateChangedAtom() throws Exception {
         if (!TESTS_ENABLED) {return;}
 
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index 8743277..1a7f42e0 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -19,6 +19,7 @@
 
 import com.android.os.AtomsProto.Atom;
 import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil.CLog;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -104,6 +105,14 @@
 
     private static final int PROC_STATE_ATOM_TAG = Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER;
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!TESTS_ENABLED) {
+            CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+        }
+    }
+
     public void testForegroundService() throws Exception {
         if (!TESTS_ENABLED) return;
         Set<Integer> onStates = new HashSet<>(Arrays.asList(
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 36e34f8..a8a32ce 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -33,6 +33,7 @@
 import com.android.os.AtomsProto.WifiLockStateChanged;
 import com.android.os.AtomsProto.WifiScanStateChanged;
 import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil.CLog;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -57,6 +58,13 @@
     private static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
     private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!TESTS_ENABLED) {
+            CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+        }
+    }
     public void testBleScan() throws Exception {
         if (!TESTS_ENABLED) return;
         if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
diff --git a/tests/AlarmManager/app/AndroidManifest.xml b/tests/AlarmManager/app/AndroidManifest.xml
index ceb8fb9..f5b04b6 100644
--- a/tests/AlarmManager/app/AndroidManifest.xml
+++ b/tests/AlarmManager/app/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="android.alarmmanager.alarmtestapp.cts">
 
     <application>
-        <activity android:name=".TestAlarmActivity"
+        <receiver android:name=".TestAlarmScheduler"
                   android:exported="true" />
         <receiver android:name=".TestAlarmReceiver" />
     </application>
diff --git a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmScheduler.java
similarity index 75%
rename from tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
rename to tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmScheduler.java
index 8f666dd8..35763be 100644
--- a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
+++ b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmScheduler.java
@@ -16,39 +16,38 @@
 
 package android.alarmmanager.alarmtestapp.cts;
 
-import android.app.Activity;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.Intent;
-import android.os.Bundle;
 import android.util.Log;
 
 /**
- * This Activity is to be used as part of
- * {@link android.alarmmanager.cts.BackgroundRestrictedAlarmsTest}
+ * This receiver is to be used to schedule alarms as part of tests in
+ * {@link android.alarmmanager.cts}
  */
-public class TestAlarmActivity extends Activity {
-    private static final String TAG = TestAlarmActivity.class.getSimpleName();
+public class TestAlarmScheduler extends BroadcastReceiver {
+    private static final String TAG = TestAlarmScheduler.class.getSimpleName();
     private static final String PACKAGE_NAME = "android.alarmmanager.alarmtestapp.cts";
 
     public static final String ACTION_SET_ALARM = PACKAGE_NAME + ".action.SET_ALARM";
     public static final String EXTRA_TRIGGER_TIME = PACKAGE_NAME + ".extra.TRIGGER_TIME";
     public static final String EXTRA_REPEAT_INTERVAL = PACKAGE_NAME + ".extra.REPEAT_INTERVAL";
     public static final String EXTRA_TYPE = PACKAGE_NAME + ".extra.TYPE";
+    public static final String EXTRA_ALLOW_WHILE_IDLE = PACKAGE_NAME + ".extra.ALLOW_WHILE_IDLE";
     public static final String ACTION_SET_ALARM_CLOCK = PACKAGE_NAME + ".action.SET_ALARM_CLOCK";
     public static final String EXTRA_ALARM_CLOCK_INFO = PACKAGE_NAME + ".extra.ALARM_CLOCK_INFO";
     public static final String ACTION_CANCEL_ALL_ALARMS = PACKAGE_NAME + ".action.CANCEL_ALARMS";
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final AlarmManager am = getSystemService(AlarmManager.class);
-        final Intent intent = getIntent();
-        final Intent receiverIntent = new Intent(this, TestAlarmReceiver.class);
+    public void onReceive(Context context, Intent intent) {
+        final AlarmManager am = context.getSystemService(AlarmManager.class);
+        final Intent receiverIntent = new Intent(context, TestAlarmReceiver.class);
         receiverIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         final PendingIntent alarmClockSender =
-                PendingIntent.getBroadcast(this, 0, receiverIntent, 0);
-        final PendingIntent alarmSender = PendingIntent.getBroadcast(this, 1, receiverIntent, 0);
+                PendingIntent.getBroadcast(context, 0, receiverIntent, 0);
+        final PendingIntent alarmSender = PendingIntent.getBroadcast(context, 1, receiverIntent, 0);
         switch (intent.getAction()) {
             case ACTION_SET_ALARM_CLOCK:
                 if (!intent.hasExtra(EXTRA_ALARM_CLOCK_INFO)) {
@@ -68,10 +67,14 @@
                 final int type = intent.getIntExtra(EXTRA_TYPE, 0);
                 final long triggerTime = intent.getLongExtra(EXTRA_TRIGGER_TIME, 0);
                 final long interval = intent.getLongExtra(EXTRA_REPEAT_INTERVAL, 0);
+                final boolean allowWhileIdle = intent.getBooleanExtra(EXTRA_ALLOW_WHILE_IDLE,
+                        false);
                 Log.d(TAG, "Setting alarm: type=" + type + ", triggerTime=" + triggerTime
-                        + ", interval=" + interval);
+                        + ", interval=" + interval + ", allowWhileIdle=" + allowWhileIdle);
                 if (interval > 0) {
                     am.setRepeating(type, triggerTime, interval, alarmSender);
+                } else if (allowWhileIdle) {
+                    am.setExactAndAllowWhileIdle(type, triggerTime, alarmSender);
                 } else {
                     am.setExact(type, triggerTime, alarmSender);
                 }
@@ -85,6 +88,5 @@
                 Log.e(TAG, "Unspecified action " + intent.getAction());
                 break;
         }
-        finish();
     }
 }
\ No newline at end of file
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
index 2b37bfd..cf83c8f 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.alarmmanager.alarmtestapp.cts.TestAlarmActivity;
+import android.alarmmanager.alarmtestapp.cts.TestAlarmScheduler;
 import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
 import android.app.AlarmManager;
 import android.content.BroadcastReceiver;
@@ -41,23 +41,27 @@
 
 import java.io.IOException;
 
+/**
+ * Tests that apps put in forced app standby by the user do not get to run alarms while in the
+ * background
+ */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class BackgroundRestrictedAlarmsTest {
     private static final String TAG = BackgroundRestrictedAlarmsTest.class.getSimpleName();
     private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
-    private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestAlarmActivity";
+    private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestAlarmScheduler";
     private static final String APP_OP_RUN_ANY_IN_BACKGROUND = "RUN_ANY_IN_BACKGROUND";
     private static final String APP_OP_MODE_ALLOWED = "allow";
     private static final String APP_OP_MODE_IGNORED = "ignore";
 
     private static final long DEFAULT_WAIT = 1_000;
-    private static final long POLL_INTERVAL = 1_000;
+    private static final long POLL_INTERVAL = 200;
     private static final long MIN_REPEATING_INTERVAL = 10_000;
 
     private Object mLock = new Object();
     private Context mContext;
-    private ComponentName mAlarmActivity;
+    private ComponentName mAlarmScheduler;
     private UiDevice mUiDevice;
     private int mAlarmCount;
 
@@ -76,7 +80,7 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        mAlarmActivity = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+        mAlarmScheduler = new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER);
         mAlarmCount = 0;
         updateAlarmManagerConstants();
         setAppOpsMode(APP_OP_MODE_IGNORED);
@@ -86,23 +90,21 @@
     }
 
     private void scheduleAlarm(int type, long triggerMillis, long interval) {
-        final Intent setAlarmIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM);
-        setAlarmIntent.setComponent(mAlarmActivity);
-        setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TYPE, type);
-        setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TRIGGER_TIME, triggerMillis);
-        setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_REPEAT_INTERVAL, interval);
-        setAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(setAlarmIntent);
+        final Intent setAlarmIntent = new Intent(TestAlarmScheduler.ACTION_SET_ALARM);
+        setAlarmIntent.setComponent(mAlarmScheduler);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TYPE, type);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TRIGGER_TIME, triggerMillis);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_REPEAT_INTERVAL, interval);
+        mContext.sendBroadcast(setAlarmIntent);
     }
 
     private void scheduleAlarmClock(long triggerRTC) {
         AlarmManager.AlarmClockInfo alarmInfo = new AlarmManager.AlarmClockInfo(triggerRTC, null);
 
-        final Intent setAlarmClockIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM_CLOCK);
-        setAlarmClockIntent.setComponent(mAlarmActivity);
-        setAlarmClockIntent.putExtra(TestAlarmActivity.EXTRA_ALARM_CLOCK_INFO, alarmInfo);
-        setAlarmClockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(setAlarmClockIntent);
+        final Intent setAlarmClockIntent = new Intent(TestAlarmScheduler.ACTION_SET_ALARM_CLOCK);
+        setAlarmClockIntent.setComponent(mAlarmScheduler);
+        setAlarmClockIntent.putExtra(TestAlarmScheduler.EXTRA_ALARM_CLOCK_INFO, alarmInfo);
+        mContext.sendBroadcast(setAlarmClockIntent);
     }
 
     private static int getMinExpectedExpirations(long now, long start, long interval) {
@@ -118,7 +120,6 @@
         final long triggerElapsed = SystemClock.elapsedRealtime() + interval;
         scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed, interval);
         Thread.sleep(DEFAULT_WAIT);
-        makeTestPackageIdle();
         Thread.sleep(2 * interval);
         assertFalse("Alarm got triggered even under restrictions", waitForAlarms(1, DEFAULT_WAIT));
         Thread.sleep(interval);
@@ -147,10 +148,9 @@
         deleteAlarmManagerConstants();
         setAppOpsMode(APP_OP_MODE_ALLOWED);
         // Cancel any leftover alarms
-        final Intent cancelAlarmsIntent = new Intent(TestAlarmActivity.ACTION_CANCEL_ALL_ALARMS);
-        cancelAlarmsIntent.setComponent(mAlarmActivity);
-        cancelAlarmsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(cancelAlarmsIntent);
+        final Intent cancelAlarmsIntent = new Intent(TestAlarmScheduler.ACTION_CANCEL_ALL_ALARMS);
+        cancelAlarmsIntent.setComponent(mAlarmScheduler);
+        mContext.sendBroadcast(cancelAlarmsIntent);
         mContext.unregisterReceiver(mAlarmStateReceiver);
         // Broadcast unregister may race with the next register in setUp
         Thread.sleep(DEFAULT_WAIT);
@@ -176,12 +176,6 @@
         mUiDevice.executeShellCommand(commandBuilder.toString());
     }
 
-    private void makeTestPackageIdle() throws IOException {
-        StringBuilder commandBuilder = new StringBuilder("am make-uid-idle --user current ")
-                .append(TEST_APP_PACKAGE);
-        mUiDevice.executeShellCommand(commandBuilder.toString());
-    }
-
     private boolean waitForAlarms(int expectedAlarms, long timeout) throws InterruptedException {
         final long deadLine = SystemClock.uptimeMillis() + timeout;
         int alarmCount;
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/BatterySaverTests.java b/tests/AlarmManager/src/android/alarmmanager/cts/BatterySaverTests.java
new file mode 100644
index 0000000..12e80a2
--- /dev/null
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/BatterySaverTests.java
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+package android.alarmmanager.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.alarmmanager.alarmtestapp.cts.TestAlarmScheduler;
+import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+/**
+ * Tests that battery saver imposes the appropriate restrictions on alarms
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BatterySaverTests {
+    private static final String TAG = BatterySaverTests.class.getSimpleName();
+    private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
+    private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestAlarmScheduler";
+
+    private static final long DEFAULT_WAIT = 1_000;
+    private static final long POLL_INTERVAL = 200;
+
+    // Tweaked alarm manager constants to facilitate testing
+    private static final long MIN_REPEATING_INTERVAL = 5_000;
+    private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 10_000;
+    private static final long MIN_FUTURITY = 2_000;
+
+    private Context mContext;
+    private ComponentName mAlarmScheduler;
+    private UiDevice mUiDevice;
+    private volatile int mAlarmCount;
+
+    private final BroadcastReceiver mAlarmStateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mAlarmCount = intent.getIntExtra(TestAlarmReceiver.EXTRA_ALARM_COUNT, 1);
+            Log.d(TAG, "No. of expirations: " + mAlarmCount
+                    + " elapsed: " + SystemClock.elapsedRealtime());
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mAlarmScheduler = new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER);
+        mAlarmCount = 0;
+        updateAlarmManagerConstants();
+        setBatteryCharging(false);
+        setBatterySaverMode(true);
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(TestAlarmReceiver.ACTION_REPORT_ALARM_EXPIRED);
+        mContext.registerReceiver(mAlarmStateReceiver, intentFilter);
+    }
+
+    private void scheduleAlarm(int type, boolean whileIdle, long triggerMillis, long interval) {
+        final Intent setAlarmIntent = new Intent(TestAlarmScheduler.ACTION_SET_ALARM);
+        setAlarmIntent.setComponent(mAlarmScheduler);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TYPE, type);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TRIGGER_TIME, triggerMillis);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_REPEAT_INTERVAL, interval);
+        setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_ALLOW_WHILE_IDLE, whileIdle);
+        mContext.sendBroadcast(setAlarmIntent);
+    }
+
+    @Test
+    public void testAllowWhileIdleNotBlocked() throws Exception {
+        final long triggerElapsed1 = SystemClock.elapsedRealtime() + MIN_FUTURITY;
+        scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, true, triggerElapsed1, 0);
+        Thread.sleep(MIN_FUTURITY);
+        assertTrue("Allow-while-idle alarm blocked in battery saver",
+                waitForAlarms(1));
+        final long triggerElapsed2 = triggerElapsed1 + 2_000;
+        scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, true, triggerElapsed2, 0);
+        Thread.sleep(2_000);
+        assertFalse("Follow up allow-while-idle alarm went off before short time",
+                waitForAlarms(1));
+        final long expectedTriggerElapsed = triggerElapsed1 + ALLOW_WHILE_IDLE_SHORT_TIME;
+        Thread.sleep(expectedTriggerElapsed - SystemClock.elapsedRealtime());
+        assertTrue("Follow-up allow-while-idle alarm did not go off even after short time",
+                waitForAlarms(1));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        setBatterySaverMode(false);
+        setBatteryCharging(true);
+        deleteAlarmManagerConstants();
+        final Intent cancelAlarmsIntent = new Intent(TestAlarmScheduler.ACTION_CANCEL_ALL_ALARMS);
+        cancelAlarmsIntent.setComponent(mAlarmScheduler);
+        mContext.sendBroadcast(cancelAlarmsIntent);
+        mContext.unregisterReceiver(mAlarmStateReceiver);
+        // Broadcast unregister may race with the next register in setUp
+        Thread.sleep(1000);
+    }
+
+    private void updateAlarmManagerConstants() throws IOException {
+        final String cmd = "settings put global alarm_manager_constants "
+                + "min_interval=" + MIN_REPEATING_INTERVAL + ","
+                + "min_futurity=" + MIN_FUTURITY + ","
+                + "allow_while_idle_short_time=" + ALLOW_WHILE_IDLE_SHORT_TIME;
+        executeAndLog(cmd);
+    }
+
+    private void deleteAlarmManagerConstants() throws IOException {
+        executeAndLog("settings delete global alarm_manager_constants");
+    }
+
+    private void setBatteryCharging(final boolean charging) throws Exception {
+        final BatteryManager bm = mContext.getSystemService(BatteryManager.class);
+        final String cmd = "dumpsys battery " + (charging ? "reset" : "unplug");
+        executeAndLog(cmd);
+        if (!charging) {
+            assertTrue("Battery could not be unplugged", waitUntil(() -> !bm.isCharging(), 5_000));
+        }
+    }
+
+    private void setBatterySaverMode(final boolean enabled) throws Exception {
+        final PowerManager pm = mContext.getSystemService(PowerManager.class);
+        final String cmd = "settings put global low_power " + (enabled ? "1" : "0");
+        executeAndLog(cmd);
+        assertTrue("Battery saver state could not be changed to " + enabled,
+                waitUntil(() -> (enabled == pm.isPowerSaveMode()), 5_000));
+    }
+
+    private void executeAndLog(String cmd) throws IOException {
+        final String output = mUiDevice.executeShellCommand(cmd);
+        Log.d(TAG, "command: [" + cmd + "], output: [" + output.trim() + "]");
+    }
+
+    private boolean waitForAlarms(final int minExpirations) throws InterruptedException {
+        final boolean success = waitUntil(() -> (mAlarmCount >= minExpirations), DEFAULT_WAIT);
+        mAlarmCount = 0;
+        return success;
+    }
+
+    private boolean waitUntil(Condition condition, long timeout) throws InterruptedException {
+        final long deadLine = SystemClock.uptimeMillis() + timeout;
+        while (!condition.isMet() && SystemClock.uptimeMillis() < deadLine) {
+            Thread.sleep(POLL_INTERVAL);
+        }
+        return condition.isMet();
+    }
+
+    @FunctionalInterface
+    interface Condition {
+        boolean isMet();
+    }
+}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index eb3578b..e9902f8 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -36,7 +36,7 @@
 public class AccessibilityNodeInfoTest extends AndroidTestCase {
 
     /** The number of properties of the {@link AccessibilityNodeInfo} class that are marshalled. */
-    private static final int NUM_MARSHALLED_PROPERTIES = 34;
+    private static final int NUM_MARSHALLED_PROPERTIES = 35;
 
     /**
      * The number of properties that are purposely not marshalled
@@ -205,6 +205,7 @@
         info.setBoundsInScreen(new Rect(2,2,2,2));
         info.setClassName("foo.bar.baz.Class");
         info.setContentDescription("content description");
+        info.setTooltipText("tooltip");
         info.setPackageName("foo.bar.baz");
         info.setText("text");
         info.setHintText("hint");
@@ -255,6 +256,8 @@
                 receivedInfo.getClassName());
         assertEquals("contentDescription has incorrect value", expectedInfo.getContentDescription(),
                 receivedInfo.getContentDescription());
+        assertEquals("tooltip text has incorrect value", expectedInfo.getTooltipText(),
+                receivedInfo.getTooltipText());
         assertEquals("packageName has incorrect value", expectedInfo.getPackageName(),
                 receivedInfo.getPackageName());
         assertEquals("text has incorrect value", expectedInfo.getText(), receivedInfo.getText());
@@ -324,6 +327,7 @@
         assertTrue("boundsInScreen not properly recycled", bounds.isEmpty());
         assertNull("className not properly recycled", info.getClassName());
         assertNull("contentDescription not properly recycled", info.getContentDescription());
+        assertNull("tooltiptext not properly recycled", info.getTooltipText());
         assertNull("packageName not properly recycled", info.getPackageName());
         assertNull("text not properly recycled", info.getText());
         assertNull("Hint text not properly recycled", info.getHintText());
diff --git a/tests/accessibilityservice/res/layout/end_to_end_test.xml b/tests/accessibilityservice/res/layout/end_to_end_test.xml
index cba3a15..fdc3998 100644
--- a/tests/accessibilityservice/res/layout/end_to_end_test.xml
+++ b/tests/accessibilityservice/res/layout/end_to_end_test.xml
@@ -39,6 +39,7 @@
         <Button android:id="@+id/button"
                 android:text="@string/button_title"
                 android:accessibilityPaneTitle="@string/paneTitle"
+                android:tooltipText="@string/button_tooltip"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:bufferType="normal">
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index 0e8d78a..454102a 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -34,6 +34,8 @@
     <!-- String title of the button -->
     <string name="button_title">Click me</string>
 
+    <string name="button_tooltip">Never press this button</string>
+
     <!-- String value of the first list item -->
     <string name="first_list_item">First list item</string>
 
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 7052fc3..8202399 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -18,6 +18,12 @@
 
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
 import static android.accessibilityservice.cts.utils.RunOnMainUtils.getOnMain;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP;
+
+import static org.hamcrest.Matchers.in;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
 
 import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
 import android.app.Activity;
@@ -41,6 +47,7 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -51,6 +58,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * This class performs end-to-end testing of the accessibility feature by
@@ -581,6 +589,42 @@
                 editTextNode.isHeading());
     }
 
+    @MediumTest
+    public void testTooltipTextReportedToAccessibility() {
+        final Instrumentation instrumentation = getInstrumentation();
+        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+        final AccessibilityNodeInfo buttonNode = uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByViewId(
+                        "android.accessibilityservice.cts:id/button")
+                .get(0);
+        assertEquals("Tooltip text not reported to accessibility",
+                instrumentation.getContext().getString(R.string.button_tooltip),
+                buttonNode.getTooltipText());
+    }
+
+    @MediumTest
+    public void testTooltipTextActionsReportedToAccessibility() throws Exception {
+        final Instrumentation instrumentation = getInstrumentation();
+        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+        final AccessibilityNodeInfo buttonNode = uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByViewId(
+                        "android.accessibilityservice.cts:id/button")
+                .get(0);
+        assertFalse(hasTooltip(R.id.button));
+        assertThat(ACTION_SHOW_TOOLTIP, in(buttonNode.getActionList()));
+        assertThat(ACTION_HIDE_TOOLTIP, not(in(buttonNode.getActionList())));
+        uiAutomation.executeAndWaitForEvent(() -> buttonNode.performAction(
+                ACTION_SHOW_TOOLTIP.getId()),
+                filterForEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED),
+                TIMEOUT_ASYNC_PROCESSING);
+
+        // The button should now be showing the tooltip, so it should have the option to hide it.
+        buttonNode.refresh();
+        assertThat(ACTION_HIDE_TOOLTIP, in(buttonNode.getActionList()));
+        assertThat(ACTION_SHOW_TOOLTIP, not(in(buttonNode.getActionList())));
+        assertTrue(hasTooltip(R.id.button));
+    }
+
     private static void assertPackageName(AccessibilityNodeInfo node, String packageName) {
         if (node == null) {
             return;
@@ -690,4 +734,15 @@
         }
         return true;
     }
+
+    private boolean hasTooltip(int id) {
+        return getOnMain(getInstrumentation(), () -> {
+            final View viewWithTooltip = getActivity().findViewById(id);
+            if (viewWithTooltip == null) {
+                return false;
+            }
+            final View tooltipView = viewWithTooltip.getTooltipView();
+            return (tooltipView != null) && (tooltipView.getParent() != null);
+        });
+    }
 }
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 3b3c4d5..9751d05 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -29,10 +29,17 @@
 import android.os.Process;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import java.io.ByteArrayInputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -940,4 +947,44 @@
             assertProfileOwnerMessage(e.getMessage());
         }
     }
+
+    public void testGenerateKeyPair_failIfNotProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testGenerateKeyPair_failIfNotProfileOwner");
+            return;
+        }
+        try {
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                    "gen-should-fail",
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setKeySize(2048)
+                    .setDigests(KeyProperties.DIGEST_SHA256)
+                    .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                    .build();
+
+            mDevicePolicyManager.generateKeyPair(mComponent, "RSA", spec, 0);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetKeyPairCertificate_failIfNotProfileOwner() throws CertificateException {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetKeyPairCertificate_failIfNotProfileOwner");
+            return;
+        }
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            Certificate cert  = cf.generateCertificate(
+                    new ByteArrayInputStream(TEST_CA_STRING1.getBytes()));
+            List<Certificate> certs = new ArrayList();
+            certs.add(cert);
+            mDevicePolicyManager.setKeyPairCertificate(mComponent, "set-should-fail", certs, true);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
 }
diff --git a/tests/app/src/android/app/backup/cts/BackupManagerTest.java b/tests/app/src/android/app/backup/cts/BackupManagerTest.java
deleted file mode 100644
index 510e8d1..0000000
--- a/tests/app/src/android/app/backup/cts/BackupManagerTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 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.backup.cts;
-
-import android.app.backup.BackupManager;
-import android.app.backup.RestoreObserver;
-import android.test.AndroidTestCase;
-
-public class BackupManagerTest extends AndroidTestCase {
-
-    public void testBackupManager() throws Exception {
-        // Check that these don't crash as if they were called in an app...
-        BackupManager backupManager = new BackupManager(mContext);
-        backupManager.dataChanged();
-        BackupManager.dataChanged("android.app.stubs");
-
-        // Backup isn't expected to work in this test but check for obvious bugs...
-        int result = backupManager.requestRestore(new RestoreObserver() {});
-        assertTrue(result != 0);
-    }
-}
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 53b1bf2..fb31878 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -125,6 +125,7 @@
             assertNotAvailable(PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR);
             assertNotAvailable(PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING);
             assertNotAvailable(PackageManager.FEATURE_CAMERA_CAPABILITY_RAW);
+            assertNotAvailable(PackageManager.FEATURE_CAMERA_AR);
 
             assertFalse("Devices supporting external cameras must have a representative camera " +
                     "connected for testing",
@@ -142,6 +143,7 @@
         boolean fullCamera = false;
         boolean manualSensor = false;
         boolean manualPostProcessing = false;
+        boolean motionTracking = false;
         boolean raw = false;
         CameraCharacteristics[] cameraChars = new CameraCharacteristics[cameraIds.length];
         for (String cameraId : cameraIds) {
@@ -163,6 +165,9 @@
                     case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW:
                         raw = true;
                         break;
+                  case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING:
+                        motionTracking = true;
+                        break;
                     default:
                         // Capabilities don't have a matching system feature
                         break;
@@ -174,6 +179,7 @@
         assertFeature(manualPostProcessing,
                 PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING);
         assertFeature(raw, PackageManager.FEATURE_CAMERA_CAPABILITY_RAW);
+        assertFeature(motionTracking, PackageManager.FEATURE_CAMERA_AR);
     }
 
     private void checkFrontCamera() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index b77dae0..deb9fca 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -22,6 +22,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraMetadata;
@@ -37,10 +38,13 @@
 import android.hardware.camera2.params.RggbChannelVector;
 import android.hardware.camera2.params.TonemapCurve;
 import android.media.Image;
+import android.os.Parcel;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Range;
 import android.util.Rational;
 import android.util.Size;
+import android.view.Surface;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -129,6 +133,84 @@
     }
 
     /**
+     * Test CaptureRequest settings parcelling.
+     */
+    public void testSettingsBinderParcel() throws Exception {
+        SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
+        Surface surface = new Surface(outputTexture);
+
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                requestBuilder.addTarget(surface);
+
+                // Check regular/default case
+                CaptureRequest captureRequestOriginal = requestBuilder.build();
+                Parcel p;
+                p = Parcel.obtain();
+                captureRequestOriginal.writeToParcel(p, 0);
+                p.setDataPosition(0);
+                CaptureRequest captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+                assertEquals("Parcelled camera settings should match",
+                        captureRequestParcelled.get(CaptureRequest.CONTROL_CAPTURE_INTENT),
+                        new Integer(CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW));
+                p.recycle();
+
+                // Check capture request with additional physical camera settings
+                String physicalId = new String(Integer.toString(i + 1));
+                ArraySet<String> physicalIds = new ArraySet<String> ();
+                physicalIds.add(physicalId);
+
+                requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW,
+                        physicalIds);
+                requestBuilder.addTarget(surface);
+                captureRequestOriginal = requestBuilder.build();
+                captureRequestOriginal.writeToParcel(p, 0);
+                p.setDataPosition(0);
+                captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+                assertEquals("Parcelled camera settings should match",
+                        captureRequestParcelled.get(CaptureRequest.CONTROL_CAPTURE_INTENT),
+                        new Integer(CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW));
+                p.recycle();
+
+                // Check various invalid cases
+                p.writeInt(-1);
+                p.setDataPosition(0);
+                try {
+                    captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+                    fail("should get RuntimeException due to invalid number of settings");
+                } catch (RuntimeException e) {
+                    // Expected
+                }
+                p.recycle();
+
+                p.writeInt(0);
+                p.setDataPosition(0);
+                try {
+                    captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+                    fail("should get RuntimeException due to invalid number of settings");
+                } catch (RuntimeException e) {
+                    // Expected
+                }
+                p.recycle();
+
+                p.writeInt(1);
+                p.setDataPosition(0);
+                try {
+                    captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+                    fail("should get RuntimeException due to absent settings");
+                } catch (RuntimeException e) {
+                    // Expected
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
      * Test black level lock when exposure value change.
      * <p>
      * When {@link CaptureRequest#BLACK_LEVEL_LOCK} is true in a request, the
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 41366fe..aa0c064 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -908,7 +908,6 @@
                 verifyLensCalibration(poseRotation, poseTranslation, poseReference,
                         cameraIntrinsics, radialDistortion, precorrectionArray);
 
-
             } else {
                 boolean hasFields =
                     hasDepth16 && (poseTranslation != null) &&
@@ -1463,6 +1462,63 @@
             counter++;
         }
     }
+
+    /**
+     * Check Logical camera capability
+     */
+    public void testLogicalCameraCharacteristics() throws Exception {
+        int counter = 0;
+        List<String> cameraIdList = Arrays.asList(mIds);
+
+        for (CameraCharacteristics c : mCharacteristics) {
+            int[] capabilities = CameraTestUtils.getValueNotNull(
+                    c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+            boolean supportLogicalCamera = arrayContains(capabilities,
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
+            if (supportLogicalCamera) {
+                List<String> physicalCameraIds = c.getPhysicalCameraIds();
+                assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null",
+                    physicalCameraIds);
+                assertTrue("Logical camera must contain at least 2 physical camera ids",
+                    physicalCameraIds.size() >= 2);
+
+                mCollector.expectKeyValueInRange(c,
+                        CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
+                        CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
+                        CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED);
+
+                for (String physicalCameraId : physicalCameraIds) {
+                    assertNotNull("Physical camera id shouldn't be null", physicalCameraId);
+                    assertTrue(
+                            String.format("Physical camera id %s shouldn't be the same as logical"
+                                    + " camera id %s", physicalCameraId, mIds[counter]),
+                            physicalCameraId != mIds[counter]);
+                    assertTrue(
+                            String.format("Physical camera id %s should be in available camera ids",
+                                    physicalCameraId),
+                            cameraIdList.contains(physicalCameraId));
+
+                    //validation for depth static metadata of physical cameras
+                    CameraCharacteristics pc =
+                            mCameraManager.getCameraCharacteristics(physicalCameraId);
+
+                    float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION);
+                    float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
+                    Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE);
+                    float[] cameraIntrinsics = pc.get(
+                            CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
+                    float[] radialDistortion = pc.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
+                    Rect precorrectionArray = pc.get(
+                            CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+
+                    verifyLensCalibration(poseRotation, poseTranslation, poseReference,
+                            cameraIntrinsics, radialDistortion, precorrectionArray);
+                }
+            }
+            counter++;
+        }
+    }
+
     /**
      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
      */
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
new file mode 100644
index 0000000..b7bd5aa
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2018 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.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.CamcorderProfile;
+import android.media.ImageReader;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+import android.view.Display;
+import android.view.Surface;
+import android.view.WindowManager;
+
+
+import com.android.compatibility.common.util.Stat;
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.mockito.Mockito.*;
+
+/**
+ * Tests exercising logical camera setup, configuration, and usage.
+ */
+public final class LogicalCameraDeviceTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "LogicalCameraTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final int CONFIGURE_TIMEOUT = 5000; //ms
+
+    private static final double NS_PER_MS = 1000000.0;
+    private static final int MAX_IMAGE_COUNT = 3;
+    private static final int NUM_FRAMES_CHECKED = 30;
+
+    private static final double FRAME_DURATION_THRESHOLD = 0.1;
+
+    /**
+     * Test that passing in invalid physical camera ids in OutputConfiguragtion behaves as expected
+     * for logical multi-camera and non-logical multi-camera.
+     */
+    public void testInvalidPhysicalCameraIdInOutputConfiguration() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Camera " + id);
+                openDevice(id);
+
+                Size yuvSize = mOrderedPreviewSizes.get(0);
+                // Create a YUV image reader.
+                ImageReader imageReader = ImageReader.newInstance(yuvSize.getWidth(),
+                        yuvSize.getHeight(), ImageFormat.YUV_420_888, /*maxImages*/1);
+
+                CameraCaptureSession.StateCallback sessionListener =
+                        mock(CameraCaptureSession.StateCallback.class);
+                List<OutputConfiguration> outputs = new ArrayList<>();
+                OutputConfiguration outputConfig = new OutputConfiguration(
+                        imageReader.getSurface());
+                outputConfig.setPhysicalCameraId(id);
+
+                // Regardless of logical camera or non-logical camera, create a session of an
+                // output configuration with invalid physical camera id, verify that the
+                // createCaptureSession fails.
+                outputs.add(outputConfig);
+                CameraCaptureSession session =
+                        CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs,
+                                sessionListener, mHandler);
+                verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+                        onConfigureFailed(any(CameraCaptureSession.class));
+                verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class));
+                verify(sessionListener, never()).onReady(any(CameraCaptureSession.class));
+                verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
+                verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test for making sure that streaming from physical streams work as expected, and
+     * FPS isn't slowed down.
+     */
+    public void testBasicPhysicalStreaming() throws Exception {
+
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Camera " + id);
+                openDevice(id);
+
+                if (!mStaticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+                    continue;
+                }
+
+                if (!mStaticInfo.isLogicalMultiCamera()) {
+                    Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
+                    continue;
+                }
+
+                assertTrue("Logical multi-camera must be LIMITED or higher",
+                        mStaticInfo.isHardwareLevelAtLeastLimited());
+
+                // Figure out yuv size to use.
+                Size yuvSize = findMaxPhysicalYuvSize(id);
+                if (yuvSize == null) {
+                    Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
+                    continue;
+                }
+
+                if (VERBOSE) {
+                    Log.v(TAG, "Camera " + id + ": Testing YUV size of " + yuvSize.getWidth() +
+                        " x " + yuvSize.getHeight());
+                }
+                List<String> physicalCameraIds =
+                        mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+                assertTrue("Logical camera must contain at least 2 physical camera ids",
+                        physicalCameraIds.size() >= 2);
+
+                List<String> noPhysicalIds = new ArrayList<>();
+                double avgLogicalDurationsMs = measureYuvFrameDuration(id, noPhysicalIds, yuvSize);
+
+                List<String> onePhysicalIds = new ArrayList<>();
+                onePhysicalIds.add(physicalCameraIds.get(0));
+                double avg1PhysicalDurationsMs = measureYuvFrameDuration(id,
+                        onePhysicalIds, yuvSize);
+                mCollector.expectLessOrEqual("The average frame duration increase of a physical "
+                        + "stream is larger than threshold: "
+                        + String.format("increase = %.2f, threshold = %.2f",
+                          (avg1PhysicalDurationsMs - avgLogicalDurationsMs)/avgLogicalDurationsMs,
+                          FRAME_DURATION_THRESHOLD),
+                        avgLogicalDurationsMs*(1+FRAME_DURATION_THRESHOLD),
+                        avg1PhysicalDurationsMs);
+
+                double avgAllPhysicalDurationsMs = measureYuvFrameDuration(
+                        id, physicalCameraIds, yuvSize);
+
+                mCollector.expectLessOrEqual("The average frame duration increase of all physical "
+                        + "streams is larger than threshold: "
+                        + String.format("increase = %.2f, threshold = %.2f",
+                          (avgAllPhysicalDurationsMs - avgLogicalDurationsMs)/avgLogicalDurationsMs,
+                          FRAME_DURATION_THRESHOLD),
+                        avgLogicalDurationsMs*(1+FRAME_DURATION_THRESHOLD),
+                        avgAllPhysicalDurationsMs);
+
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test for making sure that multiple requests for physical cameras work as expected.
+     */
+    public void testBasicPhysicalRequests() throws Exception {
+
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Camera " + id);
+                openDevice(id);
+
+                if (!mStaticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+                    continue;
+                }
+
+                if (!mStaticInfo.isLogicalMultiCamera()) {
+                    Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
+                    continue;
+                }
+
+                assertTrue("Logical multi-camera must be LIMITED or higher",
+                        mStaticInfo.isHardwareLevelAtLeastLimited());
+
+                // Figure out yuv size to use.
+                Size yuvSize= findMaxPhysicalYuvSize(id);
+                if (yuvSize == null) {
+                    Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
+                    continue;
+                }
+
+                if (VERBOSE) {
+                    Log.v(TAG, "Camera " + id + ": Testing YUV size of " + yuvSize.getWidth() +
+                        " x " + yuvSize.getHeight());
+                }
+                List<CaptureRequest.Key<?>> physicalRequestKeys =
+                    mStaticInfo.getCharacteristics().getAvailablePhysicalCameraRequestKeys();
+                if (physicalRequestKeys == null) {
+                    Log.i(TAG, "Camera " + id + ": no available physical request keys, skipping");
+                    continue;
+                }
+
+                List<String> physicalCameraIds =
+                        mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+                assertTrue("Logical camera must contain at least 2 physical camera ids",
+                        physicalCameraIds.size() >= 2);
+                ArraySet<String> physicalIdSet = new ArraySet<String>(physicalCameraIds.size());
+                physicalIdSet.addAll(physicalCameraIds);
+
+                List<OutputConfiguration> outputConfigs = new ArrayList<>();
+                List<ImageReader> imageReaders = new ArrayList<>();
+                SimpleImageReaderListener readerListener = new SimpleImageReaderListener();
+                ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+                        ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+                        readerListener, mHandler);
+                imageReaders.add(yuvTarget);
+                OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+                outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
+
+                CaptureRequest.Builder requestBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
+                requestBuilder.addTarget(config.getSurface());
+
+                mSessionListener = new BlockingSessionCallback();
+                mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+                        mSessionListener, mHandler);
+
+                for (int i = 0; i < MAX_IMAGE_COUNT; i++) {
+                    mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(), mHandler);
+                    readerListener.getImage(WAIT_FOR_RESULT_TIMEOUT_MS);
+                }
+
+                if (mSession != null) {
+                    mSession.close();
+                }
+
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Tests invalid/incorrect multiple physical capture request cases.
+     */
+    public void testInvalidPhysicalCameraRequests() throws Exception {
+
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Camera " + id);
+                openDevice(id);
+
+                if (!mStaticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+                    continue;
+                }
+
+                assertTrue("Logical multi-camera must be LIMITED or higher",
+                        mStaticInfo.isHardwareLevelAtLeastLimited());
+
+                // Figure out yuv size to use.
+                Size yuvSize= findMaxPhysicalYuvSize(id);
+                if (yuvSize == null) {
+                    Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
+                    continue;
+                }
+
+                List<OutputConfiguration> outputConfigs = new ArrayList<>();
+                List<ImageReader> imageReaders = new ArrayList<>();
+                SimpleImageReaderListener readerListener = new SimpleImageReaderListener();
+                ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+                        ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+                        readerListener, mHandler);
+                imageReaders.add(yuvTarget);
+                OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+                outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
+
+                ArraySet<String> physicalIdSet = new ArraySet<String>();
+                // Invalid physical id
+                physicalIdSet.add("-2");
+
+                CaptureRequest.Builder requestBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
+                requestBuilder.addTarget(config.getSurface());
+
+                // Check for invalid setting get/set
+                try {
+                    requestBuilder.getPhysicalCameraKey(CaptureRequest.CONTROL_CAPTURE_INTENT, "-1");
+                    fail("No exception for invalid physical camera id");
+                } catch (IllegalArgumentException e) {
+                    //expected
+                }
+
+                try {
+                    requestBuilder.setPhysicalCameraKey(CaptureRequest.CONTROL_CAPTURE_INTENT,
+                            new Integer(0), "-1");
+                    fail("No exception for invalid physical camera id");
+                } catch (IllegalArgumentException e) {
+                    //expected
+                }
+
+                mSessionListener = new BlockingSessionCallback();
+                mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+                        mSessionListener, mHandler);
+
+                try {
+                    mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(),
+                            mHandler);
+                    fail("No exception for invalid physical camera id");
+                } catch (IllegalArgumentException e) {
+                    //expected
+                }
+
+                if (mStaticInfo.isLogicalMultiCamera()) {
+                    List<String> physicalCameraIds =
+                        mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+                    assertTrue("Logical camera must contain at least 2 physical camera ids",
+                            physicalCameraIds.size() >= 2);
+
+                    physicalIdSet.clear();
+                    physicalIdSet.addAll(physicalCameraIds);
+                    requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
+                    requestBuilder.addTarget(config.getSurface());
+                    CaptureRequest request = requestBuilder.build();
+
+                    // Streaming requests with individual physical camera settings are not
+                    // supported.
+                    try {
+                        mSession.setRepeatingRequest(request, new SimpleCaptureCallback(),
+                                mHandler);
+                        fail("Streaming requests that include physical camera settings are " +
+                                "supported");
+                    } catch (IllegalArgumentException e) {
+                        //expected
+                    }
+
+                    try {
+                        ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+                        requestList.add(request);
+                        mSession.setRepeatingBurst(requestList, new SimpleCaptureCallback(),
+                                mHandler);
+                        fail("Streaming requests that include physical camera settings are " +
+                                "supported");
+                    } catch (IllegalArgumentException e) {
+                        //expected
+                    }
+                }
+
+                if (mSession != null) {
+                    mSession.close();
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Find the maximum YUV stream size that's supported by physical cameras.
+     */
+    private Size findMaxPhysicalYuvSize(String cameraId) throws Exception {
+        List<String> physicalCameras =
+                mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+        List<Size> yuvSizes = CameraTestUtils.getSortedSizesForFormat(
+                cameraId, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null);
+        Size bestYuvSize = null;
+        for (Size yuvSize : yuvSizes) {
+            boolean physicalSizeSupported = true;
+            for (String physicalCameraId : physicalCameras) {
+                List<Size> yuvSizesForPhysicalCamera =
+                        CameraTestUtils.getSortedSizesForFormat(physicalCameraId,
+                        mCameraManager, ImageFormat.YUV_420_888, /*bound*/null);
+                if (!yuvSizesForPhysicalCamera.contains(yuvSize)) {
+                    physicalSizeSupported = false;
+                    break;
+                }
+            }
+            if (physicalSizeSupported) {
+                bestYuvSize = yuvSize;
+                break;
+            }
+        }
+        return bestYuvSize;
+    }
+
+    /**
+     * Measure the average frame duration of logical YUV stream.
+     */
+    private double measureYuvFrameDuration(String logicalCameraId,
+            List<String> physicalCameraIds, Size yuvSize) throws Exception {
+        List<OutputConfiguration> outputConfigs = new ArrayList<>();
+        List<ImageReader> imageReaders = new ArrayList<>();
+        if (physicalCameraIds.size() == 0) {
+            ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+                    ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+                    new ImageDropperListener(), mHandler);
+            imageReaders.add(yuvTarget);
+            OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+            outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
+        } else {
+            for (String physicalCameraId : physicalCameraIds) {
+                ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+                        ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+                        new ImageDropperListener(), mHandler);
+                OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+                config.setPhysicalCameraId(physicalCameraId);
+                outputConfigs.add(config);
+                imageReaders.add(yuvTarget);
+            }
+        }
+
+        // Stream YUV size and note down the FPS
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        for (OutputConfiguration c : outputConfigs) {
+            requestBuilder.addTarget(c.getSurface());
+        }
+
+        mSessionListener = new BlockingSessionCallback();
+        mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+                mSessionListener, mHandler);
+
+        SimpleCaptureCallback simpleResultListener =
+                new SimpleCaptureCallback();
+        StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        final long minFrameDuration = config.getOutputMinFrameDuration(
+                ImageFormat.YUV_420_888, yuvSize);
+        if (minFrameDuration > 0) {
+            Range<Integer> targetRange = getSuitableFpsRangeForDuration(logicalCameraId,
+                    minFrameDuration);
+            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, targetRange);
+        }
+        mSession.setRepeatingRequest(requestBuilder.build(),
+                simpleResultListener, mHandler);
+
+        // Converge AE
+        waitForAeStable(simpleResultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+        if (mStaticInfo.isAeLockSupported()) {
+            // Lock AE if supported.
+            requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+            mSession.setRepeatingRequest(requestBuilder.build(), simpleResultListener,
+                    mHandler);
+            waitForResultValue(simpleResultListener, CaptureResult.CONTROL_AE_STATE,
+                    CaptureResult.CONTROL_AE_STATE_LOCKED, NUM_RESULTS_WAIT_TIMEOUT);
+        }
+
+        long prevTimestamp = -1;
+        double[] frameDurationMs = new double[NUM_FRAMES_CHECKED-1];
+        for (int i = 0; i < NUM_FRAMES_CHECKED; i++) {
+            CaptureResult captureResult =
+                    simpleResultListener.getCaptureResult(
+                    CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+            long timestamp = captureResult.get(
+                    CaptureResult.SENSOR_TIMESTAMP);
+            if (prevTimestamp != -1) {
+                frameDurationMs[i-1] = (double)(timestamp - prevTimestamp)/NS_PER_MS;
+            }
+            prevTimestamp = timestamp;
+        }
+        double avgDurationMs = Stat.getAverage(frameDurationMs);
+        if (VERBOSE) {
+            Log.v(TAG, "average Duration is " + avgDurationMs + " ms");
+        }
+
+        // Stop preview
+        if (mSession != null) {
+            mSession.close();
+        }
+
+        return avgDurationMs;
+    }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index b449118..3045da0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -30,10 +30,11 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.params.InputConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.CamcorderProfile;
 import android.media.Image;
 import android.media.ImageReader;
@@ -1680,36 +1681,69 @@
     private void testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)
             throws Exception {
 
-        Log.i(TAG, String.format("Testing Camera %s, config %s",
+        //TODO: Remove below test workaround once HAL is fixed.
+        if (mStaticInfo.isLogicalMultiCamera()) {
+            int yuvStreams = 0;
+            int rawStreams = 0;
+            for (int i = 0; i < config.length; i+= 2) {
+                int format = config[i];
+                if (format == JPEG) {
+                    return;
+                } else if (format == YUV || format == PRIV) {
+                    yuvStreams++;
+                } else if (format == RAW) {
+                    rawStreams++;
+                }
+            }
+            if ((yuvStreams > 2) || (yuvStreams == 2 && rawStreams == 1)) {
+                return;
+            }
+        }
+
+        Log.i(TAG, String.format("Testing single Camera %s, config %s",
                         cameraId, MaxStreamSizes.configToString(config)));
 
+        testSingleCameraOutputCombination(cameraId, config, maxSizes);
+
+        if (mStaticInfo.isLogicalMultiCamera()) {
+            Log.i(TAG, String.format("Testing logical Camera %s, config %s",
+                    cameraId, MaxStreamSizes.configToString(config)));
+
+            testMultiCameraOutputCombination(cameraId, config, maxSizes);
+        }
+    }
+
+    private void testSingleCameraOutputCombination(String cameraId, int[] config,
+        MaxStreamSizes maxSizes) throws Exception {
+
         // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
         final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
         final int MIN_RESULT_COUNT = 3;
 
         // Set up outputs
-        List<Surface> outputSurfaces = new ArrayList<Surface>();
+        List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
         List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
         List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
         List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
         List<ImageReader> rawTargets = new ArrayList<ImageReader>();
 
         setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
-                rawTargets, outputSurfaces, MIN_RESULT_COUNT);
+                rawTargets, outputConfigs, MIN_RESULT_COUNT, -1 /*overrideStreamIndex*/,
+                null /*overridePhysicalCameraIds*/);
 
         boolean haveSession = false;
         try {
             CaptureRequest.Builder requestBuilder =
                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
 
-            for (Surface s : outputSurfaces) {
-                requestBuilder.addTarget(s);
+            for (OutputConfiguration c : outputConfigs) {
+                requestBuilder.addTarget(c.getSurface());
             }
 
             CameraCaptureSession.CaptureCallback mockCaptureCallback =
                     mock(CameraCaptureSession.CaptureCallback.class);
 
-            createSession(outputSurfaces);
+            createSessionByConfigs(outputConfigs);
             haveSession = true;
             CaptureRequest request = requestBuilder.build();
             mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
@@ -1756,55 +1790,209 @@
         }
     }
 
-    private void setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes,
+    private void testMultiCameraOutputCombination(String cameraId, int[] config,
+        MaxStreamSizes maxSizes) throws Exception {
+
+        // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
+        final int MIN_RESULT_COUNT = 3;
+        List<String> physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+
+        for (int i = 0; i < config.length; i += 2) {
+            int format = config[i];
+            if (format != YUV && format != RAW) {
+                continue;
+            }
+
+            // Set up outputs
+            List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+            List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
+            List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
+            List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
+            List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+
+            setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
+                    rawTargets, outputConfigs, MIN_RESULT_COUNT, i, physicalCameraIds);
+
+            boolean haveSession = false;
+            try {
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                for (OutputConfiguration c : outputConfigs) {
+                    requestBuilder.addTarget(c.getSurface());
+                }
+
+                CameraCaptureSession.CaptureCallback mockCaptureCallback =
+                        mock(CameraCaptureSession.CaptureCallback.class);
+
+                createSessionByConfigs(outputConfigs);
+                haveSession = true;
+                CaptureRequest request = requestBuilder.build();
+                mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
+
+                verify(mockCaptureCallback,
+                        timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
+                        .onCaptureCompleted(
+                            eq(mCameraSession),
+                            eq(request),
+                            isA(TotalCaptureResult.class));
+                verify(mockCaptureCallback, never()).
+                        onCaptureFailed(
+                            eq(mCameraSession),
+                            eq(request),
+                            isA(CaptureFailure.class));
+
+            } catch (Throwable e) {
+                mCollector.addMessage(String.format("Output combination %s failed due to: %s",
+                        MaxStreamSizes.configToString(config), e.getMessage()));
+            }
+            if (haveSession) {
+                try {
+                    Log.i(TAG, String.format("Done with camera %s, config %s, closing session",
+                                    cameraId, MaxStreamSizes.configToString(config)));
+                    stopCapture(/*fast*/false);
+                } catch (Throwable e) {
+                    mCollector.addMessage(
+                        String.format("Closing down for output combination %s failed due to: %s",
+                                MaxStreamSizes.configToString(config), e.getMessage()));
+                }
+            }
+
+            for (SurfaceTexture target : privTargets) {
+                target.release();
+            }
+            for (ImageReader target : jpegTargets) {
+                target.close();
+            }
+            for (ImageReader target : yuvTargets) {
+                target.close();
+            }
+            for (ImageReader target : rawTargets) {
+                target.close();
+            }
+        }
+    }
+
+    private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
             List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
             List<ImageReader> yuvTargets, List<ImageReader> rawTargets,
             List<Surface> outputSurfaces, int numBuffers) {
+        List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration> ();
+
+        setupConfigurationTargets(configs, maxSizes, privTargets, jpegTargets, yuvTargets,
+                rawTargets, outputConfigs, numBuffers, -1 /*overrideStreamIndex*/,
+                null /*overridePhysicalCameraIds*/);
+
+        for (OutputConfiguration outputConfig : outputConfigs) {
+            outputSurfaces.add(outputConfig.getSurface());
+        }
+    }
+
+    private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
+            List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
+            List<ImageReader> yuvTargets, List<ImageReader> rawTargets,
+            List<OutputConfiguration> outputConfigs, int numBuffers,
+            int overrideStreamIndex, List<String> overridePhysicalCameraIds) {
 
         ImageDropperListener imageDropperListener = new ImageDropperListener();
 
-        for (int i = 0; i < outputConfigs.length; i += 2) {
-            int format = outputConfigs[i];
-            int sizeLimit = outputConfigs[i + 1];
+        for (int i = 0; i < configs.length; i += 2) {
+            int format = configs[i];
+            int sizeLimit = configs[i + 1];
+            Surface newSurface;
 
-            switch (format) {
-                case PRIV: {
-                    Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
-                    SurfaceTexture target = new SurfaceTexture(/*random int*/1);
-                    target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
-                    outputSurfaces.add(new Surface(target));
-                    privTargets.add(target);
-                    break;
+            int numConfigs = 1;
+            StaticMetadata physicalStaticMetadata;
+
+            if (overridePhysicalCameraIds != null &&
+                    overridePhysicalCameraIds.size() > 1) {
+                numConfigs = overridePhysicalCameraIds.size();
+            }
+            for (int j = 0; j < numConfigs; j++) {
+                switch (format) {
+                    case PRIV: {
+                        Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
+                        SurfaceTexture target = new SurfaceTexture(/*random int*/1);
+                        target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
+                        OutputConfiguration config = new OutputConfiguration(new Surface(target));
+                        if (numConfigs > 1) {
+                            physicalStaticMetadata =
+                                mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+                            Size[] privSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(PRIV,
+                                    StaticMetadata.StreamDirection.Output);
+                            if (!Arrays.asList(privSizes).contains(targetSize)) {
+                                continue;
+                            }
+                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+                        }
+                        outputConfigs.add(config);
+                        privTargets.add(target);
+                        break;
+                    }
+                    case JPEG: {
+                        Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
+                        ImageReader target = ImageReader.newInstance(
+                            targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
+                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                        if (numConfigs > 1) {
+                            physicalStaticMetadata =
+                                    mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+                            Size[] jpegSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(JPEG,
+                                    StaticMetadata.StreamDirection.Output);
+                            if (!Arrays.asList(jpegSizes).contains(targetSize)) {
+                                continue;
+                            }
+                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+                        }
+                        outputConfigs.add(config);
+                        jpegTargets.add(target);
+                        break;
+                    }
+                    case YUV: {
+                        Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
+                        ImageReader target = ImageReader.newInstance(
+                            targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
+                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                        if (numConfigs > 1) {
+                            physicalStaticMetadata =
+                                    mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+                            Size[] yuvSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(YUV,
+                                    StaticMetadata.StreamDirection.Output);
+                            if (!Arrays.asList(yuvSizes).contains(targetSize)) {
+                                continue;
+                            }
+                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+                        }
+                        outputConfigs.add(config);
+                        yuvTargets.add(target);
+                        break;
+                    }
+                    case RAW: {
+                        Size targetSize = maxSizes.maxRawSize;
+                        ImageReader target = ImageReader.newInstance(
+                            targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
+                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                        if (numConfigs > 1) {
+                            physicalStaticMetadata =
+                                    mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+                            Size[] rawSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(RAW,
+                                    StaticMetadata.StreamDirection.Output);
+                            if (!Arrays.asList(rawSizes).contains(targetSize)) {
+                                continue;
+                            }
+                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+                        }
+                        outputConfigs.add(config);
+                        rawTargets.add(target);
+                        break;
+                    }
+                    default:
+                        fail("Unknown output format " + format);
                 }
-                case JPEG: {
-                    Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
-                    ImageReader target = ImageReader.newInstance(
-                        targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
-                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                    outputSurfaces.add(target.getSurface());
-                    jpegTargets.add(target);
-                    break;
-                }
-                case YUV: {
-                    Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
-                    ImageReader target = ImageReader.newInstance(
-                        targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
-                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                    outputSurfaces.add(target.getSurface());
-                    yuvTargets.add(target);
-                    break;
-                }
-                case RAW: {
-                    Size targetSize = maxSizes.maxRawSize;
-                    ImageReader target = ImageReader.newInstance(
-                        targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
-                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                    outputSurfaces.add(target.getSurface());
-                    rawTargets.add(target);
-                    break;
-                }
-                default:
-                    fail("Unknown output format " + format);
             }
         }
     }
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 0108ee6..777e0cd 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -27,6 +27,7 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.util.Size;
 import android.hardware.camera2.cts.CameraTestUtils;
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -48,6 +49,7 @@
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 public class Camera2AndroidTestCase extends AndroidTestCase {
@@ -66,6 +68,7 @@
     protected BlockingSessionCallback mCameraSessionListener;
     protected BlockingStateCallback mCameraListener;
     protected String[] mCameraIds;
+    protected HashMap<String, StaticMetadata> mAllStaticInfo;
     protected ImageReader mReader;
     protected Surface mReaderSurface;
     protected Handler mHandler;
@@ -109,6 +112,14 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         mCollector = new CameraErrorCollector();
+
+        mAllStaticInfo = new HashMap<String, StaticMetadata>();
+        for (String cameraId : mCameraIds) {
+            StaticMetadata staticMetadata = new StaticMetadata(
+                    mCameraManager.getCameraCharacteristics(cameraId),
+                    CheckLevel.ASSERT, /*collector*/null);
+            mAllStaticInfo.put(cameraId, staticMetadata);
+        }
     }
 
     @Override
@@ -217,6 +228,18 @@
     }
 
     /**
+     * Create a {@link #CameraCaptureSession} using the currently open camera with
+     * OutputConfigurations.
+     *
+     * @param outputSurfaces The set of output surfaces to configure for this session
+     */
+    protected void createSessionByConfigs(List<OutputConfiguration> outputConfigs) throws Exception {
+        mCameraSessionListener = new BlockingSessionCallback();
+        mCameraSession = CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputConfigs,
+                mCameraSessionListener, mHandler);
+    }
+
+    /**
      * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
      * given camera id. The default mCameraListener is used to wait for states.
      * <p>
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 30f56eb..24b948b 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -1890,7 +1890,7 @@
 
         checkArrayValuesInRange(key, availableCaps,
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING);
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
         capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
         return capList;
     }
@@ -2212,6 +2212,18 @@
     }
 
     /**
+     * Check if this camera device is a logical multi-camera backed by multiple
+     * physical cameras.
+     *
+     * @return true if this is a logical multi-camera.
+     */
+    public boolean isLogicalMultiCamera() {
+        List<Integer> availableCapabilities = getAvailableCapabilitiesChecked();
+        return (availableCapabilities.contains(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA));
+    }
+
+    /**
      * Check if high speed video is supported (HIGH_SPEED_VIDEO scene mode is
      * supported, supported high speed fps ranges and sizes are valid).
      *
diff --git a/tests/framework/base/activitymanager/AndroidTest.xml b/tests/framework/base/activitymanager/AndroidTest.xml
index 7f487ef..213cabb 100644
--- a/tests/framework/base/activitymanager/AndroidTest.xml
+++ b/tests/framework/base/activitymanager/AndroidTest.xml
@@ -25,6 +25,7 @@
         <option name="test-file-name" value="CtsDeviceServicesTestSecondApp.apk" />
         <option name="test-file-name" value="CtsDeviceServicesTestThirdApp.apk" />
         <option name="test-file-name" value="CtsDeviceDebuggableApp.apk" />
+        <option name="test-file-name" value="CtsDeviceDeprecatedSdkApp.apk" />
         <option name="test-file-name" value="CtsDeviceDisplaySizeApp.apk" />
         <option name="test-file-name" value="CtsDevicePrereleaseSdkApp.apk" />
         <option name="test-file-name" value="CtsDisplayServiceApp.apk" />
diff --git a/tests/framework/base/activitymanager/appDeprecatedSdk/Android.mk b/tests/framework/base/activitymanager/appDeprecatedSdk/Android.mk
new file mode 100644
index 0000000..d28d30b
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDeprecatedSdk/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 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
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := 16
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDeviceDeprecatedSdkApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/activitymanager/appDeprecatedSdk/AndroidManifest.xml b/tests/framework/base/activitymanager/appDeprecatedSdk/AndroidManifest.xml
new file mode 100644
index 0000000..19847b3
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDeprecatedSdk/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+
+<!-- Target sdk version set < 17. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.server.am.deprecatedsdk">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application>
+        <activity android:name=".MainActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appDeprecatedSdk/src/android/server/am/deprecatedsdk/MainActivity.java b/tests/framework/base/activitymanager/appDeprecatedSdk/src/android/server/am/deprecatedsdk/MainActivity.java
new file mode 100644
index 0000000..57b4e44
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDeprecatedSdk/src/android/server/am/deprecatedsdk/MainActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 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.am.deprecatedsdk;
+
+import android.app.Activity;
+
+public class MainActivity extends Activity {
+
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
index 18bf397..56aedd0 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -72,7 +72,6 @@
     }
 
     @Presubmit
-    @FlakyTest(bugId = 71877849)
     @Test
     public void testChangeFontScaleRelaunch() throws Exception {
         // Should relaunch and receive no onConfigurationChanged()
@@ -80,7 +79,6 @@
     }
 
     @Presubmit
-    @FlakyTest(bugId = 71877849)
     @Test
     public void testChangeFontScaleNoRelaunch() throws Exception {
         // Should receive onConfigurationChanged() and no relaunch
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index 36e6f04..c5f13f1 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -83,7 +83,6 @@
 
     @Test
     @Presubmit
-    @FlakyTest(bugId = 71918731)
     public void testStackList() throws Exception {
         launchActivity(TEST_ACTIVITY_NAME);
         mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
@@ -235,7 +234,6 @@
 
     @Test
     @Presubmit
-    @FlakyTest(bugId = 71918731)
     public void testLaunchToSideSingleInstance() throws Exception {
         launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
     }
@@ -245,7 +243,6 @@
         launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
     }
 
-    @FlakyTest
     @Presubmit
     @Test
     public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/DeprecatedTargetSdkTest.java b/tests/framework/base/activitymanager/src/android/server/am/DeprecatedTargetSdkTest.java
new file mode 100644
index 0000000..42edc5f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/DeprecatedTargetSdkTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Ensure that compatibility dialog is shown when launching an application
+ * targeting a deprecated version of SDK.
+ * <p>Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:DeprecatedTargetSdkTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeprecatedTargetSdkTest extends ActivityManagerTestBase {
+    private static final String AM_START_COMMAND = "am start -n %s/%s.%s";
+    private static final String AM_FORCE_STOP = "am force-stop %s";
+
+    private static final int ACTIVITY_TIMEOUT_MILLIS = 1000;
+    private static final int WINDOW_TIMEOUT_MILLIS = 1000;
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        // Ensure app process is stopped.
+        forceStopPackage("android.server.am.deprecatedsdk");
+        forceStopPackage("android.server.am");
+    }
+
+    @Test
+    public void testCompatibilityDialog() throws Exception {
+        // Launch target app.
+        startActivity("android.server.am.deprecatedsdk", "MainActivity");
+        verifyWindowDisplayed("MainActivity", ACTIVITY_TIMEOUT_MILLIS);
+        verifyWindowDisplayed("DeprecatedTargetSdkVersionDialog", WINDOW_TIMEOUT_MILLIS);
+
+        // Go back to dismiss the warning dialog.
+        executeShellCommand("input keyevent 4");
+
+        // Go back again to formally stop the app. If we just kill the process, it'll attempt to
+        // resume rather than starting from scratch (as far as ActivityStack is concerned) and it
+        // won't invoke the warning dialog.
+        executeShellCommand("input keyevent 4");
+    }
+
+    private void forceStopPackage(String packageName) {
+        final String forceStopCmd = String.format(AM_FORCE_STOP, packageName);
+        executeShellCommand(forceStopCmd);
+    }
+
+    private void startActivity(String packageName, String activityName){
+        executeShellCommand(getStartCommand(packageName, activityName));
+    }
+
+    private String getStartCommand(String packageName, String activityName) {
+        return String.format(AM_START_COMMAND, packageName, packageName, activityName);
+    }
+
+    private void verifyWindowDisplayed(String windowName, long timeoutMillis) {
+        boolean success = false;
+
+        // Verify that compatibility dialog is shown within 1000ms.
+        final long timeoutTimeMillis = System.currentTimeMillis() + timeoutMillis;
+        while (!success && System.currentTimeMillis() < timeoutTimeMillis) {
+            final String output = executeShellCommand("dumpsys window");
+            success = output.contains(windowName);
+        }
+
+        assertTrue(windowName + " was not displayed", success);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 89ff760..14c0179 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -53,7 +53,6 @@
  * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.CrossAppDragAndDropTests
  */
 @Presubmit
-@FlakyTest(bugId = 65739235)
 public class CrossAppDragAndDropTests {
     private static final String TAG = "CrossAppDragAndDrop";
 
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 79c35f1..b4d1c27 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -31,6 +31,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Process;
@@ -132,6 +133,12 @@
                         getCurrentInputConnection().commitText(text, newCursorPosition);
                         break;
                     }
+                    case "reportLanguageHint": {
+                        final LocaleList languageHint =
+                                command.getExtras().getParcelable("languageHint");
+                        getCurrentInputConnection().reportLanguageHint(languageHint);
+                        break;
+                    }
                 }
             }
         });
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 4fe9f5a..ed48d05 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
@@ -297,4 +298,30 @@
         mContext.sendBroadcast(intent);
         return command;
     }
+
+    /**
+     * Lets {@link MockIme} to call
+     * {@link android.view.inputmethod.InputConnection#reportLanguageHint(LocaleList)} with the
+     * given parameters.
+     *
+     * <p>This triggers {@code getCurrentInputConnection().reportLanguageHint(languageHint)}.</p>
+     *
+     * @param languageHint to be passed as the {@code languageHint} parameter
+     * @return {@link ImeCommand} object that can be passed to
+     *         {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to
+     *         wait until this event is handled by {@link MockIme}
+     */
+    @NonNull
+    public ImeCommand callReportLnaguageHint(@NonNull LocaleList languageHint) {
+        final Bundle params = new Bundle();
+        params.putParcelable("languageHint", languageHint);
+        final ImeCommand command = new ImeCommand(
+                "reportLanguageHint", SystemClock.elapsedRealtimeNanos(), true, params);
+        final Intent intent = new Intent();
+        intent.setPackage(mContext.getPackageName());
+        intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
+        intent.putExtras(command.toBundle());
+        mContext.sendBroadcast(intent);
+        return command;
+    }
 }
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionTest.java
new file mode 100644
index 0000000..ce05e01
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 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.cts.util.TestUtils.getOnMainSync;
+import static android.view.inputmethod.cts.util.TestUtils.waitOnMainUntil;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.LocaleList;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import com.android.cts.mockime.ImeCommand;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class InputConnectionTest extends EndToEndImeTestBase {
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+    @Test
+    public void testReportLanguageHint() throws Exception {
+        final String testMarker = "testReportLanguageHint-" + SystemClock.elapsedRealtimeNanos();
+
+        try(MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final AtomicReference<ArrayList<LocaleList>> languageHintHistoryRef =
+                    new AtomicReference<>();
+            TestActivity.startSync(activity -> {
+                final LinearLayout layout = new LinearLayout(activity);
+                layout.setOrientation(LinearLayout.VERTICAL);
+                final EditText editText = new EditText(activity) {
+                    @Override
+                    public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+                        final InputConnection original = super.onCreateInputConnection(editorInfo);
+                        final ArrayList<LocaleList> languageHintHistory = new ArrayList<>();
+                        final InputConnectionWrapper wrapper =
+                                new InputConnectionWrapper(original, false) {
+                                    @Override
+                                    public void reportLanguageHint(LocaleList languageHint) {
+                                        languageHintHistory.add(languageHint);
+                                    }
+                                };
+                        // In case onCreateInputConnection() gets called twice, make sure that only
+                        // the first call is used in later tests.
+                        if (languageHintHistoryRef.compareAndSet(null, languageHintHistory)) {
+                            editorInfo.privateImeOptions = testMarker;
+                        }
+                        return wrapper;
+                    }
+                };
+                editText.requestFocus();
+                layout.addView(editText);
+                return layout;
+            });
+
+            // Wait until "onStartInput" gets called for the EditText.
+            expectEvent(stream, event -> {
+                if (!TextUtils.equals("onStartInput", event.getEventName())) {
+                    return false;
+                }
+                final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+                return TextUtils.equals(testMarker, editorInfo.privateImeOptions);
+            }, TIMEOUT);
+
+            final List<LocaleList> languageHintHistory = languageHintHistoryRef.get();
+            assertNotNull(languageHintHistory);
+            assertTrue(getOnMainSync(() -> languageHintHistoryRef.get().isEmpty()));
+
+            final LocaleList localeList1 = LocaleList.forLanguageTags("sr-Cyrl-RS");
+            final ImeCommand reportLanguageHint1 = imeSession.callReportLnaguageHint(localeList1);
+            expectCommand(stream, reportLanguageHint1, TIMEOUT);
+            waitOnMainUntil(() -> languageHintHistoryRef.get().size() == 1
+                    && localeList1.equals(languageHintHistoryRef.get().get(0)), TIMEOUT);
+
+            final LocaleList localeList2 = LocaleList.forLanguageTags("sr-Latn-RS-x-android,en-US");
+            final ImeCommand reportLanguageHint2 = imeSession.callReportLnaguageHint(localeList2);
+            expectCommand(stream, reportLanguageHint2, TIMEOUT);
+            waitOnMainUntil(() -> languageHintHistoryRef.get().size() == 2
+                    && localeList2.equals(languageHintHistoryRef.get().get(1)), TIMEOUT);
+        }
+    }
+}
diff --git a/tests/tests/car/src/android/car/cts/CarAudioManagerTest.java b/tests/tests/car/src/android/car/cts/CarAudioManagerTest.java
deleted file mode 100644
index 0f9c8f1..0000000
--- a/tests/tests/car/src/android/car/cts/CarAudioManagerTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2016 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.car.cts;
-
-import android.car.Car;
-import android.car.media.CarAudioManager;
-import android.media.AudioAttributes;
-import android.platform.test.annotations.RequiresDevice;
-import android.test.suitebuilder.annotation.SmallTest;
-
-
-@SmallTest
-@RequiresDevice
-/** Unit tests for {@link CarAudioManager}. */
-public class CarAudioManagerTest extends CarApiTestBase {
-    private static final String TAG = CarAudioManagerTest.class.getSimpleName();
-    private CarAudioManager mManager;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
-        assertNotNull(mManager);
-    }
-
-    public void testGetAudioAttributesForCarUsageForMusic() throws Exception {
-        AudioAttributes.Builder musicBuilder = new AudioAttributes.Builder();
-        musicBuilder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-                .setUsage(AudioAttributes.USAGE_MEDIA);
-
-        assertEquals(musicBuilder.build(), mManager.getAudioAttributesForCarUsage(
-                             CarAudioManager.CAR_AUDIO_USAGE_MUSIC));
-    }
-
-    public void testGetAudioAttributesForCarUsageForUnknown() throws Exception {
-        AudioAttributes.Builder unknownBuilder = new AudioAttributes.Builder();
-        unknownBuilder.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
-                .setUsage(AudioAttributes.USAGE_UNKNOWN);
-
-        assertEquals(unknownBuilder.build(), mManager.getAudioAttributesForCarUsage(10007));
-    }
-}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 2695fac..1ba9753 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -181,6 +181,17 @@
     }
 
     /**
+     * Test ACTION_SHOW_ASSISTED_DIALING_SETTINGS, it will display the assisted dialing preferences.
+     */
+    public void testShowAssistedDialingSettings() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            Intent intent = new Intent(TelecomManager.ACTION_SHOW_ASSISTED_DIALING_SETTINGS);
+            assertCanBeHandled(intent);
+        }
+    }
+
+    /**
      * Test ACTION_SHOW_CALL_SETTINGS, it will display the call preferences.
      */
     public void testShowCallSettings() {
diff --git a/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java b/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java
index 32c1a3a..32db789 100644
--- a/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java
+++ b/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java
@@ -15,114 +15,145 @@
  */
 package android.content.res.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 
 import android.content.res.AssetManager;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 
-public class AssetManager_AssetInputStreamTest extends AndroidTestCase {
-    private AssetManager.AssetInputStream mAssetInputStream;
-    private final String CONTENT_STRING = "OneTwoThreeFourFiveSixSevenEightNineTen";
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mAssetInputStream = (AssetManager.AssetInputStream)mContext.getAssets().open("text.txt");
+@SmallTest
+public class AssetManager_AssetInputStreamTest {
+    private static final byte[] EXPECTED_BYTES = "OneTwoThreeFourFiveSixSevenEightNineTen".getBytes(
+            StandardCharsets.UTF_8);
+    private InputStream mAssetInputStream;
+
+    @Before
+    public void setUp() throws Exception {
+        mAssetInputStream = InstrumentationRegistry.getContext().getAssets().open("text.txt");
     }
 
-    public void testClose() throws IOException {
+    @After
+    public void tearDown() throws Exception {
         mAssetInputStream.close();
+    }
 
+    @Test
+    public void testClose() throws Exception {
+        mAssetInputStream.close();
         try {
             mAssetInputStream.read();
-            fail("should throw exception");
-        } catch (NullPointerException e) {
+            fail("read after close should throw an exception");
+        } catch (IllegalStateException e) {
             // expected
         }
     }
 
+    @Test
     public void testGetAssetInt() {
+        AssetManager.AssetInputStream assetInputStream =
+                (AssetManager.AssetInputStream) mAssetInputStream;
         try {
             // getAssetInt is no longer supported.
-            mAssetInputStream.getAssetInt();
-            fail();
+            assetInputStream.getAssetInt();
+            fail("getAssetInt should throw an exception");
         } catch (UnsupportedOperationException expected) {
         }
     }
 
+    @Test
     public void testMarkReset() throws IOException {
         assertTrue(mAssetInputStream.markSupported());
-        final int readlimit = 10;
-        final byte[] bytes = CONTENT_STRING.getBytes();
-        for (int i = 0; i < readlimit; i++) {
-            assertEquals(bytes[i], mAssetInputStream.read());
+        for (int i = 0; i < 10; i++) {
+            assertEquals(EXPECTED_BYTES[i], mAssetInputStream.read());
         }
-        mAssetInputStream.mark(readlimit);
+        mAssetInputStream.mark(10);
         mAssetInputStream.reset();
-        for (int i = 0; i < readlimit; i++) {
-            assertEquals(bytes[i + readlimit], mAssetInputStream.read());
+        for (int i = 0; i < 10; i++) {
+            assertEquals(EXPECTED_BYTES[10 + i], mAssetInputStream.read());
         }
     }
 
-    public void testReadMethods() throws IOException {
-        // test available()
-        final byte[] bytes = CONTENT_STRING.getBytes();
-        int len = mAssetInputStream.available();
-        int end = -1;
-        assertEquals(CONTENT_STRING.length(), len);
-        for (int i = 0; i < len; i++) {
-            assertEquals(bytes[i], mAssetInputStream.read());
+    @Test
+    public void testSingleByteRead() throws Exception {
+        assertEquals(EXPECTED_BYTES.length, mAssetInputStream.available());
+        for (int i = 0; i < EXPECTED_BYTES.length; i++) {
+            assertEquals(EXPECTED_BYTES[i], mAssetInputStream.read());
+            assertEquals(EXPECTED_BYTES.length - i - 1, mAssetInputStream.available());
         }
-        assertEquals(end, mAssetInputStream.read());
 
-        // test read(byte[])
-        mAssetInputStream.reset();
-        int dataLength = 10;
-        byte[] data = new byte[dataLength];
-        int ret = mAssetInputStream.read(data);
-        assertEquals(dataLength, ret);
-        for (int i = 0; i < dataLength; i++) {
-            assertEquals(bytes[i], data[i]);
-        }
-        data = new byte[len - dataLength];
-        assertEquals(len - dataLength, mAssetInputStream.read(data));
-        for (int i = 0; i < len - dataLength; i++) {
-            assertEquals(bytes[i + dataLength], data[i]);
-        }
-        assertEquals(end, mAssetInputStream.read(data));
+        // Check end-of-file condition.
+        assertEquals(-1, mAssetInputStream.read());
+        assertEquals(0, mAssetInputStream.available());
+    }
 
-        // test read(bytep[], int, int)
-        mAssetInputStream.reset();
-        int offset = 0;
-        ret = mAssetInputStream.read(data, offset, dataLength);
-        assertEquals(dataLength, ret);
-        for (int i = offset; i < ret; i++) {
-            assertEquals(bytes[i], data[offset + i]);
+    @Test
+    public void testByteArrayRead() throws Exception {
+        byte[] buffer = new byte[10];
+        assertEquals(10, mAssetInputStream.read(buffer));
+        for (int i = 0; i < 10; i++) {
+            assertEquals(EXPECTED_BYTES[i], buffer[i]);
         }
-        mAssetInputStream.reset();
-        offset = 2;
-        ret = mAssetInputStream.read(data, offset, dataLength);
-        assertEquals(dataLength, ret);
-        for (int i = offset; i < ret; i++) {
-            assertEquals(bytes[i], data[offset + i]);
-        }
-        data = new byte[len + offset];
-        ret = mAssetInputStream.read(data, offset, len);
-        assertEquals(len - dataLength, ret);
-        for (int i = offset; i < ret; i++) {
-            assertEquals(bytes[i + dataLength], data[offset + i]);
-        }
-        assertEquals(end, mAssetInputStream.read(data, offset, len));
-        // test len is zero,
-        mAssetInputStream.reset();
-        assertEquals(0, mAssetInputStream.read(data, 0, 0));
-        // test skip(int)
-        int skipLenth = 8;
-        mAssetInputStream.reset();
-        mAssetInputStream.skip(skipLenth);
-        assertEquals(CONTENT_STRING.charAt(skipLenth), mAssetInputStream.read());
 
-        // test read(byte[] b), b is null
+        buffer = new byte[5];
+        assertEquals(5, mAssetInputStream.read(buffer));
+        for (int i = 0; i < 5; i++) {
+            assertEquals(EXPECTED_BYTES[10 + i], buffer[i]);
+        }
+
+        // Check end-of-file condition.
+        buffer = new byte[EXPECTED_BYTES.length - 15];
+        assertEquals(buffer.length, mAssetInputStream.read(buffer));
+        assertEquals(-1, mAssetInputStream.read(buffer));
+        assertEquals(0, mAssetInputStream.available());
+    }
+
+    @Test
+    public void testByteArrayReadOffset() throws Exception {
+        byte[] buffer = new byte[15];
+        assertEquals(10, mAssetInputStream.read(buffer, 0, 10));
+        assertEquals(EXPECTED_BYTES.length - 10, mAssetInputStream.available());
+        for (int i = 0; i < 10; i++) {
+            assertEquals(EXPECTED_BYTES[i], buffer[i]);
+        }
+
+        assertEquals(5, mAssetInputStream.read(buffer, 10, 5));
+        assertEquals(EXPECTED_BYTES.length - 15, mAssetInputStream.available());
+        for (int i = 0; i < 15; i++) {
+            assertEquals(EXPECTED_BYTES[i], buffer[i]);
+        }
+
+        // Check end-of-file condition.
+        buffer = new byte[EXPECTED_BYTES.length];
+        assertEquals(EXPECTED_BYTES.length - 15,
+                mAssetInputStream.read(buffer, 15, EXPECTED_BYTES.length - 15));
+        assertEquals(-1, mAssetInputStream.read(buffer, 0, 1));
+        assertEquals(0, mAssetInputStream.available());
+    }
+
+    @Test
+    public void testSkip() throws Exception {
+        assertEquals(8, mAssetInputStream.skip(8));
+        assertEquals(EXPECTED_BYTES.length - 8, mAssetInputStream.available());
+        assertEquals(EXPECTED_BYTES[8], mAssetInputStream.read());
+
+        // Check that skip respects the available space.
+        assertEquals(EXPECTED_BYTES.length - 8 - 1, mAssetInputStream.skip(1000));
+        assertEquals(0, mAssetInputStream.available());
+    }
+
+    @Test
+    public void testArgumentEdgeCases() throws Exception {
+        // test read(byte[]): byte[] is null
         try {
             mAssetInputStream.read(null);
             fail("should throw NullPointerException ");
@@ -130,34 +161,39 @@
             // expected
         }
 
+        // test read(byte[], int, int): byte[] is null
         try {
             mAssetInputStream.read(null, 0, mAssetInputStream.available());
             fail("should throw NullPointerException ");
         } catch (NullPointerException e) {
             // expected
         }
-        // test read(bytep[], int, int): offset is negative,
+
+        // test read(byte[]): byte[] is len 0
+        final int previousAvailable = mAssetInputStream.available();
+        assertEquals(0, mAssetInputStream.read(new byte[0]));
+        assertEquals(previousAvailable, mAssetInputStream.available());
+
+        // test read(byte[]): byte[] is len 0
+        assertEquals(0, mAssetInputStream.read(new byte[0], 0, 0));
+        assertEquals(previousAvailable, mAssetInputStream.available());
+
+        // test read(byte[], int, int): offset is negative
         try {
-            data = new byte[10];
+            byte[] data = new byte[10];
             mAssetInputStream.read(data, -1, mAssetInputStream.available());
             fail("should throw IndexOutOfBoundsException ");
         } catch (IndexOutOfBoundsException e) {
             // expected
         }
 
-        // test read(bytep[], int, int): len+offset greater than data length
+        // test read(byte[], int, int): len + offset greater than data length
         try {
-            data = new byte[10];
+            byte[] data = new byte[10];
             assertEquals(0, mAssetInputStream.read(data, 0, data.length + 2));
             fail("should throw IndexOutOfBoundsException ");
         } catch (IndexOutOfBoundsException e) {
+            // expected
         }
     }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        mAssetInputStream.close();
-    }
-
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 0ea3ae0..16c6417 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -466,6 +466,89 @@
         }
     }
 
+    private MediaFormat createVideoFormatForBitrateMode(String mime, int width, int height,
+            int bitrateMode, CodecCapabilities caps) {
+        MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
+        if (!encoderCaps.isBitrateModeSupported(bitrateMode)) {
+            return null;
+        }
+
+        VideoCapabilities vidCaps = caps.getVideoCapabilities();
+        MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+
+        // bitrate
+        int maxWidth = vidCaps.getSupportedWidths().getUpper();
+        int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
+        int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
+        format.setInteger(MediaFormat.KEY_BITRATE_MODE, bitrateMode);
+        if (bitrateMode == MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ) {
+            int quality = encoderCaps.getQualityRange().getLower();
+            Log.i(TAG, "reasonable quality for " + width + "x" + height + "@" + maxRate
+                    + " " + mime + " = " + quality);
+            format.setInteger(MediaFormat.KEY_QUALITY, quality);
+        } else {
+            int bitrate = vidCaps.getBitrateRange().clamp(
+                    (int)(vidCaps.getBitrateRange().getUpper()
+                            / Math.sqrt((double)maxWidth * maxHeight / width / height)));
+            Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
+                    + " " + mime + " = " + bitrate + " mode " + bitrateMode);
+            format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+        }
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, maxRate);
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                CodecCapabilities.COLOR_FormatYUV420Flexible);
+
+        return format;
+    }
+
+    public void testAllAdvertisedVideoEncoderBitrateModes() throws IOException {
+        boolean skipped = true;
+        final int[] modes = {
+                MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ,
+                MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR,
+                MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR
+        };
+        for (MediaCodecInfo info : mAllInfos) {
+            if (!info.isEncoder()) {
+                continue;
+            }
+
+            for (String mime: info.getSupportedTypes()) {
+                boolean isVideo = isVideoMime(mime);
+                if (!isVideo) {
+                    continue;
+                }
+                skipped = false;
+
+                int numSupportedModes = 0;
+                for (int mode : modes) {
+                    MediaFormat format = createVideoFormatForBitrateMode(
+                            mime, 176, 144, mode, info.getCapabilitiesForType(mime));
+                    if (format == null) {
+                        continue;
+                    }
+                    MediaCodec codec = null;
+                    try {
+                        codec = MediaCodec.createByCodecName(info.getName());
+                        codec.configure(format, null /* surface */, null /* crypto */,
+                                MediaCodec.CONFIGURE_FLAG_ENCODE);
+                    } finally {
+                        if (codec != null) {
+                            codec.release();
+                        }
+                    }
+                    numSupportedModes++;
+                }
+                assertTrue(info.getName() + " has no supported bitrate mode",
+                        numSupportedModes > 0);
+            }
+        }
+        if (skipped) {
+            MediaUtils.skipTest("no video encoders found");
+        }
+    }
+
     public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
         boolean skipped = true;
         for (MediaCodecInfo info : mAllInfos) {
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmMetricsTest.java b/tests/tests/media/src/android/media/cts/MediaDrmMetricsTest.java
new file mode 100644
index 0000000..88e10e4
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaDrmMetricsTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 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.media.cts;
+
+import android.media.MediaDrm;
+import android.media.MediaDrm.MediaDrmStateException;
+import android.os.PersistableBundle;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import java.util.UUID;
+
+
+/**
+ * MediaDrm tests covering {@link MediaDrm#getMetrics} and related
+ * functionality.
+ */
+public class MediaDrmMetricsTest extends AndroidTestCase {
+    private static final String TAG = MediaDrmMetricsTest.class.getSimpleName();
+
+    private static final UUID CLEARKEY_SCHEME_UUID =
+            new UUID(0xe2719d58a985b3c9L, 0x781ab030af78d30eL);
+
+    public void testGetMetricsEmpty() throws Exception {
+        MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+        assertNotNull(drm);
+
+        PersistableBundle metrics = drm.getMetrics();
+        assertNotNull(metrics);
+        assertTrue(metrics.isEmpty());
+    }
+
+    public void testGetMetricsSession() throws Exception {
+        MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+        assertNotNull(drm);
+        byte[] sid = drm.openSession();
+        assertNotNull(sid);
+        byte[] sid2 = drm.openSession();
+        assertNotNull(sid2);
+
+        PersistableBundle metrics = drm.getMetrics();
+        assertNotNull(metrics);
+        assertEquals(1, metrics.keySet().size());
+
+        assertEquals(2, metrics.getLong(
+            MediaDrm.MetricsConstants.OPEN_SESSION_OK_COUNT, -1));
+        assertEquals(0, metrics.getLong(
+            MediaDrm.MetricsConstants.OPEN_SESSION_ERROR_COUNT));
+    }
+
+    public void testGetMetricsGetKeyRequest() throws Exception {
+        MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+        assertNotNull(drm);
+        byte[] sid = drm.openSession();
+        assertNotNull(sid);
+
+        try {
+          drm.getKeyRequest(sid, null, "", 2, null);
+        } catch (MediaDrmStateException e) {
+          // Exception expected.
+        }
+
+        PersistableBundle metrics = drm.getMetrics();
+        assertNotNull(metrics);
+        Log.v(TAG, metrics.toString());
+        Log.v(TAG, metrics.keySet().toString());
+        for(String key : metrics.keySet()) {
+          Log.v(TAG, "key, value: " + key + "," + metrics.get(key));
+        }
+
+        assertEquals(2, metrics.keySet().size());
+        assertEquals(-1, metrics.getLong(
+            MediaDrm.MetricsConstants.GET_KEY_REQUEST_OK_COUNT, -1));
+        assertEquals(1, metrics.getLong(
+            MediaDrm.MetricsConstants.GET_KEY_REQUEST_ERROR_COUNT));
+    }
+}
diff --git a/tests/tests/security/res/raw/cve_2017_0763.mp4 b/tests/tests/security/res/raw/cve_2017_0763.mp4
new file mode 100644
index 0000000..94027bf
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_0763.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index df37d77..3e75ea6 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -255,6 +255,11 @@
         doStagefrightTest(R.raw.cve_2017_13229);
     }
 
+    @SecurityTest
+    public void testStagefright_cve_2017_0763() throws Exception {
+        doStagefrightTest(R.raw.cve_2017_0763);
+    }
+
     /***********************************************************
      to prevent merge conflicts, add M tests below this comment,
      before any existing test methods
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index f0cad67..76e7743 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
+    <uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
 
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
 
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 321d0b5..7610176 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -81,6 +81,8 @@
     TestUtils.InvokeCounter mOnRttStatusChangedCounter;
     TestUtils.InvokeCounter mOnRttInitiationFailedCounter;
     TestUtils.InvokeCounter mOnRttRequestCounter;
+    TestUtils.InvokeCounter mOnHandoverCompleteCounter;
+    TestUtils.InvokeCounter mOnHandoverFailedCounter;
     Bundle mPreviousExtras;
     int mPreviousProperties = -1;
 
@@ -316,6 +318,15 @@
                 mOnRttInitiationFailedCounter.invoke(call, reason);
             }
 
+            @Override
+            public void onHandoverComplete(Call call) {
+                mOnHandoverCompleteCounter.invoke(call);
+            }
+
+            @Override
+            public void onHandoverFailed(Call call, int reason) {
+                mOnHandoverFailedCounter.invoke(call, reason);
+            }
         };
 
         MockInCallService.setCallbacks(mInCallCallbacks);
@@ -335,6 +346,8 @@
         mOnRttInitiationFailedCounter =
                 new TestUtils.InvokeCounter("mOnRttInitiationFailedCounter");
         mOnRttRequestCounter = new TestUtils.InvokeCounter("mOnRttRequestCounter");
+        mOnHandoverCompleteCounter = new TestUtils.InvokeCounter("mOnHandoverCompleteCounter");
+        mOnHandoverFailedCounter = new TestUtils.InvokeCounter("mOnHandoverFailedCounter");
     }
 
     /**
@@ -600,7 +613,10 @@
         if (extras == null) {
             extras = new Bundle();
         }
-        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
+        if (!extras.containsKey(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE)) {
+            extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                    TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
+        }
 
         if (!VideoProfile.isAudioOnly(videoState)) {
             extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
index 16e7b27..6610705 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
@@ -24,6 +24,7 @@
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -41,7 +42,8 @@
     public static int CONNECTION_CREATED_LOCK = 0;
     public static int CREATE_INCOMING_CONNECTION_FAILED_LOCK = 1;
     public static int CREATE_OUTGOING_CONNECTION_FAILED_LOCK = 2;
-    private static int NUM_LOCKS = CREATE_OUTGOING_CONNECTION_FAILED_LOCK + 1;
+    public static int HANDOVER_FAILED_LOCK = 3;
+    private static int NUM_LOCKS = HANDOVER_FAILED_LOCK + 1;
 
     private static CtsSelfManagedConnectionService sConnectionService;
 
@@ -60,6 +62,10 @@
 
     private Object mLock = new Object();
     private List<SelfManagedConnection> mConnections = new ArrayList<>();
+    private TestUtils.InvokeCounter mOnCreateIncomingHandoverConnectionCounter =
+            new TestUtils.InvokeCounter("incomingHandoverConnection");
+    private TestUtils.InvokeCounter mOnCreateOutgoingHandoverConnectionCounter =
+            new TestUtils.InvokeCounter("outgoingHandoverConnection");
 
     public static CtsSelfManagedConnectionService getConnectionService() {
         return sConnectionService;
@@ -106,6 +112,26 @@
         mLocks[CREATE_OUTGOING_CONNECTION_FAILED_LOCK].countDown();
     }
 
+    @Override
+    public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+            ConnectionRequest request) {
+        mOnCreateIncomingHandoverConnectionCounter.invoke(fromPhoneAccountHandle, request);
+        return createSelfManagedConnection(request, true /* incoming */);
+    }
+
+    @Override
+    public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+            ConnectionRequest request) {
+        mOnCreateOutgoingHandoverConnectionCounter.invoke(fromPhoneAccountHandle, request);
+        return createSelfManagedConnection(request, false /* incoming */);
+    }
+
+    @Override
+    public void onHandoverFailed(ConnectionRequest request, int error) {
+        mLocks[HANDOVER_FAILED_LOCK].countDown();
+    }
+
+
     public void tearDown() {
         synchronized(mLock) {
             if (mConnections != null && mConnections.size() > 0) {
@@ -192,4 +218,12 @@
             return null;
         }
     }
+
+    public TestUtils.InvokeCounter getOnCreateIncomingHandoverConnectionCounter() {
+        return mOnCreateIncomingHandoverConnectionCounter;
+    }
+
+    public TestUtils.InvokeCounter getOnCreateOutgoingHandoverConnectionCounter() {
+        return mOnCreateOutgoingHandoverConnectionCounter;
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/HandoverTest.java b/tests/tests/telecom/src/android/telecom/cts/HandoverTest.java
new file mode 100644
index 0000000..597694b
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/HandoverTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2018 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.telecom.cts;
+
+import static android.telecom.cts.TestUtils.TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE;
+import static android.telecom.cts.TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE;
+import static android.telecom.cts.TestUtils.TEST_PHONE_ACCOUNT_HANDOVER_SRC;
+import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS;
+import static android.telecom.cts.TestUtils.waitOnLocalMainLooper;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+
+/**
+ * Tests the Telecom handover APIs.
+ */
+public class HandoverTest extends BaseTelecomTestWithMockServices {
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        if (mShouldTestTelecom) {
+            setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+
+            // Test handover source is a managed ConnectionService
+            mTelecomManager.registerPhoneAccount(TestUtils.TEST_PHONE_ACCOUNT_HANDOVER_SRC);
+            TestUtils.enablePhoneAccount(getInstrumentation(),
+                    TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+            assertPhoneAccountEnabled(TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+
+            // Test handover destination is a self-managed ConnectionService.
+            mTelecomManager.registerPhoneAccount(TestUtils.TEST_PHONE_ACCOUNT_HANDOVER_DEST);
+        }
+
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        CtsSelfManagedConnectionService connectionService =
+                CtsSelfManagedConnectionService.getConnectionService();
+        if (connectionService != null) {
+            connectionService.tearDown();
+            mTelecomManager.unregisterPhoneAccount(
+                    TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+            mTelecomManager.unregisterPhoneAccount(
+                    TestUtils.TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE);
+        }
+
+        super.tearDown();
+    }
+
+    /**
+     * Ensures a call handover cannot be initiated for a {@link android.telecom.PhoneAccount} which
+     * does not declare {@link android.telecom.PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}.
+     */
+    public void testHandoverSourceFailed() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        placeAndVerifyCall();
+        Call call = mInCallCallbacks.getService().getLastCall();
+
+        call.handoverTo(TestUtils.TEST_SELF_MANAGED_HANDLE_1, VideoProfile.STATE_BIDIRECTIONAL,
+                null);
+
+        // Expect the handover failed callback to be called.
+        mOnHandoverFailedCounter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+        Call callbackCall = (Call) mOnHandoverFailedCounter.getArgs(0)[0];
+        int failureReason = (int) mOnHandoverFailedCounter.getArgs(0)[1];
+        assertEquals(call, callbackCall);
+        assertEquals(Call.Callback.HANDOVER_FAILURE_DEST_NOT_SUPPORTED, failureReason);
+
+        call.disconnect();
+    }
+
+    /**
+     * Ensures a call handover cannot be initiated to a {@link android.telecom.PhoneAccount} which
+     * does not declare {@link android.telecom.PhoneAccount#EXTRA_SUPPORTS_HANDOVER_TO}.
+     */
+    public void testHandoverDestinationFailed() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+        startSourceCall();
+        Call call = mInCallCallbacks.getService().getLastCall();
+
+        // Now try to handover to an account which does not support handover.
+        call.handoverTo(TestUtils.TEST_SELF_MANAGED_HANDLE_1, VideoProfile.STATE_BIDIRECTIONAL,
+                null);
+
+        // Expect the handover failed callback to be called.
+        mOnHandoverFailedCounter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+        Call callbackCall = (Call) mOnHandoverFailedCounter.getArgs(0)[0];
+        int failureReason = (int) mOnHandoverFailedCounter.getArgs(0)[1];
+        assertEquals(call, callbackCall);
+        assertEquals(Call.Callback.HANDOVER_FAILURE_DEST_NOT_SUPPORTED, failureReason);
+
+        call.disconnect();
+    }
+
+    /**
+     * Ensures that when the source and destination both support handover that an outgoing handover
+     * request will be successfully relayed.
+     */
+    public void testOutgoingHandoverRequestValid() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        // Begin our source call on the CS which supports handover from it.
+        startSourceCall();
+        final Call call = mInCallCallbacks.getService().getLastCall();
+
+        // Now try to handover to an account which does support handover to it.
+        call.handoverTo(TestUtils.TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE,
+                VideoProfile.STATE_BIDIRECTIONAL, null);
+
+        // Ensure Telecom bound to the self managed CS
+        if (!CtsSelfManagedConnectionService.waitForBinding()) {
+            fail("Could not bind to Self-Managed ConnectionService");
+        }
+
+        // Wait for binding to self managed CS and invocation of outgoing handover method.
+        TestUtils.InvokeCounter counter =
+                CtsSelfManagedConnectionService.getConnectionService()
+                        .getOnCreateOutgoingHandoverConnectionCounter();
+        counter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+        SelfManagedConnection connection = TestUtils.waitForAndGetConnection(
+                call.getDetails().getHandle());
+
+        // Verify the from handle is as expected.
+        PhoneAccountHandle fromHandle = (PhoneAccountHandle) counter.getArgs(0)[0];
+        assertEquals(TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, fromHandle);
+        // Verify the to handle is as expected.
+        ConnectionRequest request = (ConnectionRequest) counter.getArgs(0)[1];
+        assertEquals(TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, request.getAccountHandle());
+
+        completeHandoverAndVerify(call, connection);
+    }
+
+    /**
+     * Tests use of the
+     * {@link android.telecom.TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} API on
+     * the receiving side of the handover.
+     */
+    public void testIncomingHandoverRequestValid() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        // Begin our source call on the CS which supports handover from it.
+        startSourceCall();
+        final Call call = mInCallCallbacks.getService().getLastCall();
+
+        // Request to accept handover of that call to another app.
+        mTelecomManager.acceptHandover(call.getDetails().getHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE);
+
+        // Ensure Telecom bound to the self managed CS
+        if (!CtsSelfManagedConnectionService.waitForBinding()) {
+            fail("Could not bind to Self-Managed ConnectionService");
+        }
+
+        // Wait for binding to self managed CS and invocation of incoming handover method.
+        TestUtils.InvokeCounter counter =
+                CtsSelfManagedConnectionService.getConnectionService()
+                        .getOnCreateIncomingHandoverConnectionCounter();
+        counter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+        SelfManagedConnection connection = TestUtils.waitForAndGetConnection(
+                call.getDetails().getHandle());
+
+        // Verify the from handle is as expected.
+        PhoneAccountHandle fromHandle = (PhoneAccountHandle) counter.getArgs(0)[0];
+        assertEquals(TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, fromHandle);
+        // Verify the to handle is as expected.
+        ConnectionRequest request = (ConnectionRequest) counter.getArgs(0)[1];
+        assertEquals(TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, request.getAccountHandle());
+        // The original call's address should match the address of the handover request.
+        assertEquals(call.getDetails().getHandle(), request.getAddress());
+
+        completeHandoverAndVerify(call, connection);
+    }
+
+    /**
+     * Begins a call which will be the source of a handover.
+     */
+    private void startSourceCall() {
+        // Ensure the ongoing account is on a PhoneAccount which supports handover from.
+        Bundle extras = new Bundle();
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+        placeAndVerifyCall(extras);
+    }
+
+    /**
+     * Complete the a call handover and verify that it was successfully reported.
+     * @param call
+     * @param connection
+     */
+    private void completeHandoverAndVerify(final Call call, SelfManagedConnection connection) {
+        // Make the connection active, indicating that the user has accepted the handover.
+        connection.setActive();
+
+        // Expect the original call to have been informed of handover completion.
+        mOnHandoverCompleteCounter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+        assertEquals(1, mOnHandoverCompleteCounter.getInvokeCount());
+
+        // Also expect the connection to be informed of handover completion.
+        connection.getHandoverCompleteCounter().waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+        assertEquals(1, connection.getHandoverCompleteCounter().getInvokeCount());
+
+        // Now, we expect that the original connection will get disconnected.
+        waitUntilConditionIsTrueOrTimeout(new Condition() {
+                                              @Override
+                                              public Object expected() {
+                                                  return Call.STATE_DISCONNECTED;
+                                              }
+                                              @Override
+                                              public Object actual() {
+                                                  return call.getState();
+                                              }
+                                          },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Expected original call to be disconnected."
+        );
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index e13335e..338a225 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -65,6 +65,8 @@
         public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) {}
         public void onRttRequest(Call call, int id) {}
         public void onRttInitiationFailure(Call call, int reason) {}
+        public void onHandoverComplete(Call call) {}
+        public void onHandoverFailed(Call call, int failureReason) {}
 
         final public MockInCallService getService() {
             return mService;
@@ -189,6 +191,22 @@
                 getCallbacks().onRttInitiationFailure(call, reason);
             }
         }
+
+        @Override
+        public void onHandoverComplete(Call call) {
+            super.onHandoverComplete(call);
+            if (getCallbacks() != null) {
+                getCallbacks().onHandoverComplete(call);
+            }
+        }
+
+        @Override
+        public void onHandoverFailed(Call call, int failureReason) {
+            super.onHandoverFailed(call, failureReason);
+            if (getCallbacks() != null) {
+                getCallbacks().onHandoverFailed(call, failureReason);
+            }
+        }
     };
 
     private void saveVideoCall(Call call, VideoCall videoCall) {
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java
index f9a3d91..e53d789 100644
--- a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java
@@ -16,6 +16,7 @@
 
 package android.telecom.cts;
 
+import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
@@ -31,6 +32,8 @@
     InvokeCounter mCallAudioRouteInvokeCounter = new InvokeCounter("onCallAudioStateChanged");
     InvokeCounter mOnShowIncomingUiInvokeCounter = new InvokeCounter(
             "onShowIncomingUiInvokeCounter");
+    InvokeCounter mCallEventCounter = new InvokeCounter("onCallEvent");
+    InvokeCounter mHandoverCompleteCounter = new InvokeCounter("handoverCompleteCounter");
     CountDownLatch mOnHoldLatch = new CountDownLatch(1);
 
     public static abstract class Listener {
@@ -70,6 +73,16 @@
         mOnHoldLatch.countDown();
     }
 
+    @Override
+    public void onCallEvent(String event, Bundle extras) {
+        mCallEventCounter.invoke(event, extras);
+    }
+
+    @Override
+    public void onHandoverComplete() {
+        mHandoverCompleteCounter.invoke();
+    }
+
     public InvokeCounter getCallAudioStateChangedInvokeCounter() {
         return mCallAudioRouteInvokeCounter;
     }
@@ -78,6 +91,14 @@
         return mOnShowIncomingUiInvokeCounter;
     }
 
+    public InvokeCounter getCallEventCounter() {
+        return mCallEventCounter;
+    }
+
+    public InvokeCounter getHandoverCompleteCounter() {
+        return mHandoverCompleteCounter;
+    }
+
     public boolean waitOnHold() {
         mOnHoldLatch = TestUtils.waitForLock(mOnHoldLatch);
         return mOnHoldLatch != null;
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index ac717d7..71ce645 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -63,6 +63,11 @@
     public static final String ACCOUNT_ID = "xtstest_CALL_PROVIDER_ID";
     public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID);
+    public static final PhoneAccountHandle TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE =
+            new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), "handoverFrom");
+    public static final PhoneAccountHandle TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE =
+            new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
+                    "handoverTo");
     public static final String REMOTE_ACCOUNT_ID = "xtstest_REMOTE_CALL_PROVIDER_ID";
     public static final String SELF_MANAGED_ACCOUNT_ID_1 = "ctstest_SELF_MANAGED_ID_1";
     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_1 =
@@ -87,6 +92,34 @@
             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
             .build();
+    private static final Bundle SUPPORTS_HANDOVER_FROM_EXTRAS = new Bundle();
+    private static final Bundle SUPPORTS_HANDOVER_TO_EXTRAS = new Bundle();
+    static {
+        SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true);
+        SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true);
+    }
+    public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_SRC = PhoneAccount.builder(
+            TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
+            .setAddress(Uri.parse("tel:555-TEST"))
+            .setExtras(SUPPORTS_HANDOVER_FROM_EXTRAS)
+            .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+            .setHighlightColor(Color.BLUE)
+            .setShortDescription(ACCOUNT_LABEL)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+            .build();
+    public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_DEST = PhoneAccount.builder(
+            TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
+            .setAddress(Uri.parse("tel:555-TEST"))
+            .setExtras(SUPPORTS_HANDOVER_TO_EXTRAS)
+            .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+            .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+            .setHighlightColor(Color.MAGENTA)
+            .setShortDescription(ACCOUNT_LABEL)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+            .build();
     public static final String REMOTE_ACCOUNT_LABEL = "CTSRemoteConnectionService";
     public static final String SELF_MANAGED_ACCOUNT_LABEL = "android.telecom.cts";
     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = PhoneAccount.builder(
diff --git a/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
index a074783..6b68a4a 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
@@ -110,6 +110,8 @@
     }
 
     private void verifyCdmaInfo(CellInfoCdma cdma) {
+        verifyCellConnectionStatus(cdma.getCellConnectionStatus());
+
         String alphaLong = (String) cdma.getCellIdentity().getOperatorAlphaLong();
         assertNotNull("getOperatorAlphaLong() returns NULL!", alphaLong);
         String alphaShort = (String) cdma.getCellIdentity().getOperatorAlphaShort();
@@ -128,11 +130,17 @@
         // Physical cell id should be within [0, 503].
         assertTrue("getPci() out of range [0, 503], pci=" + pci, pci >= 0 && pci <= 503);
 
+        verifyCellConnectionStatus(lte.getCellConnectionStatus());
+
         String alphaLong = (String) lte.getCellIdentity().getOperatorAlphaLong();
         assertNotNull("getOperatorAlphaLong() returns NULL!", alphaLong);
         String alphaShort = (String) lte.getCellIdentity().getOperatorAlphaShort();
         assertNotNull("getOperatorAlphaShort() returns NULL!", alphaShort);
 
+        int bw = lte.getCellIdentity().getBandwidth();
+        assertTrue("getBandwidth out of range [1400, 20000] | Integer.Max_Value, bw=",
+            bw == Integer.MAX_VALUE || bw >= 1400 && bw <= 20000);
+
         String mccStr = lte.getCellIdentity().getMccStr();
         // mccStr is set as NULL if empty, unknown or invalid.
         assertTrue("getMccStr() out of range [0, 999], mcc=" + mccStr,
@@ -185,6 +193,8 @@
         int psc = wcdma.getCellIdentity().getPsc();
         assertTrue("getPsc() out of range [0, 511], psc=" + psc, psc >= 0 && psc <= 511);
 
+        verifyCellConnectionStatus(wcdma.getCellConnectionStatus());
+
         int uarfcn = wcdma.getCellIdentity().getUarfcn();
         // Reference 3GPP 25.101 Table 5.2
         assertTrue("getUarfcn() out of range [400,11000], uarfcn=" + uarfcn,
@@ -229,6 +239,8 @@
         assertTrue("getCid() out range [0, 65535], cid=" + cid, !gsm.isRegistered() ||
             cid >= 0 && cid <= 65535);
 
+        verifyCellConnectionStatus(gsm.getCellConnectionStatus());
+
         int arfcn = gsm.getCellIdentity().getArfcn();
         // Reference 3GPP 45.005 Table 2-2
         assertTrue("getArfcn() out of range [0,1024], arfcn=" + arfcn,
@@ -274,4 +286,12 @@
         assertTrue("getCellSignalStrength().getDbm() out of range, dbm=" + dbm,
                 dbm >= MIN_RSSI && dbm <= MAX_RSSI);
     }
+
+    private void verifyCellConnectionStatus(int status) {
+        assertTrue("getCellConnectionStatus() invalid [0,2] | Integer.MAX_VALUE, status=",
+            status == CellInfo.CONNECTION_NONE
+                || status == CellInfo.CONNECTION_PRIMARY_SERVING
+                || status == CellInfo.CONNECTION_SECONDARY_SERVING
+                || status == CellInfo.CONNECTION_UNKNOWN);
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 51e6102..7bbc08a 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -41,6 +41,7 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -60,11 +61,13 @@
 import android.util.Xml;
 import android.view.ActionMode;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
 import android.widget.ListView;
@@ -413,6 +416,53 @@
     }
 
     @Test
+    public void testSelectorOnScreen() throws Throwable {
+        // leave touch-mode
+        mInstrumentation.setInTouchMode(false);
+        setAdapter();
+        mInstrumentation.waitForIdleSync();
+        // Entering touch mode hides selector
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
+            // make sure we've left touchmode (including message sending. instrumentation just sets
+            // a variable without any broadcast).
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+            mActivityRule.runOnUiThread(() -> {
+                mListView.requestFocus();
+                mListView.setSelectionFromTop(1, 0);
+            });
+            mInstrumentation.waitForIdleSync();
+            assertEquals(1, mListView.getSelectedItemPosition());
+            final int[] pt = new int[2];
+            mListView.getLocationOnScreen(pt);
+            CtsTouchUtils.emulateDragGesture(mInstrumentation, pt[0] + 2, pt[1] + 2, 0, 10);
+            mInstrumentation.waitForIdleSync();
+            assertEquals(AdapterView.INVALID_POSITION, mListView.getSelectedItemPosition());
+            // leave touch-mode
+            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+        }
+
+        // Scroll off-screen hides selector, but shows up again when on-screen
+        mActivityRule.runOnUiThread(() -> {
+            mListView.requestFocus();
+            mListView.setSelectionFromTop(1, 0);
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(1, mListView.getSelectedItemPosition());
+        int selViewHeight = mListView.getSelectedView().getHeight();
+        final int[] pt = new int[2];
+        mListView.getLocationOnScreen(pt);
+        pt[0] += mListView.getWidth() / 2;
+        pt[1] += selViewHeight / 2;
+        mActivityRule.runOnUiThread(() -> mListView.scrollListBy(selViewHeight * 2));
+        mInstrumentation.waitForIdleSync();
+        assertEquals(1, mListView.getSelectedItemPosition());
+        assertFalse(mListView.shouldDrawSelector());
+        mActivityRule.runOnUiThread(() -> mListView.scrollListBy(-(selViewHeight * 4) / 3));
+        assertEquals(1, mListView.getSelectedItemPosition());
+        assertTrue(mListView.shouldDrawSelector());
+    }
+
+    @Test
     public void testSetScrollIndicators() throws Throwable {
         final Activity activity = mActivityRule.getActivity();
         TextView tv1 = (TextView) activity.findViewById(R.id.headerview1);
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 01917d3..cfa369a 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -524,6 +524,7 @@
         charsKeyNames.add(CameraCharacteristics.SYNC_MAX_LATENCY.getName());
         charsKeyNames.add(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL.getName());
         charsKeyNames.add(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE.getName());
+        charsKeyNames.add(CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE.getName());
 
         return charsKeyNames;
     }
diff --git a/tools/cts-tradefed/res/config/cts-suite.xml b/tools/cts-tradefed/res/config/cts-suite.xml
index f2672ec..9f50292 100644
--- a/tools/cts-tradefed/res/config/cts-suite.xml
+++ b/tools/cts-tradefed/res/config/cts-suite.xml
@@ -16,7 +16,9 @@
 <configuration description="Runs CTS as a suite">
     <!-- running config -->
     <build_provider class="com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider" />
-    <test class="com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite" />
+    <test class="com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite">
+        <option name="run-suite-tag" value="cts" />
+    </test>
 
     <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:rerun-from-file:true" />
     <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:fallback-to-serial-rerun:false" />