Merge "Statsd CTS tests for proc state"
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 77807bb..10c33ba 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -38,12 +38,14 @@
                                mockito-target-minus-junit4 \
                                mockwebserver \
                                compatibility-device-util \
-                               platform-test-annotations
+                               platform-test-annotations \
+                               cts-security-test-support-library
 
 LOCAL_JAVA_LIBRARIES += telephony-common
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
+LOCAL_JAVA_LIBRARIES += bouncycastle
 
 LOCAL_PACKAGE_NAME := CtsVerifier
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index cc38c8d..b15532b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1580,7 +1580,7 @@
                 android:value="@string/test_category_sensors" />
             <meta-data
                 android:name="test_required_features"
-                android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera.any" />
+                android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera" />
         </activity>
         <activity
             android:name=".sensors.RVCVRecordActivity"
@@ -2391,6 +2391,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".managedprovisioning.KeyChainTestActivity"
+                android:label="@string/provisioning_byod_keychain">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.KEYCHAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".managedprovisioning.PermissionLockdownTestActivity"
                 android:label="@string/device_profile_owner_permission_lockdown_test">
             <intent-filter>
diff --git a/apps/CtsVerifier/res/layout/keychain_test.xml b/apps/CtsVerifier/res/layout/keychain_test.xml
new file mode 100644
index 0000000..09708c7
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/keychain_test.xml
@@ -0,0 +1,62 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="320dip"
+            android:layout_weight="2">
+        <TextView
+                android:id="@+id/provisioning_byod_keychain_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:padding="10dip"
+                android:text="@string/provisioning_byod_keychain_info_start"
+                android:textSize="18dip" />
+    </ScrollView>
+
+    <TextView
+          android:id="@+id/provisioning_byod_keychain_test_log"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:padding="10dip"
+          android:textSize="18dip" />
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/prepare_test_button"
+            android:layout_width="204dip"
+            android:layout_height="wrap_content"
+            android:text="@string/provisioning_byod_keyguard_disabled_features_prepare_button"/>
+
+        <Button
+            android:id="@+id/run_test_button"
+            android:layout_width="204dip"
+            android:layout_height="wrap_content"
+            android:text="@string/go_button_text"/>
+
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 0e6ab31..14706dd 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1935,6 +1935,35 @@
         The work profile still has a separate password. Please remove this before continuing.
     </string>
 
+    <string name="provisioning_byod_keychain">KeyChain test</string>
+    <string name="provisioning_byod_keychain_info_start">
+        In this test, you\'ll verify that keys generated by KeyChain keys are as usable as keys
+        installed into KeyChain and that they can be hidden from users.\n
+        The test has two parts:\n
+        1) Testing that a generated key can be selectable by the user.\n
+        2) Testing that a generated key can be hidden from users.\n
+        \n
+        Tap \"Prepare Test\" button below to begin.\n
+        \n
+        NOTE: A screen lock must be configured for this test. Otherwise, test preparation
+        will fail to generate a key for use by the test.
+    </string>
+    <string name="provisioning_byod_keychain_info_first_test">
+        Once you press \'Go\', a prompt titled \"Choose certificate\" should appear.\n
+        Verify that the list in this dialog has one item, starting with \'cts-verifier-gen\'.
+        Press \'Select\' to select it.\n
+        If the test passes, you\'ll see the text \"Second test ready\" at the bottom.\n
+        \n
+        Press \'Go\'.\n
+    </string>
+    <string name="provisioning_byod_keychain_info_second_test">
+        Once you press \'Run 2nd test\', the same prompt should appear again.\n
+        This time, verify that the title is \"No certificates found\" and the list is empty,
+        then press \'Cancel\'.\n
+        \n
+        Mark the test as passed if the text at the bottom shows \"PASSED (2/2)\"\n
+    </string>
+
     <!-- Strings for DeskClock -->
     <string name="deskclock_tests">Alarms and Timers Tests</string>
     <string name="deskclock_tests_info">
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 15808a7..d7a5033 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -101,6 +101,7 @@
     private DialogTestListItem mConfirmWorkCredentials;
     private DialogTestListItem mParentProfilePassword;
     private TestListItem mVpnTest;
+    private TestListItem mKeyChainTest;
     private TestListItem mAlwaysOnVpnSettingsTest;
     private TestListItem mRecentsTest;
     private TestListItem mDisallowAppsControlTest;
@@ -415,6 +416,12 @@
                 new Intent(this, OrganizationInfoTestActivity.class),
                 null);
 
+        mKeyChainTest = TestListItem.newTest(this,
+                R.string.provisioning_byod_keychain,
+                KeyChainTestActivity.class.getName(),
+                new Intent(KeyChainTestActivity.ACTION_KEYCHAIN),
+                null);
+
         mParentProfilePassword = new DialogTestListItem(this,
                 R.string.provisioning_byod_parent_profile_password,
                 "BYOD_ParentProfilePasswordTest",
@@ -563,6 +570,7 @@
                 }
             };
             adapter.add(mDisableNfcBeamTest);
+            adapter.add(mKeyChainTest);
         }
 
         /* If there is an application that handles RECORD_SOUND_ACTION, test that it handles it
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
index 5053a90..bfa65b7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -42,7 +42,8 @@
                 AlwaysOnVpnSettingsTestActivity.class.getName(),
                 RecentsRedactionActivity.class.getName(),
                 CommandReceiverActivity.class.getName(),
-                SetSupportMessageActivity.class.getName()
+                SetSupportMessageActivity.class.getName(),
+                KeyChainTestActivity.class.getName()
         };
         for (String component : components) {
             mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
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 a9cee2b..8486765 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -149,6 +149,7 @@
         filter.addAction(ByodHelperActivity.ACTION_SET_ORGANIZATION_INFO);
         filter.addAction(ByodHelperActivity.ACTION_TEST_PARENT_PROFILE_PASSWORD);
         filter.addAction(SetSupportMessageActivity.ACTION_SET_SUPPORT_MSG);
+        filter.addAction(KeyChainTestActivity.ACTION_KEYCHAIN);
         filter.addAction(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
         dpm.addCrossProfileIntentFilter(getWho(context), filter,
                 DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java
new file mode 100644
index 0000000..a59261c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java
@@ -0,0 +1,314 @@
+/*
+ * 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.cts.verifier.managedprovisioning;
+
+import static android.keystore.cts.CertificateUtils.createCertificate;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.security.AttestedKeyPair;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Activity to test KeyChain key generation. The following flows are tested: * Generating a key. *
+ * Installing a (self-signed) certificate associated with the key, visible to users. * Setting
+ * visibility of the certificate to not be visible to user.
+ *
+ * <p>After the key generation and certificate installation, it should be possible for a user to
+ * select the key from the certificate selection prompt when {@code KeyChain.choosePrivateKeyAlias}
+ * is called. The test then tests that the key is indeed usable for signing.
+ *
+ * <p>After the visibility is set to not-user-visible, the prompt is shown again, this time the
+ * testes is asked to verify no keys are selectable and cancel the dialog.
+ */
+public class KeyChainTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = "ByodKeyChainActivity";
+
+    public static final String ACTION_KEYCHAIN =
+            "com.android.cts.verifier.managedprovisioning.KEYCHAIN";
+
+    public static final String ALIAS = "cts-verifier-gen-rsa-1";
+    public static final String KEY_ALGORITHM = "RSA";
+
+    private DevicePolicyManager mDevicePolicyManager;
+    private AttestedKeyPair mAttestedKeyPair;
+    private X509Certificate mCert;
+    private TextView mLogView;
+    private TextView mInstructionsView;
+    private Button mSetupButton;
+    private Button mGoButton;
+
+    // Callback interface for when a key is generated.
+    static interface KeyGenerationListener {
+        void onKeyPairGenerated(AttestedKeyPair keyPair);
+    }
+
+    // Task for generating a key pair using {@code DevicePolicyManager.generateKeyPair}.
+    // The listener, if provided, will be invoked after the key has been generated successfully.
+    class GenerateKeyTask extends AsyncTask<KeyGenParameterSpec, Integer, AttestedKeyPair> {
+        KeyGenerationListener mListener;
+
+        public GenerateKeyTask(KeyGenerationListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        protected AttestedKeyPair doInBackground(KeyGenParameterSpec... specs) {
+            Log.i(TAG, "Generating key pair.");
+            try {
+                AttestedKeyPair kp =
+                        mDevicePolicyManager.generateKeyPair(
+                                DeviceAdminTestReceiver.getReceiverComponentName(),
+                                KEY_ALGORITHM,
+                                specs[0],
+                                0);
+                if (kp != null) {
+                    mLogView.setText("Key generated successfully.");
+                } else {
+                    mLogView.setText("Failed generating key.");
+                }
+                return kp;
+            } catch (SecurityException e) {
+                mLogView.setText("Security exception while generating key.");
+                Log.w(TAG, "Security exception", e);
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(AttestedKeyPair kp) {
+            super.onPostExecute(kp);
+            if (mListener != null && kp != null) {
+                mListener.onKeyPairGenerated(kp);
+            }
+        }
+    }
+
+    // Helper for generating and installing a self-signed certificate.
+    class CertificateInstaller implements KeyGenerationListener {
+        @Override
+        public void onKeyPairGenerated(AttestedKeyPair keyPair) {
+            mAttestedKeyPair = keyPair;
+            X500Principal issuer = new X500Principal("CN=SelfSigned, O=Android, C=US");
+            X500Principal subject = new X500Principal("CN=Subject, O=Android, C=US");
+            try {
+                mCert = createCertificate(mAttestedKeyPair.getKeyPair(), subject, issuer);
+                boolean installResult = installCertificate(mCert, true);
+                // called from onPostExecute so safe to interact with the UI here.
+                if (installResult) {
+                    mLogView.setText("Test ready");
+                    mInstructionsView.setText(R.string.provisioning_byod_keychain_info_first_test);
+                    mGoButton.setEnabled(true);
+                } else {
+                    mLogView.setText("FAILED certificate installation.");
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Failed installing certificate", e);
+                mLogView.setText("Error generating a certificate.");
+            }
+        }
+    }
+
+    // Helper for calling {@code DevicePolicyManager.setKeyPairCertificate} with the user-visibility
+    // specified in the constructor. Returns true if the call was successful (and no exceptions
+    // were thrown).
+    protected boolean installCertificate(X509Certificate cert, boolean isUserVisible) {
+        try {
+            return mDevicePolicyManager.setKeyPairCertificate(
+                    DeviceAdminTestReceiver.getReceiverComponentName(),
+                    ALIAS,
+                    Arrays.asList(new X509Certificate[] {cert}),
+                    isUserVisible);
+        } catch (SecurityException e) {
+            logStatus("Security exception while installing cert.");
+            Log.w(TAG, "Security exception", e);
+        }
+        return false;
+    }
+
+    // Invokes choosePrivateKeyAlias.
+    void selectCertificate(KeyChainAliasCallback callback) {
+        String[] keyTypes = new String[] {KEY_ALGORITHM};
+        Principal[] issuers = new Principal[0];
+        KeyChain.choosePrivateKeyAlias(
+                KeyChainTestActivity.this, callback, keyTypes, issuers, null, null);
+    }
+
+    class TestPreparator implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            mLogView.setText("Starting key generation");
+            KeyGenParameterSpec spec =
+                    new KeyGenParameterSpec.Builder(
+                                    ALIAS,
+                                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                            .setKeySize(2048)
+                            .setDigests(KeyProperties.DIGEST_SHA256)
+                            .setSignaturePaddings(
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                            .build();
+            new GenerateKeyTask(new CertificateInstaller()).execute(spec);
+        }
+    }
+
+    class SelectCertificate implements View.OnClickListener, KeyChainAliasCallback {
+        @Override
+        public void onClick(View v) {
+            Log.i(TAG, "Selecting certificate");
+            mLogView.setText("Waiting for prompt");
+            selectCertificate(this);
+        }
+
+        @Override
+        public void alias(String alias) {
+            Log.i(TAG, "Got alias: " + alias);
+            if (alias == null) {
+                logStatus("FAILED (no alias)");
+                return;
+            } else if (!alias.equals(ALIAS)) {
+                logStatus("FAILED (wrong alias)");
+                return;
+            }
+            logStatus("Got right alias.");
+            try {
+                PrivateKey privateKey = KeyChain.getPrivateKey(KeyChainTestActivity.this, alias);
+                byte[] data = new String("hello").getBytes();
+                Signature sign = Signature.getInstance("SHA256withRSA");
+                sign.initSign(privateKey);
+                sign.update(data);
+                if (sign.sign() != null) {
+                    prepareSecondTest();
+                } else {
+                    logStatus("FAILED (cannot sign)");
+                }
+            } catch (GeneralSecurityException | KeyChainException | InterruptedException e) {
+                Log.w(TAG, "Failed using the key", e);
+                logStatus("FAILED (key unusable)");
+            }
+        }
+    }
+
+    class SelectCertificateExpectingNone implements View.OnClickListener, KeyChainAliasCallback {
+        @Override
+        public void onClick(View v) {
+            Log.i(TAG, "Selecting certificate");
+            mLogView.setText("Waiting for prompt");
+            selectCertificate(this);
+        }
+
+        @Override
+        public void alias(String alias) {
+            Log.i(TAG, "Got alias: " + alias);
+            if (alias != null) {
+                logStatus("FAILED: Should have no certificate.");
+            } else {
+                logStatus("PASSED (2/2)");
+                runOnUiThread(
+                        () -> {
+                            getPassButton().setEnabled(true);
+                        });
+            }
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.keychain_test);
+        setPassFailButtonClickListeners();
+        mDevicePolicyManager =
+                (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+
+        mLogView = (TextView) findViewById(R.id.provisioning_byod_keychain_test_log);
+        mLogView.setMovementMethod(new ScrollingMovementMethod());
+
+        mInstructionsView = (TextView) findViewById(R.id.provisioning_byod_keychain_instructions);
+
+        mSetupButton = (Button) findViewById(R.id.prepare_test_button);
+        mSetupButton.setOnClickListener(new TestPreparator());
+
+        mGoButton = (Button) findViewById(R.id.run_test_button);
+        mGoButton.setOnClickListener(new SelectCertificate());
+        mGoButton.setEnabled(false);
+
+        // Disable the pass button here, only enable it when the 2nd test passes.
+        getPassButton().setEnabled(false);
+    }
+
+    protected void prepareSecondTest() {
+        Runnable uiChanges;
+        if (installCertificate(mCert, false)) {
+            uiChanges =
+                    () -> {
+                        mLogView.setText("Second test ready.");
+                        mInstructionsView.setText(
+                                R.string.provisioning_byod_keychain_info_second_test);
+                        mGoButton.setText("Run 2nd test");
+                        mGoButton.setOnClickListener(new SelectCertificateExpectingNone());
+                    };
+        } else {
+            uiChanges =
+                    () -> {
+                        mLogView.setText("FAILED second test setup.");
+                        mGoButton.setEnabled(false);
+                    };
+        }
+
+        runOnUiThread(uiChanges);
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+        try {
+            mDevicePolicyManager.removeKeyPair(
+                    DeviceAdminTestReceiver.getReceiverComponentName(), ALIAS);
+            Log.i(TAG, "Deleted alias " + ALIAS);
+        } catch (SecurityException e) {
+            Log.w(TAG, "Failed deleting alias", e);
+        }
+    }
+
+    private void logStatus(String status) {
+        runOnUiThread(
+                () -> {
+                    mLogView.setText(status);
+                });
+    }
+}
diff --git a/build/compatibility_test_suite.mk b/build/compatibility_test_suite.mk
index d133c3e..9bad76e 100644
--- a/build/compatibility_test_suite.mk
+++ b/build/compatibility_test_suite.mk
@@ -46,6 +46,14 @@
 
 LOCAL_MODULE_TAGS := optional
 
+# If DynamicConfig.xml exists copy it inside the jar
+ifneq (,$(wildcard $(LOCAL_PATH)/DynamicConfig.xml))
+  dynamic_config_local := $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE),true,COMMON)/$(LOCAL_MODULE).dynamic
+  $(eval $(call copy-one-file,$(LOCAL_PATH)/DynamicConfig.xml,$(dynamic_config_local)))
+  LOCAL_JAVA_RESOURCE_FILES += $(dynamic_config_local)
+endif
+
 include $(BUILD_HOST_JAVA_LIBRARY)
 
+dynamic_config_local :=
 suite_info_prop :=
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
new file mode 100644
index 0000000..fd05398
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.android.compatibility.common.util;
+
+import org.junit.Before;
+
+/**
+ *  Device-side base class for tests leveraging the Business Logic service for rules that are
+ *  conditionally added based on the device characteristics.
+ */
+public class BusinessLogicConditionalTestCase extends BusinessLogicTestCase {
+
+    @Override
+    @Before
+    public void handleBusinessLogic() {
+        super.loadBuisnessLogic();
+        ensureAuthenticated();
+        super.executeBusinessLogic();
+    }
+
+    protected void ensureAuthenticated() {
+        if (!mCanReadBusinessLogic) {
+            // super class handles the condition that the service is unavailable.
+            return;
+        }
+
+        if (!mBusinessLogic.mConditionalTestsEnabled) {
+            skipTest("Execution of device specific tests is not enabled. "
+                    + "Enable with '--conditional-business-logic-tests-enabled'");
+        }
+
+        if (mBusinessLogic.isAuthorized()) {
+            // Run test as normal.
+            return;
+        }
+        String message = mBusinessLogic.getAuthenticationStatusMessage();
+
+        // Fail test since request was not authorized.
+        failTest(String.format("Unable to execute because %s.", message));
+    }
+}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
index 2316637..45f979a 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
@@ -45,21 +45,16 @@
     /* Test name rule that tracks the current test method under execution */
     @Rule public TestName mTestCase = new TestName();
 
-    private static BusinessLogic mBusinessLogic;
-    private static boolean mCanReadBusinessLogic = true;
-
-    @BeforeClass
-    public static void prepareBusinessLogic() {
-        File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
-        if (businessLogicFile.canRead()) {
-            mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
-        } else {
-            mCanReadBusinessLogic = false;
-        }
-    }
+    protected BusinessLogic mBusinessLogic;
+    protected boolean mCanReadBusinessLogic = true;
 
     @Before
-    public void executeBusinessLogic() {
+    public void handleBusinessLogic() {
+        loadBuisnessLogic();
+        executeBusinessLogic();
+    }
+
+    protected void executeBusinessLogic() {
         String methodName = mTestCase.getMethodName();
         assertTrue(String.format("Test \"%s\" is unable to execute as it depends on the missing "
                 + "remote configuration.", methodName), mCanReadBusinessLogic);
@@ -75,6 +70,15 @@
         }
     }
 
+    protected void loadBuisnessLogic() {
+        File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
+        if (businessLogicFile.canRead()) {
+            mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
+        } else {
+            mCanReadBusinessLogic = false;
+        }
+    }
+
     protected static Instrumentation getInstrumentation() {
         return InstrumentationRegistry.getInstrumentation();
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
index d33a471..e4ad8fd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
@@ -18,6 +18,7 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.util.DynamicConfigFileReader;
 import com.android.compatibility.common.util.BusinessLogic;
+import com.android.compatibility.common.util.BusinessLogicFactory;
 import com.android.compatibility.common.util.FeatureUtil;
 import com.android.compatibility.common.util.PropertyUtil;
 import com.android.tradefed.build.IBuildInfo;
@@ -36,12 +37,19 @@
 import com.android.tradefed.util.net.HttpHelper;
 import com.android.tradefed.util.net.IHttpHelper;
 
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+import com.google.common.base.Strings;
+
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -59,13 +67,15 @@
     private static final String FILE_LOCATION = "business-logic";
     /* Extension of business logic files */
     private static final String FILE_EXT = ".bl";
-
-    /* Default amount of time to attempt connection to the business logic service, in seconds */
-    private static final int DEFAULT_CONNECTION_TIME = 10;
-
+    /* URI of api scope to use when retrieving business logic rules */
+    private static final String APE_API_SCOPE = "https://www.googleapis.com/auth/androidPartner";
     /* Dynamic config constants */
     private static final String DYNAMIC_CONFIG_FEATURES_KEY = "business_logic_device_features";
     private static final String DYNAMIC_CONFIG_PROPERTIES_KEY = "business_logic_device_properties";
+    private static final String DYNAMIC_CONFIG_CONDITIONAL_TESTS_ENABLED_KEY =
+            "conditional_business_logic_tests_enabled";
+    /* Format used to append the enabled attribute to the serialized business logic string. */
+    private static final String ENABLED_ATTRIBUTE_SNIPPET = ", \"%s\":%s }";
 
     @Option(name = "business-logic-url", description = "The URL to use when accessing the " +
             "business logic service, parameters not included", mandatory = true)
@@ -83,9 +93,10 @@
             "suite invocation if retrieval of business logic fails.")
     private boolean mIgnoreFailure = false;
 
-    @Option(name = "business-logic-connection-time", description = "Amount of time to attempt " +
-            "connection to the business logic service, in seconds.")
-    private int mMaxConnectionTime = DEFAULT_CONNECTION_TIME;
+    @Option(name="conditional-business-logic-tests-enabled",
+            description="Setting to true will ensure the device specific tests are executed.")
+    private boolean mConditionalTestsEnabled = false;
+
 
     private String mDeviceFilePushed;
     private String mHostFilePushed;
@@ -99,18 +110,11 @@
         String requestString = buildRequestString(device, buildInfo);
         // Retrieve business logic string from service
         String businessLogicString = null;
-        long start = System.currentTimeMillis();
-        boolean success = false;
-        CLog.i("Attempting to connect to business logic service...");
-        while (System.currentTimeMillis() < (start + (mMaxConnectionTime * 1000))) {
-            try {
-                URL request = new URL(requestString);
-                businessLogicString = StreamUtil.getStringFromStream(request.openStream());
-                success = true;
-                break;
-            } catch (IOException e) {} // ignore, re-attempt connection with remaining time
-        }
-        if (!success || businessLogicString == null) {
+        try {
+            URL request = new URL(requestString);
+            businessLogicString = StreamUtil.getStringFromStream(request.openStream());
+            businessLogicString = addRuntimeConfig(businessLogicString, buildInfo);
+        } catch (IOException e) {
             if (mIgnoreFailure) {
                 CLog.e("Failed to connect to business logic service.\nProceeding with test "
                         + "invocation, tests depending on the remote configuration will fail.\n");
@@ -152,9 +156,15 @@
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
         String baseUrl = mUrl.replace(SUITE_PLACEHOLDER, getSuiteName());
         MultiMap<String, String> paramMap = new MultiMap<>();
-        paramMap.put("key", mApiKey);
         paramMap.put("suite_version", buildHelper.getSuiteVersion());
         paramMap.put("oem", PropertyUtil.getManufacturer(device));
+        String accessToken = getToken();
+        // Add api key (not authenticated) or Oath token, but not both.
+        if (Strings.isNullOrEmpty(accessToken)) {
+            paramMap.put("key", mApiKey);
+        } else {
+            paramMap.put("access_token", accessToken);
+        }
         for (String feature : getBusinessLogicFeatures(device, buildInfo)) {
             paramMap.put("features", feature);
         }
@@ -209,6 +219,37 @@
     }
 
     /**
+     * Append runtime configuration attributes to the end of the Json string.
+     * Determine if conditional tests should execute and add the value to the serialized business
+     * logic settings.
+     */
+    private String addRuntimeConfig(String businessLogicString, IBuildInfo buildInfo) {
+        int indexOfClosingParen = businessLogicString.lastIndexOf("}");
+        // Replace the closing paren with th enabled flag and closing paren. ex
+        // { "a":4 } -> {"a":4, "enabled":true }
+        return businessLogicString.substring(0, indexOfClosingParen) +
+                String.format(ENABLED_ATTRIBUTE_SNIPPET,
+                        BusinessLogicFactory.CONDITIONAL_TESTS_ENABLED,
+                        shouldExecuteConditionalTests(buildInfo));
+    }
+
+    /**
+     * Execute device specific test if enabled in config or through the command line.
+     * Otherwise skip all conditional tests.
+     */
+    private boolean shouldExecuteConditionalTests(IBuildInfo buildInfo) {
+        boolean enabledInConfig = false;
+        try {
+            String enabledInConfigValue = DynamicConfigFileReader.getValueFromConfig(
+                    buildInfo, getSuiteName(), DYNAMIC_CONFIG_CONDITIONAL_TESTS_ENABLED_KEY);
+            enabledInConfig = Boolean.parseBoolean(enabledInConfigValue);
+        } catch (XmlPullParserException | IOException e) {
+            CLog.e("Failed to pull business logic features from dynamic config");
+        }
+        return enabledInConfig || mConditionalTestsEnabled;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -229,4 +270,29 @@
     private static void removeDeviceFile(ITestDevice device) throws DeviceNotAvailableException {
         device.executeShellCommand(String.format("rm -rf %s", BusinessLogic.DEVICE_FILE));
     }
+
+    /*
+    * Returns an OAuth2 token string obtained using a service account json key file.
+    *
+    * Uses the service account key file location stored in environment variable 'APE_API_KEY'
+    * to request an OAuth2 token.
+    */
+    private String getToken() {
+        String keyFilePath = System.getenv("APE_API_KEY");
+        if (Strings.isNullOrEmpty(keyFilePath)) {
+            CLog.d("Environment variable APE_API_KEY not set.");
+            return null;
+        }
+        try {
+            Credential credential = GoogleCredential.fromStream(new FileInputStream(keyFilePath))
+                    .createScoped(Collections.singleton(APE_API_SCOPE));
+            credential.refreshToken();
+            return credential.getAccessToken();
+        } catch (FileNotFoundException e) {
+            CLog.e(String.format("Service key file %s doesn't exist.", keyFilePath));
+        } catch (IOException e) {
+            CLog.e(String.format("Can't read the service key file, %s", keyFilePath));
+        }
+        return null;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
index 3d34f3e..0e23165 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
@@ -15,6 +15,7 @@
  */
 package com.android.compatibility.common.tradefed.targetprep;
 
+import com.android.annotations.VisibleForTesting;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.util.DynamicConfig;
 import com.android.compatibility.common.util.DynamicConfigHandler;
@@ -25,9 +26,11 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.targetprep.BaseTargetPreparer;
 import com.android.tradefed.targetprep.BuildError;
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.StreamUtil;
 
 import org.json.JSONException;
@@ -36,13 +39,14 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 
 /**
  * Pushes dynamic config files from config repository
  */
 @OptionClass(alias="dynamic-config-pusher")
-public class DynamicConfigPusher implements ITargetCleaner {
+public class DynamicConfigPusher extends BaseTargetPreparer implements ITargetCleaner {
     public enum TestTarget {
         DEVICE,
         HOST
@@ -66,6 +70,18 @@
             "from the server, e.g. \"1.0\". Defaults to suite version string.")
     private String mVersion;
 
+    // Options for getting the dynamic file from resources.
+    @Option(name = "extract-from-resource",
+            description = "Whether to look for the local dynamic config inside the jar resources "
+                + "or on the local disk.")
+    private boolean mExtractFromResource = false;
+
+    @Option(name = "dynamic-resource-name",
+            description = "When using --extract-from-resource, this option allow to specify the "
+                + "resource name, instead of the module name for the lookup. File will still be "
+                + "logged under the module name.")
+    private String mResourceFileName = null;
+
     private String mDeviceFilePushed;
 
     void setModuleName(String moduleName) {
@@ -81,13 +97,7 @@
 
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
 
-        File localConfigFile = null;
-        try {
-            localConfigFile = buildHelper.getTestFile(mModuleName + ".dynamic");
-        } catch (FileNotFoundException e) {
-            throw new TargetSetupError("Cannot get local dynamic config file from test directory",
-                    e, device.getDeviceDescriptor());
-        }
+        File localConfigFile = getLocalConfigFile(buildHelper, device);
 
         if (mVersion == null) {
             mVersion = buildHelper.getSuiteVersion();
@@ -148,4 +158,34 @@
             device.executeShellCommand("rm -r " + mDeviceFilePushed);
         }
     }
+
+    @VisibleForTesting
+    final File getLocalConfigFile(CompatibilityBuildHelper buildHelper, ITestDevice device)
+            throws TargetSetupError {
+        File localConfigFile = null;
+        if (mExtractFromResource) {
+            String lookupName = (mResourceFileName != null) ? mResourceFileName : mModuleName;
+            InputStream dynamicFileRes = getClass().getResourceAsStream(
+                    String.format("/%s.dynamic", lookupName));
+            try {
+                localConfigFile = FileUtil.createTempFile(lookupName, ".dynamic");
+                FileUtil.writeToFile(dynamicFileRes, localConfigFile);
+            } catch (IOException e) {
+                FileUtil.deleteFile(localConfigFile);
+                throw new TargetSetupError(
+                        String.format("Fail to unpack '%s.dynamic' from resources", lookupName),
+                        e, device.getDeviceDescriptor());
+            }
+            return localConfigFile;
+        }
+
+        // If not from resources look at local path.
+        try {
+            localConfigFile = buildHelper.getTestFile(String.format("%s.dynamic", mModuleName));
+        } catch (FileNotFoundException e) {
+            throw new TargetSetupError("Cannot get local dynamic config file from test directory",
+                    e, device.getDeviceDescriptor());
+        }
+        return localConfigFile;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicConditionalHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicConditionalHostTestBase.java
new file mode 100644
index 0000000..1bee3a3
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicConditionalHostTestBase.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.android.compatibility.common.tradefed.testtype;
+
+import org.junit.Before;
+
+/**
+ * Host-side base class for tests leveraging the Business Logic service.
+ */
+public class BusinessLogicConditionalHostTestBase extends BusinessLogicHostTestBase {
+
+    @Override
+    @Before
+    public void handleBusinessLogic() {
+        super.loadBusinessLogic();
+        ensureAuthenticated();
+        super.executeBusinessLogic();
+    }
+
+    protected void ensureAuthenticated() {
+        if (!mCanReadBusinessLogic) {
+            // super class handles the condition that the service is unavailable.
+            return;
+        }
+
+        if (!mBusinessLogic.mConditionalTestsEnabled) {
+            skipTest("Execution of device specific tests is not enabled. "
+                    + "Enable with '--conditional-business-logic-tests-enabled'");
+        }
+
+        if (mBusinessLogic.isAuthorized()) {
+            // Run test as normal.
+            return;
+        }
+        String message = mBusinessLogic.getAuthenticationStatusMessage();
+
+        // Fail test since request was not authorized.
+        failTest(String.format("Unable to execute because %s.", message));
+    }
+
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
index 749be97..ed6e7bc 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
@@ -30,6 +30,7 @@
 import com.android.compatibility.common.util.BusinessLogicHostExecutor;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.suite.TestSuiteInfo;
 
 import java.io.File;
 
@@ -44,23 +45,16 @@
     /* Test name rule that tracks the current test method under execution */
     @Rule public TestName mTestCase = new TestName();
 
-    private static BusinessLogic mBusinessLogic;
-    private static boolean mCanReadBusinessLogic = true;
+    protected BusinessLogic mBusinessLogic;
+    protected boolean mCanReadBusinessLogic = true;
 
     @Before
-    public void executeBusinessLogic() {
-        // Business logic must be retrieved in this @Before method, since the build info contains
-        // the location of the business logic file and cannot be referenced from a static context
-        if (mBusinessLogic == null) {
-            CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild());
-            File businessLogicFile = helper.getBusinessLogicHostFile();
-            if (businessLogicFile != null && businessLogicFile.canRead()) {
-                mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
-            } else {
-                mCanReadBusinessLogic = false; // failed to retrieve business logic
-            }
-        }
+    public void handleBusinessLogic() {
+        loadBusinessLogic();
+        executeBusinessLogic();
+    }
 
+    protected void executeBusinessLogic() {
         String methodName = mTestCase.getMethodName();
         assertTrue(String.format("Test \"%s\" is unable to execute as it depends on the missing "
                 + "remote configuration.", methodName), mCanReadBusinessLogic);
@@ -77,6 +71,16 @@
         }
     }
 
+    protected void loadBusinessLogic() {
+        CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild());
+        File businessLogicFile = helper.getBusinessLogicHostFile();
+        if (businessLogicFile != null && businessLogicFile.canRead()) {
+            mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
+        } else {
+            mCanReadBusinessLogic = false; // failed to retrieve business logic
+        }
+    }
+
     public static void skipTest(String message) {
         assumeTrue(message, false);
     }
@@ -85,3 +89,4 @@
         fail(message);
     }
 }
+
diff --git a/common/host-side/tradefed/tests/Android.mk b/common/host-side/tradefed/tests/Android.mk
index c61594a..9d8220b 100644
--- a/common/host-side/tradefed/tests/Android.mk
+++ b/common/host-side/tradefed/tests/Android.mk
@@ -17,8 +17,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-LOCAL_JAVA_RESOURCE_DIRS := ../res
 include cts/error_prone_rules.mk
 
 LOCAL_SUITE_BUILD_NUMBER := 2
@@ -28,6 +26,7 @@
 LOCAL_SUITE_VERSION := 1
 
 LOCAL_MODULE := compatibility-mock-tradefed
+LOCAL_STATIC_JAVA_LIBRARIES := cts-tradefed-harness
 include cts/error_prone_rules.mk
 include $(BUILD_COMPATIBILITY_SUITE)
 
diff --git a/common/host-side/tradefed/tests/res/test-dynamic-config.dynamic b/common/host-side/tradefed/tests/res/test-dynamic-config.dynamic
new file mode 100644
index 0000000..8762861
--- /dev/null
+++ b/common/host-side/tradefed/tests/res/test-dynamic-config.dynamic
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+
+<dynamicConfig>
+    <entry key="media_files_url">
+         <value>https://dl.google.com/dl/android/cts/android-cts-media-1.4.zip</value>
+    </entry>
+</dynamicConfig>
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index 960d788..7f63f00 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -30,6 +30,7 @@
 import com.android.compatibility.common.tradefed.result.ResultReporterBuildInfoTest;
 import com.android.compatibility.common.tradefed.result.ResultReporterTest;
 import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
+import com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusherTest;
 import com.android.compatibility.common.tradefed.targetprep.MediaPreparerTest;
 import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
 import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
@@ -83,6 +84,7 @@
     SubPlanHelperTest.class,
 
     // targetprep
+    DynamicConfigPusherTest.class,
     MediaPreparerTest.class,
     PropertyCheckTest.class,
     SettingsPreparerTest.class,
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusherTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusherTest.java
new file mode 100644
index 0000000..a59634b
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusherTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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 com.android.compatibility.common.tradefed.targetprep;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.util.FileUtil;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Unit tests for {@link DynamicConfigPusher}.
+ */
+@RunWith(JUnit4.class)
+public class DynamicConfigPusherTest {
+    private static final String RESOURCE_DYNAMIC_CONFIG = "test-dynamic-config";
+    private DynamicConfigPusher mPreparer;
+    private ITestDevice mMockDevice;
+    private CompatibilityBuildHelper mMockBuildHelper;
+    private IBuildInfo mMockBuildInfo;
+
+    @Before
+    public void setUp() {
+        mPreparer = new DynamicConfigPusher();
+        mMockDevice = EasyMock.createMock(ITestDevice.class);
+        mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
+        mMockBuildHelper = new CompatibilityBuildHelper(mMockBuildInfo);
+        EasyMock.expect(mMockDevice.getDeviceDescriptor()).andStubReturn(null);
+    }
+
+    /**
+     * Test that when we look up resources locally, we search them from the build helper.
+     */
+    @Test
+    public void testLocalRead() throws Exception {
+        OptionSetter setter = new OptionSetter(mPreparer);
+        setter.setOptionValue("config-filename", "config-test-name");
+        setter.setOptionValue("extract-from-resource", "false");
+
+        File check = new File("anyfilewilldo");
+        mMockBuildHelper = new CompatibilityBuildHelper(mMockBuildInfo) {
+            @Override
+            public File getTestFile(String filename) throws FileNotFoundException {
+                return check;
+            }
+        };
+
+        EasyMock.replay(mMockDevice, mMockBuildInfo);
+        File res = mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+        assertEquals(check, res);
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+    }
+
+    /**
+     * Test that when we look up resources locally, we search them from the build helper and throw
+     * if it's not found.
+     */
+    @Test
+    public void testLocalRead_fileNotFound() throws Exception {
+        OptionSetter setter = new OptionSetter(mPreparer);
+        setter.setOptionValue("config-filename", "config-test-name");
+        setter.setOptionValue("extract-from-resource", "false");
+
+        mMockBuildHelper = new CompatibilityBuildHelper(mMockBuildInfo) {
+            @Override
+            public File getTestFile(String filename) throws FileNotFoundException {
+                throw new FileNotFoundException("test");
+            }
+        };
+        try {
+            EasyMock.replay(mMockDevice, mMockBuildInfo);
+            mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+            fail("Should have thrown an exception.");
+        } catch (TargetSetupError expected) {
+            // expected
+            assertEquals("Cannot get local dynamic config file from test directory null",
+                    expected.getMessage());
+        }
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+    }
+
+    /**
+     * Test when we try to unpack a resource but it does not exists.
+     */
+    @Test
+    public void testResourceRead_notFound() throws Exception {
+        OptionSetter setter = new OptionSetter(mPreparer);
+        setter.setOptionValue("config-filename", "not-an-existing-resource-name");
+        setter.setOptionValue("extract-from-resource", "true");
+        try {
+            EasyMock.replay(mMockDevice, mMockBuildInfo);
+            mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+            fail("Should have thrown an exception.");
+        } catch (TargetSetupError expected) {
+            // expected
+            assertEquals("Fail to unpack 'not-an-existing-resource-name.dynamic' from resources "
+                    + "null", expected.getMessage());
+        }
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+    }
+
+    /**
+     * Test when we get a config from the resources.
+     */
+    @Test
+    public void testResourceRead() throws Exception {
+        OptionSetter setter = new OptionSetter(mPreparer);
+        setter.setOptionValue("config-filename", RESOURCE_DYNAMIC_CONFIG);
+        setter.setOptionValue("extract-from-resource", "true");
+        File res = null;
+        try {
+            EasyMock.replay(mMockDevice, mMockBuildInfo);
+            res = mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+            assertTrue(res.exists());
+            assertTrue(FileUtil.readStringFromFile(res).contains("<dynamicConfig>"));
+        } finally {
+            FileUtil.deleteFile(res);
+        }
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+    }
+
+    /**
+     * Test when we get a config from the resources under the alternative name.
+     */
+    @Test
+    public void testResourceRead_resourceFileName() throws Exception {
+        OptionSetter setter = new OptionSetter(mPreparer);
+        setter.setOptionValue("config-filename", "moduleName");
+        setter.setOptionValue("extract-from-resource", "true");
+        // Look up the file under that name instead of the config-filename
+        setter.setOptionValue("dynamic-resource-name", RESOURCE_DYNAMIC_CONFIG);
+        File res = null;
+        try {
+            EasyMock.replay(mMockDevice, mMockBuildInfo);
+            res = mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+            assertTrue(res.exists());
+            assertTrue(FileUtil.readStringFromFile(res).contains("<dynamicConfig>"));
+        } finally {
+            FileUtil.deleteFile(res);
+        }
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+    }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
index adab1eb..26fae1e 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
@@ -30,6 +30,9 @@
 
     /* A map from testcase name to the business logic rules for the test case */
     protected Map<String, List<BusinessLogicRule>> mRules;
+    /* Feature flag determining if device specific tests are executed. */
+    public boolean mConditionalTestsEnabled;
+    private AuthenticationStatusEnum mAuthenticationStatus = AuthenticationStatusEnum.UNKNOWN;
 
     /**
      * Determines whether business logic exists for a given test name
@@ -61,6 +64,40 @@
         }
     }
 
+    public void setAuthenticationStatus(String authenticationStatus) {
+        try {
+            mAuthenticationStatus = Enum.valueOf(AuthenticationStatusEnum.class,
+                    authenticationStatus);
+        } catch (IllegalArgumentException e) {
+            // Invalid value, set to unknown
+            mAuthenticationStatus = AuthenticationStatusEnum.UNKNOWN;
+        }
+    }
+
+    public boolean isAuthorized() {
+        return AuthenticationStatusEnum.AUTHORIZED.equals(mAuthenticationStatus);
+    }
+
+    /**
+     * Builds a user readable string tha explains the authentication status and the effect on tests
+     * which require authentication to execute.
+     */
+    public String getAuthenticationStatusMessage() {
+        switch (mAuthenticationStatus) {
+            case AUTHORIZED:
+                return "Authorized";
+            case NOT_AUTHENTICATED:
+                return "authorization failed, please ensure the service account key is "
+                        + "properly installed.";
+            case NOT_AUTHORIZED:
+                return "service account is not authorized to access information for this device. "
+                        + "Please verify device properties are set correctly and account "
+                        + "permissions are configured in Google's systems.";
+            default:
+                return "something went wrong, please try again.";
+        }
+    }
+
     /**
      * Nested class representing an Business Logic Rule. Stores a collection of conditions
      * and actions for later invokation.
@@ -153,4 +190,15 @@
                     mMethodArgs.toArray(new String[mMethodArgs.size()]));
         }
     }
+
+    /**
+     * Nested enum of the possible authentication statuses.
+     */
+    protected enum AuthenticationStatusEnum {
+        UNKNOWN,
+        NOT_AUTHENTICATED,
+        NOT_AUTHORIZED,
+        AUTHORIZED
+    }
+
 }
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
index 2d3db01..4a0087e 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
@@ -51,6 +51,9 @@
     private static final String METHOD_NAME = "methodName";
     // Name of method args array of strings
     private static final String METHOD_ARGS = "methodArgs";
+    // Name of the field in the response object that stores that the auth status of the request.
+    private static final String AUTHENTICATION_STATUS = "authenticationStatus";
+    public static final String CONDITIONAL_TESTS_ENABLED = "conditionalTestsEnabled";
 
     /**
      * Create a BusinessLogic instance from a file of business logic data, formatted in JSON.
@@ -65,6 +68,14 @@
             String businessLogicString = readFile(f);
             JSONObject root = new JSONObject(businessLogicString);
             JSONArray rulesLists = null;
+            if (root.has(AUTHENTICATION_STATUS)){
+                String authStatus = root.getString(AUTHENTICATION_STATUS);
+                bl.setAuthenticationStatus(authStatus);
+            }
+            if (root.has(CONDITIONAL_TESTS_ENABLED)){
+                boolean enabled = root.getBoolean(CONDITIONAL_TESTS_ENABLED);
+                bl.mConditionalTestsEnabled = enabled;
+            }
             try {
                 rulesLists = root.getJSONArray(BUSINESS_LOGIC_RULES_LISTS);
             } catch (JSONException e) {
diff --git a/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java b/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java
deleted file mode 100644
index f6f9d10..0000000
--- a/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java
+++ /dev/null
@@ -1,108 +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.content.cts;
-
-import android.appsecurity.cts.Utils;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-
-/**
- * Set of tests that verify behavior of the content framework.
- */
-public class SyncAdapterAccountAccessHostTest extends DeviceTestCase
-        implements IAbiReceiver, IBuildReceiver {
-    private static final String ACCOUNT_ACCESS_TESTS_OTHER_CERT_APK =
-            "CtsSyncAccountAccessOtherCertTestCases.apk";
-    private static final String ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG =
-            "com.android.cts.content";
-
-    private static final String ACCOUNT_ACCESS_TESTS_SAME_CERT_APK =
-            "CtsSyncAccountAccessSameCertTestCases.apk";
-    private static final String ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG =
-            "com.android.cts.content";
-
-    private static final String STUBS_APK =
-            "CtsSyncAccountAccessStubs.apk";
-    private static final String STUBS_PKG =
-            "com.android.cts.stub";
-
-    private IAbi mAbi;
-    private CompatibilityBuildHelper mBuildHelper;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildHelper = new CompatibilityBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        getDevice().uninstallPackage(STUBS_PKG);
-
-        assertNull(getDevice().installPackage(mBuildHelper
-                .getTestFile(STUBS_APK), false, false));
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        getDevice().uninstallPackage(STUBS_PKG);
-    }
-
-    public void testSameCertAuthenticatorCanSeeAccount() throws Exception {
-        getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG);
-
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
-                ACCOUNT_ACCESS_TESTS_SAME_CERT_APK), false, false));
-        try {
-            runDeviceTests(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG,
-                    "com.android.cts.content.CtsSyncAccountAccessSameCertTestCases",
-                    "testAccountAccess_sameCertAsAuthenticatorCanSeeAccount");
-        } finally {
-            getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG);
-        }
-    }
-
-    public void testOtherCertAuthenticatorCanSeeAccount() throws Exception {
-        getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG);
-
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
-                ACCOUNT_ACCESS_TESTS_OTHER_CERT_APK), false, false));
-        try {
-            runDeviceTests(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG,
-                    "com.android.cts.content.CtsSyncAccountAccessOtherCertTestCases",
-                    "testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount");
-        } finally {
-            getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG);
-        }
-    }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
-    }
-}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk
deleted file mode 100644
index 116da94..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk
+++ /dev/null
@@ -1,45 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    ctstestrunner \
-    ub-uiautomator \
-    compatibility-device-util
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java \
-  ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
-
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessOtherCertTestCases
-
-LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
deleted file mode 100644
index 2ecd27d..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.cts.content">
-
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-
-        <activity android:name=".StubActivity"/>
-
-        <service android:name=".SyncService">
-            <intent-filter>
-                <action android:name="android.content.SyncAdapter"/>
-            </intent-filter>
-            <meta-data android:name="android.content.SyncAdapter"
-                android:resource="@xml/syncadapter" />
-        </service>
-
-    </application>
-
-    <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.cts.content" />
-
-</manifest>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml
deleted file mode 100644
index f55a19a..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<sync-adapter
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:contentAuthority="com.android.cts.stub.provider"
-    android:accountType="com.stub"
-    android:userVisible="false"
-    android:supportsUploading="false"
-    android:allowParallelSyncs="false"
-    android:isAlwaysSyncable="true">
-</sync-adapter>
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index 9cbd49e..95af5e7 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -27,7 +27,7 @@
 LOCAL_CTS_TEST_PACKAGE := android.adminhostside
 
 # tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts arcts vts general-tests
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index e0ea522..f170f81 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -39,7 +39,6 @@
     compatibility-device-util \
     android-support-v4 \
     android-support-test \
-    bouncycastle \
     cts-security-test-support-library
 
 LOCAL_SDK_VERSION := test_current
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
index 0910b70..4863192 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
@@ -18,7 +18,7 @@
 
 import java.lang.Character;
 
-public class LockScreenInfoTest extends BaseAffiliatedProfileOwnerTest {
+public class LockScreenInfoTest extends BaseDeviceOwnerTest {
 
     @Override
     public void tearDown() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
index 0d22c38..868ae3b 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
@@ -15,28 +15,59 @@
  */
 package com.android.cts.transferowner;
 
+import static junit.framework.Assert.assertTrue;
+
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.PersistableBundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Before;
+import org.junit.Test;
 
 @SmallTest
 public class DeviceAndProfileOwnerTransferIncomingTest {
     public static class BasicAdminReceiver extends DeviceAdminReceiver {
         public BasicAdminReceiver() {}
+
+        @Override
+        public void onTransferOwnershipComplete(Context context, PersistableBundle bundle) {
+            putBooleanPref(context, KEY_TRANSFER_COMPLETED_CALLED, true);
+        }
     }
 
+    private final static String SHARED_PREFERENCE_NAME = "shared-preference-name";
+    private final static String KEY_TRANSFER_COMPLETED_CALLED = "key-transfer-completed-called";
+
+    protected Context mContext;
     protected ComponentName mIncomingComponentName;
     protected DevicePolicyManager mDevicePolicyManager;
 
     @Before
     public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getTargetContext();
-        mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
-        mIncomingComponentName = new ComponentName(context, BasicAdminReceiver.class.getName());
+        mContext = InstrumentationRegistry.getTargetContext();
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+        mIncomingComponentName = new ComponentName(mContext, BasicAdminReceiver.class.getName());
+    }
+
+    @Test
+    public void testTransferCompleteCallbackIsCalled() {
+        assertTrue(getBooleanPref(mContext, KEY_TRANSFER_COMPLETED_CALLED));
+    }
+
+    private static SharedPreferences getPrefs(Context context) {
+        return context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
+    }
+
+    private static void putBooleanPref(Context context, String key, boolean value) {
+        getPrefs(context).edit().putBoolean(key, value).apply();
+    }
+
+    private static boolean getBooleanPref(Context context, String key) {
+        return getPrefs(context).getBoolean(key, false);
     }
 }
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
index 2b20c59..b33da04 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
@@ -37,16 +37,16 @@
         assertEquals(Collections.singletonList("test.package"),
                 mDevicePolicyManager.getKeepUninstalledPackages(mIncomingComponentName));
         assertEquals(123, mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
-        assertTrue(areSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
-                mDevicePolicyManager.getSystemUpdatePolicy()));
+        assertSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
+                mDevicePolicyManager.getSystemUpdatePolicy());
         assertThrows(SecurityException.class, () -> {
             mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
         });
     }
 
-    private boolean areSystemPoliciesEqual(SystemUpdatePolicy policy1, SystemUpdatePolicy policy2) {
-        return policy1.getPolicyType() == policy2.getPolicyType()
+    private void assertSystemPoliciesEqual(SystemUpdatePolicy policy1, SystemUpdatePolicy policy2) {
+        assertTrue(policy1.getPolicyType() == policy2.getPolicyType()
                 && policy1.getInstallWindowStart() == policy2.getInstallWindowStart()
-                && policy1.getInstallWindowEnd() == policy2.getInstallWindowEnd();
+                && policy1.getInstallWindowEnd() == policy2.getInstallWindowEnd());
     }
 }
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
index 888ca49..328a7ae 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
@@ -15,15 +15,20 @@
  */
 package com.android.cts.transferowner;
 
+import static junit.framework.Assert.assertNotNull;
+
 import static org.testng.Assert.assertThrows;
 
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.os.PersistableBundle;
 import android.support.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -38,20 +43,26 @@
             "com.android.cts.transferownerincoming";
     private static final String TRANSFER_OWNER_INCOMING_TEST_RECEIVER_CLASS =
             "com.android.cts.transferowner.DeviceAndProfileOwnerTransferIncomingTest$BasicAdminReceiver";
-    static final ComponentName mIncomingComponentName =
+    static final ComponentName INCOMING_COMPONENT_NAME =
             new ComponentName(
                     TRANSFER_OWNER_INCOMING_PKG, TRANSFER_OWNER_INCOMING_TEST_RECEIVER_CLASS);
-    private static final ComponentName mInvalidTargetComponent =
+    private static final ComponentName INVALID_TARGET_COMPONENT =
             new ComponentName("com.android.cts.intent.receiver", ".BroadcastIntentReceiver");
 
     protected DevicePolicyManager mDevicePolicyManager;
     protected ComponentName mOutgoingComponentName;
+    protected Context mContext;
+    private String mOwnerChangedBroadcastAction;
 
     @Before
     public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getTargetContext();
-        mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
-        mOutgoingComponentName = new ComponentName(context, BasicAdminReceiver.class.getName());
+        mContext = InstrumentationRegistry.getTargetContext();
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+        mOutgoingComponentName = new ComponentName(mContext, BasicAdminReceiver.class.getName());
+    }
+
+    protected final void setupTestParameters(String ownerChangedBroadcastAction) {
+        mOwnerChangedBroadcastAction = ownerChangedBroadcastAction;
     }
 
     @Test
@@ -60,7 +71,7 @@
         assertThrows(
                 IllegalArgumentException.class,
                 () -> {
-                    transferOwner(
+                    transferOwnership(
                             mOutgoingComponentName, mOutgoingComponentName, b);
                 });
     }
@@ -71,20 +82,40 @@
         assertThrows(
                 IllegalArgumentException.class,
                 () -> {
-                    transferOwner(
-                            mOutgoingComponentName, mInvalidTargetComponent, b);
+                    transferOwnership(mOutgoingComponentName, INVALID_TARGET_COMPONENT, b);
                 });
     }
 
-    protected void transferOwner(ComponentName outgoing, ComponentName incoming,
+    protected void transferOwnership(ComponentName outgoing, ComponentName incoming,
             PersistableBundle parameters)
             throws Throwable {
         try {
-            mDevicePolicyManager.getClass().getMethod("transferOwner",
+            mDevicePolicyManager.getClass().getMethod("transferOwnership",
                     ComponentName.class, ComponentName.class, PersistableBundle.class)
                     .invoke(mDevicePolicyManager, outgoing, incoming, parameters);
         } catch (InvocationTargetException e) {
             throw e.getTargetException();
         }
     }
+
+    @Test
+    public void testTransferOwnerChangedBroadcast() throws Throwable {
+        BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+                mOwnerChangedBroadcastAction);
+        try {
+            receiver.register();
+            PersistableBundle b = new PersistableBundle();
+            transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+            Intent intent = receiver.awaitForBroadcast();
+            assertNotNull(intent);
+        } finally {
+            receiver.unregisterQuietly();
+        }
+    }
+
+    @Test
+    public void testTransferOwner() throws Throwable {
+        PersistableBundle b = new PersistableBundle();
+        transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
index 67c3760..3a1709f 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
@@ -20,6 +20,7 @@
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertThrows;
 
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.SystemUpdatePolicy;
 import android.os.PersistableBundle;
 import android.support.test.filters.SmallTest;
@@ -30,6 +31,13 @@
 
 @SmallTest
 public class TransferDeviceOwnerOutgoingTest extends DeviceAndProfileOwnerTransferOutgoingTest {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setupTestParameters(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
+    }
+
     @Test
     public void testTransferWithPoliciesOutgoing() throws Throwable {
         int passwordLength = 123;
@@ -41,15 +49,15 @@
                 SystemUpdatePolicy.createWindowedInstallPolicy(123, 456));
 
         PersistableBundle b = new PersistableBundle();
-        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+        transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
     }
 
     @Test
     public void testTransfer() throws Throwable {
         PersistableBundle b = new PersistableBundle();
-        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
-        assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
-        assertTrue(mDevicePolicyManager.isDeviceOwnerApp(mIncomingComponentName.getPackageName()));
+        transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+        assertTrue(mDevicePolicyManager.isAdminActive(INCOMING_COMPONENT_NAME));
+        assertTrue(mDevicePolicyManager.isDeviceOwnerApp(INCOMING_COMPONENT_NAME.getPackageName()));
         assertFalse(
                 mDevicePolicyManager.isDeviceOwnerApp(mOutgoingComponentName.getPackageName()));
         assertFalse(mDevicePolicyManager.isAdminActive(mOutgoingComponentName));
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
index 370539d..cf760c6 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
@@ -28,6 +28,13 @@
 
 @SmallTest
 public class TransferProfileOwnerOutgoingTest extends DeviceAndProfileOwnerTransferOutgoingTest {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setupTestParameters(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
+    }
+
     @Test
     public void testTransferWithPoliciesOutgoing() throws Throwable {
         int passwordLength = 123;
@@ -41,15 +48,15 @@
                 mOutgoingComponentName, passwordExpirationTimeout);
 
         PersistableBundle b = new PersistableBundle();
-        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+        transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
     }
 
     @Test
     public void testTransfer() throws Throwable {
         PersistableBundle b = new PersistableBundle();
-        transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
-        assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
-        assertTrue(mDevicePolicyManager.isProfileOwnerApp(mIncomingComponentName.getPackageName()));
+        transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+        assertTrue(mDevicePolicyManager.isAdminActive(INCOMING_COMPONENT_NAME));
+        assertTrue(mDevicePolicyManager.isProfileOwnerApp(INCOMING_COMPONENT_NAME.getPackageName()));
         assertFalse(
                 mDevicePolicyManager.isProfileOwnerApp(mOutgoingComponentName.getPackageName()));
         assertFalse(mDevicePolicyManager.isAdminActive(mOutgoingComponentName));
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
similarity index 75%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java
rename to hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
index b9cb16b..2456eb1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
@@ -1,6 +1,6 @@
 package com.android.cts.devicepolicy;
 
-public class DeviceAndProfileOwnerTransferTest extends BaseDevicePolicyTest {
+public abstract class DeviceAndProfileOwnerHostSideTransferTest extends BaseDevicePolicyTest {
     protected static final String TRANSFER_OWNER_OUTGOING_PKG =
             "com.android.cts.transferowneroutgoing";
     protected static final String TRANSFER_OWNER_OUTGOING_APK = "CtsTransferOwnerOutgoingApp.apk";
@@ -21,7 +21,6 @@
         if (!mHasFeature) {
             return;
         }
-        installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
         runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
                 mOutgoingTestClassName,
                 "testTransfer", mUserId);
@@ -31,7 +30,6 @@
         if (!mHasFeature) {
             return;
         }
-        installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
         runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
                 mOutgoingTestClassName,
                 "testTransferSameAdmin", mUserId);
@@ -51,7 +49,6 @@
         if (!mHasFeature) {
             return;
         }
-        installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
         runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
                 mOutgoingTestClassName,
                 "testTransferWithPoliciesOutgoing", mUserId);
@@ -60,6 +57,30 @@
                 "testTransferPoliciesAreRetainedAfterTransfer", mUserId);
     }
 
+    public void testTransferOwnerChangedBroadcast() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+                mOutgoingTestClassName,
+                "testTransferOwnerChangedBroadcast", mUserId);
+    }
+
+    public void testTransferCompleteCallback() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+                mOutgoingTestClassName,
+                "testTransferOwner", mUserId);
+
+        waitForBroadcastIdle();
+
+        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
+                mIncomingTestClassName,
+                "testTransferCompleteCallbackIsCalled", mUserId);
+    }
+
     protected void setupTestParameters(int userId, String outgoingTestClassName,
             String incomingTestClassName) {
         mUserId = userId;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index ed9b51d..e37c494 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -122,14 +122,6 @@
         executeDeviceOwnerTest("LockScreenInfoTest");
     }
 
-    public void testLockScreenInfo_affiliatedSecondaryUser() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
-            return;
-        }
-        final int userId = createAffiliatedSecondaryUser();
-        executeAffiliatedProfileOwnerTest("LockScreenInfoTest", userId);
-    }
-
     public void testWifi() throws Exception {
         if (!hasDeviceFeature("android.hardware.wifi")) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
similarity index 67%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java
rename to hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
index 6fbc087..1521282 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
@@ -22,7 +22,8 @@
  * process, first we setup some policies in the client side in CtsTransferOwnerOutgoingApp and then
  * we verify the policies are still there in CtsTransferOwnerIncomingApp.
  */
-public class MixedDeviceOwnerTransferTest extends DeviceAndProfileOwnerTransferTest {
+public class MixedDeviceOwnerHostSideTransferTest extends
+        DeviceAndProfileOwnerHostSideTransferTest {
     private static final String TRANSFER_DEVICE_OWNER_OUTGOING_TEST =
             "com.android.cts.transferowner.TransferDeviceOwnerOutgoingTest";
     private static final String TRANSFER_DEVICE_OWNER_INCOMING_TEST =
@@ -32,15 +33,17 @@
     protected void setUp() throws Exception {
         super.setUp();
         if (mHasFeature) {
-            setupDeviceOwner(TRANSFER_OWNER_OUTGOING_APK,
-                    TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
-            setupTestParameters(mPrimaryUserId, TRANSFER_DEVICE_OWNER_OUTGOING_TEST,
-                    TRANSFER_DEVICE_OWNER_INCOMING_TEST);
+            installAppAsUser(TRANSFER_OWNER_OUTGOING_APK, mPrimaryUserId);
+            if (setDeviceOwner(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, mPrimaryUserId,
+                    false)) {
+                setupTestParameters(mPrimaryUserId, TRANSFER_DEVICE_OWNER_OUTGOING_TEST,
+                        TRANSFER_DEVICE_OWNER_INCOMING_TEST);
+                installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+            } else {
+                removeAdmin(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, mUserId);
+                getDevice().uninstallPackage(TRANSFER_OWNER_OUTGOING_PKG);
+                fail("Failed to set device owner");
+            }
         }
     }
-
-    private void setupDeviceOwner(String apkName, String adminReceiverClassName) throws Exception {
-        installAppAsUser(apkName, mPrimaryUserId);
-        setDeviceOwnerOrFail(adminReceiverClassName, mPrimaryUserId);
-    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
similarity index 75%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java
rename to hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
index 3167729..f5a25d0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
@@ -22,7 +22,8 @@
  * process, first we setup some policies in the client side in CtsTransferOwnerOutgoingApp and then
  * we verify the policies are still there in CtsTransferOwnerIncomingApp.
  */
-public class MixedProfileOwnerTransferTest extends DeviceAndProfileOwnerTransferTest {
+public class MixedProfileOwnerHostSideTransferTest extends
+        DeviceAndProfileOwnerHostSideTransferTest {
     private static final String TRANSFER_PROFILE_OWNER_OUTGOING_TEST =
             "com.android.cts.transferowner.TransferProfileOwnerOutgoingTest";
     private static final String TRANSFER_PROFILE_OWNER_INCOMING_TEST =
@@ -36,16 +37,25 @@
         if (mHasFeature) {
             int profileOwnerUserId = setupManagedProfile(TRANSFER_OWNER_OUTGOING_APK,
                     TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
-            setupTestParameters(profileOwnerUserId, TRANSFER_PROFILE_OWNER_OUTGOING_TEST,
-                    TRANSFER_PROFILE_OWNER_INCOMING_TEST);
+            if (profileOwnerUserId != -1) {
+                setupTestParameters(profileOwnerUserId, TRANSFER_PROFILE_OWNER_OUTGOING_TEST,
+                        TRANSFER_PROFILE_OWNER_INCOMING_TEST);
+                installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+            }
         }
     }
 
     private int setupManagedProfile(String apkName, String adminReceiverClassName)
             throws Exception {
         final int userId = createManagedProfile(mPrimaryUserId);
+
         installAppAsUser(apkName, userId);
-        setProfileOwnerOrFail(adminReceiverClassName, userId);
+        if (!setProfileOwner(adminReceiverClassName, userId, false)) {
+            removeAdmin(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, userId);
+            getDevice().uninstallPackage(TRANSFER_OWNER_OUTGOING_PKG);
+            fail("Failed to set device owner");
+            return -1;
+        }
         startUser(userId);
         return userId;
     }
diff --git a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
new file mode 100644
index 0000000..fa23d9a
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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 com.android.server.cts;
+
+import android.app.JobParametersProto;
+import android.net.NetworkCapabilitiesProto;
+import android.net.NetworkRequestProto;
+import com.android.server.job.ConstantsProto;
+import com.android.server.job.DataSetProto;
+import com.android.server.job.JobPackageHistoryProto;
+import com.android.server.job.JobPackageTrackerDumpProto;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.JobStatusDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
+import com.android.server.job.StateControllerProto;
+
+/** Test to check that the jobscheduler service properly outputs its dump state. */
+public class JobSchedulerIncidentTest extends ProtoDumpTestCase {
+    public void testJobSchedulerServiceDump() throws Exception {
+        final JobSchedulerServiceDumpProto dump =
+                getDump(JobSchedulerServiceDumpProto.parser(), "dumpsys jobscheduler --proto");
+
+        testConstantsProto(dump.getSettings());
+
+        for (int u : dump.getStartedUsersList()) {
+            assertTrue(0 <= u);
+        }
+
+        for (JobSchedulerServiceDumpProto.RegisteredJob rj : dump.getRegisteredJobsList()) {
+            testJobStatusShortInfoProto(rj.getInfo());
+            testJobStatusDumpProto(rj.getDump());
+        }
+
+        for (StateControllerProto c : dump.getControllersList()) {
+            testStateControllerProto(c);
+        }
+
+        for (JobSchedulerServiceDumpProto.PriorityOverride po : dump.getPriorityOverridesList()) {
+            assertTrue(0 <= po.getUid());
+        }
+
+        for (int buu : dump.getBackingUpUidsList()) {
+            assertTrue(0 <= buu);
+        }
+
+        testJobPackageHistoryProto(dump.getHistory());
+
+        testJobPackageTrackerDumpProto(dump.getPackageTracker());
+
+        for (JobSchedulerServiceDumpProto.PendingJob pj : dump.getPendingJobsList()) {
+            testJobStatusShortInfoProto(pj.getInfo());
+            testJobStatusDumpProto(pj.getDump());
+            assertTrue(0 <= pj.getEnqueuedDurationMs());
+        }
+
+        for (JobSchedulerServiceDumpProto.ActiveJob aj : dump.getActiveJobsList()) {
+            JobSchedulerServiceDumpProto.ActiveJob.InactiveJob ajIj = aj.getInactive();
+            assertTrue(0 <= ajIj.getTimeSinceStoppedMs());
+
+            JobSchedulerServiceDumpProto.ActiveJob.RunningJob ajRj = aj.getRunning();
+            testJobStatusShortInfoProto(ajRj.getInfo());
+            assertTrue(0 <= ajRj.getRunningDurationMs());
+            assertTrue(0 <= ajRj.getTimeUntilTimeoutMs());
+            testJobStatusDumpProto(ajRj.getDump());
+            assertTrue(0 <= ajRj.getTimeSinceMadeActiveMs());
+            assertTrue(0 <= ajRj.getPendingDurationMs());
+        }
+
+        assertTrue(0 <= dump.getMaxActiveJobs());
+    }
+
+    private void testConstantsProto(ConstantsProto c) throws Exception {
+        assertNotNull(c);
+
+        assertTrue(0 <= c.getMinIdleCount());
+        assertTrue(0 <= c.getMinChargingCount());
+        assertTrue(0 <= c.getMinBatteryNotLowCount());
+        assertTrue(0 <= c.getMinStorageNotLowCount());
+        assertTrue(0 <= c.getMinConnectivityCount());
+        assertTrue(0 <= c.getMinContentCount());
+        assertTrue(0 <= c.getMinReadyJobsCount());
+        assertTrue(0 <= c.getHeavyUseFactor());
+        assertTrue(0 <= c.getModerateUseFactor());
+        assertTrue(0 <= c.getFgJobCount());
+        assertTrue(0 <= c.getBgNormalJobCount());
+        assertTrue(0 <= c.getBgModerateJobCount());
+        assertTrue(0 <= c.getBgLowJobCount());
+        assertTrue(0 <= c.getBgCriticalJobCount());
+        assertTrue(0 <= c.getMaxStandardRescheduleCount());
+        assertTrue(0 <= c.getMaxWorkRescheduleCount());
+        assertTrue(0 <= c.getMinLinearBackoffTimeMs());
+        assertTrue(0 <= c.getMinExpBackoffTimeMs());
+        assertTrue(0 <= c.getStandbyHeartbeatTimeMs());
+        for (int sb : c.getStandbyBeatsList()) {
+            assertTrue(0 <= sb);
+        }
+    }
+
+    private void testDataSetProto(DataSetProto ds) throws Exception {
+        assertNotNull(ds);
+
+        assertTrue(0 <= ds.getStartClockTimeMs());
+        assertTrue(0 <= ds.getElapsedTimeMs());
+        assertTrue(0 <= ds.getPeriodMs());
+
+        for (DataSetProto.PackageEntryProto pe : ds.getPackageEntriesList()) {
+            assertTrue(0 <= pe.getUid());
+
+            assertTrue(0 <= pe.getPendingState().getDurationMs());
+            assertTrue(0 <= pe.getPendingState().getCount());
+            assertTrue(0 <= pe.getActiveState().getDurationMs());
+            assertTrue(0 <= pe.getActiveState().getCount());
+            assertTrue(0 <= pe.getActiveTopState().getDurationMs());
+            assertTrue(0 <= pe.getActiveTopState().getCount());
+
+            for (DataSetProto.PackageEntryProto.StopReasonCount src : pe.getStopReasonsList()) {
+                assertTrue(JobParametersProto.CancelReason.getDescriptor().getValues()
+                        .contains(src.getReason().getValueDescriptor()));
+                assertTrue(0 <= src.getCount());
+            }
+        }
+        assertTrue(0 <= ds.getMaxConcurrency());
+        assertTrue(0 <= ds.getMaxForegroundConcurrency());
+    }
+
+    private void testJobPackageHistoryProto(JobPackageHistoryProto jph) throws Exception {
+        assertNotNull(jph);
+
+        for (JobPackageHistoryProto.HistoryEvent he : jph.getHistoryEventList()) {
+            assertTrue(JobPackageHistoryProto.Event.getDescriptor().getValues()
+                    .contains(he.getEvent().getValueDescriptor()));
+            assertTrue(0 <= he.getTimeSinceEventMs()); // Should be positive.
+            assertTrue(0 <= he.getUid());
+            assertTrue(JobParametersProto.CancelReason.getDescriptor().getValues()
+                    .contains(he.getStopReason().getValueDescriptor()));
+        }
+    }
+
+    private void testJobPackageTrackerDumpProto(JobPackageTrackerDumpProto jptd) throws Exception {
+        assertNotNull(jptd);
+
+        for (DataSetProto ds : jptd.getHistoricalStatsList()) {
+            testDataSetProto(ds);
+        }
+        testDataSetProto(jptd.getCurrentStats());
+    }
+
+    private void testJobStatusShortInfoProto(JobStatusShortInfoProto jssi) throws Exception {
+        assertNotNull(jssi);
+
+        assertTrue(0 <= jssi.getCallingUid());
+    }
+
+    private void testJobStatusDumpProto(JobStatusDumpProto jsd) throws Exception {
+        assertNotNull(jsd);
+
+        assertTrue(0 <= jsd.getCallingUid());
+        assertTrue(0 <= jsd.getSourceUid());
+        assertTrue(0 <= jsd.getSourceUserId());
+
+        JobStatusDumpProto.JobInfo ji = jsd.getJobInfo();
+        if (ji.getIsPeriodic()) {
+            assertTrue(0 <= ji.getPeriodIntervalMs());
+            assertTrue(0 <= ji.getPeriodFlexMs());
+        }
+        assertTrue(0 <= ji.getTriggerContentUpdateDelayMs());
+        assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
+        testNetworkRequestProto(ji.getRequiredNetwork());
+        assertTrue(0 <= ji.getTotalNetworkBytes());
+        assertTrue(0 <= ji.getMinLatencyMs());
+        assertTrue(0 <= ji.getMaxExecutionDelayMs());
+        JobStatusDumpProto.JobInfo.Backoff bp = ji.getBackoffPolicy();
+        assertTrue(JobStatusDumpProto.JobInfo.Backoff.Policy.getDescriptor().getValues()
+                .contains(bp.getPolicy().getValueDescriptor()));
+        assertTrue(0 <= bp.getInitialBackoffMs());
+
+        for (JobStatusDumpProto.Constraint c : jsd.getRequiredConstraintsList()) {
+            assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+                    .contains(c.getValueDescriptor()));
+        }
+        for (JobStatusDumpProto.Constraint c : jsd.getSatisfiedConstraintsList()) {
+            assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+                    .contains(c.getValueDescriptor()));
+        }
+        for (JobStatusDumpProto.Constraint c : jsd.getUnsatisfiedConstraintsList()) {
+            assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+                    .contains(c.getValueDescriptor()));
+        }
+
+        for (JobStatusDumpProto.TrackingController tc : jsd.getTrackingControllersList()) {
+            assertTrue(JobStatusDumpProto.TrackingController.getDescriptor().getValues()
+                    .contains(tc.getValueDescriptor()));
+        }
+
+        for (JobStatusDumpProto.JobWorkItem jwi : jsd.getPendingWorkList()) {
+            assertTrue(0 <= jwi.getDeliveryCount());
+        }
+        for (JobStatusDumpProto.JobWorkItem jwi : jsd.getExecutingWorkList()) {
+            assertTrue(0 <= jwi.getDeliveryCount());
+        }
+
+        assertTrue(JobStatusDumpProto.Bucket.getDescriptor().getValues()
+                .contains(jsd.getStandbyBucket().getValueDescriptor()));
+
+        assertTrue(0 <= jsd.getEnqueueDurationMs());
+
+        assertTrue(0 <= jsd.getNumFailures());
+
+        assertTrue(0 <= jsd.getLastSuccessfulRunTime());
+        assertTrue(0 <= jsd.getLastFailedRunTime());
+    }
+
+    private void testNetworkRequestProto(NetworkRequestProto nr) throws Exception {
+        assertNotNull(nr);
+
+        assertTrue(NetworkRequestProto.Type.getDescriptor().getValues()
+                .contains(nr.getType().getValueDescriptor()));
+        testNetworkCapabilitesProto(nr.getNetworkCapabilities());
+    }
+
+    private void testNetworkCapabilitesProto(NetworkCapabilitiesProto nc) throws Exception {
+        assertNotNull(nc);
+
+        for (NetworkCapabilitiesProto.Transport t : nc.getTransportsList()) {
+            assertTrue(NetworkCapabilitiesProto.Transport.getDescriptor().getValues()
+                .contains(t.getValueDescriptor()));
+        }
+        for (NetworkCapabilitiesProto.NetCapability c : nc.getCapabilitiesList()) {
+            assertTrue(NetworkCapabilitiesProto.NetCapability.getDescriptor().getValues()
+                .contains(c.getValueDescriptor()));
+        }
+
+        assertTrue(0 <= nc.getLinkUpBandwidthKbps());
+        assertTrue(0 <= nc.getLinkDownBandwidthKbps());
+    }
+
+    private void testStateControllerProto(StateControllerProto sc) throws Exception {
+        assertNotNull(sc);
+
+        StateControllerProto.AppIdleController aic = sc.getAppIdle();
+        for (StateControllerProto.AppIdleController.TrackedJob tj : aic.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.BackgroundJobsController bjc = sc.getBackground();
+        for (StateControllerProto.BackgroundJobsController.TrackedJob tj : bjc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.BatteryController bc = sc.getBattery();
+        for (StateControllerProto.BatteryController.TrackedJob tj : bc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.ConnectivityController cc = sc.getConnectivity();
+        for (StateControllerProto.ConnectivityController.TrackedJob tj : cc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+            testNetworkRequestProto(tj.getRequiredNetwork());
+        }
+        StateControllerProto.ContentObserverController coc = sc.getContentObserver();
+        for (StateControllerProto.ContentObserverController.TrackedJob tj : coc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        for (StateControllerProto.ContentObserverController.Observer o : coc.getObserversList()) {
+            assertTrue(0 <= o.getUserId());
+
+            for (StateControllerProto.ContentObserverController.Observer.TriggerContentData tcd : o.getTriggersList()) {
+                for (StateControllerProto.ContentObserverController.Observer.TriggerContentData.JobInstance ji : tcd.getJobsList()) {
+                    testJobStatusShortInfoProto(ji.getInfo());
+
+                    assertTrue(0 <= ji.getSourceUid());
+                    assertTrue(0 <= ji.getTriggerContentUpdateDelayMs());
+                    assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
+                }
+            }
+        }
+        StateControllerProto.DeviceIdleJobsController dijc = sc.getDeviceIdle();
+        for (StateControllerProto.DeviceIdleJobsController.TrackedJob tj : dijc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.IdleController ic = sc.getIdle();
+        for (StateControllerProto.IdleController.TrackedJob tj : ic.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.StorageController scr = sc.getStorage();
+        for (StateControllerProto.StorageController.TrackedJob tj : scr.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+        StateControllerProto.TimeController tc = sc.getTime();
+        assertTrue(0 <=  tc.getNowElapsedRealtime());
+        assertTrue(0 <= tc.getTimeUntilNextDelayAlarmMs());
+        assertTrue(0 <= tc.getTimeUntilNextDeadlineAlarmMs());
+        for (StateControllerProto.TimeController.TrackedJob tj : tc.getTrackedJobsList()) {
+            testJobStatusShortInfoProto(tj.getInfo());
+            assertTrue(0 <= tj.getSourceUid());
+        }
+    }
+}
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index 5339629..0f83f74 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
@@ -39,8 +39,7 @@
         mInitialUserId = getDevice().getCurrentUser();
     }
 
-    // TODO (b/71573557): Re-add to presubmit
-    //@Presubmit
+    @Presubmit
     public void testCanCreateGuestUser() throws Exception {
         if (!mSupportsMultiUser) {
             return;
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index ed581db..8104245 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -24,12 +24,15 @@
 import android.app.RemoteInput;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.test.AndroidTestCase;
 import android.widget.RemoteViews;
 
-import org.mockito.internal.matchers.Not;
+import java.util.ArrayList;
 
 public class NotificationTest extends AndroidTestCase {
     private static final String TEXT_RESULT_KEY = "text";
@@ -236,6 +239,61 @@
         assertEquals(true, mAction.getAllowGeneratedReplies());
     }
 
+    public void testNotification_addPerson() {
+        String name = "name";
+        String key = "key";
+        String uri = "name:name";
+        Notification.Person person = new Notification.Person()
+                .setName(name)
+                .setIcon(Icon.createWithResource(mContext, 1))
+                .setKey(key)
+                .setUri(uri);
+        mNotification = new Notification.Builder(mContext, CHANNEL.getId())
+                .setSmallIcon(1)
+                .setContentTitle(CONTENT_TITLE)
+                .addPerson(person)
+                .build();
+
+        ArrayList<Notification.Person> restoredPeople = mNotification.extras.getParcelableArrayList(
+                Notification.EXTRA_PEOPLE_LIST);
+        assertNotNull(restoredPeople);
+        Notification.Person restoredPerson = restoredPeople.get(0);
+        assertNotNull(restoredPerson);
+        assertNotNull(restoredPerson.getIcon());
+        assertEquals(name, restoredPerson.getName());
+        assertEquals(key, restoredPerson.getKey());
+        assertEquals(uri, restoredPerson.getUri());
+    }
+
+    public void testNotification_MessagingStyle_people() {
+        String name = "name";
+        String key = "key";
+        String uri = "name:name";
+        Notification.Person user = new Notification.Person()
+                .setName(name)
+                .setIcon(Icon.createWithResource(mContext, 1))
+                .setKey(key)
+                .setUri(uri);
+        Notification.Person participant = new Notification.Person().setName("sender");
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(user)
+                .addMessage("text", 0, participant)
+                .addMessage(new Message("text 2", 0, participant));
+        mNotification = new Notification.Builder(mContext, CHANNEL.getId())
+                .setSmallIcon(1)
+                .setStyle(messagingStyle)
+                .build();
+
+        Notification.Person restoredPerson = mNotification.extras.getParcelable(
+                Notification.EXTRA_MESSAGING_PERSON);
+        assertNotNull(restoredPerson);
+        assertNotNull(restoredPerson.getIcon());
+        assertEquals(name, restoredPerson.getName());
+        assertEquals(key, restoredPerson.getKey());
+        assertEquals(uri, restoredPerson.getUri());
+        assertNotNull(mNotification.extras.getParcelableArray(Notification.EXTRA_MESSAGES));
+    }
+
+
     public void testMessagingStyle_historicMessages() {
         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
                 .setSmallIcon(1)
@@ -253,17 +311,65 @@
     }
 
     public void testMessagingStyle_isGroupConversation() {
-        Notification.MessagingStyle messagingStyle =
-                new Notification.MessagingStyle("self name")
-                        .setGroupConversation(true);
-        mNotification = new Notification.Builder(mContext, "test id")
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle("test conversation title");
+        Notification notification = new Notification.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
         assertTrue(messagingStyle.isGroupConversation());
-        assertTrue(mNotification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    public void testMessagingStyle_isGroupConversation_noConversationTitle() {
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle(null);
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
+        // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(false)
+                .setConversationTitle("test conversation title");
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+        // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle(null);
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertFalse(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
     }
 
     public void testToString() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index a1ff068..488c0f3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -57,6 +57,7 @@
     private static final int WAIT_FOR_PICTURE_TIMEOUT_MS = 5000;
     private static final int FRAMES_TO_WAIT_FOR_CAPTURE = 100;
 
+    @Presubmit
     public void testCamera2() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index 5880cd1..a9f960d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -23,6 +23,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityManagerState.STATE_PAUSED;
 import static android.server.am.ActivityManagerState.STATE_RESUMED;
 
 import static org.junit.Assert.assertFalse;
@@ -58,13 +59,6 @@
     private static final String TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY =
             "TurnScreenOnWithRelayoutActivity";
 
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-        tearDownLockCredentials();
-    }
-
     @Presubmit
     @Test
     public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
@@ -156,9 +150,9 @@
         launchActivity(TURN_SCREEN_ON_ACTIVITY_NAME);
         mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ACTIVITY_NAME });
         mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY_NAME, true);
+        assertTrue(isDisplayOn());
     }
 
-    @FlakyTest(bugId = 69229402)
     @Presubmit
     @Test
     public void testFinishActivityInNonFocusedStack() throws Exception {
@@ -175,6 +169,10 @@
         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
         // Finish activity in non-focused (docked) stack.
         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+        mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_PAUSED);
+        mAmWmState.waitForAllExitingWindows();
+
         mAmWmState.computeState(new String[] { LAUNCHING_ACTIVITY });
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
         mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
@@ -338,13 +336,15 @@
     public void testTurnScreenOnAttrWithLockScreen() throws Exception {
         assumeTrue(isHandheld());
 
-        setLockCredential();
-        sleepDevice();
-        final String logSeparator = clearLogcat();
-        launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
-        mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
-        assertFalse(isDisplayOn());
-        assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            sleepDevice();
+            final String logSeparator = clearLogcat();
+            launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
+            mAmWmState.computeState(new String[]{TURN_SCREEN_ON_ATTR_ACTIVITY_NAME});
+            assertFalse(isDisplayOn());
+            assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
+        }
     }
 
     @Test
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index 404e5c7..b669db1 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -38,6 +38,7 @@
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 
 import org.junit.Test;
 
@@ -100,6 +101,7 @@
      */
     @Presubmit
     @Test
+    @FlakyTest(bugId = 71792393)
     public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
 
@@ -415,6 +417,7 @@
      */
     @Presubmit
     @Test
+    @FlakyTest(bugId = 71792393)
     public void testTaskMoveToBackOrientation() throws Exception {
         // Start landscape activity.
         launchActivity(LANDSCAPE_ACTIVITY_NAME);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
index b17881f..4837963 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
@@ -43,27 +43,22 @@
         setLockDisabled(false);
     }
 
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-        tearDownLockCredentials();
-    }
-
     /**
      * Tests whether a FLAG_DISMISS_KEYGUARD activity on a secondary display is visible (for an
      * insecure keyguard).
      */
     @Test
     public void testDismissKeyguardActivity_secondaryDisplay() throws Exception {
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+        }
     }
 }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
index c773651..c3c5d33 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
@@ -20,6 +20,7 @@
 import static android.server.am.ActivityManagerState.STATE_STOPPED;
 
 import static org.junit.Assume.assumeTrue;
+
 import android.server.am.ActivityManagerState.ActivityDisplay;
 
 import org.junit.After;
@@ -46,16 +47,6 @@
 
         assumeTrue(supportsMultiDisplay());
         assumeTrue(isHandheld());
-
-        setLockCredential();
-    }
-
-
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        tearDownLockCredentials();
-        super.tearDown();
     }
 
     /**
@@ -63,25 +54,30 @@
      */
     @Test
     public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
-        // Create new usual virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+             final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
 
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            // Create new usual virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
 
-        // Lock the device.
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
 
-        // Unlock and check if visibility is back.
-        unlockDeviceWithCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            // Lock the device.
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
+
+            // Unlock and check if visibility is back.
+            lockCredentialSession.unlockDeviceWithCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+        }
     }
 
     /**
@@ -89,34 +85,41 @@
      */
     @Test
     public void testDismissKeyguard_secondaryDisplay() throws Exception {
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+             final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.assertKeyguardGone();
-        mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+            lockCredentialSession.enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+        }
     }
 
     @Test
     public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+             final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(
-                new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.assertKeyguardGone();
-        mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+            lockCredentialSession.enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+        }
     }
 }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
index bc53952..09d793f 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -24,13 +24,18 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.res.Configuration;
+import android.provider.Settings;
 import android.server.am.ActivityManagerState.ActivityDisplay;
-
-import org.junit.After;
+import android.server.am.settings.SettingsSession;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Size;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Collections;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Base class for ActivityManager display tests.
@@ -39,26 +44,14 @@
  * @see ActivityManagerDisplayLockedKeyguardTests
  */
 public class ActivityManagerDisplayTestBase extends ActivityManagerTestBase {
+    private static final String WM_SIZE = "wm size";
+    private static final String WM_DENSITY = "wm density";
 
     static final int CUSTOM_DENSITY_DPI = 222;
 
     private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
     private static final int INVALID_DENSITY_DPI = -1;
 
-    private boolean mVirtualDisplayCreated;
-    private boolean mDisplaySimulated;
-
-    /** Temp storage used for parsing. */
-    final LinkedList<String> mDumpLines = new LinkedList<>();
-
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        destroyVirtualDisplays();
-        destroySimulatedDisplays();
-        super.tearDown();
-    }
-
     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
         for (ActivityDisplay display : displays) {
             if (display.mId == displayId) {
@@ -103,110 +96,101 @@
         return result;
     }
 
-    /**
-     * Create new virtual display.
-     * @param densityDpi provide custom density for the display.
-     * @param launchInSplitScreen start {@link VirtualDisplayActivity} to side from
-     *                            {@link LaunchingActivity} on primary display.
-     * @param canShowWithInsecureKeyguard allow showing content when device is showing an insecure
-     *                                    keyguard.
-     * @param mustBeCreated should assert if the display was or wasn't created.
-     * @param publicDisplay make display public.
-     * @param resizeDisplay should resize display when surface size changes.
-     * @param launchActivity should launch test activity immediately after display creation.
-     * @return A list of {@link ActivityDisplay} that represent newly created displays.
-     * @throws Exception
-     */
-    private List<ActivityDisplay> createVirtualDisplays(int densityDpi,
-            boolean launchInSplitScreen, boolean canShowWithInsecureKeyguard, boolean mustBeCreated,
-            boolean publicDisplay, boolean resizeDisplay, String launchActivity, int displayCount)
-            throws Exception {
-        // Start an activity that is able to create virtual displays.
-        if (launchInSplitScreen) {
-            getLaunchActivityBuilder().setToSide(true)
-                    .setTargetActivityName(VIRTUAL_DISPLAY_ACTIVITY).execute();
-        } else {
-            launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
-        }
-        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
-                new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
-        final List<ActivityDisplay> originalDS = getDisplaysStates();
+    static class ReportedDisplayMetrics {
+        private static final String WM_SIZE = "wm size";
+        private static final String WM_DENSITY = "wm density";
+        private static final Pattern PHYSICAL_SIZE =
+                Pattern.compile("Physical size: (\\d+)x(\\d+)");
+        private static final Pattern OVERRIDE_SIZE =
+                Pattern.compile("Override size: (\\d+)x(\\d+)");
+        private static final Pattern PHYSICAL_DENSITY =
+                Pattern.compile("Physical density: (\\d+)");
+        private static final Pattern OVERRIDE_DENSITY =
+                Pattern.compile("Override density: (\\d+)");
 
-        // Create virtual display with custom density dpi.
-        executeShellCommand(getCreateVirtualDisplayCommand(densityDpi, canShowWithInsecureKeyguard,
-                publicDisplay, resizeDisplay, launchActivity, displayCount));
-        mVirtualDisplayCreated = true;
+        @NonNull
+        final Size physicalSize;
+        final int physicalDensity;
 
-        return assertAndGetNewDisplays(mustBeCreated ? displayCount : -1, originalDS);
-    }
+        @Nullable
+        final Size overrideSize;
+        @Nullable
+        final Integer overrideDensity;
 
-    /**
-     * Simulate new display.
-     * @param densityDpi provide custom density for the display.
-     * @return {@link ActivityDisplay} of newly created display.
-     */
-    private List<ActivityDisplay> simulateDisplay(int densityDpi)
-            throws Exception {
-        final List<ActivityDisplay> originalDs = getDisplaysStates();
-
-        // Create virtual display with custom density dpi.
-        executeShellCommand(getSimulateDisplayCommand(densityDpi));
-        mDisplaySimulated = true;
-
-        return assertAndGetNewDisplays(1, originalDs);
-    }
-
-    /**
-     * Wait for desired number of displays to be created and get their properties.
-     * @param newDisplayCount expected display count, -1 if display should not be created.
-     * @param originalDS display states before creation of new display(s).
-     */
-    private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
-            List<ActivityDisplay> originalDS) throws Exception {
-        final int originalDisplayCount = originalDS.size();
-
-        // Wait for the display(s) to be created and get configurations.
-        final List<ActivityDisplay> ds = getDisplayStateAfterChange(
-                originalDisplayCount + newDisplayCount);
-        if (newDisplayCount != -1) {
-            assertEquals("New virtual display(s) must be created",
-                    originalDisplayCount + newDisplayCount, ds.size());
-        } else {
-            assertEquals("New virtual display must not be created",
-                    originalDisplayCount, ds.size());
-            return null;
+        /** Get physical and override display metrics from WM. */
+        static ReportedDisplayMetrics getDisplayMetrics() throws Exception {
+            return new ReportedDisplayMetrics(
+                    executeShellCommand(WM_SIZE) + executeShellCommand(WM_DENSITY));
         }
 
-        // Find the newly added display(s).
-        final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
-        assertTrue("New virtual display must be created", newDisplayCount == newDisplays.size());
+        void setDisplayMetrics(final Size size, final int density) {
+            setSize(size);
+            setDensity(density);
+        }
 
-        return newDisplays;
-    }
+        void restoreDisplayMetrics() {
+            if (overrideSize != null) {
+                setSize(overrideSize);
+            } else {
+                executeShellCommand(WM_SIZE + " reset");
+            }
+            if (overrideDensity != null) {
+                setDensity(overrideDensity);
+            } else {
+                executeShellCommand(WM_DENSITY + " reset");
+            }
+        }
 
-    /**
-     * Destroy existing virtual display.
-     */
-    void destroyVirtualDisplays() throws Exception {
-        if (mVirtualDisplayCreated) {
-            executeShellCommand(getDestroyVirtualDisplayCommand());
-            mVirtualDisplayCreated = false;
+        private void setSize(final Size size) {
+            executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
+        }
+
+        private void setDensity(final int density) {
+            executeShellCommand(WM_DENSITY + " " + density);
+        }
+
+        /** Get display size that WM operates with. */
+        Size getSize() {
+            return overrideSize != null ? overrideSize : physicalSize;
+        }
+
+        /** Get density that WM operates with. */
+        int getDensity() {
+            return overrideDensity != null ? overrideDensity : physicalDensity;
+        }
+
+        private ReportedDisplayMetrics(final String lines) throws Exception {
+            Matcher matcher = PHYSICAL_SIZE.matcher(lines);
+            assertTrue("Physical display size must be reported", matcher.find());
+            log(matcher.group());
+            physicalSize = new Size(
+                    Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
+
+            matcher = PHYSICAL_DENSITY.matcher(lines);
+            assertTrue("Physical display density must be reported", matcher.find());
+            log(matcher.group());
+            physicalDensity = Integer.parseInt(matcher.group(1));
+
+            matcher = OVERRIDE_SIZE.matcher(lines);
+            if (matcher.find()) {
+                log(matcher.group());
+                overrideSize = new Size(
+                        Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
+            } else {
+                overrideSize = null;
+            }
+
+            matcher = OVERRIDE_DENSITY.matcher(lines);
+            if (matcher.find()) {
+                log(matcher.group());
+                overrideDensity = Integer.parseInt(matcher.group(1));
+            } else {
+                overrideDensity = null;
+            }
         }
     }
 
-    /**
-     * Destroy existing simulated display.
-     */
-    private void destroySimulatedDisplays() throws Exception {
-        if (mDisplaySimulated) {
-            executeShellCommand(getDestroySimulatedDisplayCommand());
-            mDisplaySimulated = false;
-        }
-    }
-
-    static class VirtualDisplayBuilder {
-        private final ActivityManagerDisplayTestBase mTests;
-
+    protected class VirtualDisplaySession implements AutoCloseable {
         private int mDensityDpi = CUSTOM_DENSITY_DPI;
         private boolean mLaunchInSplitScreen = false;
         private boolean mCanShowWithInsecureKeyguard = false;
@@ -216,99 +200,196 @@
         private boolean mSimulateDisplay = false;
         private boolean mMustBeCreated = true;
 
-        public VirtualDisplayBuilder(ActivityManagerDisplayTestBase tests) {
-            mTests = tests;
-        }
+        private boolean mVirtualDisplayCreated = false;
+        private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
+                new OverlayDisplayDevicesSession();
 
-        public VirtualDisplayBuilder setDensityDpi(int densityDpi) {
+        public VirtualDisplaySession setDensityDpi(int densityDpi) {
             mDensityDpi = densityDpi;
             return this;
         }
 
-        public VirtualDisplayBuilder setLaunchInSplitScreen(boolean launchInSplitScreen) {
+        public VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
             mLaunchInSplitScreen = launchInSplitScreen;
             return this;
         }
 
-        public VirtualDisplayBuilder setCanShowWithInsecureKeyguard(
+        public VirtualDisplaySession setCanShowWithInsecureKeyguard(
                 boolean canShowWithInsecureKeyguard) {
             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
             return this;
         }
 
-        public VirtualDisplayBuilder setPublicDisplay(boolean publicDisplay) {
+        public VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
             mPublicDisplay = publicDisplay;
             return this;
         }
 
-        public VirtualDisplayBuilder setResizeDisplay(boolean resizeDisplay) {
+        public VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
             mResizeDisplay = resizeDisplay;
             return this;
         }
 
-        public VirtualDisplayBuilder setLaunchActivity(String launchActivity) {
+        public VirtualDisplaySession setLaunchActivity(String launchActivity) {
             mLaunchActivity = launchActivity;
             return this;
         }
 
-        public VirtualDisplayBuilder setSimulateDisplay(boolean simulateDisplay) {
+        public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
             mSimulateDisplay = simulateDisplay;
             return this;
         }
 
-        public VirtualDisplayBuilder setMustBeCreated(boolean mustBeCreated) {
+        public VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
             mMustBeCreated = mustBeCreated;
             return this;
         }
 
-        public ActivityDisplay build() throws Exception {
-            final List<ActivityDisplay> displays = build(1);
-            return displays != null && !displays.isEmpty() ? displays.get(0) : null;
+        @Nullable
+        public ActivityDisplay createDisplay() throws Exception {
+            return createDisplays(1).stream().findFirst().orElse(null);
         }
 
-        public List<ActivityDisplay> build(int count) throws Exception {
+        @NonNull
+        public List<ActivityDisplay> createDisplays(int count) throws Exception {
             if (mSimulateDisplay) {
-                return mTests.simulateDisplay(mDensityDpi);
+                return simulateDisplay();
+            } else {
+                return createVirtualDisplays(count);
+            }
+        }
+
+        @Override
+        public void close() throws Exception {
+            mOverlayDisplayDeviceSession.close();
+            if (mVirtualDisplayCreated) {
+                destroyVirtualDisplays();
+                mVirtualDisplayCreated = false;
+            }
+        }
+
+        /**
+         * Simulate new display.
+         * <pre>
+         * <code>mDensityDpi</code> provide custom density for the display.
+         * </pre>
+         * @return {@link ActivityDisplay} of newly created display.
+         */
+        private List<ActivityDisplay> simulateDisplay() throws Exception {
+            final List<ActivityDisplay> originalDs = getDisplaysStates();
+
+            // Create virtual display with custom density dpi.
+            mOverlayDisplayDeviceSession.set("1024x768/" + mDensityDpi);
+
+            return assertAndGetNewDisplays(1, originalDs);
+        }
+
+        /**
+         * Create new virtual display.
+         * <pre>
+         * <code>mDensityDpi</code> provide custom density for the display.
+         * <code>mLaunchInSplitScreen</code> start {@link VirtualDisplayActivity} to side from
+         *     {@link LaunchingActivity} on primary display.
+         * <code>mCanShowWithInsecureKeyguard</code>  allow showing content when device is
+         *     showing an insecure keyguard.
+         * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
+         * <code>mPublicDisplay</code> make display public.
+         * <code>mResizeDisplay</code> should resize display when surface size changes.
+         * <code>LaunchActivity</code> should launch test activity immediately after display
+         *     creation.
+         * </pre>
+         * @param displayCount number of displays to be created.
+         * @return A list of {@link ActivityDisplay} that represent newly created displays.
+         * @throws Exception
+         */
+        private List<ActivityDisplay> createVirtualDisplays(int displayCount) throws Exception {
+            // Start an activity that is able to create virtual displays.
+            if (mLaunchInSplitScreen) {
+                getLaunchActivityBuilder()
+                        .setToSide(true)
+                        .setTargetActivityName(VIRTUAL_DISPLAY_ACTIVITY)
+                        .execute();
+            } else {
+                launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
+            }
+            mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                    new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            final List<ActivityDisplay> originalDS = getDisplaysStates();
+
+            // Create virtual display with custom density dpi.
+            final StringBuilder createVirtualDisplayCommand = new StringBuilder(
+                    getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
+                    .append(" -f 0x20000000")
+                    .append(" --es command create_display");
+            if (mDensityDpi != INVALID_DENSITY_DPI) {
+                createVirtualDisplayCommand
+                        .append(" --ei density_dpi ")
+                        .append(mDensityDpi);
+            }
+            createVirtualDisplayCommand.append(" --ei count ").append(displayCount)
+                    .append(" --ez can_show_with_insecure_keyguard ")
+                    .append(mCanShowWithInsecureKeyguard)
+                    .append(" --ez public_display ").append(mPublicDisplay)
+                    .append(" --ez resize_display ").append(mResizeDisplay);
+            if (mLaunchActivity != null) {
+                createVirtualDisplayCommand
+                        .append(" --es launch_target_activity ")
+                        .append(mLaunchActivity);
+            }
+            executeShellCommand(createVirtualDisplayCommand.toString());
+            mVirtualDisplayCreated = true;
+
+            return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
+        }
+
+        /**
+         * Destroy existing virtual display.
+         */
+        void destroyVirtualDisplays() throws Exception {
+            final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
+                    + " -f 0x20000000"
+                    + " --es command destroy_display";
+            executeShellCommand(destroyVirtualDisplayCommand);
+        }
+
+        /**
+         * Wait for desired number of displays to be created and get their properties.
+         * @param newDisplayCount expected display count, -1 if display should not be created.
+         * @param originalDS display states before creation of new display(s).
+         * @return list of new displays, empty list if no new display is created.
+         */
+        private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
+                List<ActivityDisplay> originalDS) throws Exception {
+            final int originalDisplayCount = originalDS.size();
+
+            // Wait for the display(s) to be created and get configurations.
+            final List<ActivityDisplay> ds = getDisplayStateAfterChange(
+                    originalDisplayCount + newDisplayCount);
+            if (newDisplayCount != -1) {
+                assertEquals("New virtual display(s) must be created",
+                        originalDisplayCount + newDisplayCount, ds.size());
+            } else {
+                assertEquals("New virtual display must not be created",
+                        originalDisplayCount, ds.size());
+                return Collections.emptyList();
             }
 
-            return mTests.createVirtualDisplays(mDensityDpi, mLaunchInSplitScreen,
-                    mCanShowWithInsecureKeyguard, mMustBeCreated, mPublicDisplay, mResizeDisplay,
-                    mLaunchActivity, count);
+            // Find the newly added display(s).
+            final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
+            assertTrue("New virtual display must be created",
+                    newDisplayCount == newDisplays.size());
+
+            return newDisplays;
         }
     }
 
-    private static String getCreateVirtualDisplayCommand(int densityDpi,
-            boolean canShowWithInsecureKeyguard, boolean publicDisplay, boolean resizeDisplay,
-            String launchActivity, int displayCount) {
-        final StringBuilder commandBuilder
-                = new StringBuilder(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY));
-        commandBuilder.append(" -f 0x20000000");
-        commandBuilder.append(" --es command create_display");
-        if (densityDpi != INVALID_DENSITY_DPI) {
-            commandBuilder.append(" --ei density_dpi ").append(densityDpi);
+    /** Helper class to save, set, and restore overlay_display_devices preference. */
+    private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
+        OverlayDisplayDevicesSession() {
+            super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
+                    Settings.Global::getString,
+                    Settings.Global::putString);
         }
-        commandBuilder.append(" --ei count ").append(displayCount);
-        commandBuilder.append(" --ez can_show_with_insecure_keyguard ")
-                .append(canShowWithInsecureKeyguard);
-        commandBuilder.append(" --ez public_display ").append(publicDisplay);
-        commandBuilder.append(" --ez resize_display ").append(resizeDisplay);
-        if (launchActivity != null) {
-            commandBuilder.append(" --es launch_target_activity ").append(launchActivity);
-        }
-        return commandBuilder.toString();
-    }
-
-    private static String getDestroyVirtualDisplayCommand() {
-        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
-                " --es command destroy_display";
-    }
-
-    private static String getSimulateDisplayCommand(int densityDpi) {
-        return "settings put global overlay_display_devices 1024x768/" + densityDpi;
-    }
-
-    private static String getDestroySimulatedDisplayCommand() {
-        return "settings delete global overlay_display_devices";
     }
 
     /** Wait for provided number of displays and report their configurations. */
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
index 3229905..d82c7c5 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -16,124 +16,27 @@
 
 package android.server.am;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.am.ActivityManagerState.STATE_RESUMED;
-import static android.server.am.ActivityManagerState.STATE_STOPPED;
-import static android.server.am.StateLogger.log;
-import static android.server.am.StateLogger.logE;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
 
-import android.content.ComponentName;
 import android.platform.test.annotations.Presubmit;
 import android.server.am.ActivityManagerState.ActivityDisplay;
-import android.server.am.displayservice.DisplayHelper;
-import android.support.test.filters.FlakyTest;
+import android.util.Size;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Build/Install/Run:
  *     atest CtsActivityManagerDeviceTestCases:ActivityManagerDisplayTests
  */
 public class ActivityManagerDisplayTests extends ActivityManagerDisplayTestBase {
-    private static final String WM_SIZE = "wm size";
-    private static final String WM_DENSITY = "wm density";
-
     private static final String TEST_ACTIVITY_NAME = "TestActivity";
-    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
-    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
-    private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
-    private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
-    private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
-    private static final String SECOND_PACKAGE = "android.server.am.second";
-    private static final String THIRD_PACKAGE = "android.server.am.third";
-    private static final ComponentName SECOND_ACTIVITY = ComponentName.createRelative(
-            SECOND_PACKAGE, ".SecondActivity");
-    private static final ComponentName SECOND_NO_EMBEDDING_ACTIVITY = ComponentName.createRelative(
-            SECOND_PACKAGE, ".SecondActivityNoEmbedding");
-    private static final ComponentName LAUNCH_BROADCAST_RECEIVER = ComponentName.createRelative(
-            SECOND_PACKAGE, ".LaunchBroadcastReceiver");
-    /** See AndroidManifest.xml of appSecondUid. */
-    private static final String LAUNCH_BROADCAST_ACTION =
-            SECOND_PACKAGE + ".LAUNCH_BROADCAST_ACTION";
-    private static final ComponentName THIRD_ACTIVITY = ComponentName.createRelative(
-            THIRD_PACKAGE, ".ThirdActivity");
-    private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
-    private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 900;
-    private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
-
-    private DisplayHelper mExternalDisplayHelper;
-
-    /** Physical display metrics and overrides in the beginning of the test. */
-    private ReportedDisplayMetrics mInitialDisplayMetrics;
-
-    // TODO: We might consider breaking this class up into
-    //   ActivityManagerDisplayTests for general
-    //   ActivityManagerMultiDisplayTests
-    //   ActivityManagerVrDisplayTests
-    // so the assumption can be put in the setUp().
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mInitialDisplayMetrics = getDisplayMetrics();
-    }
-
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        enablePersistentVrMode(false);
-        restoreDisplayMetricsOverrides();
-        if (mExternalDisplayHelper != null) {
-            mExternalDisplayHelper.releaseDisplay();
-            mExternalDisplayHelper = null;
-        }
-        setPrimaryDisplayState(true);
-        super.tearDown();
-    }
-
-    private void enablePersistentVrMode(boolean enabled) throws Exception {
-        if (enabled) {
-            executeShellCommand("setprop vr_virtualdisplay true");
-            executeShellCommand("vr set-persistent-vr-mode-enabled true");
-        } else {
-            executeShellCommand("vr set-persistent-vr-mode-enabled false");
-            executeShellCommand("setprop vr_virtualdisplay false");
-        }
-    }
-
-    private void restoreDisplayMetricsOverrides() throws Exception {
-        if (mInitialDisplayMetrics.sizeOverrideSet) {
-            executeShellCommand(WM_SIZE + " " + mInitialDisplayMetrics.overrideWidth + "x"
-                    + mInitialDisplayMetrics.overrideHeight);
-        } else {
-            executeShellCommand("wm size reset");
-        }
-        if (mInitialDisplayMetrics.densityOverrideSet) {
-            executeShellCommand(WM_DENSITY + " " + mInitialDisplayMetrics.overrideDensity);
-        } else {
-            executeShellCommand("wm density reset");
-        }
-    }
 
     /**
      * Tests that the global configuration is equal to the default display's override configuration.
@@ -154,11 +57,13 @@
      */
     @Test
     public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
-        // Find the density of created display.
-        final int newDensityDpi = newDisplay.mFullConfiguration.densityDpi;
-        assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
+            // Find the density of created display.
+            final int newDensityDpi = newDisplay.mFullConfiguration.densityDpi;
+            assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
+        }
     }
 
     /**
@@ -171,1567 +76,41 @@
         // Only check devices with the feature disabled.
         assumeFalse(supportsMultiDisplay());
 
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
 
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
 
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-        assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
-                frontStack.mDisplayId);
-        mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
-    }
-
-    /**
-     * Tests that any new activity launch in Vr mode is in Vr display.
-     */
-    @Test
-    public void testVrActivityLaunch() throws Exception {
-        assumeTrue(supportsVrMode());
-        assumeTrue(supportsMultiDisplay());
-
-        // Put the device in persistent vr mode.
-        enablePersistentVrMode(true);
-
-        // Launch the VR activity.
-        launchActivity(VR_TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
-        mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Launch the non-VR 2D activity and check where it ends up.
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Launched activity must be resumed in focused stack",
-            getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
-        // Check if the launch activity is in Vr virtual display id.
-        final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
-        final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
-                VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
-        assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
-        // Check if the focused activity is on this virtual stack.
-        assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
-                focusedStack.mDisplayId);
-
-        // Put the device out of persistent vr mode.
-        enablePersistentVrMode(false);
-    }
-
-    /**
-     * Tests that any activity already present is re-launched in Vr display in vr mode.
-     */
-    @Test
-    public void testVrActivityReLaunch() throws Exception {
-        assumeTrue(supportsVrMode());
-        assumeTrue(supportsMultiDisplay());
-
-        // Launch a 2D activity.
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Put the device in persistent vr mode.
-        enablePersistentVrMode(true);
-
-        // Launch the VR activity.
-        launchActivity(VR_TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
-        mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Re-launch the non-VR 2D activity and check where it ends up.
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Launched activity must be resumed in focused stack",
-            getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
-        // Check if the launch activity is in Vr virtual display id.
-        final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
-        final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
-                VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
-        assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
-        // Check if the focused activity is on this virtual stack.
-        assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
-            focusedStack.mDisplayId);
-
-        // Put the device out of persistent vr mode.
-        enablePersistentVrMode(false);
-    }
-
-    /**
-     * Tests that any new activity launch post Vr mode is in the main display.
-     */
-    @Test
-    public void testActivityLaunchPostVr() throws Exception {
-        assumeTrue(supportsVrMode());
-        assumeTrue(supportsMultiDisplay());
-
-        // Put the device in persistent vr mode.
-        enablePersistentVrMode(true);
-
-        // Launch the VR activity.
-        launchActivity(VR_TEST_ACTIVITY_NAME);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
-        mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
-        // Launch the non-VR 2D activity and check where it ends up.
-        launchActivity(ALT_LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", ALT_LAUNCHING_ACTIVITY);
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Launched activity must be resumed in focused stack",
-            getActivityComponentName(ALT_LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
-        // Check if the launch activity is in Vr virtual display id.
-        final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
-        final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
-                VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
-        assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
-        // Check if the focused activity is on this virtual stack.
-        assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
-            focusedStack.mDisplayId);
-
-        // Put the device out of persistent vr mode.
-        enablePersistentVrMode(false);
-
-        // There isn't a direct launch of activity which can take an user out of persistent VR mode.
-        // This sleep is to account for that delay and let device settle once it comes out of VR
-        // mode.
-        try {
-            Thread.sleep(2000);
-        } catch (Exception e) {
-            e.printStackTrace();
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+            assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
+                    frontStack.mDisplayId);
+            mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
         }
-
-        // Launch the non-VR 2D activity and check where it ends up.
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build());
-
-        // Ensure that the subsequent activity is visible
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", RESIZEABLE_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
-        assertEquals("Front stack must be on primary display",
-                DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
     }
 
     @Test
     public void testCreateMultipleVirtualDisplays() throws Exception {
-        List<ActivityDisplay> originalDs = getDisplaysStates();
-        // Create new virtual displays
-        new VirtualDisplayBuilder(this).build(3);
-        getDisplayStateAfterChange(originalDs.size() + 3);
-        destroyVirtualDisplays();
+        final List<ActivityDisplay> originalDs = getDisplaysStates();
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual displays
+            virtualDisplaySession.createDisplays(3);
+            getDisplayStateAfterChange(originalDs.size() + 3);
+        }
         getDisplayStateAfterChange(originalDs.size());
     }
 
     /**
-     * Tests launching an activity on virtual display.
-     */
-    @Presubmit
-    @Test
-    public void testLaunchActivityOnSecondaryDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        final String logSeparator = clearLogcat();
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the secondary display and resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Check that activity config corresponds to display config.
-        final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
-                logSeparator);
-        assertEquals("Activity launched on secondary display must have proper configuration",
-                CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display. It should land on the
-     * default display.
-     */
-    @Test
-    public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME)
-                .build());
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                NON_RESIZEABLE_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display while split-screen is active
-     * on the primary display. It should land on the primary display and dismiss docked stack.
-     */
-    @Test
-    public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-        assumeTrue(supportsSplitScreenMultiWindow());
-
-        // Start launching activity.
-        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
-        // Create new virtual display.
-        final ActivityDisplay newDisplay =
-                new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                NON_RESIZEABLE_ACTIVITY_NAME);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-        mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
-    }
-
-    /**
-     * Tests moving a non-resizeable activity to a virtual display. It should stay on the default
-     * display with no action performed.
-     */
-    @Test
-    public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        // Launch a non-resizeable activity on a primary display.
-        launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
-        // Launch a resizeable activity on new secondary display to create a new stack there.
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        final int externalFrontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-
-        // Try to move the non-resizeable activity to new secondary display.
-        moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, externalFrontStackId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME)
-                .build());
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                RESIZEABLE_ACTIVITY_NAME);
-
-        // Check that activity is in the same stack
-        final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack defaultFrontStack =
-                mAmWmState.getAmState().getStackById(defaultFrontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                defaultFrontStack.getTopTask().mRealActivity);
-        mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
-                externalFrontStackId);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display from activity there. It should
-     * land on the secondary display based on the resizeability of the root activity of the task.
-     */
-    @Test
-    public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                BROADCAST_RECEIVER_ACTIVITY);
-
-        // Check that launching activity is on the secondary display.
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the secondary display and resumed",
-                getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
-        // Launch non-resizeable activity from secondary display.
-        executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
-                + "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
-
-        // Check that non-resizeable activity is on the secondary display, because of the resizeable
-        // root of the task.
-        frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
-    }
-
-    /**
-     * Tests launching a non-resizeable activity on virtual display from activity there. It should
-     * land on some different suitable display (usually - on the default one).
-     */
-    @Test
-    public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                LAUNCHING_ACTIVITY);
-
-        // Check that launching activity is on the secondary display.
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the secondary display and resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
-        // Launch non-resizeable activity from secondary display.
-        getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
-                .setNewTask(true).setMultipleTask(true).execute();
-
-        // Check that non-resizeable activity is on the primary display.
-        frontStackId = mAmWmState.getAmState().getFocusedStackId();
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertFalse("Launched activity must be on a different display",
-                newDisplay.mId == frontStack.mDisplayId);
-        assertEquals("Launched activity must be resumed",
-                getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on a just launched activity", frontStackId);
-    }
-
-    /**
-     * Tests launching an activity on a virtual display without special permission must not be
-     * allowed.
-     */
-    @Test
-    public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        final String logSeparator = clearLogcat();
-
-        // Try to launch an activity and check it security exception was triggered.
-        final String broadcastTarget = "-a " + LAUNCH_BROADCAST_ACTION
-                + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName();
-        final String includeStoppedPackagesFlag = " -f 0x00000020";
-        executeShellCommand("am broadcast " + broadcastTarget
-                + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
-                + " --es package_name " + componentName
-                + " --ei display_id " + newDisplay.mId
-                + includeStoppedPackagesFlag);
-
-        assertSecurityException("LaunchBroadcastReceiver", logSeparator);
-
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-        assertFalse("Restricted activity must not be launched",
-                mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
-    }
-
-    /**
-     * Tests launching an activity on a virtual display without special permission must be allowed
-     * for activities with same UID.
-     */
-    @Test
-    public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Try to launch an activity and check it security exception was triggered.
-        final String broadcastTarget = "-a " + componentName + ".LAUNCH_BROADCAST_ACTION"
-                + " -p " + componentName;
-        executeShellCommand("am broadcast " + broadcastTarget
-                + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
-                + " --es package_name " + componentName
-                + " --ei display_id " + newDisplay.mId);
-
-        mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
-
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState()
-                .getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                focusedStack.mDisplayId);
-
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
-        assertEquals("Activity launched by owner must be on external display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity via shell
-     * command and without specifying the display id - the second activity must appear on the
-     * primary display.
-     */
-    @Presubmit
-    @Test
-    public void testConsequentLaunchActivity() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-
-        // Launch second activity without specifying display.
-        launchActivity(LAUNCHING_ACTIVITY);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
-        // Check that activity is launched in focused stack on primary display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
-        assertEquals("Front stack must be on primary display",
-                DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
-    }
-
-    /**
-     * Tests launching an activity on simulated display and then launching another activity from the
-     * first one - it must appear on the secondary display, because it was launched from there.
-     */
-    @FlakyTest(bugId = 71564456)
-    @Presubmit
-    @Test
-    public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-                LAUNCHING_ACTIVITY);
-
-        // Launch second activity from app on secondary display without specifying display id.
-        getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity from the
-     * first one - it must appear on the secondary display, because it was launched from there.
-     */
-    @Test
-    public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-            LAUNCHING_ACTIVITY);
-
-        // Launch second activity from app on secondary display without specifying display id.
-        getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
-        mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack frontStack
-            = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-            getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity from the
-     * first one with specifying the target display - it must appear on the secondary display.
-     */
-    @Test
-    public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(LAUNCHING_ACTIVITY);
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-            LAUNCHING_ACTIVITY);
-
-        // Launch second activity from app on secondary display specifying same display id.
-        getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY)
-            .setDisplayId(newDisplay.mId).execute();
-        mAmWmState.computeState(TEST_ACTIVITY_NAME);
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY);
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        ActivityManagerState.ActivityStack frontStack
-            = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-            SECOND_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
-
-        // Launch other activity with different uid and check if it has launched successfully.
-        getLaunchActivityBuilder()
-                .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
-                .setDisplayId(newDisplay.mId)
-                .setTargetActivity(THIRD_ACTIVITY)
-                .execute();
-        mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
-
-        // Check that activity is launched in focused stack on external display.
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY);
-        frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-            THIRD_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching an activity on virtual display and then launching another activity that
-     * doesn't allow embedding - it should fail with security exception.
-     */
-    @Test
-    public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
-
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
-            LAUNCHING_ACTIVITY);
-
-        final String logSeparator = clearLogcat();
-
-        // Launch second activity from app on secondary display specifying same display id.
-        getLaunchActivityBuilder().setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
-                .setDisplayId(newDisplay.mId)
-                .execute();
-
-        assertSecurityException("ActivityLauncher", logSeparator);
-    }
-
-    /**
-     * Tests launching an activity to secondary display from activity on primary display.
-     */
-    @Test
-    public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Start launching activity.
-        launchActivity(LAUNCHING_ACTIVITY);
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity on secondary display from the app on primary display.
-        getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
-                .setDisplayId(newDisplay.mId).execute();
-
-        // Check that activity is launched on external display.
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be resumed in front stack",
-                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
-    }
-
-    /**
-     * Tests launching activities on secondary and then on primary display to see if the stack
-     * visibility is not affected.
-     */
-    @Presubmit
-    @Test
-    public void testLaunchActivitiesAffectsVisibility() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Start launching activity.
-        launchActivity(LAUNCHING_ACTIVITY);
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on primary display and check if it doesn't affect activity on secondary
-        // display.
-        getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
-        mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-    }
-
-    /**
-     * Test that move-task works when moving between displays.
-     */
-    @Presubmit
-    @Test
-    public void testMoveTaskBetweenDisplays() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
-        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
-        int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Focused stack must be on secondary display",
-                newDisplay.mId, focusedStack.mDisplayId);
-
-        // Move activity from secondary display to primary.
-        moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
-        mAmWmState.waitForFocusedStack(defaultDisplayStackId);
-        mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
-        focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     * This version launches virtual display creator to fullscreen stack in split-screen.
-     */
-    @FlakyTest(bugId = 69573940)
-    @Presubmit
-    @Test
-    public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-        assumeTrue(supportsSplitScreenMultiWindow());
-
-        // Start launching activity into docked stack.
-        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     * This version launches virtual display creator to docked stack in split-screen.
-     */
-    @Test
-    public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-        assumeTrue(supportsSplitScreenMultiWindow());
-
-        // Setup split-screen.
-        launchActivitiesInSplitScreen(RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
-        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     * This version works without split-screen.
-     */
-    @Test
-    public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Start an activity on default display to determine default stack.
-        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
-        final int focusedStackWindowingMode =
-                mAmWmState.getAmState().getFrontStackWindowingMode(DEFAULT_DISPLAY_ID);
-        // Finish probing activity.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-        tryCreatingAndRemovingDisplayWithActivity(
-                false /* splitScreen */, focusedStackWindowingMode);
-    }
-
-    /**
-     * Create a virtual display, launch a test activity there, destroy the display and check if test
-     * activity is moved to a stack on the default display.
-     */
-    private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
-            throws Exception {
-        // Create new virtual display.
-        final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
-                .setPublicDisplay(true);
-        if (splitScreen) {
-            builder.setLaunchInSplitScreen(true);
-        }
-        final ActivityDisplay newDisplay = builder.build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        if (splitScreen) {
-            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-        }
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Destroy virtual display.
-        final String logSeparator = clearLogcat();
-        destroyVirtualDisplays();
-        assertActivityLifecycle(TEST_ACTIVITY_NAME, false /* relaunched */, logSeparator);
-        mAmWmState.waitForValidState(TEST_ACTIVITY_NAME, windowingMode, ACTIVITY_TYPE_STANDARD);
-        mAmWmState.assertSanity();
-        mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
-
-        // Check if the focus is switched back to primary display.
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedStack(
-                "Default stack on primary display must be focused after display removed",
-                windowingMode, ACTIVITY_TYPE_STANDARD);
-        mAmWmState.assertFocusedActivity(
-                "Focus must be switched back to activity on primary display",
-                TEST_ACTIVITY_NAME);
-    }
-
-    /**
-     * Tests launching activities on secondary display and then removing it to see if stack focus
-     * is moved correctly.
-     */
-    @Test
-    public void testStackFocusSwitchOnStackEmptied() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                BROADCAST_RECEIVER_ACTIVITY);
-
-        // Lock the device, so that activity containers will be detached.
-        sleepDevice();
-
-        // Finish activity on secondary display.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-        // Unlock and check if the focus is switched back to primary display.
-        wakeUpAndUnlockDevice();
-        mAmWmState.waitForFocusedStack(focusedStackId);
-        mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                VIRTUAL_DISPLAY_ACTIVITY);
-    }
-
-    /**
-     * Tests that input events on the primary display take focus from the virtual display.
-     */
-    @Test
-    public void testStackFocusSwitchOnTouchEvent() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                VIRTUAL_DISPLAY_ACTIVITY);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-        mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
-                TEST_ACTIVITY_NAME);
-
-        final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
-        final int width = displayMetrics.getWidth();
-        final int height = displayMetrics.getHeight();
-        executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
-
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                VIRTUAL_DISPLAY_ACTIVITY);
-    }
-
-    /** Test that shell is allowed to launch on secondary displays. */
-    @Test
-    public void testPermissionLaunchFromShell() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                focusedStack.mDisplayId);
-
-        // Launch other activity with different uid and check it is launched on dynamic stack on
-        // secondary display.
-        final String startCmd =  "am start -n " + SECOND_ACTIVITY.flattenToShortString()
-                + " --display " + newDisplay.mId;
-        executeShellCommand(startCmd);
-
-        mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
-        assertEquals("Activity launched by system must be on external display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /** Test that launching from app that is on external display is allowed. */
-    @Test
-    public void testPermissionLaunchFromAppOnSecondary() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        // Launch activity with different uid on secondary display.
-        final String startCmd =  "am start -n " + SECOND_ACTIVITY.flattenToShortString()
-                + " --display " + newDisplay.mId;
-        executeShellCommand(startCmd);
-
-        mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                focusedStack.mDisplayId);
-
-        // Launch another activity with third different uid from app on secondary display and check
-        // it is launched on secondary display.
-        final String targetActivity = " --es target_activity " + THIRD_ACTIVITY.getShortClassName()
-                + " --es package_name " + THIRD_ACTIVITY.getPackageName()
-                + " --ei display_id " + newDisplay.mId;
-        final String includeStoppedPackagesFlag = " -f 0x00000020";
-        executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
-                + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
-                + targetActivity + includeStoppedPackagesFlag);
-
-        mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY);
-        assertEquals("Activity launched by app on secondary display must be on that display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /** Tests that an activity can launch an activity from a different UID into its own task. */
-    @Test
-    public void testPermissionLaunchMultiUidTask() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
-        // Check that the first activity is launched onto the secondary display
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        ActivityManagerState.ActivityStack frontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY),
-                frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Launch an activity from a different UID into the first activity's task
-        getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY)
-                .execute();
-
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
-        assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
-    }
-
-    /**
-     * Test that launching from display owner is allowed even when the the display owner
-     * doesn't have anything on the display.
-     */
-    @Test
-    public void testPermissionLaunchFromOwner() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-
-        // Launch other activity with different uid on secondary display.
-        final String startCmd =  "am start -n " + SECOND_ACTIVITY.flattenToShortString()
-                + " --display " + newDisplay.mId;
-        executeShellCommand(startCmd);
-
-        mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                focusedStack.mDisplayId);
-
-        // Check that owner uid can launch its own activity on secondary display.
-        final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
-        executeShellCommand("am broadcast -a " + broadcastAction + " -p " + componentName
-                + " --ez launch_activity true --ez new_task true --ez multiple_task true"
-                + " --ei display_id " + newDisplay.mId);
-
-        mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
-        mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
-        assertEquals("Activity launched by owner must be on external display",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    /**
-     * Test that launching from app that is not present on external display and doesn't own it to
-     * that external display is not allowed.
-     */
-    @Test
-    public void testPermissionLaunchFromDifferentApp() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
-                VIRTUAL_DISPLAY_ACTIVITY);
-        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-
-        // Launch activity on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                TEST_ACTIVITY_NAME);
-        final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-        assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                focusedStack.mDisplayId);
-
-        final String logSeparator = clearLogcat();
-
-        // Launch other activity with different uid and check security exception is triggered.
-        final String includeStoppedPackagesFlag = " -f 0x00000020";
-        executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
-                + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
-                + " --ei display_id " + newDisplay.mId
-                + includeStoppedPackagesFlag);
-
-        assertSecurityException("LaunchBroadcastReceiver", logSeparator);
-
-        mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
-                new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-        mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY_NAME);
-        assertEquals("Focused stack must be on secondary display's stack",
-                externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
-    }
-
-    private void assertSecurityException(String component, String logSeparator) throws Exception {
-        int tries = 0;
-        boolean match = false;
-        final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
-        while (tries < 5 && !match) {
-            String[] logs = getDeviceLogsForComponent(component, logSeparator);
-            for (String line : logs) {
-                Matcher m = pattern.matcher(line);
-                if (m.matches()) {
-                    match = true;
-                    break;
-                }
-            }
-            tries++;
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException e) {
-            }
-        }
-
-        assertTrue("Expected exception not found", match);
-    }
-
-    /**
-     * Test that only private virtual display can show content with insecure keyguard.
-     */
-    @Test
-    public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Try to create new show-with-insecure-keyguard public virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this)
-                .setPublicDisplay(true)
-                .setCanShowWithInsecureKeyguard(true)
-                .setMustBeCreated(false)
-                .build();
-
-        // Check that the display is not created.
-        assertNull(newDisplay);
-    }
-
-    /**
-     * Test that all activities that were on the private display are destroyed on display removal.
-     */
-    @FlakyTest(bugId = 63404575)
-    @Presubmit
-    @Test
-    public void testContentDestroyOnDisplayRemoved() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new private virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activities on new secondary display.
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                RESIZEABLE_ACTIVITY_NAME);
-
-        // Destroy the display and check if activities are removed from system.
-        final String logSeparator = clearLogcat();
-        destroyVirtualDisplays();
-        final String activityName1
-                = ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
-        final String activityName2
-                = ActivityManagerTestBase.getActivityComponentName(RESIZEABLE_ACTIVITY_NAME);
-        final String windowName1
-                = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
-        final String windowName2
-                = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
-        mAmWmState.waitForWithAmState(
-                (state) -> !state.containsActivity(activityName1)
-                        && !state.containsActivity(activityName2),
-                "Waiting for activity to be removed");
-        mAmWmState.waitForWithWmState(
-                (state) -> !state.containsWindow(windowName1)
-                        && !state.containsWindow(windowName2),
-                "Waiting for activity window to be gone");
-
-        // Check AM state.
-        assertFalse("Activity from removed display must be destroyed",
-                mAmWmState.getAmState().containsActivity(activityName1));
-        assertFalse("Activity from removed display must be destroyed",
-                mAmWmState.getAmState().containsActivity(activityName2));
-        // Check WM state.
-        assertFalse("Activity windows from removed display must be destroyed",
-                mAmWmState.getWmState().containsWindow(windowName1));
-        assertFalse("Activity windows from removed display must be destroyed",
-                mAmWmState.getWmState().containsWindow(windowName2));
-        // Check activity logs.
-        assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
-        assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
-    }
-
-    /**
-     * Test that the update of display metrics updates all its content.
-     */
-    @Presubmit
-    @Test
-    public void testDisplayResize() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch a resizeable activity on new secondary display.
-        final String initialLogSeparator = clearLogcat();
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                RESIZEABLE_ACTIVITY_NAME);
-
-        // Grab reported sizes and compute new with slight size change.
-        final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
-                initialLogSeparator);
-
-        // Resize the docked stack, so that activity with virtual display will also be resized.
-        final String logSeparator = clearLogcat();
-        executeShellCommand(getResizeVirtualDisplayCommand());
-
-        mAmWmState.waitForWithAmState(amState -> {
-            try {
-                return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
-                        && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
-            } catch (Exception e) {
-                logE("Error waiting for valid state: " + e.getMessage());
-                return false;
-            }
-        }, "Wait for the configuration change to happen and for activity to be resumed.");
-
-        mAmWmState.computeState(false /* compareTaskAndStackBounds */,
-                new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build(),
-                new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
-
-        // Check if activity in virtual display was resized properly.
-        assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
-                1 /* numConfigChange */, logSeparator);
-
-        final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
-                logSeparator);
-        assertTrue(updatedSize.widthDp <= initialSize.widthDp);
-        assertTrue(updatedSize.heightDp <= initialSize.heightDp);
-        assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
-        assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
-    }
-
-    /** Read the number of configuration changes sent to activity from logs. */
-    private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
-        return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
-    }
-
-    /**
-     * Tests that when an activity is launched with displayId specified and there is an existing
-     * matching task on some other display - that task will moved to the target display.
-     */
-    @Test
-    public void testMoveToDisplayOnLaunch() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Launch activity with unique affinity, so it will the only one in its task.
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        // Launch something to that display so that a new stack is created. We need this to be able
-        // to compare task numbers in stacks later.
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-
-        final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
-                .mStacks.size();
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
-                .getTasks().size();
-
-        // Launch activity on new secondary display.
-        // Using custom command here, because normally we add flags Intent#FLAG_ACTIVITY_NEW_TASK
-        // and Intent#FLAG_ACTIVITY_MULTIPLE_TASK when launching on some specific display. We don't
-        // do it here as we want an existing task to be used.
-        final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
-                + " --display " + newDisplay.mId;
-        executeShellCommand(launchCommand);
-        mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
-
-        // Check that activity is brought to front.
-        mAmWmState.assertFocusedActivity("Existing task must be brought to front",
-                LAUNCHING_ACTIVITY);
-        mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
-
-        // Check that activity is on the right display.
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity must be moved to the secondary display",
-                getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Check that task has moved from primary display to secondary.
-        final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
-                .mStacks.size();
-        assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
-                stackNumFinal);
-        final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
-                .getTasks().size();
-        assertEquals("Task number in stack on external display must be incremented.",
-                taskNumOnSecondary + 1, taskNumFinalOnSecondary);
-    }
-
-    /**
-     * Tests that when an activity is launched with displayId specified and there is an existing
-     * matching task on some other display - that task will moved to the target display.
-     */
-    @Test
-    public void testMoveToEmptyDisplayOnLaunch() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Launch activity with unique affinity, so it will the only one in its task.
-        launchActivity(LAUNCHING_ACTIVITY);
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
-                .mStacks.size();
-
-        // Launch activity on new secondary display.
-        // Using custom command here, because normally we add flags Intent#FLAG_ACTIVITY_NEW_TASK
-        // and Intent#FLAG_ACTIVITY_MULTIPLE_TASK when launching on some specific display. We don't
-        // do it here as we want an existing task to be used.
-        final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
-                + " --display " + newDisplay.mId;
-        executeShellCommand(launchCommand);
-        mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
-
-        // Check that activity is brought to front.
-        mAmWmState.assertFocusedActivity("Existing task must be brought to front",
-                LAUNCHING_ACTIVITY);
-        mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
-
-        // Check that activity is on the right display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity must be moved to the secondary display",
-                getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // Check that task has moved from primary display to secondary.
-        final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
-                .mStacks.size();
-        assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
-                stackNumFinal);
-    }
-
-    /**
-     * Tests that when primary display is rotated secondary displays are not affected.
-     */
-    @Test
-    public void testRotationNotAffectingSecondaryScreen() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this)
-                .setResizeDisplay(false)
-                .build();
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
-        // Launch activity on new secondary display.
-        String logSeparator = clearLogcat();
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
-        mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                RESIZEABLE_ACTIVITY_NAME);
-        final ReportedSizes initialSizes = getLastReportedSizesForActivity(
-                RESIZEABLE_ACTIVITY_NAME, logSeparator);
-        assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
-
-        try (final RotationSession rotationSession = new RotationSession()) {
-            // Rotate primary display and check that activity on secondary display is not affected.
-            rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY_NAME);
-
-            // Launch activity to secondary display when primary one is rotated.
-            final int initialRotation = mAmWmState.getWmState().getRotation();
-            rotationSession.set((initialRotation + 1) % 4);
-
-            logSeparator = clearLogcat();
-            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    TEST_ACTIVITY_NAME);
-            final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
-                    TEST_ACTIVITY_NAME, logSeparator);
-            assertEquals(
-                    "Sizes of secondary display must not change after rotation of primary display",
-                    initialSizes, testActivitySizes);
-        }
-    }
-
-    private void rotateAndCheckSameSizes(RotationSession rotationSession, String activityName)
-            throws Exception {
-        for (int rotation = 3; rotation >= 0; --rotation) {
-            final String logSeparator = clearLogcat();
-            rotationSession.set(rotation);
-            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
-                    logSeparator);
-            assertNull("Sizes must not change after rotation", rotatedSizes);
-        }
-    }
-
-    /**
-     * Tests that task affinity does affect what display an activity is launched on but that
-     * matching the task component root does.
-     */
-    @Test
-    public void testTaskMatchAcrossDisplays() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
-        // Check that activity is on the secondary display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
-        mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
-                new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
-
-        // Check that second activity gets launched on the default display despite
-        // the affinity match on the secondary display.
-        final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
-                DEFAULT_DISPLAY_ID);
-        final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
-                mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
-        assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
-                defaultDisplayFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display",
-                defaultDisplayFrontStackId);
-
-        executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
-        mAmWmState.waitForFocusedStack(frontStackId);
-
-        // Check that the third intent is redirected to the first task due to the root
-        // component match on the secondary display.
-        final ActivityManagerState.ActivityStack secondFrontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
-        assertEquals("Focused stack must only contain 1 task",
-                1, secondFrontStack.getTasks().size());
-        assertEquals("Focused task must only contain 1 activity",
-                1, secondFrontStack.getTasks().get(0).mActivities.size());
-    }
-
-    /**
-     * Tests that the task affinity search respects the launch display id.
-     */
-    @Test
-    public void testLaunchDisplayAffinityMatch() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-
-        // Check that activity is on the secondary display.
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
-        executeShellCommand("am start -n "
-                + getActivityComponentName(ALT_LAUNCHING_ACTIVITY)
-                + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
-                + " --display " + newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
-
-        // Check that second activity gets launched into the affinity matching
-        // task on the secondary display
-        final int secondFrontStackId =
-                mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack secondFrontStack =
-                mAmWmState.getAmState().getStackById(secondFrontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
-                secondFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display",
-                secondFrontStackId);
-        assertEquals("Focused stack must only contain 1 task",
-                1, secondFrontStack.getTasks().size());
-        assertEquals("Focused task must contain 2 activities",
-                2, secondFrontStack.getTasks().get(0).mActivities.size());
-    }
-
-    /**
-     * Tests than a new task launched by an activity will end up on that activity's display
-     * even if the focused stack is not on that activity's display.
-     */
-    @Test
-    public void testNewTaskSameDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
-                .build();
-
-        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(BROADCAST_RECEIVER_ACTIVITY).build());
-
-        // Check that the first activity is launched onto the secondary display
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
-                firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
-        executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
-        mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
-                new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
-        // Check that the second activity is launched on the default display
-        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        final ActivityManagerState.ActivityStack focusedStack
-                = mAmWmState.getAmState().getStackById(focusedStackId);
-        assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
-        assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
-                focusedStack.mDisplayId);
-
-        executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
-                + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
-
-        // Check that the third activity ends up in a new task in the same stack as the
-        // first activity
-        mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
-                new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-        final ActivityManagerState.ActivityStack secondFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity must be launched on secondary display",
-                getActivityComponentName(LAUNCHING_ACTIVITY),
-                secondFrontStack.mResumedActivity);
-        assertEquals("Secondary display must contain 2 tasks",
-                2, secondFrontStack.getTasks().size());
-    }
-
-    /**
      * Test that display overrides apply correctly and won't be affected by display changes.
      * This sets overrides to display size and density, initiates a display changed event by locking
      * and unlocking the phone and verifies that overrides are kept.
@@ -1741,401 +120,60 @@
     public void testForceDisplayMetrics() throws Exception {
         launchHomeActivity();
 
-        // Read initial sizes.
-        final ReportedDisplayMetrics originalDisplayMetrics = getDisplayMetrics();
+        try (final DisplayMetricsSession displayMetricsSession = new DisplayMetricsSession()) {
+            // Read initial sizes.
+            final ReportedDisplayMetrics originalDisplayMetrics =
+                    displayMetricsSession.getInitialDisplayMetrics();
 
-        // Apply new override values that don't match the physical metrics.
-        final int overrideWidth = (int) (originalDisplayMetrics.physicalWidth * 1.5);
-        final int overrideHeight = (int) (originalDisplayMetrics.physicalHeight * 1.5);
-        executeShellCommand(WM_SIZE + " " + overrideWidth + "x" + overrideHeight);
-        final int overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
-        executeShellCommand(WM_DENSITY + " " + overrideDensity);
+            // Apply new override values that don't match the physical metrics.
+            final Size overrideSize = new Size(
+                    (int) (originalDisplayMetrics.physicalSize.getWidth() * 1.5),
+                    (int) (originalDisplayMetrics.physicalSize.getHeight() * 1.5));
+            final Integer overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
+            displayMetricsSession.overrideDisplayMetrics(overrideSize, overrideDensity);
 
-        // Check if overrides applied correctly.
-        ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
-        assertEquals(overrideWidth, displayMetrics.overrideWidth);
-        assertEquals(overrideHeight, displayMetrics.overrideHeight);
-        assertEquals(overrideDensity, displayMetrics.overrideDensity);
+            // Check if overrides applied correctly.
+            ReportedDisplayMetrics displayMetrics = displayMetricsSession.getDisplayMetrics();
+            assertEquals(overrideSize, displayMetrics.overrideSize);
+            assertEquals(overrideDensity, displayMetrics.overrideDensity);
 
-        // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
-        // might update the metrics.
-        sleepDevice();
-        wakeUpAndUnlockDevice();
-        mAmWmState.waitForHomeActivityVisible();
+            // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
+            // might update the metrics.
+            sleepDevice();
 
-        // Check if overrides are still applied.
-        displayMetrics = getDisplayMetrics();
-        assertEquals(overrideWidth, displayMetrics.overrideWidth);
-        assertEquals(overrideHeight, displayMetrics.overrideHeight);
-        assertEquals(overrideDensity, displayMetrics.overrideDensity);
+            wakeUpAndUnlockDevice();
+            mAmWmState.waitForHomeActivityVisible();
 
-        // All overrides will be cleared in tearDown.
-    }
-
-    /**
-     * Tests than an immediate launch after new display creation is handled correctly.
-     */
-    @Test
-    public void testImmediateLaunchOnNewDisplay() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Create new virtual display and immediately launch an activity on it.
-        final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this)
-                .setLaunchActivity(TEST_ACTIVITY_NAME).build();
-
-        // Check that activity is launched and placed correctly.
-        mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
-        mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
-                TEST_ACTIVITY_NAME);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-        final ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Activity launched on secondary display must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), firstFrontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-    }
-
-    /**
-     * Tests that turning the primary display off does not affect the activity running
-     * on an external secondary display.
-     */
-    @Test
-    public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Launch something on the primary display so we know there is a resumed activity there
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-
-        final ActivityDisplay newDisplay = createExternalVirtualDisplay(
-                true /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
-        // Check that the activity is launched onto the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
-                "Activity launched on external display must be resumed");
-
-        setPrimaryDisplayState(false);
-
-        // Wait for the fullscreen stack to start sleeping, and then make sure the
-        // test activity is still resumed.
-        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
-                "Activity launched on primary display must be stopped after turning off");
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
-                "Activity launched on external display must be resumed");
-    }
-
-    /**
-     * Tests that an activity can be launched on a secondary display while the primary
-     * display is off.
-     */
-    @Test
-    public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Launch something on the primary display so we know there is a resumed activity there
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-
-        setPrimaryDisplayState(false);
-
-        // Make sure there is no resumed activity when the primary display is off
-        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
-                "Activity launched on primary display must be stopped after turning off");
-        assertEquals("Unexpected resumed activity",
-                0, mAmWmState.getAmState().getResumedActivitiesCount());
-
-        final ActivityDisplay newDisplay = createExternalVirtualDisplay(
-                true /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
-        // Check that the test activity is resumed on the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
-                "Activity launched on external display must be resumed");
-    }
-
-    /**
-     * Tests that turning the secondary display off stops activities running on that display.
-     */
-    @Test
-    public void testExternalDisplayToggleState() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        final ActivityDisplay newDisplay = createExternalVirtualDisplay(
-                false /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
-        // Check that the test activity is resumed on the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
-                "Activity launched on external display must be resumed");
-
-        mExternalDisplayHelper.turnDisplayOff();
-
-        // Check that turning off the external display stops the activity
-        waitAndAssertActivityStopped(TEST_ACTIVITY_NAME,
-                "Activity launched on external display must be stopped after turning off");
-
-        mExternalDisplayHelper.turnDisplayOn();
-
-        // Check that turning on the external display resumes the activity
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
-                "Activity launched on external display must be resumed");
-    }
-
-    /**
-     * Tests that tapping on the primary display after showing the keyguard resumes the
-     * activity on the primary display.
-     */
-    @Test
-    public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        // Launch something on the primary display so we know there is a resumed activity there
-        launchActivity(RESIZEABLE_ACTIVITY_NAME);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-
-        sleepDevice();
-
-        // Make sure there is no resumed activity when the primary display is off
-        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
-                "Activity launched on primary display must be stopped after turning off");
-        assertEquals("Unexpected resumed activity",
-                0, mAmWmState.getAmState().getResumedActivitiesCount());
-
-        final ActivityDisplay newDisplay = createExternalVirtualDisplay(
-                true /* showContentWhenLocked */);
-
-        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
-        // Check that the test activity is resumed on the external display
-        waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
-                "Activity launched on external display must be resumed");
-
-        // Unlock the device and tap on the middle of the primary display
-        wakeUpDevice();
-        executeShellCommand("wm dismiss-keyguard");
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME)
-                .build());
-        final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
-        final int width = displayMetrics.getWidth();
-        final int height = displayMetrics.getHeight();
-        executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
-
-        // Check that the activity on the primary display is resumed
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
-                "Activity launched on primary display must be resumed");
-        assertEquals("Unexpected resumed activity",
-                1, mAmWmState.getAmState().getResumedActivitiesCount());
-    }
-
-    private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
-            throws Exception {
-        mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
-
-        final String fullActivityName = getActivityComponentName(activityName);
-        assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
-        ActivityManagerState.ActivityStack firstFrontStack =
-                mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals(message, fullActivityName, firstFrontStack.mResumedActivity);
-        assertTrue(message,
-                mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
-        mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
-        mAmWmState.assertVisibility(activityName, true /* visible */);
-    }
-
-    private void waitAndAssertActivityStopped(String activityName, String message)
-            throws Exception {
-        mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
-
-        assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
-                STATE_STOPPED));
-    }
-
-    /**
-     * Tests that showWhenLocked works on a secondary display.
-     */
-    public void testSecondaryDisplayShowWhenLocked() throws Exception {
-        assumeTrue(supportsMultiDisplay());
-
-        try {
-            setLockCredential();
-
-            launchActivity(TEST_ACTIVITY_NAME);
-
-            final ActivityDisplay newDisplay = createExternalVirtualDisplay(
-                    false /* showContentWhenLocked */);
-            launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mId);
-
-            gotoKeyguard();
-            mAmWmState.waitForKeyguardShowingAndNotOccluded();
-
-            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
-            mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
-
-            mAmWmState.computeState(new String[] { SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME });
-            assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
-                    .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
-        } finally {
-            tearDownLockCredentials();
+            // Check if overrides are still applied.
+            displayMetrics = displayMetricsSession.getDisplayMetrics();
+            assertEquals(overrideSize, displayMetrics.overrideSize);
+            assertEquals(overrideDensity, displayMetrics.overrideDensity);
         }
     }
 
-    /** Get physical and override display metrics from WM. */
-    private ReportedDisplayMetrics getDisplayMetrics() throws Exception {
-        mDumpLines.clear();
-        final String dump = executeShellCommand(WM_SIZE)
-                + executeShellCommand(WM_DENSITY);
-        mDumpLines.clear();
-        Collections.addAll(mDumpLines, dump.split("\\n"));
-        return ReportedDisplayMetrics.create(mDumpLines);
-    }
+    private static class DisplayMetricsSession implements AutoCloseable {
 
-    private static class ReportedDisplayMetrics {
-        private static final Pattern sPhysicalSizePattern =
-                Pattern.compile("Physical size: (\\d+)x(\\d+)");
-        private static final Pattern sOverrideSizePattern =
-                Pattern.compile("Override size: (\\d+)x(\\d+)");
-        private static final Pattern sPhysicalDensityPattern =
-                Pattern.compile("Physical density: (\\d+)");
-        private static final Pattern sOverrideDensityPattern =
-                Pattern.compile("Override density: (\\d+)");
+        private final ReportedDisplayMetrics mInitialDisplayMetrics;
 
-        int physicalWidth;
-        int physicalHeight;
-        int physicalDensity;
-
-        boolean sizeOverrideSet;
-        int overrideWidth;
-        int overrideHeight;
-        boolean densityOverrideSet;
-        int overrideDensity;
-
-        /** Get width that WM operates with. */
-        int getWidth() {
-            return sizeOverrideSet ? overrideWidth : physicalWidth;
+        DisplayMetricsSession() throws Exception {
+            mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics();
         }
 
-        /** Get height that WM operates with. */
-        int getHeight() {
-            return sizeOverrideSet ? overrideHeight : physicalHeight;
+        ReportedDisplayMetrics getInitialDisplayMetrics() {
+            return mInitialDisplayMetrics;
         }
 
-        /** Get density that WM operates with. */
-        int getDensity() {
-            return densityOverrideSet ? overrideDensity : physicalDensity;
+        ReportedDisplayMetrics getDisplayMetrics() throws Exception {
+            return ReportedDisplayMetrics.getDisplayMetrics();
         }
 
-        static ReportedDisplayMetrics create(LinkedList<String> dump) {
-            final ReportedDisplayMetrics result = new ReportedDisplayMetrics();
-
-            boolean physicalSizeFound = false;
-            boolean physicalDensityFound = false;
-
-            while (!dump.isEmpty()) {
-                final String line = dump.pop().trim();
-
-                Matcher matcher = sPhysicalSizePattern.matcher(line);
-                if (matcher.matches()) {
-                    physicalSizeFound = true;
-                    log(line);
-                    result.physicalWidth = Integer.parseInt(matcher.group(1));
-                    result.physicalHeight = Integer.parseInt(matcher.group(2));
-                    continue;
-                }
-
-                matcher = sOverrideSizePattern.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    result.overrideWidth = Integer.parseInt(matcher.group(1));
-                    result.overrideHeight = Integer.parseInt(matcher.group(2));
-                    result.sizeOverrideSet = true;
-                    continue;
-                }
-
-                matcher = sPhysicalDensityPattern.matcher(line);
-                if (matcher.matches()) {
-                    physicalDensityFound = true;
-                    log(line);
-                    result.physicalDensity = Integer.parseInt(matcher.group(1));
-                    continue;
-                }
-
-                matcher = sOverrideDensityPattern.matcher(line);
-                if (matcher.matches()) {
-                    log(line);
-                    result.overrideDensity = Integer.parseInt(matcher.group(1));
-                    result.densityOverrideSet = true;
-                    continue;
-                }
-            }
-
-            assertTrue("Physical display size must be reported", physicalSizeFound);
-            assertTrue("Physical display density must be reported", physicalDensityFound);
-
-            return result;
+        void overrideDisplayMetrics(final Size size, final int density) {
+            mInitialDisplayMetrics.setDisplayMetrics(size, density);
         }
-    }
 
-    /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
-    private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
-        final ActivityLifecycleCounts lifecycleCounts
-                = new ActivityLifecycleCounts(componentName, logSeparator);
-        if (lifecycleCounts.mDestroyCount != 0) {
-            fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
-                    + " time(s), wasn't expecting any");
-        } else if (lifecycleCounts.mCreateCount != 0) {
-            fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
-                    + " time(s), wasn't expecting any");
-        } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
-            fail(componentName + " has received "
-                    + lifecycleCounts.mConfigurationChangedCount
-                    + " onConfigurationChanged() calls, expecting " + 1);
-        } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
-            fail(componentName + " has received "
-                    + lifecycleCounts.mMovedToDisplayCount
-                    + " onMovedToDisplay() calls, expecting " + 1);
+        @Override
+        public void close() throws Exception {
+            mInitialDisplayMetrics.restoreDisplayMetrics();
         }
     }
-
-    private static String getResizeVirtualDisplayCommand() {
-        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
-                " --es command resize_display";
-    }
-
-    /**
-     * Creates a private virtual display with the external and show with insecure
-     * keyguard flags set.
-     */
-    private ActivityDisplay createExternalVirtualDisplay(boolean showContentWhenLocked)
-            throws Exception {
-        final List<ActivityDisplay> originalDS = getDisplaysStates();
-        final int originalDisplayCount = originalDS.size();
-
-        mExternalDisplayHelper = new DisplayHelper();
-        mExternalDisplayHelper.createAndWaitForDisplay(true /* external */, showContentWhenLocked);
-
-        // Wait for the virtual display to be created and get configurations.
-        final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
-        assertEquals("New virtual display must be created", originalDisplayCount + 1, ds.size());
-
-        // Find the newly added display.
-        final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
-        return newDisplays.get(0);
-    }
-
-    /** Turns the primary display on/off by pressing the power key */
-    private void setPrimaryDisplayState(boolean wantOn) {
-        // Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
-        int keycode = wantOn ? 224 : 223;
-        executeShellCommand("input keyevent " + keycode);
-        DisplayHelper.waitForDefaultDisplayState(wantOn);
-    }
 }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
new file mode 100644
index 0000000..5ea0d083
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -0,0 +1,1800 @@
+/*
+ * 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.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics
+        .getDisplayMetrics;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.server.am.displayservice.DisplayHelper;
+import android.support.annotation.Nullable;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests
+ */
+@FlakyTest(bugId = 71792368, detail = "Might be affecting tests that run after. See bug for details")
+public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase {
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+    private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
+    private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
+    private static final String SECOND_PACKAGE = "android.server.am.second";
+    private static final String THIRD_PACKAGE = "android.server.am.third";
+    private static final ComponentName SECOND_ACTIVITY = ComponentName.createRelative(
+            SECOND_PACKAGE, ".SecondActivity");
+    private static final ComponentName SECOND_NO_EMBEDDING_ACTIVITY = ComponentName.createRelative(
+            SECOND_PACKAGE, ".SecondActivityNoEmbedding");
+    private static final ComponentName LAUNCH_BROADCAST_RECEIVER = ComponentName.createRelative(
+            SECOND_PACKAGE, ".LaunchBroadcastReceiver");
+    /** See AndroidManifest.xml of appSecondUid. */
+    private static final String LAUNCH_BROADCAST_ACTION =
+            SECOND_PACKAGE + ".LAUNCH_BROADCAST_ACTION";
+    private static final ComponentName THIRD_ACTIVITY = ComponentName.createRelative(
+            THIRD_PACKAGE, ".ThirdActivity");
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(supportsMultiDisplay());
+    }
+
+    /**
+     * Tests launching an activity on virtual display.
+     */
+    @Presubmit
+    @Test
+    public void testLaunchActivityOnSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            final String logSeparator = clearLogcat();
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the secondary display and resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Check that activity config corresponds to display config.
+            final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
+                    logSeparator);
+            assertEquals("Activity launched on secondary display must have proper configuration",
+                    CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display. It should land on the
+     * default display.
+     */
+    @Test
+    public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    NON_RESIZEABLE_ACTIVITY_NAME);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display while split-screen is active
+     * on the primary display. It should land on the primary display and dismiss docked stack.
+     */
+    @Test
+    public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Start launching activity.
+        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setLaunchInSplitScreen(true)
+                    .createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    NON_RESIZEABLE_ACTIVITY_NAME);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+            mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        }
+    }
+
+    /**
+     * Tests moving a non-resizeable activity to a virtual display. It should stay on the default
+     * display with no action performed.
+     */
+    @Test
+    public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            // Launch a non-resizeable activity on a primary display.
+            launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
+            // Launch a resizeable activity on new secondary display to create a new stack there.
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            final int externalFrontStackId = mAmWmState.getAmState()
+                    .getFrontStackId(newDisplay.mId);
+
+            // Try to move the non-resizeable activity to new secondary display.
+            moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, externalFrontStackId);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    RESIZEABLE_ACTIVITY_NAME);
+
+            // Check that activity is in the same stack
+            final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(
+                    DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack defaultFrontStack =
+                    mAmWmState.getAmState().getStackById(defaultFrontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    defaultFrontStack.getTopTask().mRealActivity);
+            mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
+                    externalFrontStackId);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display from activity there. It should
+     * land on the secondary display based on the resizeability of the root activity of the task.
+     */
+    @Test
+    public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    BROADCAST_RECEIVER_ACTIVITY);
+
+            // Check that launching activity is on the secondary display.
+            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the secondary display and resumed",
+                    getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+
+            // Launch non-resizeable activity from secondary display.
+            executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+                    + "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+            // Check that non-resizeable activity is on the secondary display, because of the
+            // resizeable root of the task.
+            frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the primary display and resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+        }
+    }
+
+    /**
+     * Tests launching a non-resizeable activity on virtual display from activity there. It should
+     * land on some different suitable display (usually - on the default one).
+     */
+    @Test
+    public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    LAUNCHING_ACTIVITY);
+
+            // Check that launching activity is on the secondary display.
+            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be on the secondary display and resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+
+            // Launch non-resizeable activity from secondary display.
+            getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
+                    .setNewTask(true).setMultipleTask(true).execute();
+
+            // Check that non-resizeable activity is on the primary display.
+            frontStackId = mAmWmState.getAmState().getFocusedStackId();
+            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+            assertFalse("Launched activity must be on a different display",
+                    newDisplay.mId == frontStack.mDisplayId);
+            assertEquals("Launched activity must be resumed",
+                    getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on a just launched activity",
+                    frontStackId);
+        }
+    }
+
+    /**
+     * Tests launching an activity on a virtual display without special permission must not be
+     * allowed.
+     */
+    @Test
+    public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            final String logSeparator = clearLogcat();
+
+            // Try to launch an activity and check it security exception was triggered.
+            final String broadcastTarget = "-a " + LAUNCH_BROADCAST_ACTION
+                    + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName();
+            final String includeStoppedPackagesFlag = " -f 0x00000020";
+            executeShellCommand("am broadcast " + broadcastTarget
+                    + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
+                    + " --es package_name " + componentName
+                    + " --ei display_id " + newDisplay.mId
+                    + includeStoppedPackagesFlag);
+
+            assertSecurityException("LaunchBroadcastReceiver", logSeparator);
+
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            assertFalse("Restricted activity must not be launched",
+                    mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
+        }
+    }
+
+    /**
+     * Tests launching an activity on a virtual display without special permission must be allowed
+     * for activities with same UID.
+     */
+    @Test
+    public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Try to launch an activity and check it security exception was triggered.
+            final String broadcastTarget = "-a " + componentName + ".LAUNCH_BROADCAST_ACTION"
+                    + " -p " + componentName;
+            executeShellCommand("am broadcast " + broadcastTarget
+                    + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
+                    + " --es package_name " + componentName
+                    + " --ei display_id " + newDisplay.mId);
+
+            mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
+
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack =
+                    mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
+                    TEST_ACTIVITY_NAME);
+            assertEquals("Activity launched by owner must be on external display",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity via shell
+     * command and without specifying the display id - the second activity must appear on the
+     * primary display.
+     */
+    @Presubmit
+    @Test
+    public void testConsequentLaunchActivity() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            // Launch second activity without specifying display.
+            launchActivity(LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    LAUNCHING_ACTIVITY);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack frontStack
+                    = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
+            assertEquals("Front stack must be on primary display",
+                    DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests launching an activity on simulated display and then launching another activity from the
+     * first one - it must appear on the secondary display, because it was launched from there.
+     */
+    @FlakyTest(bugId = 71564456)
+    @Presubmit
+    @Test
+    public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be resumed",
+                    LAUNCHING_ACTIVITY);
+
+            // Launch second activity from app on secondary display without specifying display id.
+            getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            // Check that activity is launched in focused stack on external display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack frontStack
+                    = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity from the
+     * first one - it must appear on the secondary display, because it was launched from there.
+     */
+    @Test
+    public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(LAUNCHING_ACTIVITY);
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be resumed",
+                    LAUNCHING_ACTIVITY);
+
+            // Launch second activity from app on secondary display without specifying display id.
+            getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
+            mAmWmState.computeState(TEST_ACTIVITY_NAME);
+
+            // Check that activity is launched in focused stack on external display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState()
+                    .getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity from the
+     * first one with specifying the target display - it must appear on the secondary display.
+     */
+    @Test
+    public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(LAUNCHING_ACTIVITY);
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be resumed",
+                    LAUNCHING_ACTIVITY);
+
+            // Launch second activity from app on secondary display specifying same display id.
+            getLaunchActivityBuilder()
+                    .setTargetActivity(SECOND_ACTIVITY)
+                    .setDisplayId(newDisplay.mId)
+                    .execute();
+            mAmWmState.computeState(TEST_ACTIVITY_NAME);
+
+            // Check that activity is launched in focused stack on external display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY);
+            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    SECOND_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
+
+            // Launch other activity with different uid and check if it has launched successfully.
+            getLaunchActivityBuilder()
+                    .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
+                    .setDisplayId(newDisplay.mId)
+                    .setTargetActivity(THIRD_ACTIVITY)
+                    .execute();
+            mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
+
+            // Check that activity is launched in focused stack on external display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY);
+            frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    THIRD_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching an activity on virtual display and then launching another activity that
+     * doesn't allow embedding - it should fail with security exception.
+     */
+    @Test
+    public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(LAUNCHING_ACTIVITY);
+
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be resumed",
+                    LAUNCHING_ACTIVITY);
+
+            final String logSeparator = clearLogcat();
+
+            // Launch second activity from app on secondary display specifying same display id.
+            getLaunchActivityBuilder()
+                    .setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
+                    .setDisplayId(newDisplay.mId)
+                    .execute();
+
+            assertSecurityException("ActivityLauncher", logSeparator);
+        }
+    }
+
+    /**
+     * Tests launching an activity to secondary display from activity on primary display.
+     */
+    @Test
+    public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
+        // Start launching activity.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // Launch activity on secondary display from the app on primary display.
+            getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
+                    .setDisplayId(newDisplay.mId).execute();
+
+            // Check that activity is launched on external display.
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Launched activity must be resumed in front stack",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        }
+    }
+
+    /**
+     * Tests launching activities on secondary and then on primary display to see if the stack
+     * visibility is not affected.
+     */
+    @Presubmit
+    @Test
+    public void testLaunchActivitiesAffectsVisibility() throws Exception {
+        // Start launching activity.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on primary display and check if it doesn't affect activity on
+            // secondary display.
+            getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
+            mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY_NAME);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+        }
+    }
+
+    /**
+     * Test that move-task works when moving between displays.
+     */
+    @Presubmit
+    @Test
+    public void testMoveTaskBetweenDisplays() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+                    defaultDisplayStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Focused stack must be on secondary display",
+                    newDisplay.mId, focusedStack.mDisplayId);
+
+            // Move activity from secondary display to primary.
+            moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
+            mAmWmState.waitForFocusedStack(defaultDisplayStackId);
+            mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
+            focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version launches virtual display creator to fullscreen stack in split-screen.
+     */
+    @FlakyTest(bugId = 69573940)
+    @Presubmit
+    @Test
+    public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Start launching activity into docked stack.
+        launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version launches virtual display creator to docked stack in split-screen.
+     */
+    @Test
+    public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
+        assumeTrue(supportsSplitScreenMultiWindow());
+
+        // Setup split-screen.
+        launchActivitiesInSplitScreen(RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY);
+        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version works without split-screen.
+     */
+    @Test
+    public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
+        // Start an activity on default display to determine default stack.
+        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+        final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode(
+                DEFAULT_DISPLAY_ID);
+        // Finish probing activity.
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+        tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
+                focusedStackWindowingMode);
+    }
+
+    /**
+     * Create a virtual display, launch a test activity there, destroy the display and check if test
+     * activity is moved to a stack on the default display.
+     */
+    private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
+            throws Exception {
+        String logSeparator;
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession
+                    .setPublicDisplay(true)
+                    .setLaunchInSplitScreen(splitScreen)
+                    .createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            if (splitScreen) {
+                mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+            }
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Destroy virtual display.
+            logSeparator = clearLogcat();
+        }
+
+        assertActivityLifecycle(TEST_ACTIVITY_NAME, false /* relaunched */, logSeparator);
+        mAmWmState.waitForValidState(TEST_ACTIVITY_NAME, windowingMode, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertSanity();
+        mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
+
+        // Check if the focus is switched back to primary display.
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+        mAmWmState.assertFocusedStack(
+                "Default stack on primary display must be focused after display removed",
+                windowingMode, ACTIVITY_TYPE_STANDARD);
+        mAmWmState.assertFocusedActivity(
+                "Focus must be switched back to activity on primary display",
+                TEST_ACTIVITY_NAME);
+    }
+
+    /**
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     */
+    @Test
+    public void testStackFocusSwitchOnStackEmptied() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    BROADCAST_RECEIVER_ACTIVITY);
+
+            // Lock the device, so that activity containers will be detached.
+            sleepDevice();
+
+            // Finish activity on secondary display.
+            executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+            // Unlock and check if the focus is switched back to primary display.
+            wakeUpAndUnlockDevice();
+            mAmWmState.waitForFocusedStack(focusedStackId);
+            mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+        }
+    }
+
+    /**
+     * Tests that input events on the primary display take focus from the virtual display.
+     */
+    @Test
+    public void testStackFocusSwitchOnTouchEvent() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            mAmWmState.computeState(new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            mAmWmState.assertFocusedActivity(
+                    "Activity launched on secondary display must be focused",
+                    TEST_ACTIVITY_NAME);
+
+            final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
+            final int width = displayMetrics.getSize().getWidth();
+            final int height = displayMetrics.getSize().getHeight();
+            executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
+
+            mAmWmState.computeState(new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+        }
+    }
+
+    /** Test that shell is allowed to launch on secondary displays. */
+    @Test
+    public void testPermissionLaunchFromShell() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+                    defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            // Launch other activity with different uid and check it is launched on dynamic stack on
+            // secondary display.
+            final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+                            + " --display " + newDisplay.mId;
+            executeShellCommand(startCmd);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
+            mAmWmState.assertFocusedActivity(
+                    "Focus must be on newly launched app", SECOND_ACTIVITY);
+            assertEquals("Activity launched by system must be on external display",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    /** Test that launching from app that is on external display is allowed. */
+    @Test
+    public void testPermissionLaunchFromAppOnSecondary() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            // Launch activity with different uid on secondary display.
+            final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+                    + " --display " + newDisplay.mId;
+            executeShellCommand(startCmd);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
+            mAmWmState.assertFocusedActivity(
+                    "Focus must be on newly launched app", SECOND_ACTIVITY);
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            // Launch another activity with third different uid from app on secondary display and
+            // check it is launched on secondary display.
+            final String targetActivity =
+                    " --es target_activity " + THIRD_ACTIVITY.getShortClassName()
+                    + " --es package_name " + THIRD_ACTIVITY.getPackageName()
+                    + " --ei display_id " + newDisplay.mId;
+            final String includeStoppedPackagesFlag = " -f 0x00000020";
+            executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
+                    + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
+                    + targetActivity + includeStoppedPackagesFlag);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
+            mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY);
+            assertEquals("Activity launched by app on secondary display must be on that display",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    /** Tests that an activity can launch an activity from a different UID into its own task. */
+    @Test
+    public void testPermissionLaunchMultiUidTask() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Check that the first activity is launched onto the secondary display
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState().getStackById(
+                    frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    frontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Launch an activity from a different UID into the first activity's task
+            getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY).execute();
+
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+            mAmWmState.assertFocusedActivity(
+                    "Focus must be on newly launched app", SECOND_ACTIVITY);
+            assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
+        }
+    }
+
+    /**
+     * Test that launching from display owner is allowed even when the the display owner
+     * doesn't have anything on the display.
+     */
+    @Test
+    public void testPermissionLaunchFromOwner() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch other activity with different uid on secondary display.
+            final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+                    + " --display " + newDisplay.mId;
+            executeShellCommand(startCmd);
+
+            mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
+            mAmWmState.assertFocusedActivity(
+                    "Focus must be on newly launched app", SECOND_ACTIVITY);
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            // Check that owner uid can launch its own activity on secondary display.
+            final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
+            executeShellCommand("am broadcast -a " + broadcastAction + " -p " + componentName
+                    + " --ez launch_activity true --ez new_task true --ez multiple_task true"
+                    + " --ei display_id " + newDisplay.mId);
+
+            mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
+            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
+                    TEST_ACTIVITY_NAME);
+            assertEquals("Activity launched by owner must be on external display",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    /**
+     * Test that launching from app that is not present on external display and doesn't own it to
+     * that external display is not allowed.
+     */
+    @Test
+    public void testPermissionLaunchFromDifferentApp() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+                    VIRTUAL_DISPLAY_ACTIVITY);
+            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+                    defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            // Launch activity on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    TEST_ACTIVITY_NAME);
+            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+                    focusedStack.mDisplayId);
+
+            final String logSeparator = clearLogcat();
+
+            // Launch other activity with different uid and check security exception is triggered.
+            final String includeStoppedPackagesFlag = " -f 0x00000020";
+            executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
+                    + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
+                    + " --ei display_id " + newDisplay.mId
+                    + includeStoppedPackagesFlag);
+
+            assertSecurityException("LaunchBroadcastReceiver", logSeparator);
+
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY_NAME);
+            assertEquals("Focused stack must be on secondary display's stack",
+                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+        }
+    }
+
+    private void assertSecurityException(String component, String logSeparator) throws Exception {
+        int tries = 0;
+        boolean match = false;
+        final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
+        while (tries < 5 && !match) {
+            String[] logs = getDeviceLogsForComponent(component, logSeparator);
+            for (String line : logs) {
+                Matcher m = pattern.matcher(line);
+                if (m.matches()) {
+                    match = true;
+                    break;
+                }
+            }
+            tries++;
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+            }
+        }
+
+        assertTrue("Expected exception not found", match);
+    }
+
+    /**
+     * Test that only private virtual display can show content with insecure keyguard.
+     */
+    @Test
+    public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Try to create new show-with-insecure-keyguard public virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession
+                    .setPublicDisplay(true)
+                    .setCanShowWithInsecureKeyguard(true)
+                    .setMustBeCreated(false)
+                    .createDisplay();
+
+            // Check that the display is not created.
+            assertNull(newDisplay);
+        }
+    }
+
+    /**
+     * Test that all activities that were on the private display are destroyed on display removal.
+     */
+    @FlakyTest(bugId = 63404575)
+    @Presubmit
+    @Test
+    public void testContentDestroyOnDisplayRemoved() throws Exception {
+        String logSeparator;
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new private virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activities on new secondary display.
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    TEST_ACTIVITY_NAME);
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    RESIZEABLE_ACTIVITY_NAME);
+
+            // Destroy the display and check if activities are removed from system.
+            logSeparator = clearLogcat();
+        }
+
+        final String activityName1 = ActivityManagerTestBase.getActivityComponentName(
+                TEST_ACTIVITY_NAME);
+        final String activityName2 = ActivityManagerTestBase.getActivityComponentName(
+                RESIZEABLE_ACTIVITY_NAME);
+        final String windowName1 = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
+        final String windowName2 = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
+        mAmWmState.waitForWithAmState(
+                (state) -> !state.containsActivity(activityName1)
+                        && !state.containsActivity(activityName2),
+                "Waiting for activity to be removed");
+        mAmWmState.waitForWithWmState(
+                (state) -> !state.containsWindow(windowName1)
+                        && !state.containsWindow(windowName2),
+                "Waiting for activity window to be gone");
+
+        // Check AM state.
+        assertFalse("Activity from removed display must be destroyed",
+                mAmWmState.getAmState().containsActivity(activityName1));
+        assertFalse("Activity from removed display must be destroyed",
+                mAmWmState.getAmState().containsActivity(activityName2));
+        // Check WM state.
+        assertFalse("Activity windows from removed display must be destroyed",
+                mAmWmState.getWmState().containsWindow(windowName1));
+        assertFalse("Activity windows from removed display must be destroyed",
+                mAmWmState.getWmState().containsWindow(windowName2));
+        // Check activity logs.
+        assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
+        assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
+    }
+
+    /**
+     * Test that the update of display metrics updates all its content.
+     */
+    @Presubmit
+    @Test
+    public void testDisplayResize() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch a resizeable activity on new secondary display.
+            final String initialLogSeparator = clearLogcat();
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    RESIZEABLE_ACTIVITY_NAME);
+
+            // Grab reported sizes and compute new with slight size change.
+            final ReportedSizes initialSize = getLastReportedSizesForActivity(
+                    RESIZEABLE_ACTIVITY_NAME,
+                    initialLogSeparator);
+
+            // Resize the docked stack, so that activity with virtual display will also be resized.
+            final String logSeparator = clearLogcat();
+            executeShellCommand(getResizeVirtualDisplayCommand());
+
+            mAmWmState.waitForWithAmState(amState -> {
+                try {
+                    return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
+                            && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
+                } catch (Exception e) {
+                    logE("Error waiting for valid state: " + e.getMessage());
+                    return false;
+                }
+            }, "Wait for the configuration change to happen and for activity to be resumed.");
+
+            mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+                    new WaitForValidActivityState(RESIZEABLE_ACTIVITY_NAME),
+                    new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
+
+            // Check if activity in virtual display was resized properly.
+            assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
+                    1 /* numConfigChange */, logSeparator);
+
+            final ReportedSizes updatedSize = getLastReportedSizesForActivity(
+                    RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
+            assertTrue(updatedSize.widthDp <= initialSize.widthDp);
+            assertTrue(updatedSize.heightDp <= initialSize.heightDp);
+            assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
+            assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
+        }
+    }
+
+    /** Read the number of configuration changes sent to activity from logs. */
+    private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
+        return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
+    }
+
+    /**
+     * Tests that when an activity is launched with displayId specified and there is an existing
+     * matching task on some other display - that task will moved to the target display.
+     */
+    @Test
+    public void testMoveToDisplayOnLaunch() throws Exception {
+        // Launch activity with unique affinity, so it will the only one in its task.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+            // Launch something to that display so that a new stack is created. We need this to be
+            // able to compare task numbers in stacks later.
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+
+            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
+                    .getTasks().size();
+
+            // Launch activity on new secondary display.
+            // Using custom command here, because normally we add flags
+            // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
+            // when launching on some specific display. We don't do it here as we want an existing
+            // task to be used.
+            final String launchCommand = "am start -n " + getActivityComponentName(
+                    LAUNCHING_ACTIVITY)
+                    + " --display " + newDisplay.mId;
+            executeShellCommand(launchCommand);
+            mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
+
+            // Check that activity is brought to front.
+            mAmWmState.assertFocusedActivity("Existing task must be brought to front",
+                    LAUNCHING_ACTIVITY);
+            mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
+
+            // Check that activity is on the right display.
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity must be moved to the secondary display",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Check that task has moved from primary display to secondary.
+            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+            assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
+                    stackNumFinal);
+            final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
+                    .getTasks().size();
+            assertEquals("Task number in stack on external display must be incremented.",
+                    taskNumOnSecondary + 1, taskNumFinalOnSecondary);
+        }
+    }
+
+    /**
+     * Tests that when an activity is launched with displayId specified and there is an existing
+     * matching task on some other display - that task will moved to the target display.
+     */
+    @Test
+    public void testMoveToEmptyDisplayOnLaunch() throws Exception {
+        // Launch activity with unique affinity, so it will the only one in its task.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+
+            // Launch activity on new secondary display.
+            // Using custom command here, because normally we add flags
+            // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
+            // when launching on some specific display. We don't do it here as we want an existing
+            // task to be used.
+            final String launchCommand = "am start -n " + getActivityComponentName(
+                    LAUNCHING_ACTIVITY)
+                    + " --display " + newDisplay.mId;
+            executeShellCommand(launchCommand);
+            mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
+
+            // Check that activity is brought to front.
+            mAmWmState.assertFocusedActivity("Existing task must be brought to front",
+                    LAUNCHING_ACTIVITY);
+            mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
+
+            // Check that activity is on the right display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity must be moved to the secondary display",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // Check that task has moved from primary display to secondary.
+            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+                    .mStacks.size();
+            assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
+                    stackNumFinal);
+        }
+    }
+
+    /**
+     * Tests that when primary display is rotated secondary displays are not affected.
+     */
+    @Test
+    public void testRotationNotAffectingSecondaryScreen() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setResizeDisplay(false)
+                    .createDisplay();
+            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+            // Launch activity on new secondary display.
+            String logSeparator = clearLogcat();
+            launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                    RESIZEABLE_ACTIVITY_NAME);
+            final ReportedSizes initialSizes = getLastReportedSizesForActivity(
+                    RESIZEABLE_ACTIVITY_NAME, logSeparator);
+            assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
+
+            try (final RotationSession rotationSession = new RotationSession()) {
+                // Rotate primary display and check that activity on secondary display is not
+                // affected.
+
+                rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY_NAME);
+
+                // Launch activity to secondary display when primary one is rotated.
+                final int initialRotation = mAmWmState.getWmState().getRotation();
+                rotationSession.set((initialRotation + 1) % 4);
+
+                logSeparator = clearLogcat();
+                launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+                mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+                mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+                        TEST_ACTIVITY_NAME);
+                final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
+                        TEST_ACTIVITY_NAME, logSeparator);
+                assertEquals(
+                        "Sizes of secondary display must not change after rotation of primary "
+                                + "display",
+                        initialSizes, testActivitySizes);
+            }
+        }
+    }
+
+    private void rotateAndCheckSameSizes(RotationSession rotationSession, String activityName)
+            throws Exception {
+        for (int rotation = 3; rotation >= 0; --rotation) {
+            final String logSeparator = clearLogcat();
+            rotationSession.set(rotation);
+            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
+                    logSeparator);
+            assertNull("Sizes must not change after rotation", rotatedSizes);
+        }
+    }
+
+    /**
+     * Tests that task affinity does affect what display an activity is launched on but that
+     * matching the task component root does.
+     */
+    @Test
+    public void testTaskMatchAcrossDisplays() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Check that activity is on the secondary display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+            // Check that second activity gets launched on the default display despite
+            // the affinity match on the secondary display.
+            final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
+                    DEFAULT_DISPLAY_ID);
+            final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+                    mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
+            assertEquals("Activity launched on default display must be resumed",
+                    getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                    defaultDisplayFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on primary display",
+                    defaultDisplayFrontStackId);
+
+            executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
+            mAmWmState.waitForFocusedStack(frontStackId);
+
+            // Check that the third intent is redirected to the first task due to the root
+            // component match on the secondary display.
+            final ActivityManagerState.ActivityStack secondFrontStack
+                    = mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    secondFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
+            assertEquals("Focused stack must only contain 1 task",
+                    1, secondFrontStack.getTasks().size());
+            assertEquals("Focused task must only contain 1 activity",
+                    1, secondFrontStack.getTasks().get(0).mActivities.size());
+        }
+    }
+
+    /**
+     * Tests that the task affinity search respects the launch display id.
+     */
+    @Test
+    public void testLaunchDisplayAffinityMatch() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+
+            // Check that activity is on the secondary display.
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
+            executeShellCommand("am start -n "
+                    + getActivityComponentName(ALT_LAUNCHING_ACTIVITY)
+                    + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
+                    + " --display " + newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+            // Check that second activity gets launched into the affinity matching
+            // task on the secondary display
+            final int secondFrontStackId =
+                    mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack secondFrontStack =
+                    mAmWmState.getAmState().getStackById(secondFrontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                    secondFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display",
+                    secondFrontStackId);
+            assertEquals("Focused stack must only contain 1 task",
+                    1, secondFrontStack.getTasks().size());
+            assertEquals("Focused task must contain 2 activities",
+                    2, secondFrontStack.getTasks().get(0).mActivities.size());
+        }
+    }
+
+    /**
+     * Tests than a new task launched by an activity will end up on that activity's display
+     * even if the focused stack is not on that activity's display.
+     */
+    @Test
+    public void testNewTaskSameDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+
+            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+            mAmWmState.computeState(new WaitForValidActivityState(BROADCAST_RECEIVER_ACTIVITY));
+
+            // Check that the first activity is launched onto the secondary display
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
+                    firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+            executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+            // Check that the second activity is launched on the default display
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Activity launched on default display must be resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
+            assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
+                    focusedStack.mDisplayId);
+
+            executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+                    + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
+
+            // Check that the third activity ends up in a new task in the same stack as the
+            // first activity
+            mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+                    new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            final ActivityManagerState.ActivityStack secondFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity must be launched on secondary display",
+                    getActivityComponentName(LAUNCHING_ACTIVITY),
+                    secondFrontStack.mResumedActivity);
+            assertEquals("Secondary display must contain 2 tasks",
+                    2, secondFrontStack.getTasks().size());
+        }
+    }
+
+    /**
+     * Tests than an immediate launch after new display creation is handled correctly.
+     */
+    @Test
+    public void testImmediateLaunchOnNewDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new virtual display and immediately launch an activity on it.
+            final ActivityDisplay newDisplay = virtualDisplaySession
+                    .setLaunchActivity(TEST_ACTIVITY_NAME)
+                    .createDisplay();
+
+            // Check that activity is launched and placed correctly.
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+            mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
+                    TEST_ACTIVITY_NAME);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+            final ActivityManagerState.ActivityStack firstFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
+            assertEquals("Activity launched on secondary display must be resumed",
+                    getActivityComponentName(TEST_ACTIVITY_NAME), firstFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+        }
+    }
+
+    /**
+     * Tests that turning the primary display off does not affect the activity running
+     * on an external secondary display.
+     */
+    @Test
+    public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
+        // Launch something on the primary display so we know there is a resumed activity there
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                "Activity launched on primary display must be resumed");
+
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
+             final PrimaryDisplayStateSession displayStateSession =
+                     new PrimaryDisplayStateSession()) {
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the activity is launched onto the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+
+            displayStateSession.turnScreenOff();
+
+            // Wait for the fullscreen stack to start sleeping, and then make sure the
+            // test activity is still resumed.
+            waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                    "Activity launched on primary display must be stopped after turning off");
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+        }
+    }
+
+    /**
+     * Tests that an activity can be launched on a secondary display while the primary
+     * display is off.
+     */
+    @Test
+    public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
+        // Launch something on the primary display so we know there is a resumed activity there
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                "Activity launched on primary display must be resumed");
+
+        try (final PrimaryDisplayStateSession displayStateSession =
+                     new PrimaryDisplayStateSession();
+             final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            displayStateSession.turnScreenOff();
+
+            // Make sure there is no resumed activity when the primary display is off
+            waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                    "Activity launched on primary display must be stopped after turning off");
+            assertEquals("Unexpected resumed activity",
+                    0, mAmWmState.getAmState().getResumedActivitiesCount());
+
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the test activity is resumed on the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+        }
+    }
+
+    /**
+     * Tests that turning the secondary display off stops activities running on that display.
+     */
+    @Test
+    public void testExternalDisplayToggleState() throws Exception {
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the test activity is resumed on the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+
+            externalDisplaySession.turnDisplayOff();
+
+            // Check that turning off the external display stops the activity
+            waitAndAssertActivityStopped(TEST_ACTIVITY_NAME,
+                    "Activity launched on external display must be stopped after turning off");
+
+            externalDisplaySession.turnDisplayOn();
+
+            // Check that turning on the external display resumes the activity
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+        }
+    }
+
+    /**
+     * Tests that tapping on the primary display after showing the keyguard resumes the
+     * activity on the primary display.
+     */
+    @Test
+    public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
+        // Launch something on the primary display so we know there is a resumed activity there
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                "Activity launched on primary display must be resumed");
+
+        sleepDevice();
+
+        // Make sure there is no resumed activity when the primary display is off
+        waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+                "Activity launched on primary display must be stopped after turning off");
+        assertEquals("Unexpected resumed activity",
+                0, mAmWmState.getAmState().getResumedActivitiesCount());
+
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+            launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+            // Check that the test activity is resumed on the external display
+            waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+                    "Activity launched on external display must be resumed");
+
+            // Unlock the device and tap on the middle of the primary display
+            wakeUpDevice();
+            executeShellCommand("wm dismiss-keyguard");
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.waitForValidState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+            final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
+            final int width = displayMetrics.getSize().getWidth();
+            final int height = displayMetrics.getSize().getHeight();
+            executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
+
+            // Check that the activity on the primary display is resumed
+            waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+                    "Activity launched on primary display must be resumed");
+            assertEquals("Unexpected resumed activity",
+                    1, mAmWmState.getAmState().getResumedActivitiesCount());
+        }
+    }
+
+    private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
+            throws Exception {
+        mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
+
+        final String fullActivityName = getActivityComponentName(activityName);
+        assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
+        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
+        ActivityManagerState.ActivityStack firstFrontStack =
+                mAmWmState.getAmState().getStackById(frontStackId);
+        assertEquals(message, fullActivityName, firstFrontStack.mResumedActivity);
+        assertTrue(message,
+                mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
+        mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
+        mAmWmState.assertVisibility(activityName, true /* visible */);
+    }
+
+    private void waitAndAssertActivityStopped(String activityName, String message)
+            throws Exception {
+        mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
+
+        assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
+                STATE_STOPPED));
+    }
+
+    /**
+     * Tests that showWhenLocked works on a secondary display.
+     */
+    public void testSecondaryDisplayShowWhenLocked() throws Exception {
+        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
+             final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+
+            launchActivity(TEST_ACTIVITY_NAME);
+
+            final ActivityDisplay newDisplay =
+                    externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+            launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mId);
+
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+
+            mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+            mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
+
+            mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME);
+            assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
+                    .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
+        }
+    }
+
+    /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
+    private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
+        final ActivityLifecycleCounts lifecycleCounts
+                = new ActivityLifecycleCounts(componentName, logSeparator);
+        if (lifecycleCounts.mDestroyCount != 0) {
+            fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
+                    + " time(s), wasn't expecting any");
+        } else if (lifecycleCounts.mCreateCount != 0) {
+            fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
+                    + " time(s), wasn't expecting any");
+        } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
+            fail(componentName + " has received "
+                    + lifecycleCounts.mConfigurationChangedCount
+                    + " onConfigurationChanged() calls, expecting " + 1);
+        } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
+            fail(componentName + " has received "
+                    + lifecycleCounts.mMovedToDisplayCount
+                    + " onMovedToDisplay() calls, expecting " + 1);
+        }
+    }
+
+    private static String getResizeVirtualDisplayCommand() {
+        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+                " --es command resize_display";
+    }
+
+    private class ExternalDisplaySession implements AutoCloseable {
+
+        @Nullable
+        private DisplayHelper mExternalDisplayHelper;
+
+        /**
+         * Creates a private virtual display with the external and show with insecure
+         * keyguard flags set.
+         */
+        ActivityDisplay createVirtualDisplay(boolean showContentWhenLocked)
+                throws Exception {
+            final List<ActivityDisplay> originalDS = getDisplaysStates();
+            final int originalDisplayCount = originalDS.size();
+
+            mExternalDisplayHelper = new DisplayHelper();
+            mExternalDisplayHelper.createAndWaitForDisplay(true /* external */,
+                    showContentWhenLocked);
+
+            // Wait for the virtual display to be created and get configurations.
+            final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
+            assertEquals("New virtual display must be created", originalDisplayCount + 1,
+                    ds.size());
+
+            // Find the newly added display.
+            final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
+            return newDisplays.get(0);
+        }
+
+        void turnDisplayOff() {
+            if (mExternalDisplayHelper == null) {
+                new RuntimeException("No external display created");
+            }
+            mExternalDisplayHelper.turnDisplayOff();
+        }
+
+        void turnDisplayOn() {
+            if (mExternalDisplayHelper == null) {
+                new RuntimeException("No external display created");
+            }
+            mExternalDisplayHelper.turnDisplayOn();
+        }
+
+        @Override
+        public void close() throws Exception {
+            if (mExternalDisplayHelper != null) {
+                mExternalDisplayHelper.releaseDisplay();
+                mExternalDisplayHelper = null;
+            }
+        }
+    }
+
+    private static class PrimaryDisplayStateSession implements AutoCloseable {
+
+        void turnScreenOff() {
+            setPrimaryDisplayState(false);
+        }
+
+        @Override
+        public void close() throws Exception {
+            setPrimaryDisplayState(true);
+        }
+
+        /** Turns the primary display on/off by pressing the power key */
+        private void setPrimaryDisplayState(boolean wantOn) {
+            // Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
+            int keycode = wantOn ? 224 : 223;
+            executeShellCommand("input keyevent " + keycode);
+            DisplayHelper.waitForDefaultDisplayState(wantOn);
+        }
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index 5ba794d..f5a6a22 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -1237,6 +1237,7 @@
 
     @Presubmit
     @Test
+    @FlakyTest(bugId = 71792368)
     public void testEnterPictureInPictureDiscardSavedPositionOnFinish() throws Exception {
         if (!supportsPip()) return;
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
index 06fd6bf..3187ca7 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
@@ -28,6 +28,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 
 import org.junit.Test;
 
@@ -98,6 +99,7 @@
                 false /*slowStop*/, TRANSIT_TASK_OPEN);
     }
 
+    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_NeitherWallpaper() throws Exception {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -156,6 +158,7 @@
     // Test task close -- bottom task top activity slow in stopping
     // These simulate the case where the bottom activity is resumed
     // before AM receives its activitiyStopped
+    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_NeitherWallpaper_SlowStop() throws Exception {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -201,6 +204,7 @@
                 TRANSIT_TASK_CLOSE);
     }
 
+    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_BottomWallpaper_Translucent() throws Exception {
         testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -262,7 +266,7 @@
         }
         executeShellCommand(bottomStartCmd);
 
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+        mAmWmState.computeState(new WaitForValidActivityState(BOTTOM_ACTIVITY_NAME));
 
         final String topActivityName = topTranslucent ?
                 TRANSLUCENT_TOP_ACTIVITY_NAME : TOP_ACTIVITY_NAME;
@@ -280,9 +284,9 @@
 
         Thread.sleep(5000);
         if (testOpen) {
-            mAmWmState.computeState(new WaitForValidActivityState.Builder(topActivityName).build());
+            mAmWmState.computeState(new WaitForValidActivityState(topActivityName));
         } else {
-            mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+            mAmWmState.computeState(new WaitForValidActivityState(BOTTOM_ACTIVITY_NAME));
         }
 
         assertEquals("Picked wrong transition", expectedTransit,
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
new file mode 100644
index 0000000..3c0a584
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
@@ -0,0 +1,229 @@
+/*
+ * 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.server.am;
+
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.server.am.ActivityManagerState.ActivityDisplay;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsActivityManagerDeviceTestCases:ActivityManagerVrDisplayTests
+ */
+public class ActivityManagerVrDisplayTests extends ActivityManagerDisplayTestBase {
+    private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+    private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
+    private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
+    private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 900;
+    private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        assumeTrue(supportsVrMode());
+    }
+
+    private static class VrModeSession implements AutoCloseable {
+
+        void enablePersistentVrMode() throws Exception {
+            executeShellCommand("setprop vr_virtualdisplay true");
+            executeShellCommand("vr set-persistent-vr-mode-enabled true");
+        }
+
+        @Override
+        public void close() throws Exception {
+            executeShellCommand("vr set-persistent-vr-mode-enabled false");
+            executeShellCommand("setprop vr_virtualdisplay false");
+        }
+    }
+
+    /**
+     * Tests that any new activity launch in Vr mode is in Vr display.
+     */
+    @Test
+    public void testVrActivityLaunch() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        try (final VrModeSession vrModeSession = new VrModeSession()) {
+            // Put the device in persistent vr mode.
+            vrModeSession.enablePersistentVrMode();
+
+            // Launch the VR activity.
+            launchActivity(VR_TEST_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+            mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Launch the non-VR 2D activity and check where it ends up.
+            launchActivity(LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Ensure that the subsequent activity is visible
+            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    LAUNCHING_ACTIVITY);
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Launched activity must be resumed in focused stack",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
+
+            // Check if the launch activity is in Vr virtual display id.
+            final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+            final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+                    VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
+            assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+            // Check if the focused activity is on this virtual stack.
+            assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+                    focusedStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests that any activity already present is re-launched in Vr display in vr mode.
+     */
+    @Test
+    public void testVrActivityReLaunch() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        // Launch a 2D activity.
+        launchActivity(LAUNCHING_ACTIVITY);
+
+        try (final VrModeSession vrModeSession = new VrModeSession()) {
+            // Put the device in persistent vr mode.
+            vrModeSession.enablePersistentVrMode();
+
+            // Launch the VR activity.
+            launchActivity(VR_TEST_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+            mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Re-launch the non-VR 2D activity and check where it ends up.
+            launchActivity(LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+            // Ensure that the subsequent activity is visible
+            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    LAUNCHING_ACTIVITY);
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Launched activity must be resumed in focused stack",
+                    getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
+
+            // Check if the launch activity is in Vr virtual display id.
+            final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+            final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+                    VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
+            assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+            // Check if the focused activity is on this virtual stack.
+            assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+                    focusedStack.mDisplayId);
+        }
+    }
+
+    /**
+     * Tests that any new activity launch post Vr mode is in the main display.
+     */
+    @Test
+    public void testActivityLaunchPostVr() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        try (final VrModeSession vrModeSession = new VrModeSession()) {
+            // Put the device in persistent vr mode.
+            vrModeSession.enablePersistentVrMode();
+
+            // Launch the VR activity.
+            launchActivity(VR_TEST_ACTIVITY_NAME);
+            mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+            mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+            // Launch the non-VR 2D activity and check where it ends up.
+            launchActivity(ALT_LAUNCHING_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+            // Ensure that the subsequent activity is visible
+            mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
+
+            // Check that activity is launched in focused stack on primary display.
+            mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                    ALT_LAUNCHING_ACTIVITY);
+            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityManagerState.ActivityStack focusedStack
+                    = mAmWmState.getAmState().getStackById(focusedStackId);
+            assertEquals("Launched activity must be resumed in focused stack",
+                    getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                    focusedStack.mResumedActivity);
+
+            // Check if the launch activity is in Vr virtual display id.
+            final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+            final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+                    VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT,
+                    VR_VIRTUAL_DISPLAY_DPI);
+            assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+            // Check if the focused activity is on this virtual stack.
+            assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+                    focusedStack.mDisplayId);
+
+        }
+
+        // There isn't a direct launch of activity which can take an user out of persistent VR mode.
+        // This sleep is to account for that delay and let device settle once it comes out of VR
+        // mode.
+        try {
+            Thread.sleep(2000);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // Launch the non-VR 2D activity and check where it ends up.
+        launchActivity(RESIZEABLE_ACTIVITY_NAME);
+        mAmWmState.computeState(new WaitForValidActivityState(RESIZEABLE_ACTIVITY_NAME));
+
+        // Ensure that the subsequent activity is visible
+        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+
+        // Check that activity is launched in focused stack on primary display.
+        mAmWmState.assertFocusedActivity("Launched activity must be focused",
+                RESIZEABLE_ACTIVITY_NAME);
+        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+        final ActivityManagerState.ActivityStack frontStack
+                = mAmWmState.getAmState().getStackById(frontStackId);
+        assertEquals("Launched activity must be resumed in front stack",
+                getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
+        assertEquals("Front stack must be on primary display",
+                DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
index d6c61a4..fd8ed2f 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
@@ -23,6 +23,8 @@
 
 import org.junit.Test;
 
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
 /**
  * Build/Install/Run:
  *     atest CtsActivityManagerDeviceTestCases:AnimationBackgroundTests
@@ -31,7 +33,7 @@
 
     @Test
     public void testAnimationBackground_duringAnimation() throws Exception {
-        launchActivity(LAUNCHING_ACTIVITY);
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY_ID);
         getLaunchActivityBuilder()
                 .setTargetActivityName("AnimationTestActivity")
                 .setWaitForLaunched(false)
@@ -49,7 +51,7 @@
 
     @Test
     public void testAnimationBackground_gone() throws Exception {
-        launchActivity(LAUNCHING_ACTIVITY);
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY_ID);
         getLaunchActivityBuilder().setTargetActivityName("AnimationTestActivity").execute();
         mAmWmState.computeState(new WaitForValidActivityState.Builder("AnimationTestActivity").build());
         assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 55c8ebf..ce22ae7 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -44,164 +44,182 @@
         super.setUp();
 
         assumeTrue(isHandheld());
-
-        setLockCredential();
-    }
-
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-        tearDownLockCredentials();
     }
 
     @Test
     public void testLockAndUnlock() throws Exception {
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        unlockDeviceWithCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.assertKeyguardGone();
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            lockCredentialSession.unlockDeviceWithCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+        }
     }
 
     @Test
     public void testDismissKeyguard() throws Exception {
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        launchActivity("DismissKeyguardActivity");
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.assertKeyguardGone();
-        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivity("DismissKeyguardActivity");
+            lockCredentialSession.enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+        }
     }
 
     @Test
     public void testDismissKeyguard_whileOccluded() throws Exception {
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        launchActivity("DismissKeyguardActivity");
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.assertKeyguardGone();
-        mAmWmState.assertVisibility("DismissKeyguardActivity", true);
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            launchActivity("DismissKeyguardActivity");
+            lockCredentialSession.enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+        }
     }
 
     @Test
     public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
-        enterAndConfirmLockCredential();
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
+            lockCredentialSession.enterAndConfirmLockCredential();
 
-        // Make sure we stay on Keyguard.
-        mAmWmState.assertKeyguardShowingAndOccluded();
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            // Make sure we stay on Keyguard.
+            mAmWmState.assertKeyguardShowingAndOccluded();
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+        }
     }
 
     @Test
     public void testDismissKeyguardActivity_method() throws Exception {
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.computeState();
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("DismissKeyguardMethodActivity");
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardMethodActivity").build());
-        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
-        assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat(logSeparator);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            final String logSeparator = clearLogcat();
+            gotoKeyguard();
+            mAmWmState.computeState();
+            assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+            launchActivity("DismissKeyguardMethodActivity");
+            lockCredentialSession.enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.computeState(new WaitForValidActivityState("DismissKeyguardMethodActivity"));
+            mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
+            assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+            assertOnDismissSucceededInLogcat(logSeparator);
+        }
     }
 
     @Test
     public void testDismissKeyguardActivity_method_cancelled() throws Exception {
-        final String logSeparator = clearLogcat();
-        gotoKeyguard();
-        mAmWmState.computeState();
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity("DismissKeyguardMethodActivity");
-        pressBackButton();
-        assertOnDismissCancelledInLogcat(logSeparator);
-        mAmWmState.computeState();
-        mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        unlockDeviceWithCredential();
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            final String logSeparator = clearLogcat();
+            gotoKeyguard();
+            mAmWmState.computeState();
+            assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+            launchActivity("DismissKeyguardMethodActivity");
+            pressBackButton();
+            assertOnDismissCancelledInLogcat(logSeparator);
+            mAmWmState.computeState();
+            mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
+            assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+            lockCredentialSession.unlockDeviceWithCredential();
+        }
     }
 
     @Test
     public void testEnterPipOverKeyguard() throws Exception {
         assumeTrue(supportsPip());
 
-        // Go to the keyguard
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            // Go to the keyguard
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
 
-        // Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user for
-        // their credentials and does not enter picture-in-picture yet
-        launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.waitForKeyguardShowingAndOccluded();
-        mAmWmState.assertKeyguardShowingAndOccluded();
-        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+            // Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user
+            // for their credentials and does not enter picture-in-picture yet
+            launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
+            executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+            mAmWmState.waitForKeyguardShowingAndOccluded();
+            mAmWmState.assertKeyguardShowingAndOccluded();
+            mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
 
-        // Enter the credentials and ensure that the activity actually entered picture-in-picture
-        enterAndConfirmLockCredential();
-        mAmWmState.waitForKeyguardGone();
-        mAmWmState.assertKeyguardGone();
-        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD);
+            // Enter the credentials and ensure that the activity actually entered picture-in
+            // -picture
+            lockCredentialSession.enterAndConfirmLockCredential();
+            mAmWmState.waitForKeyguardGone();
+            mAmWmState.assertKeyguardGone();
+            mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                    ACTIVITY_TYPE_STANDARD);
+        }
     }
 
     @Test
     public void testShowWhenLockedActivityAndPipActivity() throws Exception {
         assumeTrue(supportsPip());
 
-        launchActivity(PIP_ACTIVITY);
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
-        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            launchActivity(PIP_ACTIVITY);
+            executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+            mAmWmState.computeState(new WaitForValidActivityState(PIP_ACTIVITY));
+            mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                    ACTIVITY_TYPE_STANDARD);
+            mAmWmState.assertVisibility(PIP_ACTIVITY, true);
 
-        launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-        mAmWmState.computeState(
-                new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+            mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
 
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndOccluded();
-        mAmWmState.assertKeyguardShowingAndOccluded();
-        mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndOccluded();
+            mAmWmState.assertKeyguardShowingAndOccluded();
+            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+            mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+        }
     }
 
     @Test
     public void testShowWhenLockedPipActivity() throws Exception {
         assumeTrue(supportsPip());
 
-        launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
-        mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
-        mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD);
-        mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
+            executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+            mAmWmState.computeState(new WaitForValidActivityState(PIP_ACTIVITY));
+            mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+                    ACTIVITY_TYPE_STANDARD);
+            mAmWmState.assertVisibility(PIP_ACTIVITY, true);
 
-        gotoKeyguard();
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertKeyguardShowingAndNotOccluded();
-        mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+            gotoKeyguard();
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertKeyguardShowingAndNotOccluded();
+            mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+        }
     }
 }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index 2c7c554..830615d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import android.platform.test.annotations.Presubmit;
 import android.server.am.WindowManagerState.WindowState;
 
 import org.junit.After;
@@ -50,14 +51,6 @@
         setLockDisabled(false);
     }
 
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-
-        tearDownLockCredentials();
-    }
-
     @Test
     public void testKeyguardHidesActivity() throws Exception {
         launchActivity("TestActivity");
@@ -177,6 +170,7 @@
      * Test that showWhenLocked activity is fullscreen when shown over keyguard
      */
     @Test
+    @Presubmit
     public void testShowWhenLockedActivityWhileSplit() throws Exception {
         assumeTrue(supportsSplitScreenMultiWindow());
 
@@ -251,6 +245,7 @@
         mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
         assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
         assertOnDismissSucceededInLogcat(logSeparator);
+        assertTrue(isDisplayOn());
     }
 
     @Test
@@ -327,16 +322,18 @@
     public void testDismissKeyguardAttrActivity_method_turnScreenOn_withSecureKeyguard() throws Exception {
         final String activityName = "TurnScreenOnAttrDismissKeyguardActivity";
 
-        setLockCredential();
-        sleepDevice();
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
+            sleepDevice();
 
-        mAmWmState.computeState();
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        launchActivity(activityName);
-        mAmWmState.waitForKeyguardShowingAndNotOccluded();
-        mAmWmState.assertVisibility(activityName, false);
-        assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertTrue(isDisplayOn());
+            mAmWmState.computeState();
+            assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+            launchActivity(activityName);
+            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            mAmWmState.assertVisibility(activityName, false);
+            assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+            assertTrue(isDisplayOn());
+        }
     }
 
     @Test
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
index 982f30d..1e39db6 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -25,22 +25,18 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        setLockCredential();
         gotoKeyguard();
     }
 
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        tearDownLockCredentials();
-        super.tearDown();
-    }
-
     @Test
     public void testSingleLaunch() throws Exception {
-        final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
-        waitAndAssertActivityStates(state(activity, STOPPED));
+        try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+            lockCredentialSession.setLockCredential();
 
-        LifecycleVerifier.assertLaunchAndStopSequence(FirstActivity.class, getLifecycleLog());
+            final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+            waitAndAssertActivityStates(state(activity, STOPPED));
+
+            LifecycleVerifier.assertLaunchAndStopSequence(FirstActivity.class, getLifecycleLog());
+        }
     }
 }
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
index ab3e374..7367875 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -248,6 +248,28 @@
         } while (retriesLeft-- > 0);
     }
 
+    /**
+     * Ensures all exiting windows have been removed.
+     */
+    void waitForAllExitingWindows() {
+        int retriesLeft = 5;
+        do {
+            mWmState.computeState();
+            if (mWmState.containsExitingWindow()) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log(e.toString());
+                    // Well I guess we are not waiting...
+                }
+            } else {
+                break;
+            }
+        } while (retriesLeft-- > 0);
+
+        assertFalse(mWmState.containsExitingWindow());
+    }
+
     void waitForAllStoppedActivities() throws Exception {
         int retriesLeft = 5;
         do {
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 7fe707a..16b0c0a 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -129,8 +129,6 @@
     protected ActivityManager mAm;
     protected UiDevice mDevice;
 
-    private boolean mLockCredentialsSet;
-
     private boolean mLockDisabled;
 
     @Deprecated
@@ -274,7 +272,6 @@
         wakeUpAndUnlockDevice();
         pressHomeButton();
         removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
-        mLockCredentialsSet = false;
         mLockDisabled = isLockDisabled();
     }
 
@@ -718,36 +715,52 @@
         mDevice.pressMenu();
     }
 
-    protected void unlockDeviceWithCredential() throws Exception {
-        mDevice.pressMenu();
-        try {
-            Thread.sleep(3000);
-        } catch (InterruptedException e) {
-            //ignored
-        }
-        enterAndConfirmLockCredential();
-    }
-
-    protected void enterAndConfirmLockCredential() throws Exception {
-        mDevice.waitForIdle(3000);
-
-        runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
-        mDevice.pressEnter();
-    }
-
     protected void gotoKeyguard() throws Exception {
         sleepDevice();
         wakeUpDevice();
         mAmWmState.waitForKeyguardShowingAndNotOccluded();
     }
 
-    protected void setLockCredential() {
-        mLockCredentialsSet = true;
-        runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
-    }
+    protected class LockCredentialSession implements AutoCloseable {
+        private boolean mLockCredentialSet;
 
-    protected void removeLockCredential() {
-        runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
+        public LockCredentialSession() {
+            mLockCredentialSet = false;
+        }
+
+        public void setLockCredential() {
+            mLockCredentialSet = true;
+            runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
+        }
+
+        public void unlockDeviceWithCredential() throws Exception {
+            mDevice.pressMenu();
+            enterAndConfirmLockCredential();
+        }
+
+        public void enterAndConfirmLockCredential() throws Exception {
+            mDevice.waitForIdle(3000);
+
+            runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
+            mDevice.pressEnter();
+        }
+
+        private void removeLockCredential() {
+            runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
+            mLockCredentialSet = false;
+        }
+
+        @Override
+        public void close() throws Exception {
+            if (mLockCredentialSet) {
+                removeLockCredential();
+                // Dismiss active keyguard after credential is cleared, so keyguard doesn't ask for
+                // the stale credential.
+                pressBackButton();
+                sleepDevice();
+                wakeUpAndUnlockDevice();
+            }
+        }
     }
 
     /**
@@ -815,7 +828,7 @@
         return INVALID_DEVICE_ROTATION;
     }
 
-    protected String runCommandAndPrintOutput(String command) {
+    protected static String runCommandAndPrintOutput(String command) {
         final String output = executeShellCommand(command);
         log(output);
         return output;
@@ -1492,17 +1505,4 @@
             }
         }
     }
-
-    protected void tearDownLockCredentials() throws Exception {
-        if (!mLockCredentialsSet) {
-            return;
-        }
-
-        removeLockCredential();
-        // Dismiss active keyguard after credential is cleared, so
-        // keyguard doesn't ask for the stale credential.
-        pressBackButton();
-        sleepDevice();
-        wakeUpAndUnlockDevice();
-    }
 }
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
index 140f9a2..1eb855e 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -332,6 +332,16 @@
         }
     }
 
+    public boolean containsExitingWindow() {
+        for (WindowState ws : mWindowStates) {
+            if (ws.isExitingWindow()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     public WindowState getWindowByPackageName(String packageName, int windowType) {
         for (WindowState ws : mWindowStates) {
             final String name = ws.getName();
@@ -711,7 +721,7 @@
             DisplayInfoProto infoProto = proto.displayInfo;
             if (infoProto != null) {
                 mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
-                mAppRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
+                mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
             }
             final DisplayFramesProto displayFramesProto = proto.displayFrames;
             if (displayFramesProto != null) {
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 16a90d6..79c35f1 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -16,7 +16,7 @@
 
 package com.android.cts.mockime;
 
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
 import static com.android.cts.mockime.MockImeSession.MOCK_IME_SETTINGS_FILE;
 
@@ -226,7 +226,19 @@
             final int windowFlags = mSettings.getWindowFlags(0);
             final int windowFlagsMask = mSettings.getWindowFlagsMask(0);
             if (windowFlags != 0 || windowFlagsMask != 0) {
+                final int prevFlags = getWindow().getWindow().getAttributes().flags;
                 getWindow().getWindow().setFlags(windowFlags, windowFlagsMask);
+                // For some reasons, seems that we need to post another requestLayout() when
+                // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS bit is changed.
+                // TODO: Investigate the reason.
+                if ((windowFlagsMask & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+                    final boolean hadFlag = (prevFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+                    final boolean hasFlag = (windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+                    if (hadFlag != hasFlag) {
+                        final View decorView = getWindow().getWindow().getDecorView();
+                        decorView.post(() -> decorView.requestLayout());
+                    }
+                }
             }
 
             if (mSettings.hasNavigationBarColor()) {
@@ -294,24 +306,42 @@
             this.addOnLayoutChangeListener(mLayoutListener);
         }
 
+        private void updateBottomPaddingIfNecessary(int newPaddingBottom) {
+            if (getPaddingBottom() != newPaddingBottom) {
+                setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), newPaddingBottom);
+            }
+        }
+
         @Override
         public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-            final int windowFlags = mSettings.getWindowFlags(0);
-            if ((windowFlags & FLAG_LAYOUT_IN_OVERSCAN) == 0) {
+            if (insets.isConsumed()
+                    || (getSystemUiVisibility() & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) {
+                // In this case we are not interested in consuming NavBar region.
+                // Make sure that the bottom padding is empty.
+                updateBottomPaddingIfNecessary(0);
                 return insets;
             }
 
-            final int insetBottom = insets.getSystemWindowInsetBottom();
+            // In some cases the bottom system window inset is not a navigation bar. Wear devices
+            // that have bottom chin are examples.  For now, assume that it's a navigation bar if it
+            // has the same height as the root window's stable bottom inset.
+            final WindowInsets rootWindowInsets = getRootWindowInsets();
+            if (rootWindowInsets != null && (rootWindowInsets.getStableInsetBottom() !=
+                    insets.getSystemWindowInsetBottom())) {
+                // This is probably not a NavBar.
+                updateBottomPaddingIfNecessary(0);
+                return insets;
+            }
 
-            // Somehow immediately calling setPadding doesn't properly update the layout.
-            // TODO: Figure out why we have to delay this task.
-            post(() -> setPadding(0, 0, 0, insetBottom));
-
-            return insets.replaceSystemWindowInsets(
-                    insets.getSystemWindowInsetLeft(),
-                    insets.getSystemWindowInsetTop(),
-                    insets.getSystemWindowInsetRight(),
-                    0 /* bottom */);
+            final int possibleNavBarHeight = insets.getSystemWindowInsetBottom();
+            updateBottomPaddingIfNecessary(possibleNavBarHeight);
+            return possibleNavBarHeight <= 0
+                    ? insets
+                    : insets.replaceSystemWindowInsets(
+                            insets.getSystemWindowInsetLeft(),
+                            insets.getSystemWindowInsetTop(),
+                            insets.getSystemWindowInsetRight(),
+                            0 /* bottom */);
         }
 
         @Override
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
index 3a9a05e..cccc279 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
@@ -22,6 +22,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.inputmethod.cts.util.LightNavigationBarVerifier.expectLightNavigationBarNotSupported;
+import static android.view.inputmethod.cts.util.LightNavigationBarVerifier.expectLightNavigationBarSupported;
 import static android.view.inputmethod.cts.util.NavigationBarColorVerifier.expectNavigationBarColorNotSupported;
 import static android.view.inputmethod.cts.util.NavigationBarColorVerifier.expectNavigationBarColorSupported;
 import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
@@ -213,17 +214,15 @@
     private ImeSettings.Builder imeSettingForFloatingIme(@ColorInt int navigationBarColor,
             boolean lightNavigationBar) {
         final ImeSettings.Builder builder = new ImeSettings.Builder();
-        // Currently un-setting FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS does nothing for IME windows.
-        // TODO: Fix this anomaly
         builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-        // Although the document says that Window#setNavigationBarColor() requires
-        // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS to work, currently it's not true for IME windows.
-        // TODO: Fix this anomaly
+        // As documented, Window#setNavigationBarColor() is actually ignored when the IME window
+        // does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS.  We are calling setNavigationBarColor()
+        // to ensure it.
         builder.setNavigationBarColor(navigationBarColor);
         if (lightNavigationBar) {
-            // Although the document says that Window#setNavigationBarColor() requires
-            // SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR to work, currently it's not true for IME windows.
-            // TODO: Fix this anomaly
+            // As documented, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is actually ignored when the IME
+            // window does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS.  We set this flag just to
+            // ensure it.
             builder.setInputViewSystemUiVisibility(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
         }
         return builder;
@@ -274,9 +273,8 @@
     public void testSetNavigationBarColor() throws Exception {
         final NavigationBarInfo info = NavigationBarInfo.getInstance();
 
-        // Currently Window#setNavigationBarColor() is ignored for IME windows.
-        // TODO: Support Window#setNavigationBarColor() for IME windows (Bug 25706186)
-        expectNavigationBarColorNotSupported(color ->
+        // Make sure that Window#setNavigationBarColor() works for IMEs.
+        expectNavigationBarColorSupported(color ->
                 getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, false),
                         Color.BLACK, false, info.getBottomNavigationBerHeight(),
                         DimmingTestMode.NO_DIMMING_DIALOG));
@@ -302,16 +300,16 @@
         assumeTrue("This test does not make sense if light navigation bar is not supported"
                 + " even for typical Activity", info.supportsLightNavigationBar());
 
-        // Currently SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is ignored for IME windows.
-        // TODO: Support SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR for IME windows (Bug 69002467)
-        expectLightNavigationBarNotSupported((color, lightMode) ->
+        // Make sure that SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR works for IMEs (Bug 69002467).
+        expectLightNavigationBarSupported((color, lightMode) ->
                 getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
                         Color.BLACK, false, info.getBottomNavigationBerHeight(),
                         DimmingTestMode.NO_DIMMING_DIALOG));
 
-        // Currently there is no way for IMEs to opt-out dark/light navigation bar mode.
-        // TODO: Allows IMEs to opt out dark/light navigation bar mode (Bug 69111208).
-        expectLightNavigationBarNotSupported((color, lightMode) ->
+        // Make sure that IMEs can opt-out navigation bar custom rendering, including
+        // SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, by un-setting FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag
+        // so that it can be controlled by the target application instead (Bug 69111208).
+        expectLightNavigationBarSupported((color, lightMode) ->
                 getNavigationBarBitmap(imeSettingForFloatingIme(Color.BLACK, false),
                         color, lightMode, info.getBottomNavigationBerHeight(),
                         DimmingTestMode.NO_DIMMING_DIALOG));
@@ -325,9 +323,9 @@
                 + " light navigation bar for typical Activities",
                 info.supportsDimmingWindowLightNavigationBarOverride());
 
-        // Currently SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is ignored for IME windows.
-        // TODO: Support SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR for IME windows (Bug 69002467)
-        expectLightNavigationBarNotSupported((color, lightMode) ->
+        // Make sure that SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR works for IMEs, even if a dimming
+        // window is shown behind the IME window.
+        expectLightNavigationBarSupported((color, lightMode) ->
                 getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
                         Color.BLACK, false, info.getBottomNavigationBerHeight(),
                         DimmingTestMode.DIMMING_DIALOG_BEHIND_IME));
diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml
index e5280a4..2e92706 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -31,5 +31,6 @@
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
         <option name="runtime-hint" value="10m"/>
+        <option name="test-timeout" value="600000" />
     </test>
 </configuration>
diff --git a/tests/tests/appwidget/AndroidTest.xml b/tests/tests/appwidget/AndroidTest.xml
index 778a9316..cc4fe42 100644
--- a/tests/tests/appwidget/AndroidTest.xml
+++ b/tests/tests/appwidget/AndroidTest.xml
@@ -22,6 +22,26 @@
         <option name="test-file-name" value="CtsAppWidgetLauncher3.apk" />
         <option name="test-file-name" value="CtsAppWidgetTestCases.apk" />
     </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/cts/widgetprovider/" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/cts/widgetprovider"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsAppWidgetProvider1.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider1.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsAppWidgetProvider2.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider2.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsAppWidgetProvider3.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider3.apk" />
+    </target_preparer>
+
+
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.appwidget.cts" />
         <option name="runtime-hint" value="9m30s" />
diff --git a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
index 954a81b..786b2eb 100644
--- a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
+++ b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
@@ -27,4 +27,6 @@
     public static final String EXTRA_PACKAGE = "PACKAGE_NAME";
     public static final String EXTRA_REQUEST = "REQUEST";
 
+    public static final String ACTION_APPLY_OVERRIDE =
+            "android.appwidget.cts.widgetprovider.APPLY_OVERRIDE";
 }
diff --git a/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java
new file mode 100644
index 0000000..3c29746
--- /dev/null
+++ b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java
@@ -0,0 +1,25 @@
+package android.appwidget.cts.packages;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.appwidget.cts.common.Constants;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+public class SimpleProvider extends AppWidgetProvider {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        super.onReceive(context, intent);
+
+        if (Constants.ACTION_APPLY_OVERRIDE.equals(intent.getAction())) {
+            String request = intent.getStringExtra(Constants.EXTRA_REQUEST);
+            AppWidgetManager.getInstance(context).updateAppWidgetProviderInfo(
+                    new ComponentName(context, SimpleProvider.class),
+                    request);
+            setResultCode(Activity.RESULT_OK);
+        }
+    }
+}
diff --git a/tests/tests/appwidget/packages/widgetprovider/Android.mk b/tests/tests/appwidget/packages/widgetprovider/Android.mk
new file mode 100644
index 0000000..733a4a8
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/Android.mk
@@ -0,0 +1,78 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider1
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+        $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifestV1.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider2
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+        $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifestV2.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider3
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+        $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifestV3.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml
new file mode 100644
index 0000000..77dfc5b
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest package="android.appwidget.cts.widgetprovider" >
+
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml
new file mode 100644
index 0000000..ea23176
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <application>
+        <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_no_config" />
+            <meta-data android:name="my_custom_info"
+                       android:resource="@xml/widget_config" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml
new file mode 100644
index 0000000..e46e160
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <application>
+        <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_no_config" />
+            <meta-data android:name="my_custom_info"
+                       android:resource="@xml/widget_config_no_resize" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml
new file mode 100644
index 0000000..6da740d
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <application>
+        <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_no_config" />
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml b/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml
new file mode 100644
index 0000000..b9db450
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#ff333333"
+    android:elevation="3dp" />
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml
new file mode 100644
index 0000000..f45c476
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="110dp"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/simple_widget"
+    android:resizeMode="horizontal|vertical"
+    android:configure="android.appwidget.cts.widgetprovider.ExampleAppWidgetConfigure"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml
new file mode 100644
index 0000000..428a780
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="110dp"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/simple_widget"
+    android:resizeMode="none"
+    android:configure="android.appwidget.cts.widgetprovider.ExampleAppWidgetConfigure"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml
new file mode 100644
index 0000000..0dacd1c
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="110dp"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/simple_widget"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index acafd7d..c0870f2 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -86,10 +86,6 @@
 
 
     public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // By default we should get only providers for the current user.
         List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
 
@@ -98,10 +94,6 @@
     }
 
     public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We ask only for providers for the current user.
         List<AppWidgetProviderInfo> providers = getAppWidgetManager()
                 .getInstalledProvidersForProfile(Process.myUserHandle());
@@ -111,10 +103,6 @@
     }
 
     public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We ask only for providers for all current user's profiles
         UserManager userManager = (UserManager) getInstrumentation()
                 .getTargetContext().getSystemService(Context.USER_SERVICE);
@@ -135,10 +123,6 @@
     }
 
     public void testBindAppWidget() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // Create a host and start listening.
         AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
         host.deleteHost();
@@ -176,9 +160,6 @@
     }
 
     public void testGetAppWidgetIdsForHost() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         AppWidgetHost host1 = new AppWidgetHost(getInstrumentation().getTargetContext(), 1);
         AppWidgetHost host2 = new AppWidgetHost(getInstrumentation().getTargetContext(), 2);
 
@@ -208,10 +189,6 @@
     }
 
     public void testAppWidgetProviderCallbacks() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         AtomicInteger invocationCounter = new AtomicInteger();
 
         // Set a mock to intercept provider callbacks.
@@ -319,10 +296,6 @@
     }
 
     public void testTwoAppWidgetProviderCallbacks() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         AtomicInteger invocationCounter = new AtomicInteger();
 
         // Set a mock to intercept first provider callbacks.
@@ -414,10 +387,6 @@
     }
 
     public void testGetAppWidgetIdsForProvider() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -465,10 +434,6 @@
     }
 
     public void testGetAppWidgetInfo() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -521,10 +486,6 @@
     }
 
     public void testGetAppWidgetOptions() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -570,10 +531,6 @@
     }
 
     public void testDeleteHost() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -615,10 +572,6 @@
     }
 
     public void testDeleteHosts() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -675,10 +628,6 @@
     }
 
     public void testOnProvidersChanged() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -741,10 +690,6 @@
     }
 
     public void testUpdateAppWidgetViaComponentName() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -835,10 +780,6 @@
     }
 
     public void testUpdateAppWidgetViaWidgetId() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -909,10 +850,6 @@
     }
 
     public void testUpdateAppWidgetViaWidgetIds() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1005,10 +942,6 @@
     }
 
     public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1083,10 +1016,6 @@
     }
 
     public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1197,10 +1126,6 @@
     }
 
     public void testCollectionWidgets() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
-
         // We want to bind widgets.
         grantBindAppWidgetPermission();
 
@@ -1310,9 +1235,6 @@
     }
 
     public void testWidgetFeaturesParsed() throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         assertEquals(0, getFirstAppWidgetProviderInfo().widgetFeatures);
         String packageName = getInstrumentation().getTargetContext().getPackageName();
 
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
index fb0dbd1..b98973a 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
@@ -16,6 +16,8 @@
 
 package android.appwidget.cts;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.appwidget.AppWidgetProviderInfo;
 import android.appwidget.cts.provider.FirstAppWidgetProvider;
 import android.appwidget.cts.provider.SecondAppWidgetProvider;
@@ -36,6 +38,12 @@
     private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
             "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        assumeTrue(hasAppWidgets());
+    }
+
     public boolean hasAppWidgets() {
         return getInstrumentation().getTargetContext().getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
index 019fb66..4f6c657 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
@@ -52,9 +52,6 @@
     }
 
     private void runPinWidgetTest(final String launcherPkg) throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
 
         Context context = getInstrumentation().getContext();
@@ -109,9 +106,6 @@
 
     public void verifyIsRequestPinAppWidgetSupported(String launcherPkg, boolean expectedSupport)
         throws Exception {
-        if (!hasAppWidgets()) {
-            return;
-        }
         setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
 
         Context context = getInstrumentation().getContext();
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java b/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java
new file mode 100644
index 0000000..2ec8cf7
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.appwidget.cts;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.common.Constants;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Process;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class UpdateProviderInfoTest extends AppWidgetTestCase {
+
+    private static final String PROVIDER_PACKAGE = "android.appwidget.cts.widgetprovider";
+    private static final String PROVIDER_CLASS = "android.appwidget.cts.packages.SimpleProvider";
+
+    private static final String APK_PATH = "data/local/tmp/cts/widgetprovider/";
+    private static final String APK_V1 = APK_PATH + "CtsAppWidgetProvider1.apk";
+    private static final String APK_V2 = APK_PATH + "CtsAppWidgetProvider2.apk";
+    private static final String APK_V3 = APK_PATH + "CtsAppWidgetProvider3.apk";
+
+    private static final String EXTRA_CUSTOM_INFO = "my_custom_info";
+
+    private static final int HOST_ID = 42;
+
+    private static final int RETRY_COUNT = 3;
+
+    private CountDownLatch mProviderChangeNotifier;
+    AppWidgetHost mHost;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        uninstallProvider();
+        createHost();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        uninstallProvider();
+
+        if (mHost != null) {
+            mHost.deleteHost();
+        }
+    }
+
+    public void testInfoOverrides() throws Throwable {
+        // On first install the provider does not have any config activity.
+        installApk(APK_V1);
+        assertNull(getProviderInfo().configure);
+
+        // The provider info is updated
+        updateInfo(EXTRA_CUSTOM_INFO);
+        assertNotNull(getProviderInfo().configure);
+
+        // The provider info is updated
+        updateInfo(null);
+        assertNull(getProviderInfo().configure);
+    }
+
+    public void testOverridesPersistedOnUpdate() throws Exception {
+        installApk(APK_V1);
+        assertNull(getProviderInfo().configure);
+
+        updateInfo(EXTRA_CUSTOM_INFO);
+        assertNotNull(getProviderInfo().configure);
+        assertEquals((AppWidgetProviderInfo.RESIZE_BOTH & getProviderInfo().resizeMode),
+                AppWidgetProviderInfo.RESIZE_BOTH);
+
+        // Apk updated, the info is also updated
+        installApk(APK_V2);
+        assertNotNull(getProviderInfo().configure);
+        assertEquals((AppWidgetProviderInfo.RESIZE_BOTH & getProviderInfo().resizeMode), 0);
+
+        // The provider info is reverted
+        updateInfo(null);
+        assertNull(getProviderInfo().configure);
+    }
+
+    public void testOverrideClearedWhenMissingInfo() throws Exception {
+        installApk(APK_V1);
+        assertNull(getProviderInfo().configure);
+
+        updateInfo(EXTRA_CUSTOM_INFO);
+        assertNotNull(getProviderInfo().configure);
+
+        // V3 does not have the custom info definition
+        installApk(APK_V3);
+        assertNull(getProviderInfo().configure);
+    }
+
+    private void createHost() throws Exception {
+        try {
+            runTestOnUiThread(() -> {
+                mHost = new AppWidgetHost(getInstrumentation().getTargetContext(), HOST_ID) {
+
+                    @Override
+                    protected void onProvidersChanged() {
+                        super.onProvidersChanged();
+
+                        if (mProviderChangeNotifier != null) {
+                            mProviderChangeNotifier.countDown();
+                        }
+                    }
+                };
+                mHost.startListening();
+            });
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    private void updateInfo(String key) throws Exception {
+        mProviderChangeNotifier = new CountDownLatch(1);
+        Intent intent = new Intent(Constants.ACTION_APPLY_OVERRIDE)
+                .setComponent(new ComponentName(PROVIDER_PACKAGE, PROVIDER_CLASS))
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                .putExtra(Constants.EXTRA_REQUEST, key);
+        getInstrumentation().getTargetContext().sendBroadcast(intent);
+
+        // Wait until the app widget manager is notified
+        mProviderChangeNotifier.await();
+    }
+
+    private void uninstallProvider() throws Exception {
+        runShellCommand("pm uninstall " + PROVIDER_PACKAGE);
+    }
+
+    private void installApk(String path) throws Exception {
+        mProviderChangeNotifier = new CountDownLatch(1);
+        runShellCommand("pm install -r -d " + path);
+
+        // Wait until the app widget manager is notified
+        mProviderChangeNotifier.await();
+    }
+
+    private AppWidgetProviderInfo getProviderInfo() throws Exception {
+        for (int i = 0; i < RETRY_COUNT; i++) {
+            mProviderChangeNotifier = new CountDownLatch(1);
+            List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(getInstrumentation()
+                    .getTargetContext()).getInstalledProvidersForPackage(
+                    PROVIDER_PACKAGE, Process.myUserHandle());
+
+            if (providers != null && !providers.isEmpty()) {
+                return providers.get(0);
+            }
+
+            // Sometimes it could take time for the info to appear after the apk is just installed
+            mProviderChangeNotifier.await(2, TimeUnit.SECONDS);
+        }
+        return null;
+    }
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
new file mode 100644
index 0000000..372f22c
--- /dev/null
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.carrierapi.cts;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.provider.Telephony.Carriers;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+/**
+ * Build, install and run the tests by running the commands below:
+ *  make cts -j64
+ *  cts-tradefed run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.ApnDatabaseTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ApnDatabaseTest {
+    private static final String TAG = "ApnDatabaseTest";
+
+    private ContentResolver mContentResolver;
+
+    private static final String NAME = "carrierName";
+    private static final String APN = "apn";
+    private static final String PROXY = "proxy";
+    private static final String PORT = "port";
+    private static final String MMSC = "mmsc";
+    private static final String MMSPROXY = "mmsproxy";
+    private static final String MMSPORT = "mmsport";
+    private static final String NUMERIC = "numeric";
+    private static final String USER = "user";
+    private static final String PASSWORD = "password";
+    private static final String AUTH_TYPE = "auth_type";
+    private static final String TYPE = "type";
+    private static final String PROTOCOL = "protocol";
+    private static final String ROAMING_PROTOCOL = "roaming_protocol";
+    private static final String CARRIER_ENABLED = "true";
+    private static final String NETWORK_TYPE_BITMASK = "0";
+    private static final String BEARER = "0";
+
+    private static final Map<String, String> APN_MAP = new HashMap<String,String>() {{
+        put(Carriers.NAME, NAME);
+        put(Carriers.APN, APN);
+        put(Carriers.PROXY, PROXY);
+        put(Carriers.PORT, PORT);
+        put(Carriers.MMSC, MMSC);
+        put(Carriers.MMSPROXY, MMSPROXY);
+        put(Carriers.MMSPORT, MMSPORT);
+        put(Carriers.NUMERIC, NUMERIC);
+        put(Carriers.USER, USER);
+        put(Carriers.PASSWORD, PASSWORD);
+        put(Carriers.AUTH_TYPE, AUTH_TYPE);
+        put(Carriers.TYPE, TYPE);
+        put(Carriers.PROTOCOL, PROTOCOL);
+        put(Carriers.ROAMING_PROTOCOL, ROAMING_PROTOCOL);
+        put(Carriers.CARRIER_ENABLED, CARRIER_ENABLED);
+        put(Carriers.NETWORK_TYPE_BITMASK, NETWORK_TYPE_BITMASK);
+        put(Carriers.BEARER, BEARER);
+    }};
+
+    // Faked network type bitmask and its compatible bearer bitmask.
+    private static final int NETWORK_TYPE_BITMASK_NUMBER = 1 << (13 - 1);
+    private static final int RIL_RADIO_TECHNOLOGY_BITMASK_NUMBER = 1 << (14 - 1);
+
+    @Before
+    public void setUp() throws Exception {
+        mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
+    }
+
+    private void failMessage() {
+        fail("This test requires a SIM card with carrier privilege rule on it.\n" +
+                "Visit https://source.android.com/devices/tech/config/uicc.html");
+    }
+
+    /**
+     * Test inserting, querying, updating and deleting values in carriers table.
+     * Verify that the inserted values match the result of the query and are deleted.
+     */
+    @Test
+    public void testValidCase() {
+        Uri uri = Carriers.CONTENT_URI;
+        // CONTENT_URI = Uri.parse("content://telephony/carriers");
+        // Create A set of column_name/value pairs to add to the database.
+        ContentValues contentValues = makeDefaultContentValues();
+
+        try {
+            // Insert the value into database.
+            Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+            Uri newUri = mContentResolver.insert(uri, contentValues);
+            assertNotNull("Failed to insert to table", newUri);
+
+            // Get the values in table.
+            final String selection = Carriers.NUMERIC + "=?";
+            String[] selectionArgs = { NUMERIC };
+            String[] apnProjection = APN_MAP.keySet().toArray(new String[APN_MAP.size()]);
+            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(apnProjection)
+                    + "\ntestInsertCarriers selection: " + selection
+                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            Cursor cursor = mContentResolver.query(
+                    uri, apnProjection, selection, selectionArgs, null);
+
+            // Verify that the inserted value match the results of the query
+            assertNotNull("Failed to query the table", cursor);
+            assertEquals("Unexpected number of APNs returned by cursor",
+                    1, cursor.getCount());
+            cursor.moveToFirst();
+            for (Map.Entry<String, String> entry: APN_MAP.entrySet()) {
+                assertEquals(
+                        "Unexpected value returned by cursor",
+                        cursor.getString(cursor.getColumnIndex(entry.getKey())), entry.getValue());
+            }
+
+            // update the apn
+            final String newApn = "newapn";
+            Log.d(TAG, "Update the APN field to: " + newApn);
+            contentValues.put(Carriers.APN, newApn);
+            final int updateCount = mContentResolver.update(uri, contentValues, selection,
+                    selectionArgs);
+            assertEquals("Unexpected number of rows updated", 1, updateCount);
+
+            // Verify the updated value
+            cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
+            assertNotNull("Failed to query the table", cursor);
+            assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+            cursor.moveToFirst();
+            assertEquals("Unexpected value returned by cursor",
+                    cursor.getString(cursor.getColumnIndex(Carriers.APN)), newApn);
+
+            // delete test content
+            final String selectionToDelete = Carriers.NUMERIC + "=?";
+            String[] selectionArgsToDelete = { NUMERIC };
+            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            int numRowsDeleted = mContentResolver.delete(
+                    uri, selectionToDelete, selectionArgsToDelete);
+            assertEquals("Unexpected number of rows deleted",1, numRowsDeleted);
+
+            // verify that deleted values are gone
+            cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
+            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+        } catch (SecurityException e) {
+            failMessage();
+        }
+    }
+
+    @Test
+    public void testQueryConflictCase() {
+        String invalidColumn = "random";
+        Uri uri = Carriers.CONTENT_URI;
+        // CONTENT_URI = Uri.parse("content://telephony/carriers");
+        // Create A set of column_name/value pairs to add to the database.
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(Carriers.NAME, NAME);
+        contentValues.put(Carriers.APN, APN);
+        contentValues.put(Carriers.PORT, PORT);
+        contentValues.put(Carriers.PROTOCOL, PROTOCOL);
+        contentValues.put(Carriers.NUMERIC, NUMERIC);
+
+        try {
+            // Insert the value into database.
+            Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+            Uri newUri = mContentResolver.insert(uri, contentValues);
+            assertNotNull("Failed to insert to table", newUri);
+
+            // Try to get the value with invalid selection
+            final String[] testProjection =
+                    {
+                            Carriers.NAME,
+                            Carriers.APN,
+                            Carriers.PORT,
+                            Carriers.PROTOCOL,
+                            Carriers.NUMERIC,
+                    };
+            final String selection = invalidColumn + "=?";
+            String[] selectionArgs = { invalidColumn };
+            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
+                    + "\ntestInsertCarriers selection: " + selection
+                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            Cursor cursor = mContentResolver.query(
+                    uri, testProjection, selection, selectionArgs, null);
+            assertNull("Failed to query the table",cursor);
+
+            // delete test content
+            final String selectionToDelete = Carriers.NAME + "=?";
+            String[] selectionArgsToDelete = { NAME };
+            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            int numRowsDeleted = mContentResolver.delete(
+                    uri, selectionToDelete, selectionArgsToDelete);
+            assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+
+            // verify that deleted values are gone
+            cursor = mContentResolver.query(
+                    uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
+            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+        } catch (SecurityException e) {
+            failMessage();
+        }
+    }
+
+    @Test
+    public void testUpdateConflictCase() {
+        Uri uri = Carriers.CONTENT_URI;
+        // CONTENT_URI = Uri.parse("content://telephony/carriers");
+        // Create A set of column_name/value pairs to add to the database.
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(Carriers.NAME, NAME);
+        contentValues.put(Carriers.APN, APN);
+        contentValues.put(Carriers.PORT, PORT);
+        contentValues.put(Carriers.PROTOCOL, PROTOCOL);
+        contentValues.put(Carriers.NUMERIC, NUMERIC);
+
+        try {
+            // Insert the value into database.
+            Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+            Uri newUri = mContentResolver.insert(uri, contentValues);
+            assertNotNull("Failed to insert to table", newUri);
+
+            // Try to get the value with invalid selection
+            final String[] testProjection =
+                    {
+                            Carriers.NAME,
+                            Carriers.APN,
+                            Carriers.PORT,
+                            Carriers.PROTOCOL,
+                            Carriers.NUMERIC,
+                    };
+            String selection = Carriers.NAME + "=?";
+            String[] selectionArgs = { NAME };
+            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
+                    + "\ntestInsertCarriers selection: " + selection
+                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            Cursor cursor = mContentResolver.query(
+                    uri, testProjection, selection, selectionArgs, null);
+            assertEquals("Unexpected number of APNs returned by cursor",
+                    1, cursor.getCount());
+
+            // Update the table with invalid column
+            String invalidColumn = "random";
+            contentValues.put(invalidColumn, invalidColumn);
+            try {
+                mContentResolver.update(uri, contentValues, selection, selectionArgs);
+                fail();
+            } catch (SQLiteException e) {
+                // Expected: If there's no such a column, an exception will be thrown and the
+                // Activity Manager will kill this process shortly.
+            }
+
+            // delete test content
+            final String selectionToDelete = Carriers.NAME + "=?";
+            String[] selectionArgsToDelete = { NAME };
+            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            int numRowsDeleted = mContentResolver.delete(
+                    uri, selectionToDelete, selectionArgsToDelete);
+            assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+
+            // verify that deleted values are gone
+            cursor = mContentResolver.query(
+                    uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
+            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+        } catch (SecurityException e) {
+            failMessage();
+        }
+    }
+
+    @Test
+    public void testDeleteConflictCase() {
+        String invalidColumn = "random";
+        Uri uri = Carriers.CONTENT_URI;
+        // CONTENT_URI = Uri.parse("content://telephony/carriers");
+        // Create A set of column_name/value pairs to add to the database.
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(Carriers.NAME, NAME);
+        contentValues.put(Carriers.APN, APN);
+        contentValues.put(Carriers.PORT, PORT);
+        contentValues.put(Carriers.PROTOCOL, PROTOCOL);
+        contentValues.put(Carriers.NUMERIC, NUMERIC);
+
+        try {
+            // Insert the value into database.
+            Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+            Uri newUri = mContentResolver.insert(uri, contentValues);
+            assertNotNull("Failed to insert to table", newUri);
+
+            // Get the values in table.
+            final String[] testProjection =
+                    {
+                            Carriers.NAME,
+                            Carriers.APN,
+                            Carriers.PORT,
+                            Carriers.PROTOCOL,
+                            Carriers.NUMERIC,
+                    };
+            String selection = Carriers.NAME + "=?";
+            String[] selectionArgs = { NAME };
+            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
+                    + "\ntestInsertCarriers selection: " + selection
+                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            Cursor cursor = mContentResolver.query(
+                    uri, testProjection, selection, selectionArgs, null);
+            assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+
+            // try to delete with invalid selection
+            String selectionToDelete = invalidColumn + "=?";
+            String[] selectionArgsToDelete = { invalidColumn };
+            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+
+            try {
+                mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+                fail();
+            } catch (SQLiteException e) {
+                // Expected: If there's no such a column, an exception will be thrown and the
+                // Activity Manager will kill this process shortly.
+            }
+
+            // verify that deleted value is still there
+            selection = Carriers.NAME + "=?";
+            selectionArgs[0] = NAME;
+            cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+            assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+
+            // delete test content
+            selectionToDelete = Carriers.NAME + "=?";
+            selectionArgsToDelete[0] = NAME;
+            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            int numRowsDeleted = mContentResolver.delete(
+                    uri, selectionToDelete, selectionArgsToDelete);
+            assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+
+            // verify that deleted values are gone
+            cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+        } catch (SecurityException e) {
+            failMessage();
+        }
+    }
+
+    private ContentValues makeDefaultContentValues() {
+        ContentValues contentValues = new ContentValues();
+        for (Map.Entry<String, String> entry: APN_MAP.entrySet()) {
+            contentValues.put(entry.getKey(), entry.getValue());
+        }
+        return contentValues;
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index e340d00..1fa78d3 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -31,7 +31,8 @@
     ctstestrunner \
     services.core \
     junit \
-    truth-prebuilt
+    truth-prebuilt \
+    accountaccesslib
 
 LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v4
 
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index b191a11..eb6fecb 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.content.cts.permission.TEST_GRANTED" />
 
     <!-- Used for PackageManager test, don't delete this INTERNET permission -->
@@ -288,6 +289,16 @@
                 android:process=":providerProcess">
         </provider>
 
+        <activity android:name="com.android.cts.content.StubActivity"/>
+
+        <service android:name="com.android.cts.content.SyncService">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter"/>
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                android:resource="@xml/accountaccesssyncadapter" />
+        </service>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 5cab0ac..1fd262f 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -31,6 +31,7 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsContentTestCases.apk" />
+        <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
similarity index 81%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
index 0979e96..09523ba 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
@@ -24,11 +24,14 @@
     android-support-test \
     ctstestrunner \
     ub-uiautomator \
-    compatibility-device-util
+    compatibility-device-util \
+    accountaccesslib
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessSameCertTestCases
+LOCAL_PACKAGE_NAME := CtsSyncAccountAccessOtherCertTestCases
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
@@ -36,4 +39,4 @@
 
 LOCAL_DEX_PREOPT := false
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml
new file mode 100644
index 0000000..cc6da02
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<configuration description="Config for CTS tests of sync adapters with different certs as the authenticator">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsSyncAccountAccessOtherCertTestCases.apk" />
+        <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.cts.content" />
+        <option name="runtime-hint" value="30s" />
+    </test>
+</configuration>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
similarity index 88%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index e732f81..95bf60c 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -16,16 +16,18 @@
 
 package com.android.cts.content;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.Activity;
-import android.content.ContentProviderClient;
+import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SyncRequest;
-import android.content.SyncResult;
-import android.content.cts.FlakyTestRule;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.net.ConnectivityManager;
@@ -47,11 +49,9 @@
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.compatibility.common.util.SystemUtil;
 
@@ -63,7 +63,9 @@
     private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
     private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
 
-    private static final String PERMISSION_REQUESTED = "Permission Requested";
+    private static final Pattern PERMISSION_REQUESTED = Pattern.compile(
+            "Permission Requested|Permission requested");
+    private static final Pattern ALLOW_SYNC = Pattern.compile("ALLOW|Allow");
     public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
 
     @Rule
@@ -81,9 +83,8 @@
 
     @Test
     public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
-        if (!hasDataConnection() || !hasNotificationSupport()) {
-            return;
-        }
+        assumeTrue(hasDataConnection());
+        assumeTrue(hasNotificationSupport());
 
         Intent intent = new Intent(getContext(), StubActivity.class);
         Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -99,11 +100,7 @@
         waitForSyncManagerAccountChangeUpdate();
 
         try {
-            CountDownLatch latch = new CountDownLatch(1);
-
-            SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
-                    String authority, ContentProviderClient provider, SyncResult syncResult)
-                    -> latch.countDown());
+            AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
 
             Bundle extras = new Bundle();
             extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
@@ -118,7 +115,8 @@
                     .build();
             ContentResolver.requestSync(request);
 
-            assertFalse(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS).times(0)).onPerformSync(any(), any(),
+                    any(), any(), any());
 
             UiDevice uiDevice = getUiDevice();
             if (isWatch()) {
@@ -132,14 +130,15 @@
                 uiDevice.findObject(By.text(PERMISSION_REQUESTED)).click();
             }
 
-            uiDevice.wait(Until.hasObject(By.text("ALLOW")),
+            uiDevice.wait(Until.hasObject(By.text(ALLOW_SYNC)),
                     UI_TIMEOUT_MILLIS);
 
-            uiDevice.findObject(By.text("ALLOW")).click();
+            uiDevice.findObject(By.text(ALLOW_SYNC)).click();
 
             ContentResolver.requestSync(request);
 
-            assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
+                    any());
         } finally {
             // Ask the differently signed authenticator to drop all accounts
             accountManager.getAuthToken(addedAccount, TOKEN_TYPE_REMOVE_ACCOUNTS,
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/Android.mk b/tests/tests/content/SyncAccountAccessStubs/Android.mk
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/Android.mk
rename to tests/tests/content/SyncAccountAccessStubs/Android.mk
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/AndroidManifest.xml b/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/AndroidManifest.xml
rename to tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/res/xml/authenticator.xml b/tests/tests/content/SyncAccountAccessStubs/res/xml/authenticator.xml
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/res/xml/authenticator.xml
rename to tests/tests/content/SyncAccountAccessStubs/res/xml/authenticator.xml
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
rename to tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
rename to tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
diff --git a/tests/tests/content/lib/Android.mk b/tests/tests/content/lib/Android.mk
new file mode 100644
index 0000000..9aaa6ac
--- /dev/null
+++ b/tests/tests/content/lib/Android.mk
@@ -0,0 +1,17 @@
+# 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 $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk b/tests/tests/content/lib/accountaccess/Android.mk
similarity index 60%
copy from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
copy to tests/tests/content/lib/accountaccess/Android.mk
index 0979e96..559dc90 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
+++ b/tests/tests/content/lib/accountaccess/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -12,28 +11,17 @@
 # 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)
+LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    ctstestrunner \
-    ub-uiautomator \
-    compatibility-device-util
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessSameCertTestCases
+LOCAL_MODULE := accountaccesslib
 
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
 
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
similarity index 97%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
index e5664f1..2608b97 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  */
 
-package android.content.cts;
+package com.android.cts.content;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/StubActivity.java
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/StubActivity.java
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
similarity index 74%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
index e93a070..19fb8ee 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
@@ -16,6 +16,9 @@
 
 package com.android.cts.content;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.accounts.Account;
 import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentProviderClient;
@@ -25,31 +28,33 @@
 
 public class SyncAdapter extends AbstractThreadedSyncAdapter {
     private static final Object sLock = new Object();
+    private static AbstractThreadedSyncAdapter sDelegate;
 
-    private static OnPerformSyncDelegate sOnPerformSyncDelegate;
+    public static AbstractThreadedSyncAdapter setNewDelegate() {
+        AbstractThreadedSyncAdapter delegate = mock(AbstractThreadedSyncAdapter.class);
 
-    public interface OnPerformSyncDelegate {
-        void onPerformSync(Account account, Bundle extras, String authority,
-                ContentProviderClient provider, SyncResult syncResult);
-    }
-
-    public static void setOnPerformSyncDelegate(OnPerformSyncDelegate delegate) {
         synchronized (sLock) {
-            sOnPerformSyncDelegate = delegate;
+            sDelegate = delegate;
         }
+
+        return delegate;
     }
 
     public SyncAdapter(Context context, boolean autoInitialize) {
         super(context, autoInitialize);
     }
 
+    private AbstractThreadedSyncAdapter getCopyOfDelegate() {
+        synchronized (sLock) {
+            return sDelegate;
+        }
+    }
+
     @Override
     public void onPerformSync(Account account, Bundle extras, String authority,
             ContentProviderClient provider, SyncResult syncResult) {
-        OnPerformSyncDelegate delegate;
-        synchronized (sLock) {
-            delegate = sOnPerformSyncDelegate;
-        }
+        AbstractThreadedSyncAdapter delegate = getCopyOfDelegate();
+
         if (delegate != null) {
             delegate.onPerformSync(account, extras, authority, provider, syncResult);
         }
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml b/tests/tests/content/res/xml/accountaccesssyncadapter.xml
similarity index 100%
copy from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
copy to tests/tests/content/res/xml/accountaccesssyncadapter.xml
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
similarity index 86%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
rename to tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
index bfdd072..72785b5 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
+++ b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.cts.content;
+package android.content.cts;
 
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.Activity;
-import android.content.ContentProviderClient;
+import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SyncRequest;
-import android.content.SyncResult;
-import android.content.cts.FlakyTestRule;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
@@ -36,6 +37,12 @@
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.content.FlakyTestRule;
+import com.android.cts.content.StubActivity;
+import com.android.cts.content.SyncAdapter;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,16 +51,12 @@
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import com.android.compatibility.common.util.SystemUtil;
 
 /**
  * Tests whether a sync adapter can access accounts.
  */
 @RunWith(AndroidJUnit4.class)
-public class CtsSyncAccountAccessSameCertTestCases {
+public class AccountAccessSameCertTest {
     private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
 
     @Rule
@@ -71,9 +74,8 @@
 
     @Test
     public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
-        if (!hasDataConnection() || !hasNotificationSupport()) {
-            return;
-        }
+        assumeTrue(hasDataConnection());
+        assumeTrue(hasNotificationSupport());
 
         Intent intent = new Intent(getContext(), StubActivity.class);
         Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -89,11 +91,7 @@
         waitForSyncManagerAccountChangeUpdate();
 
         try {
-            CountDownLatch latch = new CountDownLatch(1);
-
-            SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
-                    String authority, ContentProviderClient provider, SyncResult syncResult)
-                    -> latch.countDown());
+            AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
 
             Bundle extras = new Bundle();
             extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
@@ -108,7 +106,8 @@
                     .build();
             ContentResolver.requestSync(request);
 
-            assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
+                    any());
         } finally {
             accountManager.removeAccount(addedAccount, activity, null, null);
             activity.finish();
diff --git a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
index 7a97434..e17d0de 100644
--- a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -28,13 +28,9 @@
 import android.os.Process;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.AttributeSet;
 
 import com.android.compatibility.common.util.SystemUtil;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -204,30 +200,11 @@
      */
     @SmallTest
     public void testCantSetModeForSelf() {
-        boolean gotToTest = false;
         try {
-            Method setMode = mAppOps.getClass().getMethod("setMode", int.class, int.class,
-                    String.class, int.class);
-            int writeSmsOp = mAppOps.getClass().getField("OP_WRITE_SMS").getInt(mAppOps);
-            gotToTest = true;
-            setMode.invoke(mAppOps, writeSmsOp, mMyUid, mOpPackageName, AppOpsManager.MODE_ALLOWED);
+            int writeSmsOp = AppOpsManager.permissionToOpCode("android.permission.WRITE_SMS");
+            mAppOps.setMode(writeSmsOp, mMyUid, mOpPackageName, AppOpsManager.MODE_ALLOWED);
             fail("Was able to set mode for self");
-        } catch (NoSuchFieldException e) {
-            throw new AssertionError("Unable to find OP_WRITE_SMS", e);
-        } catch (NoSuchMethodException e) {
-            throw new AssertionError("Unable to find setMode method", e);
-        } catch (InvocationTargetException e) {
-            if (!gotToTest) {
-                throw new AssertionError("Whoops", e);
-            }
-            // If we got to the test, we want it to have thrown a security exception.
-            // We need to look inside of the wrapper exception to see.
-            Throwable t = e.getCause();
-            if (!(t instanceof SecurityException)) {
-                throw new AssertionError("Did not throw SecurityException", e);
-            }
-        } catch (IllegalAccessException e) {
-            throw new AssertionError("Whoops", e);
+        } catch (SecurityException expected) {
         }
     }
 
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index c95898a..c72c8c1 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -183,15 +183,23 @@
     <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" />
+    <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" />
+    <protected-broadcast
         android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.input.profile.action.IDLE_TIME_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
     <protected-broadcast
-        android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
+        android:name="android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
@@ -299,6 +307,8 @@
     <protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
     <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
 
+    <protected-broadcast android:name="com.android.server.stats.action.TRIGGER_COLLECTION" />
+
     <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
     <protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
     <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
@@ -318,6 +328,7 @@
     <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
     <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
     <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -396,6 +407,7 @@
     <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
     <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+    <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
 
     <!-- Added in N -->
     <protected-broadcast android:name="android.intent.action.ANR" />
@@ -474,7 +486,6 @@
     <protected-broadcast android:name="android.content.jobscheduler.JOB_DEADLINE_EXPIRED" />
     <protected-broadcast android:name="android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_SUPL" />
-    <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
     <protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
@@ -493,6 +504,8 @@
     <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" />
     <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" />
 
     <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
     <protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
@@ -1024,7 +1037,7 @@
     <eat-comment />
 
     <!-- Used for permissions that are associated with accessing
-         camera or capturing images/video from the device. -->
+         body or environmental sensors. -->
     <permission-group android:name="android.permission-group.SENSORS"
         android:icon="@drawable/perm_group_sensors"
         android:label="@string/permgrouplab_sensors"
@@ -1607,6 +1620,11 @@
     <permission android:name="android.permission.ACCESS_PDB_STATE"
         android:protectionLevel="signature" />
 
+    <!-- Allows testing if a passwords is forbidden by the admins.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.TEST_BLACKLISTED_PASSWORD"
+        android:protectionLevel="signature" />
+
     <!-- @hide Allows system update service to notify device owner about pending updates.
    <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
@@ -1779,6 +1797,15 @@
     <permission android:name="android.permission.ALLOCATE_AGGRESSIVE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide
+         Allows an application to use reserved disk space.
+         <p>Not for use by third-party applications.  Should only be requested by
+         apps that provide core system functionality, to ensure system stability
+         when disk is otherwise completely full.
+    -->
+    <permission android:name="android.permission.USE_RESERVED_DISK"
+        android:protectionLevel="signature|privileged" />
+
     <!-- ================================== -->
     <!-- Permissions for screenlock         -->
     <!-- ================================== -->
@@ -2046,6 +2073,11 @@
     <eat-comment />
 
     <!-- Allows an application to install a shortcut in Launcher.
+         <p>In Android O (API level 26) and higher, the <code>INSTALL_SHORTCUT</code> broadcast no
+         longer has any effect on your app because it's a private, implicit
+         broadcast. Instead, you should create an app shortcut by using the
+         {@link android.content.pm.ShortcutManager#requestPinShortcut requestPinShortcut()}
+         method from the {@link android.content.pm.ShortcutManager} class.
          <p>Protection level: normal
     -->
     <permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -2767,8 +2799,9 @@
         android:protectionLevel="signature|appop" />
 
     <!-- Allows an application to request deleting packages. Apps
-         targeting APIs greater than 25 must hold this permission in
-         order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE}.
+         targeting APIs {@link android.os.Build.VERSION_CODES#P} or greater must hold this
+         permission in order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE} or
+         {@link android.content.pm.PackageInstaller#uninstall}.
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
@@ -3481,6 +3514,7 @@
         @hide -->
     <permission android:name="android.permission.LOCAL_MAC_ADDRESS"
                 android:protectionLevel="signature|privileged" />
+    <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"/>
 
     <!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
         @hide -->
@@ -3509,11 +3543,19 @@
     @hide -->
     <permission android:name="android.permission.ACCESS_INSTANT_APPS"
             android:protectionLevel="signature|installer|verifier" />
+    <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
 
     <!-- Allows the holder to view the instant applications on the device.
     @hide -->
     <permission android:name="android.permission.VIEW_INSTANT_APPS"
-            android:protectionLevel="signature|preinstalled" />
+                android:protectionLevel="signature|preinstalled" />
+
+    <!-- Allows the holder to manage whether the system can bind to services
+         provided by instant apps. This permission is intended to protect
+         test/development fucntionality and should be used only in such cases.
+    @hide -->
+    <permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"
+                android:protectionLevel="signature" />
 
     <!-- Allows receiving the usage of media resource e.g. video/audio codec and
          graphic memory.
@@ -3608,6 +3650,11 @@
     <permission android:name="android.permission.READ_RUNTIME_PROFILES"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to turn on / off quiet mode.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MODIFY_QUIET_MODE"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -3665,7 +3712,7 @@
         </activity-alias>
         <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
                 android:targetActivity="com.android.internal.app.IntentForwarderActivity"
-                android:icon="@drawable/ic_corp_icon"
+                android:icon="@drawable/ic_corp_badge"
                 android:exported="true"
                 android:label="@string/managed_profile_label">
         </activity-alias>
@@ -3960,6 +4007,14 @@
         <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
-    </application>
+
+        <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service android:name="com.android.server.display.BrightnessIdleJob"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+</application>
 
 </manifest>
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
index 3f4aea2..c0b4253 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
@@ -88,32 +88,46 @@
 
     public void testVoicemailsTable() throws Exception {
         final String[] VOICEMAILS_PROJECTION = new String[] {
-                Voicemails._ID, Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION,
-                Voicemails.IS_READ, Voicemails.SOURCE_PACKAGE, Voicemails.SOURCE_DATA,
-                Voicemails.HAS_CONTENT, Voicemails.MIME_TYPE, Voicemails.TRANSCRIPTION,
+                Voicemails._ID,
+                Voicemails.NUMBER,
+                Voicemails.DATE,
+                Voicemails.DURATION,
+                Voicemails.NEW,
+                Voicemails.IS_READ,
+                Voicemails.SOURCE_PACKAGE,
+                Voicemails.SOURCE_DATA,
+                Voicemails.HAS_CONTENT,
+                Voicemails.MIME_TYPE,
+                Voicemails.TRANSCRIPTION,
                 Voicemails.PHONE_ACCOUNT_COMPONENT_NAME,
-                Voicemails.PHONE_ACCOUNT_ID, Voicemails.DIRTY, Voicemails.DELETED,
-                Voicemails.LAST_MODIFIED, Voicemails.BACKED_UP, Voicemails.RESTORED,
-                Voicemails.ARCHIVED, Voicemails.IS_OMTP_VOICEMAIL};
+                Voicemails.PHONE_ACCOUNT_ID,
+                Voicemails.DIRTY,
+                Voicemails.DELETED,
+                Voicemails.LAST_MODIFIED,
+                Voicemails.BACKED_UP,
+                Voicemails.RESTORED,
+                Voicemails.ARCHIVED,
+                Voicemails.IS_OMTP_VOICEMAIL};
         final int ID_INDEX = 0;
         final int NUMBER_INDEX = 1;
         final int DATE_INDEX = 2;
         final int DURATION_INDEX = 3;
-        final int IS_READ_INDEX = 4;
-        final int SOURCE_PACKAGE_INDEX = 5;
-        final int SOURCE_DATA_INDEX = 6;
-        final int HAS_CONTENT_INDEX = 7;
-        final int MIME_TYPE_INDEX = 8;
-        final int TRANSCRIPTION_INDEX = 9;
-        final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 10;
-        final int PHONE_ACCOUNT_ID_INDEX = 11;
-        final int DIRTY_INDEX = 12;
-        final int DELETED_INDEX = 13;
-        final int LAST_MODIFIED_INDEX = 14;
-        final int BACKED_UP_INDEX = 15;
-        final int RESTORED_INDEX = 16;
-        final int ARCHIVED_INDEX = 17;
-        final int IS_OMTP_VOICEMAIL_INDEX = 18;
+        final int NEW_INDEX = 4;
+        final int IS_READ_INDEX = 5;
+        final int SOURCE_PACKAGE_INDEX = 6;
+        final int SOURCE_DATA_INDEX = 7;
+        final int HAS_CONTENT_INDEX = 8;
+        final int MIME_TYPE_INDEX = 9;
+        final int TRANSCRIPTION_INDEX = 10;
+        final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 11;
+        final int PHONE_ACCOUNT_ID_INDEX = 12;
+        final int DIRTY_INDEX = 13;
+        final int DELETED_INDEX = 14;
+        final int LAST_MODIFIED_INDEX = 15;
+        final int BACKED_UP_INDEX = 16;
+        final int RESTORED_INDEX = 17;
+        final int ARCHIVED_INDEX = 18;
+        final int IS_OMTP_VOICEMAIL_INDEX = 19;
 
         String insertCallsNumber = "0123456789";
         long insertCallsDuration = 120;
@@ -131,6 +145,7 @@
         value.put(Voicemails.NUMBER, insertCallsNumber);
         value.put(Voicemails.DATE, insertDate);
         value.put(Voicemails.DURATION, insertCallsDuration);
+        value.put(Voicemails.NEW, 0);
         // Source package is expected to be inserted by the provider, if not set.
         value.put(Voicemails.SOURCE_DATA, insertSourceData);
         value.put(Voicemails.MIME_TYPE, insertMimeType);
@@ -158,6 +173,7 @@
         assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
         assertEquals(insertSourceData, cursor.getString(SOURCE_DATA_INDEX));
         assertEquals(insertMimeType, cursor.getString(MIME_TYPE_INDEX));
+        assertEquals(0, cursor.getInt(NEW_INDEX));
         assertEquals(0, cursor.getInt(IS_READ_INDEX));
         assertEquals(1, cursor.getInt(HAS_CONTENT_INDEX));
         assertEquals("foo", cursor.getString(TRANSCRIPTION_INDEX));
@@ -179,6 +195,7 @@
         value.put(Voicemails.DATE, updateDate);
         value.put(Voicemails.DURATION, updateCallsDuration);
         value.put(Voicemails.SOURCE_DATA, updateSourceData);
+        value.put(Voicemails.NEW, 1);
         value.put(Voicemails.DIRTY, 1);
         value.put(Voicemails.DELETED, 1);
         value.put(Voicemails.BACKED_UP, 1);
@@ -196,6 +213,7 @@
         assertEquals(updateDate, cursor.getLong(DATE_INDEX));
         assertEquals(updateCallsDuration, cursor.getLong(DURATION_INDEX));
         assertEquals(updateSourceData, cursor.getString(SOURCE_DATA_INDEX));
+        assertEquals(1, cursor.getInt(NEW_INDEX));
         assertEquals(1, cursor.getInt(DIRTY_INDEX));
         assertEquals(1, cursor.getInt(DELETED_INDEX));
         assertEquals(1, cursor.getInt(BACKED_UP_INDEX));
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
index 7cdfb55..0843eaf 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
@@ -19,6 +19,7 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
+import android.webkit.CookieManager;
 import android.webkit.WebView;
 
 import com.android.compatibility.common.util.NullWebViewUtils;
@@ -26,6 +27,10 @@
 public class WebViewDataDirTest extends AndroidTestCase {
 
     private static final long REMOTE_TIMEOUT_MS = 5000;
+    private static final String ALTERNATE_DIR_NAME = "test";
+    private static final String COOKIE_URL = "https://www.example.com/";
+    private static final String COOKIE_VALUE = "foo=main";
+    private static final String SET_COOKIE_PARAMS = "; Max-Age=86400";
 
     static class TestDisableThenUseImpl extends TestProcessClient.TestRunnable {
         @Override
@@ -68,4 +73,115 @@
             process.run(TestUseThenDisableImpl.class, REMOTE_TIMEOUT_MS);
         }
     }
+
+    static class TestUseThenChangeDirImpl extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            new WebView(ctx);
+            try {
+                WebView.setDataDirectorySuffix("test");
+                fail("didn't throw IllegalStateException");
+            } catch (IllegalStateException e) {}
+        }
+    }
+
+    public void testUseThenChangeDir() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+            process.run(TestUseThenChangeDirImpl.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class TestInvalidDirImpl extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            try {
+                WebView.setDataDirectorySuffix("no/path/separators");
+                fail("didn't throw IllegalArgumentException");
+            } catch (IllegalArgumentException e) {}
+        }
+    }
+
+    public void testInvalidDir() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+            process.run(TestInvalidDirImpl.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class WebViewInDefaultDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            new WebView(ctx);
+        }
+    }
+
+    static class TestDefaultDirDisallowed extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            try {
+                new WebView(ctx);
+                fail("didn't throw RuntimeException");
+            } catch (RuntimeException e) {}
+        }
+    }
+
+    public void testSameDirTwoProcesses() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient processA = TestProcessClient.createProcessA(getContext());
+             TestProcessClient processB = TestProcessClient.createProcessB(getContext())) {
+            processA.run(WebViewInDefaultDir.class, REMOTE_TIMEOUT_MS);
+            processB.run(TestDefaultDirDisallowed.class, REMOTE_TIMEOUT_MS);
+        }
+    }
+
+    static class SetCookieInDefaultDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            CookieManager cm = CookieManager.getInstance();
+            cm.setCookie(COOKIE_URL, COOKIE_VALUE + SET_COOKIE_PARAMS);
+            cm.flush();
+        }
+    }
+
+    static class TestCookieInDefaultDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            CookieManager cm = CookieManager.getInstance();
+            String cookie = cm.getCookie(COOKIE_URL);
+            assertEquals("wrong cookie in default cookie jar", COOKIE_VALUE, cookie);
+        }
+    }
+
+    static class TestCookieInAlternateDir extends TestProcessClient.TestRunnable {
+        @Override
+        public void run(Context ctx) {
+            WebView.setDataDirectorySuffix(ALTERNATE_DIR_NAME);
+            CookieManager cm = CookieManager.getInstance();
+            String cookie = cm.getCookie(COOKIE_URL);
+            assertNull("cookie leaked to alternate cookie jar", cookie);
+        }
+    }
+
+    public void testCookieJarsSeparate() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        try (TestProcessClient processA = TestProcessClient.createProcessA(getContext());
+             TestProcessClient processB = TestProcessClient.createProcessB(getContext())) {
+            processA.run(SetCookieInDefaultDir.class, REMOTE_TIMEOUT_MS);
+            processA.run(TestCookieInDefaultDir.class, REMOTE_TIMEOUT_MS);
+            processB.run(TestCookieInAlternateDir.class, REMOTE_TIMEOUT_MS);
+        }
+    }
 }
diff --git a/tools/cts-api-coverage/proto/testsuite.proto b/tools/cts-api-coverage/proto/testsuite.proto
index 45a8a71..a0adb6f 100644
--- a/tools/cts-api-coverage/proto/testsuite.proto
+++ b/tools/cts-api-coverage/proto/testsuite.proto
@@ -23,6 +23,37 @@
 // [END java_declaration]
 
 // [START messages]
+message Option {
+    string name = 1;
+    string key = 2;
+    string value =3;
+}
+
+message ConfigMetadata {
+    string module_name = 1;
+    string component = 2;
+    repeated Option options = 3;
+
+    message TargetPreparer {
+        string test_class = 1;
+        repeated Option options = 2;
+    }
+    repeated TargetPreparer target_preparers = 4;
+
+    message TestClass {
+        string test_class = 1;
+        string package = 2;
+        repeated Option options = 3;
+    }
+    repeated TestClass test_classes = 5;
+}
+
+// target File Metadata for e.g. config, apk, jar, exe, so
+message FileMetadata {
+    string description = 1;
+    ConfigMetadata config_metadata = 2;
+}
+
 // An entry in a Test Suire Release messages: cts, etc.
 message Entry {
     // Entry ID
@@ -50,9 +81,11 @@
     string parent_id = 6;
     // Relative path
     string relative_path = 7;
+
+    FileMetadata file_metadata = 8;
 }
 
-// A Test Suite Release: cts, etc.
+// Test Suite Release: cts, etc.
 message TestSuiteContent {
     // Entry ID
     string id = 1;
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
index a1a6923..3c88938 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
@@ -128,7 +128,7 @@
 
             for (File testConfigFile : testConfigFiles) {
                 XMLReader xmlReader = XMLReaderFactory.createXMLReader();
-                TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler();
+                TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
                 xmlReader.setContentHandler(testModuleXmlHandler);
                 FileReader fileReader = null;
 
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
index 85423b7..8a17153 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.apicoverage;
 
+import com.android.cts.apicoverage.TestSuiteProto.*;
+
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
@@ -25,25 +27,65 @@
  * TestModule.xml.
  */
 class TestModuleConfigHandler extends DefaultHandler {
-    private String mTestClassName;
-    private String mModuleName;
-    private Boolean inTestEle = false;
+    private static final String CONFIGURATION_TAG = "configuration";
+    private static final String DESCRIPTION_TAG = "description";
+    private static final String OPTION_TAG = "option";
+    private static final String TARGET_PREPARER_TAG = "target_preparer";
+    private static final String TEST_TAG = "test";
+    private static final String CLASS_TAG = "class";
+    private static final String NAME_TAG = "name";
+    private static final String KEY_TAG = "key";
+    private static final String VALUE_TAG = "value";
+    private static final String MODULE_NAME_TAG = "module-name";
+    private static final String GTEST_CLASS_TAG = "com.android.tradefed.testtype.GTest";
+
+    private FileMetadata.Builder mFileMetadata;
+    private ConfigMetadata.Builder mConfigMetadata;
+    private ConfigMetadata.TestClass.Builder mTestCase;
+    private ConfigMetadata.TargetPreparer.Builder mTargetPreparer;
+    private String mModuleName = null;
+
+    TestModuleConfigHandler(String configFileName) {
+        mFileMetadata = FileMetadata.newBuilder();
+        mConfigMetadata = ConfigMetadata.newBuilder();
+        mTestCase = null;
+        mTargetPreparer = null;
+        // Default Module Name is the Config File Name
+        mModuleName = configFileName.replaceAll(".config$", "");
+    }
 
     @Override
     public void startElement(String uri, String localName, String name, Attributes attributes)
             throws SAXException {
         super.startElement(uri, localName, name, attributes);
 
-        if ("test".equalsIgnoreCase(localName)) {
-            mTestClassName = attributes.getValue("class");
-            inTestEle = true;
-        } else if ("option".equalsIgnoreCase(localName)) {
-            if (inTestEle) {
-                String optName = attributes.getValue("name");
-                if ("module-name".equalsIgnoreCase(optName)) {
-                    mModuleName = attributes.getValue("value");
+        if (CONFIGURATION_TAG.equalsIgnoreCase(localName)) {
+            if (null != attributes.getValue(DESCRIPTION_TAG)) {
+                mFileMetadata.setDescription(attributes.getValue(DESCRIPTION_TAG));
+            } else {
+                mFileMetadata.setDescription("WARNING: no description.");
+            }
+        } else if (TEST_TAG.equalsIgnoreCase(localName)) {
+            mTestCase = ConfigMetadata.TestClass.newBuilder();
+            mTestCase.setTestClass(attributes.getValue(CLASS_TAG));
+        } else if (TARGET_PREPARER_TAG.equalsIgnoreCase(localName)) {
+            mTargetPreparer = ConfigMetadata.TargetPreparer.newBuilder();
+            mTargetPreparer.setTestClass(attributes.getValue(CLASS_TAG));
+        } else if (OPTION_TAG.equalsIgnoreCase(localName)) {
+            Option.Builder option = Option.newBuilder();
+            option.setName(attributes.getValue(NAME_TAG));
+            option.setValue(attributes.getValue(VALUE_TAG));
+            String keyStr = attributes.getValue(KEY_TAG);
+            if (null != keyStr) {
+                option.setKey(keyStr);
+            }
+            if (null != mTestCase) {
+                mTestCase.addOptions(option);
+                if (GTEST_CLASS_TAG.equalsIgnoreCase(option.getName())) {
+                    mModuleName = option.getValue();
                 }
-                //System.out.println(String.format("%s: %s, %s, %s", localName, name, optName, attributes.getValue("value")));
+            } else if (null != mTargetPreparer) {
+                mTargetPreparer.addOptions(option);
             }
         }
     }
@@ -51,8 +93,14 @@
     @Override
     public void endElement(String uri, String localName, String name) throws SAXException {
         super.endElement(uri, localName, name);
-        if ("test".equalsIgnoreCase(localName)) {
-            inTestEle = false;
+        if (TEST_TAG.equalsIgnoreCase(localName)) {
+            mConfigMetadata.addTestClasses(mTestCase);
+            mTestCase = null;
+        } else if (TARGET_PREPARER_TAG.equalsIgnoreCase(localName)) {
+            mConfigMetadata.addTargetPreparers(mTargetPreparer);
+            mTargetPreparer = null;
+        } else if (CONFIGURATION_TAG.equalsIgnoreCase(localName)) {
+            mFileMetadata.setConfigMetadata(mConfigMetadata);
         }
     }
 
@@ -61,6 +109,11 @@
     }
 
     public String getTestClassName() {
-        return mTestClassName;
+        //return the 1st Test Class
+        return mFileMetadata.getConfigMetadata().getTestClassesList().get(0).getTestClass();
+    }
+
+    public FileMetadata getFileMetadata() {
+        return mFileMetadata.build();
     }
 }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
index e9ab1be..044acdc 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
@@ -17,12 +17,15 @@
 package com.android.cts.apicoverage;
 
 
-import com.android.cts.apicoverage.TestSuiteProto.Entry;
-import com.android.cts.apicoverage.TestSuiteProto.Entry.EntryType;
-import com.android.cts.apicoverage.TestSuiteProto.TestSuiteContent;
+import com.android.cts.apicoverage.TestSuiteProto.*;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileReader;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -38,6 +41,31 @@
 import java.util.List;
 
 class TestSuiteContentReport {
+    // configuration option
+    private static final String NOT_SHARDABLE_TAG = "not-shardable";
+    // test class option
+    private static final String RUNTIME_HIT_TAG = "runtime-hint";
+    // com.android.tradefed.testtype.AndroidJUnitTest option
+    private static final String PACKAGE_TAG = "package";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_NAME_TAG = "jar";
+    // com.android.tradefed.testtype.GTest option
+    private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
+    private static final String MODULE_TAG = "module-name";
+
+    private static final String SUITE_API_INSTALLER_TAG = "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
+    private static final String JAR_HOST_TEST_TAG = "com.android.compatibility.common.tradefed.testtype.JarHostTest";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+    // com.android.compatibility.common.tradefed.targetprep.FilePusher option
+    private static final String PUSH_TAG = "push";
+
+    // Target File Extensions
+    private static final String CONFIG_EXT_TAG = ".config";
+    private static final String JAR_EXT_TAG = ".jar";
+    private static final String APK_EXT_TAG = ".apk";
+    private static final String SO_EXT_TAG = ".so";
+
     private static void printUsage() {
         System.out.println("Usage: test-suite-content-report [OPTION]...");
         System.out.println();
@@ -73,6 +101,42 @@
         return testSuiteContent.build();
     }
 
+    // Parse a file
+    private static FileMetadata parseFileMetadata(Entry.Builder fEntry, File file)
+            throws Exception {
+        if (file.getName().endsWith(CONFIG_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.CONFIG);
+            return parseConfigFile(file);
+        } else if (file.getName().endsWith(APK_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.APK);
+        } else if (file.getName().endsWith(JAR_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.JAR);
+        } else if (file.getName().endsWith(SO_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.SO);
+        } else {
+            // Just file in general
+            fEntry.setType(Entry.EntryType.FILE);
+        }
+        return null;
+    }
+
+    private static FileMetadata parseConfigFile(File file)
+            throws Exception {
+        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+        TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
+        xmlReader.setContentHandler(testModuleXmlHandler);
+        FileReader fileReader = null;
+        try {
+            fileReader = new FileReader(file);
+            xmlReader.parse(new InputSource(fileReader));
+            return testModuleXmlHandler.getFileMetadata();
+        } finally {
+            if (null != fileReader) {
+                fileReader.close();
+            }
+        }
+    }
+
     // Parse a folder to add all entries
     private static Entry.Builder parseFolder(TestSuiteContent.Builder testSuiteContent, String fPath, String rPath)
             throws IOException, NoSuchAlgorithmException {
@@ -94,10 +158,20 @@
                 fileEntry.setId(getId(fileRelativePath));
                 fileEntry.setName(file.getName());
                 fileEntry.setSize(file.length());
-                fileEntry.setType(EntryType.FILE);
                 fileEntry.setContentId(getFileContentId(file));
                 fileEntry.setRelativePath(fileRelativePath);
                 fileEntry.setParentId(folderId);
+                try {
+                    FileMetadata fMetadata = parseFileMetadata(fileEntry, file);
+                    if (null != fMetadata) {
+                        fileEntry.setFileMetadata(fMetadata);
+                    }
+                } catch (Exception ex) {
+                    System.err.println(
+                            String.format("Cannot parse %s",
+                                    file.getAbsolutePath()));
+                    ex.printStackTrace();
+                }
                 testSuiteContent.addFileEntries(fileEntry);
                 entryList.add(fileEntry.build());
                 folderSize += file.length();
@@ -113,7 +187,7 @@
         folderEntry.setId(folderId);
         folderEntry.setName(folderRelativePath);
         folderEntry.setSize(folderSize);
-        folderEntry.setType(EntryType.FOLDER);
+        folderEntry.setType(Entry.EntryType.FOLDER);
         folderEntry.setContentId(getFolderContentId(folderEntry, entryList));
         folderEntry.setRelativePath(folderRelativePath);
         return folderEntry;
@@ -173,13 +247,68 @@
 
     // Iterates though all test suite content and prints them.
     static void Print(TestSuiteContent tsContent) {
-        System.out.printf("no,name,size,relativePath,id,cid,pid\n");
+        //Header
+        System.out.printf("no,type,name,size,relative path,id,content id,parent id,description,test class");
+        // test class header
+        System.out.printf(",%s,%s,%s,%s,%s",
+                RUNTIME_HIT_TAG, PACKAGE_TAG, JAR_NAME_TAG, NATIVE_TEST_DEVICE_PATH_TAG, MODULE_TAG);
+        // target preparer header
+        System.out.printf(",%s,%s\n",
+                TEST_FILE_NAME_TAG, PUSH_TAG);
+
         int i = 1;
-        for(Entry entry: tsContent.getFileEntriesList()) {
-            System.out.printf("%d,%s,%d,%s,%s,%s,%s\n", i++,
-                entry.getName(), entry.getSize(),
-                entry.getRelativePath(),
-                entry.getId(), entry.getContentId(),entry.getParentId());
+        for (Entry entry: tsContent.getFileEntriesList()) {
+            System.out.printf("%d,%s,%s,%d,%s,%s,%s,%s",
+                i++, entry.getType(), entry.getName(), entry.getSize(),
+                entry.getRelativePath(), entry.getId(), entry.getContentId(),
+                entry.getParentId());
+            if (Entry.EntryType.CONFIG == entry.getType()) {
+                ConfigMetadata config = entry.getFileMetadata().getConfigMetadata();
+                System.out.printf(",%s", entry.getFileMetadata().getDescription());
+                List<Option> optList;
+                List<ConfigMetadata.TestClass> testClassesList = config.getTestClassesList();
+                String rtHit = "";
+                String pkg = "";
+                String jar = "";
+                String ntdPath = "";
+                String module = "";
+
+                for (ConfigMetadata.TestClass tClass : testClassesList) {
+                    System.out.printf(",%s", tClass.getTestClass());
+                    optList = tClass.getOptionsList();
+                    for (Option opt : optList) {
+                        if (RUNTIME_HIT_TAG.equalsIgnoreCase(opt.getName())) {
+                            rtHit = rtHit + opt.getValue() + " ";
+                        } else if (PACKAGE_TAG.equalsIgnoreCase(opt.getName())) {
+                            pkg = pkg + opt.getValue() + " ";
+                        } else if (JAR_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+                            jar = jar + opt.getValue() + " ";
+                        } else if (NATIVE_TEST_DEVICE_PATH_TAG.equalsIgnoreCase(opt.getName())) {
+                            ntdPath = ntdPath + opt.getValue() + " ";
+                        } else if (MODULE_TAG.equalsIgnoreCase(opt.getName())) {
+                            module = module + opt.getValue() + " ";
+                        }
+                    }
+                }
+                System.out.printf(",%s,%s,%s,%s,%s", rtHit.trim(), pkg.trim(),
+                        jar.trim(), module.trim(), ntdPath.trim());
+
+                List<ConfigMetadata.TargetPreparer> tPrepList = config.getTargetPreparersList();
+                String testFile = "";
+                String pushList = "";
+                for (ConfigMetadata.TargetPreparer tPrep : tPrepList) {
+                    optList = tPrep.getOptionsList();
+                    for (Option opt : optList) {
+                        if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+                            testFile = testFile + opt.getValue() + " ";
+                        } else if (PUSH_TAG.equalsIgnoreCase(opt.getName())) {
+                            pushList = pushList + opt.getValue() + " ";
+                        }
+                    }
+                }
+                System.out.printf(",%s,%s", testFile.trim(), pushList.trim());
+            }
+            System.out.printf("\n");
         }
     }
 
diff --git a/tools/cts-tradefed/Android.mk b/tools/cts-tradefed/Android.mk
index 953ef63..e6c4750 100644
--- a/tools/cts-tradefed/Android.mk
+++ b/tools/cts-tradefed/Android.mk
@@ -22,6 +22,7 @@
 LOCAL_JAVA_RESOURCE_DIRS += ../../common/host-side/tradefed/res
 LOCAL_MODULE := cts-tradefed-harness
 LOCAL_JAVA_LIBRARIES += tradefed compatibility-host-util
+LOCAL_STATIC_JAVA_LIBRARIES := google-api-java-client-min-repackaged
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/tools/cts-tradefed/res/config/cts-preconditions.xml b/tools/cts-tradefed/res/config/cts-preconditions.xml
index 21215df..0f0f9b7 100644
--- a/tools/cts-tradefed/res/config/cts-preconditions.xml
+++ b/tools/cts-tradefed/res/config/cts-preconditions.xml
@@ -21,7 +21,11 @@
 
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="host" />
-        <option name="config-filename" value="cts"/>
+        <!-- the name under which to find the configuration -->
+        <option name="config-filename" value="cts" />
+        <option name="extract-from-resource" value="true" />
+        <!-- the name of the resource inside the jar -->
+        <option name="dynamic-resource-name" value="cts-tradefed" />
     </target_preparer>
 
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.StayAwakePreparer" />
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
index 512d8bd..5d5df59 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
@@ -46,7 +46,9 @@
 
     @Override
     protected void tearDown() throws Exception {
-        System.setProperty(PROPERTY_NAME, mOriginalProperty);
+        if (mOriginalProperty != null) {
+            System.setProperty(PROPERTY_NAME, mOriginalProperty);
+        }
         super.tearDown();
     }
 
diff --git a/tools/vm-tests-tf/src/util/build/D8BuildStep.java b/tools/vm-tests-tf/src/util/build/D8BuildStep.java
index cd97dba..1e2c281 100644
--- a/tools/vm-tests-tf/src/util/build/D8BuildStep.java
+++ b/tools/vm-tests-tf/src/util/build/D8BuildStep.java
@@ -19,7 +19,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
-import com.android.tools.r8.utils.OutputMode;
+import com.android.tools.r8.OutputMode;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -37,8 +37,7 @@
         D8Command.builder()
             .setMode(CompilationMode.DEBUG)
             .setMinApiLevel(1000)
-            .setEnableDesugaring(false)
-            .setOutputMode(OutputMode.Indexed);
+            .setEnableDesugaring(false);
   }
 
   @Override
@@ -46,7 +45,7 @@
 
     if (super.build()) {
       try {
-        builder.setOutputPath(Paths.get(outputFile.fileName.getAbsolutePath()));
+        builder.setOutput(Paths.get(outputFile.fileName.getAbsolutePath()), OutputMode.DexIndexed);
         Files.find(
                 Paths.get(inputFile.fileName.getAbsolutePath()),
                 1000,