Merge "Fixes versionCode assertion in UseSharedLibraryTest" into pi-dev
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 6cc678a..ada8384 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -56,7 +56,6 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni \
 		libaudioloopback_jni \
-		libnativehelper_compat_libc++
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 707310d..12eebb0 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1656,51 +1656,6 @@
         </activity>
         <!-- End sensor tests definitions -->
 
-        <activity android:name=".location.LocationModeOffTestActivity"
-                android:label="@string/location_mode_off_test">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST" />
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_location" />
-            <meta-data android:name="test_excluded_features"
-                    android:value="android.hardware.type.television:android.software.leanback" />
-        </activity>
-        <activity android:name=".location.LocationModeHighAccuracyTestActivity"
-                android:label="@string/location_mode_high_accuracy_test">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST" />
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_location" />
-            <meta-data android:name="test_required_features"
-                    android:value="android.hardware.location.network:android.hardware.location.gps" />
-            <meta-data android:name="test_excluded_features"
-                    android:value="android.hardware.type.television:android.software.leanback" />
-        </activity>
-        <activity android:name=".location.LocationModeBatterySavingTestActivity"
-                android:label="@string/location_mode_battery_saving_test">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST" />
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_location" />
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.network" />
-            <meta-data android:name="test_excluded_features"
-                    android:value="android.hardware.type.television:android.software.leanback" />
-        </activity>
-        <activity android:name=".location.LocationModeDeviceOnlyTestActivity"
-                android:label="@string/location_mode_device_only_test">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST" />
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_location" />
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-            <meta-data android:name="test_excluded_features"
-                    android:value="android.hardware.type.television:android.software.leanback" />
-        </activity>
-
         <activity android:name=".camera.formats.CameraFormatsActivity"
                  android:label="@string/camera_format"
                  android:screenOrientation="landscape">
@@ -2751,7 +2706,15 @@
         </activity>
 
         <activity android:name=".managedprovisioning.TurnOffWorkActivity"
-                android:label="@string/provisioning_byod_turn_off_work">
+                  android:label="@string/provisioning_byod_turn_off_work">
+        </activity>
+
+        <activity android:name=".managedprovisioning.WorkProfileWidgetActivity"
+                  android:label="@string/provisioning_byod_work_profile_widget">
+        <intent-filter>
+                <action android:name="com.android.cts.verifier.byod.test_work_profile_widget"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
         </activity>
 
         <receiver android:name=".managedprovisioning.DeviceAdminTestReceiver"
diff --git a/apps/CtsVerifier/jni/verifier/Android.mk b/apps/CtsVerifier/jni/verifier/Android.mk
index 112be59..9ee7eee 100644
--- a/apps/CtsVerifier/jni/verifier/Android.mk
+++ b/apps/CtsVerifier/jni/verifier/Android.mk
@@ -28,10 +28,9 @@
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
 LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_NDK_STL_VARIANT := system
 
 LOCAL_SHARED_LIBRARIES := liblog \
-		libnativehelper_compat_libc++
 
 LOCAL_CFLAGS := \
         -Wall -Werror \
diff --git a/apps/CtsVerifier/res/layout/location_mode_item.xml b/apps/CtsVerifier/res/layout/location_mode_item.xml
deleted file mode 100644
index 5e8dedb..0000000
--- a/apps/CtsVerifier/res/layout/location_mode_item.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2013 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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" >
-
-    <ImageView
-        android:id="@+id/status"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginTop="10dip"
-        android:contentDescription="@string/pass_button_text"
-        android:padding="10dip"
-        android:src="@drawable/fs_indeterminate" />
-
-    <TextView
-        android:id="@+id/instructions"
-        style="@style/InstructionsSmallFont"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentTop="true"
-        android:layout_toRightOf="@id/status"
-        android:text="@string/location_mode_select_high_accuracy" />
-
-    <Button
-        android:id="@+id/launch_settings"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_below="@id/instructions"
-        android:layout_marginLeft="20dip"
-        android:layout_marginRight="20dip"
-        android:layout_toRightOf="@id/status"
-        android:onClick="launchSettings"
-        android:text="@string/location_mode_start_settings" />
-
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 660ff84..c57f6ab 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1670,57 +1670,6 @@
     <string name="cp_get_rules">Retrieving Automatic Zen Rules</string>
     <string name="cp_get_rule">Retrieving Automatic Zen Rule</string>
 
-    <string name="location_mode_high_accuracy_test">High Accuracy Mode Test</string>
-    <string name="location_mode_high_accuracy_info">
-        This test checks that the Location Mode API is consistent with the
-        Location Provider API when the device is in High Accuracy location mode.
-    </string>
-    <string name="location_mode_select_high_accuracy">
-        Please select the \"High accuracy\" mode at Settings > Location
-        (hint: tap the "Mode" item) and return here.
-    </string>
-    <string name="location_mode_battery_saving_test">Battery Saving Mode Test</string>
-    <string name="location_mode_battery_saving_info">
-        This test checks that the Location Mode API is consistent with the
-        Location Provider API when the device is in Battery Saving location mode.
-    </string>
-    <string name="location_mode_select_battery_saving">
-        Please select the \"Battery Saving\" mode at Settings > Location
-        (hint: tap the "Mode" item) and return here.
-    </string>
-    <string name="location_mode_device_only_test">Device Only Mode Test</string>
-    <string name="location_mode_device_only_info">
-        This test checks that the Location Mode API is consistent with the
-        Location Provider API when the device is in Device Only location mode.
-    </string>
-    <string name="location_mode_select_device_only">
-        Please select the \"Device Only\" mode at
-        Settings > Location (hint: tap the "Mode" item) and return here.
-    </string>
-    <string name="location_mode_off_test">Location Mode Off Test</string>
-    <string name="location_mode_off_info">
-        This test checks that the Location Mode API is consistent with the
-        Location Provider API when the device is in the Off location mode.
-    </string>
-
-    <string name="location_mode_start_settings">Launch Settings</string>
-    <string name="location_mode_turn_on">
-        Please turn ON location access (the switch at the top of Settings > Location)
-        and return here.
-    </string>
-    <string name="location_mode_turn_off">
-        Please turn OFF location access (the switch at the top of Settings > Location)
-        and return here.
-    </string>
-    <string name="location_mode_secure_gps_on">GPS provider should be ON in secure settings.</string>
-    <string name="location_mode_secure_gps_off">GPS provider should be OFF in secure settings.</string>
-    <string name="location_mode_secure_nlp_on">Network location provider should be ON in secure settings.</string>
-    <string name="location_mode_secure_nlp_off">Network location provider should be OFF in secure settings.</string>
-    <string name="location_mode_manager_gps_on">GPS provider should be ON in LocationManager.</string>
-    <string name="location_mode_manager_gps_off">GPS provider should be OFF in LocationManager.</string>
-    <string name="location_mode_manager_nlp_on">Network location provider should be ON in LocationManager.</string>
-    <string name="location_mode_manager_nlp_off">Network location provider should be OFF in LocationManager.</string>
-
     <string name="cacert_test">CA Cert Notification Test</string>
     <string name="cacert_info">This test checks that when a CA Certificate is installed, the user is notified.</string>
     <string name="cacert_do_something">Do it</string>
@@ -1923,6 +1872,7 @@
         4. Verify that the background color of the remaining image is blue.\n
         5. Verify that the header text says \"CtsVerifier\".\n
         6. Confirm your credentials and verify that the credentials you entered previously work.
+        7. The work app should be launched.
     </string>
     <string name="provisioning_byod_confirm_work_credentials_header">
         CtsVerifier
@@ -2625,6 +2575,15 @@
         1. Press the Go button to set a new password for the personal side.\n
         2. Lock and unlock the screen to verify that the personal side password was set correctly.\n
     </string>
+    <string name="provisioning_byod_work_profile_widget">Work profile widget</string>
+    <string name="provisioning_byod_work_profile_widget_info">Verify that work profile widget can be added into launcher</string>
+    <string name="provisioning_byod_work_profile_widget_description">
+        This test verifies that the widget in work profile can be added into Launcher.\n
+
+        1. Go to home screen.\n
+        2. Add the widget titled \"CTS Verifier\" and badged with work profile briefcase to the home screen.\n
+        3. If you can add the widget to the home screen, please select \"pass\". Otherwise, select \"fail\".
+    </string>
 
     <!-- Strings for DeviceOwnerNegativeTestActivity -->
     <string name="negative_device_owner">No Device Owner Tests</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeBatterySavingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeBatterySavingTestActivity.java
deleted file mode 100644
index 08fb34d..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeBatterySavingTestActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.location.LocationManager;
-import android.provider.Settings.Secure;
-import com.android.cts.verifier.R;
-
-public class LocationModeBatterySavingTestActivity extends LocationModeTestActivity {
-
-    @Override
-    protected void createTestItems() {
-        createUserItem(R.string.location_mode_turn_on);
-        createUserItem(R.string.location_mode_select_battery_saving);
-        createAutoItem(R.string.location_mode_secure_gps_off);
-        createAutoItem(R.string.location_mode_secure_nlp_on);
-        createAutoItem(R.string.location_mode_manager_gps_off);
-        createAutoItem(R.string.location_mode_manager_nlp_on);
-    }
-
-    @Override
-    protected void setInfoResources() {
-        setInfoResources(R.string.location_mode_battery_saving_test,
-                R.string.location_mode_battery_saving_info, -1);
-    }
-
-    @Override
-    protected void testAdvance(int state) {
-        switch (state) {
-            case 0:
-                testIsOn(0);
-                break;
-            case 1:
-                testIsExpectedMode(1, Secure.LOCATION_MODE_BATTERY_SAVING);
-                break;
-            case 2:
-                testSecureProviderIsDisabled(2, LocationManager.GPS_PROVIDER);
-                break;
-            case 3:
-                testSecureProviderIsEnabled(3, LocationManager.NETWORK_PROVIDER);
-                break;
-            case 4:
-                testManagerProviderIsDisabled(4, LocationManager.GPS_PROVIDER);
-                break;
-            case 5:
-                testManagerProviderIsEnabled(5, LocationManager.NETWORK_PROVIDER);
-                break;
-        }
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeDeviceOnlyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeDeviceOnlyTestActivity.java
deleted file mode 100644
index 0ba9f76..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeDeviceOnlyTestActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.location.LocationManager;
-import android.provider.Settings.Secure;
-import com.android.cts.verifier.R;
-
-public class LocationModeDeviceOnlyTestActivity extends LocationModeTestActivity {
-
-    @Override
-    protected void createTestItems() {
-        createUserItem(R.string.location_mode_turn_on);
-        createUserItem(R.string.location_mode_select_device_only);
-        createAutoItem(R.string.location_mode_secure_gps_on);
-        createAutoItem(R.string.location_mode_secure_nlp_off);
-        createAutoItem(R.string.location_mode_manager_gps_on);
-        createAutoItem(R.string.location_mode_manager_nlp_off);
-    }
-
-    @Override
-    protected void setInfoResources() {
-        setInfoResources(R.string.location_mode_device_only_test,
-                R.string.location_mode_device_only_info, -1);
-    }
-
-    @Override
-    protected void testAdvance(int state) {
-        switch (state) {
-            case 0:
-                testIsOn(0);
-                break;
-            case 1:
-                testIsExpectedMode(1, Secure.LOCATION_MODE_SENSORS_ONLY);
-                break;
-            case 2:
-                testSecureProviderIsEnabled(2, LocationManager.GPS_PROVIDER);
-                break;
-            case 3:
-                testSecureProviderIsDisabled(3, LocationManager.NETWORK_PROVIDER);
-                break;
-            case 4:
-                testManagerProviderIsEnabled(4, LocationManager.GPS_PROVIDER);
-                break;
-            case 5:
-                testManagerProviderIsDisabled(5, LocationManager.NETWORK_PROVIDER);
-                break;
-        }
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeHighAccuracyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeHighAccuracyTestActivity.java
deleted file mode 100644
index 9c10705..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeHighAccuracyTestActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.location.LocationManager;
-import android.provider.Settings.Secure;
-import com.android.cts.verifier.R;
-
-public class LocationModeHighAccuracyTestActivity extends LocationModeTestActivity {
-
-    @Override
-    protected void createTestItems() {
-        createUserItem(R.string.location_mode_turn_on);
-        createUserItem(R.string.location_mode_select_high_accuracy);
-        createAutoItem(R.string.location_mode_secure_gps_on);
-        createAutoItem(R.string.location_mode_secure_nlp_on);
-        createAutoItem(R.string.location_mode_manager_gps_on);
-        createAutoItem(R.string.location_mode_manager_nlp_on);
-    }
-
-    @Override
-    protected void setInfoResources() {
-        setInfoResources(R.string.location_mode_high_accuracy_test,
-                R.string.location_mode_high_accuracy_info, -1);
-    }
-
-    @Override
-    protected void testAdvance(int state) {
-        switch (state) {
-            case 0:
-                testIsOn(0);
-                break;
-            case 1:
-                testIsExpectedMode(1, Secure.LOCATION_MODE_HIGH_ACCURACY);
-                break;
-            case 2:
-                testSecureProviderIsEnabled(2, LocationManager.GPS_PROVIDER);
-                break;
-            case 3:
-                testSecureProviderIsEnabled(3, LocationManager.NETWORK_PROVIDER);
-                break;
-            case 4:
-                testManagerProviderIsEnabled(4, LocationManager.GPS_PROVIDER);
-                break;
-            case 5:
-                testManagerProviderIsEnabled(5, LocationManager.NETWORK_PROVIDER);
-                break;
-        }
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeOffTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeOffTestActivity.java
deleted file mode 100644
index 92b0742..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeOffTestActivity.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.location.LocationManager;
-import android.provider.Settings.Secure;
-import com.android.cts.verifier.R;
-
-public class LocationModeOffTestActivity extends LocationModeTestActivity {
-
-    @Override
-    protected void createTestItems() {
-        createUserItem(R.string.location_mode_turn_off);
-        createAutoItem(R.string.location_mode_secure_gps_off);
-        createAutoItem(R.string.location_mode_secure_nlp_off);
-        createAutoItem(R.string.location_mode_manager_gps_off);
-        createAutoItem(R.string.location_mode_manager_nlp_off);
-    }
-
-    @Override
-    protected void setInfoResources() {
-        setInfoResources(R.string.location_mode_off_test,
-                R.string.location_mode_off_info, -1);
-    }
-
-    @Override
-    protected void testAdvance(int state) {
-        switch (state) {
-            case 0:
-                testIsExpectedMode(0, Secure.LOCATION_MODE_OFF);
-                break;
-            case 1:
-                testSecureProviderIsDisabled(1, LocationManager.GPS_PROVIDER);
-                break;
-            case 2:
-                testSecureProviderIsDisabled(2, LocationManager.NETWORK_PROVIDER);
-                break;
-            case 3:
-                testManagerProviderIsDisabled(3, LocationManager.GPS_PROVIDER);
-                break;
-            case 4:
-                testManagerProviderIsDisabled(4, LocationManager.NETWORK_PROVIDER);
-                break;
-        }
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeTestActivity.java
deleted file mode 100644
index 92d41f4..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationModeTestActivity.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Asks the user to put the device in one of the four location modes and then checks to see if
- * {@link Secure#isLocationProviderEnabled(ContentResolver, String)} and {@link
- * LocationManager#isProviderEnabled(String)} have the expected values for GPS and Wi-Fi. For
- * example in battery saving mode, Wi-Fi should be on but GPS should be off.
- *
- * It would be hard to automate these tests because the {@link Secure#LOCATION_MODE} API is only
- * accessible to apps in the system image. Furthermore, selecting two of the modes requires the user
- * to accept the NLP confirmation dialog.
- */
-public abstract class LocationModeTestActivity
-        extends PassFailButtons.Activity implements Runnable {
-
-    private static final String STATE = "state";
-    protected static final int PASS = 1;
-    protected static final int FAIL = 2;
-    protected static final int WAIT_FOR_USER = 3;
-
-    protected int mState;
-    protected int[] mStatus;
-    private LayoutInflater mInflater;
-    private ViewGroup mItemList;
-    private Runnable mRunner;
-    private View mHandler;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState != null) {
-            mState = savedInstanceState.getInt(STATE, 0);
-        }
-
-        mRunner = this;
-        mInflater = getLayoutInflater();
-        View view = mInflater.inflate(R.layout.location_mode_main, null);
-        mItemList = (ViewGroup) view.findViewById(R.id.test_items);
-        mHandler = mItemList;
-
-        createTestItems();
-        mStatus = new int[mItemList.getChildCount()];
-        setContentView(view);
-
-        setPassFailButtonClickListeners();
-
-        setInfoResources();
-
-        getPassButton().setEnabled(false);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        outState.putInt(STATE, mState);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        next();
-    }
-
-    /**
-     * Template method used by the subclass to create the checks corresponding to each value of
-     * {@link #mState}. Subclass should call {@link #createUserItem(int)} and {@link
-     * #createAutoItem(int)} as appropriate to generate each item.
-     */
-    protected abstract void createTestItems();
-
-    /**
-     * Template method used by the subclass to call {@link #setInfoResources(int, int, int)} with
-     * the appropriate resources.
-     */
-    protected abstract void setInfoResources();
-
-    /**
-     * Subclass can call this to create a test step where the user must perform some action such
-     * as setting the location mode.
-     */
-    protected View createUserItem(int stringId) {
-        View item = mInflater.inflate(R.layout.location_mode_item, mItemList, false);
-        TextView instructions = (TextView) item.findViewById(R.id.instructions);
-        instructions.setText(stringId);
-        mItemList.addView(item);
-        return item;
-    }
-
-    /**
-     * Subclass can call this to create a test step where the test automatically evaluates whether
-     * an expected condition is satisfied, such as GPS is off.
-     */
-    protected View createAutoItem(int stringId) {
-        View item = mInflater.inflate(R.layout.location_mode_item, mItemList, false);
-        TextView instructions = (TextView) item.findViewById(R.id.instructions);
-        instructions.setText(stringId);
-        View button = item.findViewById(R.id.launch_settings);
-        button.setVisibility(View.GONE);
-        mItemList.addView(item);
-        return item;
-    }
-
-    /**
-     * Set the visible state of a test item to passed or failed.
-     */
-    private void setItemState(int index, boolean passed) {
-        ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
-        ImageView status = (ImageView) item.findViewById(R.id.status);
-        status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
-        View button = item.findViewById(R.id.launch_settings);
-        button.setClickable(false);
-        button.setEnabled(false);
-        status.invalidate();
-    }
-
-    /**
-     * Set the visible state of a test item to waiting.
-     */
-    protected void markItemWaiting(int index) {
-        ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
-        ImageView status = (ImageView) item.findViewById(R.id.status);
-        status.setImageResource(R.drawable.fs_warning);
-        status.invalidate();
-    }
-
-    /**
-     * Advances the state machine.
-     */
-    public void run() {
-        // Advance test state until we find case where it hasn't passed (yet)
-        while (mState < mStatus.length && mStatus[mState] != WAIT_FOR_USER) {
-            if (mStatus[mState] == PASS) {
-                setItemState(mState, true);
-                mState++;
-            } else if (mStatus[mState] == FAIL) {
-                setItemState(mState, false);
-                return;
-            } else {
-                break;
-            }
-        }
-
-        if (mState < mStatus.length && mStatus[mState] == WAIT_FOR_USER) {
-            markItemWaiting(mState);
-        }
-
-        testAdvance(mState);
-
-        if (mState == mStatus.length - 1 && mStatus[mState] == PASS) {
-            // All tests run and pass
-            getPassButton().setEnabled(true);
-        }
-    }
-
-    /**
-     * Launches Locations &gt; Settings so the user can set the location mode. Public because it
-     * is referenced by layout.
-     */
-    public void launchSettings(View button) {
-        startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
-    }
-
-    /**
-     * Return to the state machine to progress through the tests.
-     */
-    protected void next() {
-        mHandler.post(mRunner);
-    }
-
-    /**
-     * Wait for things to settle before returning to the state machine.
-     */
-    protected void delay() {
-        mHandler.postDelayed(mRunner, 2000);
-    }
-
-    // Tests
-
-    private int getLocationMode() {
-        ContentResolver cr = getContentResolver();
-        return Secure.getInt(cr, Secure.LOCATION_MODE, Secure.LOCATION_MODE_OFF);
-    }
-
-    protected void testIsOn(int i) {
-        int mode = getLocationMode();
-        boolean passed = mode != Secure.LOCATION_MODE_OFF;
-        if (passed) {
-            mStatus[i] = PASS;
-        } else {
-            mStatus[i] = WAIT_FOR_USER;
-        }
-        next();
-    }
-
-    protected void testIsExpectedMode(int i, int expectedMode) {
-        int mode = getLocationMode();
-        boolean passed = mode == expectedMode;
-        if (passed) {
-            mStatus[i] = PASS;
-            next();
-        } else {
-            mStatus[i] = WAIT_FOR_USER;
-            delay();
-        }
-    }
-
-    protected void testSecureProviderIsEnabled(int i, String provider) {
-        ContentResolver cr = getContentResolver();
-        boolean enabled = Secure.isLocationProviderEnabled(cr, provider);
-        mStatus[i] = enabled ? PASS : FAIL;
-        next();
-    }
-
-    protected void testSecureProviderIsDisabled(int i, String provider) {
-        ContentResolver cr = getContentResolver();
-        boolean enabled = Secure.isLocationProviderEnabled(cr, provider);
-        mStatus[i] = !enabled ? PASS : FAIL;
-        next();
-    }
-
-    protected void testManagerProviderIsEnabled(int i, String gpsProvider) {
-        LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
-        boolean enabled = manager.isProviderEnabled(gpsProvider);
-        mStatus[i] = enabled ? PASS : FAIL;
-        next();
-    }
-
-    protected void testManagerProviderIsDisabled(int i, String gpsProvider) {
-        LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
-        boolean enabled = manager.isProviderEnabled(gpsProvider);
-        mStatus[i] = !enabled ? PASS : FAIL;
-        next();
-    }
-
-    protected abstract void testAdvance(int state);
-}
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 52b8027..7674c45 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -109,6 +109,7 @@
     private TestListItem mOrganizationInfoTest;
     private TestListItem mPolicyTransparencyTest;
     private TestListItem mTurnOffWorkFeaturesTest;
+    private TestListItem mWidgetTest;
 
     public ByodFlowTestActivity() {
         super(R.layout.provisioning_byod,
@@ -439,7 +440,7 @@
                 PolicyTransparencyTestListActivity.class);
         policyTransparencyTestIntent.putExtra(
                 PolicyTransparencyTestListActivity.EXTRA_MODE,
-                PolicyTransparencyTestListActivity.MODE_PROFILE_OWNER);
+                PolicyTransparencyTestListActivity.MODE_MANAGED_PROFILE);
         policyTransparencyTestIntent.putExtra(
                 PolicyTransparencyTestActivity.EXTRA_TEST_ID, "BYOD_PolicyTransparency");
         mPolicyTransparencyTest = TestListItem.newTest(this,
@@ -630,6 +631,14 @@
                     R.string.provisioning_byod_no_gps_location_feature, Toast.LENGTH_SHORT)
                     .show();
         }
+
+        mWidgetTest = TestListItem.newTest(this,
+                R.string.provisioning_byod_work_profile_widget,
+                "BYOD_WorkProfileWidget",
+                new Intent(WorkProfileWidgetActivity.ACTION_TEST_WORK_PROFILE_WIDGET),
+                new String[] {PackageManager.FEATURE_APP_WIDGETS});
+        adapter.add(mWidgetTest);
+
     }
 
     // Return whether the intent can be resolved in the current profile
@@ -758,5 +767,4 @@
             new ComponentName(ByodFlowTestActivity.this, HandleIntentActivity.class.getName()),
             enableState, PackageManager.DONT_KILL_APP);
     }
-
 }
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 bfa65b7..649eaac 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -43,7 +43,8 @@
                 RecentsRedactionActivity.class.getName(),
                 CommandReceiverActivity.class.getName(),
                 SetSupportMessageActivity.class.getName(),
-                KeyChainTestActivity.class.getName()
+                KeyChainTestActivity.class.getName(),
+                WorkProfileWidgetActivity.class.getName()
         };
         for (String component : components) {
             mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index a3c0e28..d4d2d0e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -280,11 +280,12 @@
                             return;
                         }
                         clearAllPoliciesAndRestrictions();
-                    } else if(mode == PolicyTransparencyTestListActivity.MODE_PROFILE_OWNER) {
+                    } else if (mode == PolicyTransparencyTestListActivity.MODE_MANAGED_PROFILE
+                            || mode == PolicyTransparencyTestListActivity.MODE_MANAGED_USER) {
                         if (!mDpm.isProfileOwnerApp(getPackageName())) {
                             return;
                         }
-                        clearProfileOwnerRelatedPoliciesAndRestrictions();
+                        clearProfileOwnerRelatedPoliciesAndRestrictions(mode);
                     }
                     // No policies need to be cleared for COMP at the moment.
                 } break;
@@ -598,9 +599,8 @@
                 PackageManager.DONT_KILL_APP);
     }
 
-    private void clearProfileOwnerRelatedPoliciesAndRestrictions() {
-        clearPolicyTransparencyUserRestriction(
-                PolicyTransparencyTestListActivity.MODE_PROFILE_OWNER);
+    private void clearProfileOwnerRelatedPoliciesAndRestrictions(int mode) {
+        clearPolicyTransparencyUserRestriction(mode);
         clearProfileOwnerRelatedPolicies();
     }
 
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 e9f62a1..37d84a4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -174,6 +174,7 @@
         filter.addAction(SetSupportMessageActivity.ACTION_SET_SUPPORT_MSG);
         filter.addAction(KeyChainTestActivity.ACTION_KEYCHAIN);
         filter.addAction(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
+        filter.addAction(WorkProfileWidgetActivity.ACTION_TEST_WORK_PROFILE_WIDGET);
         dpm.addCrossProfileIntentFilter(getWho(context), filter,
                 DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
index 1ce0807..aa20b68 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
@@ -48,6 +48,7 @@
     private static final String DEVICE_ADMIN_SETTINGS_ID = "DEVICE_ADMIN_SETTINGS";
     private static final String DISABLE_STATUS_BAR_TEST_ID = "DISABLE_STATUS_BAR";
     private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
+    private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -146,6 +147,20 @@
                                 createManagedUserIntentWithBooleanParameter(
                                         CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
                                         false))}));
+
+        // Policy Transparency
+        final Intent policyTransparencyTestIntent = new Intent(this,
+                PolicyTransparencyTestListActivity.class);
+        policyTransparencyTestIntent.putExtra(
+                PolicyTransparencyTestListActivity.EXTRA_MODE,
+                PolicyTransparencyTestListActivity.MODE_MANAGED_USER);
+        // So that PolicyTransparencyTestListActivity knows which test to update with the result:
+        policyTransparencyTestIntent.putExtra(
+                PolicyTransparencyTestActivity.EXTRA_TEST_ID, POLICY_TRANSPARENCY_TEST_ID);
+        adapter.add(createTestItem(this, POLICY_TRANSPARENCY_TEST_ID,
+                R.string.device_profile_owner_policy_transparency_test,
+                policyTransparencyTestIntent));
+
     }
 
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index d8dbefd..998f8a8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.cts.verifier.managedprovisioning;
 
-import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.DataSetObserver;
@@ -30,7 +29,8 @@
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Test class to verify transparency for policies enforced by device/profile owner.
@@ -44,8 +44,9 @@
             "com.android.cts.verifier.managedprovisioning.extra.mode";
 
     public static final int MODE_DEVICE_OWNER = 1;
-    public static final int MODE_PROFILE_OWNER = 2;
+    public static final int MODE_MANAGED_PROFILE = 2;
     public static final int MODE_COMP = 3;
+    public static final int MODE_MANAGED_USER = 4;
 
     /**
      * Pairs of:
@@ -100,12 +101,12 @@
         }
     }
 
-    private static final ArrayList<String> ALSO_VALID_FOR_PO = new ArrayList<String>();
-    static {
-        ALSO_VALID_FOR_PO.add(
-                PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE);
-        ALSO_VALID_FOR_PO.add(PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD);
-    }
+    private static final List<String> ALSO_VALID_FOR_MANAGED_PROFILE = Arrays.asList(
+            PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE,
+            PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD);
+    private static final List<String> ALSO_VALID_FOR_MANAGED_USER = Arrays.asList(
+            PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE,
+            PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD);
 
     private int mMode;
 
@@ -123,7 +124,8 @@
                     + EXTRA_MODE);
         }
         mMode = getIntent().getIntExtra(EXTRA_MODE, MODE_DEVICE_OWNER);
-        if (mMode != MODE_DEVICE_OWNER && mMode != MODE_PROFILE_OWNER && mMode != MODE_COMP) {
+        if (mMode != MODE_DEVICE_OWNER && mMode != MODE_MANAGED_PROFILE && mMode != MODE_COMP
+                && mMode != MODE_MANAGED_USER) {
             throw new RuntimeException("Unknown mode " + mMode);
         }
 
@@ -161,7 +163,10 @@
             if (!isPolicyValid(test)) {
                 continue;
             }
-            if (mMode == MODE_PROFILE_OWNER && !ALSO_VALID_FOR_PO.contains(test)) {
+            if (mMode == MODE_MANAGED_PROFILE && !ALSO_VALID_FOR_MANAGED_PROFILE.contains(test)) {
+                continue;
+            }
+            if (mMode == MODE_MANAGED_USER && !ALSO_VALID_FOR_MANAGED_USER.contains(test)) {
                 continue;
             }
             final String title = getString(policy.second);
@@ -175,10 +180,12 @@
     private String getTestId(String title) {
         if (mMode == MODE_DEVICE_OWNER) {
             return "DO_" + title;
-        } else if (mMode == MODE_PROFILE_OWNER) {
-            return "PO_" + title;
+        } else if (mMode == MODE_MANAGED_PROFILE) {
+            return "MP_" + title;
         } else if (mMode == MODE_COMP){
             return "COMP_" + title;
+        } else if (mMode == MODE_MANAGED_USER) {
+            return "MU_" + title;
         }
         throw new RuntimeException("Unknown mode " + mMode);
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 466eab8..bba7428 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -164,16 +164,29 @@
         }
     }
 
-    private static final ArrayList<String> ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY =
-            new ArrayList<String>();
-    static {
-        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_APPS_CONTROL);
-        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_UNINSTALL_APPS);
-        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_MODIFY_ACCOUNTS);
-        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_SHARE_LOCATION);
-        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_UNIFIED_PASSWORD);
-        ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY.add(UserManager.DISALLOW_CONFIG_LOCATION);
-    }
+    private static final List<String> ALSO_VALID_FOR_MANAGED_PROFILE_POLICY_TRANSPARENCY =
+            Arrays.asList(
+                    UserManager.DISALLOW_APPS_CONTROL,
+                    UserManager.DISALLOW_UNINSTALL_APPS,
+                    UserManager.DISALLOW_MODIFY_ACCOUNTS, UserManager.DISALLOW_SHARE_LOCATION,
+                    UserManager.DISALLOW_UNIFIED_PASSWORD,
+                    UserManager.DISALLOW_CONFIG_LOCATION);
+    private static final List<String> ALSO_VALID_FOR_MANAGED_USER_POLICY_TRANSPARENCY =
+            Arrays.asList(
+                    UserManager.DISALLOW_ADJUST_VOLUME,
+                    UserManager.DISALLOW_APPS_CONTROL,
+                    UserManager.DISALLOW_CONFIG_WIFI,
+                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                    UserManager.DISALLOW_MODIFY_ACCOUNTS,
+                    UserManager.DISALLOW_OUTGOING_BEAM,
+                    UserManager.DISALLOW_REMOVE_USER,
+                    UserManager.DISALLOW_SHARE_LOCATION,
+                    UserManager.DISALLOW_UNINSTALL_APPS,
+                    UserManager.DISALLOW_CONFIG_DATE_TIME,
+                    UserManager.DISALLOW_CONFIG_LOCATION,
+                    UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
+                    UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+                    UserManager.DISALLOW_AMBIENT_DISPLAY);
 
     public static String getRestrictionLabel(Context context, String restriction) {
         final UserRestrictionItem item = findRestrictionItem(restriction);
@@ -206,8 +219,10 @@
             return result;
         } else if (mode == PolicyTransparencyTestListActivity.MODE_COMP) {
             return Arrays.asList(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
-        } else if (mode == PolicyTransparencyTestListActivity.MODE_PROFILE_OWNER) {
-            return ALSO_VALID_FOR_PO_POLICY_TRANSPARENCY;
+        } else if (mode == PolicyTransparencyTestListActivity.MODE_MANAGED_PROFILE) {
+            return ALSO_VALID_FOR_MANAGED_PROFILE_POLICY_TRANSPARENCY;
+        } else if (mode == PolicyTransparencyTestListActivity.MODE_MANAGED_USER) {
+            return ALSO_VALID_FOR_MANAGED_USER_POLICY_TRANSPARENCY;
         }
         throw new RuntimeException("Invalid mode " + mode);
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkProfileWidgetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkProfileWidgetActivity.java
new file mode 100644
index 0000000..4cf573b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkProfileWidgetActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.R;
+
+public class WorkProfileWidgetActivity extends DialogTestListActivity {
+
+    public static final String ACTION_TEST_WORK_PROFILE_WIDGET =
+            "com.android.cts.verifier.byod.test_work_profile_widget";
+
+    private DevicePolicyManager mDpm;
+
+    public WorkProfileWidgetActivity() {
+        super(R.layout.provisioning_byod,
+                R.string.provisioning_byod_work_profile_widget,
+                R.string.provisioning_byod_work_profile_widget_info,
+                R.string.provisioning_byod_work_profile_widget_description);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPrepareTestButton.setVisibility(View.GONE);
+
+        mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+        allowToAddCtsVerifierWidget();
+    }
+
+    @Override
+    public void finish() {
+        // Revert the policy.
+        disallowToAddCtsVerifierWidget();
+        super.finish();
+    }
+
+    @Override
+    protected void setupTests(ArrayTestListAdapter adapter) {
+        // no-op
+    }
+
+    private void allowToAddCtsVerifierWidget() {
+        mDpm.addCrossProfileWidgetProvider(getAdminComponent(), getPackageName());
+    }
+
+    private void disallowToAddCtsVerifierWidget() {
+        mDpm.removeCrossProfileWidgetProvider(getAdminComponent(), getPackageName());
+    }
+
+    private ComponentName getAdminComponent() {
+        return DeviceAdminTestReceiver.getReceiverComponentName();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 39bca50..8764dbe 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -624,7 +624,7 @@
             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
             NotificationManager.Policy policy = mNm.getNotificationPolicy();
             policy = new NotificationManager.Policy(
-                    NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
+                    NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA,
                     policy.priorityCallSenders,
                     policy.priorityMessageSenders);
             mNm.setNotificationPolicy(policy);
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index 7922f28..a7bf688 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -96,6 +96,10 @@
 # And when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
+
+#TODO(b/72620511) remove this condition when AtsDeviceInfo can be built with SDK again
+ifneq ($(LOCAL_PRIVATE_PLATFORM_APIS),true)
 LOCAL_SDK_VERSION := current
+endif
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
index 2fd609a..ebf881a 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
@@ -99,4 +99,9 @@
     public static boolean hasTelephony() {
         return hasSystemFeature(TELEPHONY_FEATURE);
     }
+
+    /** Returns true if the device has feature FEATURE_MICROPHONE */
+    public static boolean hasMicrophone() {
+        return hasSystemFeature(getPackageManager().FEATURE_MICROPHONE);
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index 48ed864..1b20fdd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -303,7 +303,7 @@
 
         File testFile;
         for (String testDir: testDirs) {
-            testFile = new File(getTestsDir(), filename);
+            testFile = new File(testDir, filename);
             if (testFile.exists()) {
                 return testFile;
             }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
index 60400be..50a5c41 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
@@ -19,6 +19,7 @@
 import com.android.tradefed.build.BuildRetrievalError;
 import com.android.tradefed.build.DeviceBuildInfo;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IBuildInfo.BuildInfoProperties;
 import com.android.tradefed.build.IBuildProvider;
 import com.android.tradefed.build.IDeviceBuildInfo;
 import com.android.tradefed.build.IDeviceBuildProvider;
@@ -239,6 +240,12 @@
         info.addBuildAttribute(ROOT_DIR, rootDir.getAbsolutePath());
         // For DeviceBuildInfo we populate the testsDir folder of the build info.
         if (info instanceof IDeviceBuildInfo) {
+            if (mArtificialRootDir == null) {
+                // If the real CTS directory is used, do not copy it again.
+                info.setProperties(
+                        BuildInfoProperties.DO_NOT_LINK_TESTS_DIR,
+                        BuildInfoProperties.DO_NOT_COPY_ON_SHARDING);
+            }
             File testDir = new File(rootDir, String.format("android-%s/testcases/",
                     getSuiteInfoName().toLowerCase()));
             ((IDeviceBuildInfo) info).setTestsDir(testDir, "0");
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/FeatureUtil.java b/common/host-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
index 6fae219..87d257a 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
@@ -32,6 +32,7 @@
     public static final String TELEPHONY_FEATURE = "android.hardware.telephony";
     public static final String TV_FEATURE = "android.hardware.type.television";
     public static final String WATCH_FEATURE = "android.hardware.type.watch";
+    public static final String FEATURE_MICROPHONE = "android.hardware.microphone";
 
     /** Returns true if the device has a given system feature */
     public static boolean hasSystemFeature(ITestDevice device, String feature)
@@ -95,4 +96,9 @@
     public static boolean hasTelephony(ITestDevice device) throws DeviceNotAvailableException {
         return hasSystemFeature(device, TELEPHONY_FEATURE);
     }
+
+    /** Returns true if the device has feature FEATURE_MICROPHONE */
+    public static boolean hasMicrophone(ITestDevice device) throws DeviceNotAvailableException {
+        return hasSystemFeature(device, FEATURE_MICROPHONE);
+    }
 }
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index 95af5e7..f9a861c 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -29,6 +29,9 @@
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts arcts vts general-tests
 
+# Need the dependency to build/run the module solely by atest.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_TESTCASES)/cts-current-api/current.api
+
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
 # Build the test APKs using their own makefiles
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnMultiStageTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnMultiStageTest.java
index 6eed964..25d7be7 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnMultiStageTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnMultiStageTest.java
@@ -66,6 +66,14 @@
     }
 
     public void testAlwaysOnVpnDisabled() throws Exception {
+        // Wait until always-on vpn package is being removed (with 1 minute timeout).
+        for (int i = 0; i < 60; i++) {
+            if (mDevicePolicyManager.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT) == null) {
+                break;
+            }
+            Thread.sleep(1000);  // 1 second.
+        }
+
         // After the vpn app being uninstalled, check that always-on vpn is null
         assertNull(mDevicePolicyManager.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT));
         assertFalse(VpnTestHelper.isNetworkVpn(mContext));
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index a35574b..7d0658a 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -25,7 +25,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -94,13 +93,8 @@
     }
 
     public void testGetActivitiesForUserFails() throws Exception {
-        try {
-            List<LauncherActivityInfo> activities =
-                    mLauncherApps.getActivityList(null, mUser);
-            fail("getActivities for non-profile user failed to throw exception");
-        } catch (SecurityException e) {
-            // Expected.
-        }
+        expectSecurityException(() -> mLauncherApps.getActivityList(null, mUser),
+                "getActivities for non-profile user failed to throw exception");
     }
 
     public void testSimpleAppInstalledForUser() throws Exception {
@@ -125,18 +119,17 @@
     }
 
     public void testAccessPrimaryProfileFromManagedProfile() throws Exception {
-        // Try to access main profile from managed profile, which is not allowed.
-        assertEquals(0, mLauncherApps.getActivityList(null, mUser).size());
-        try {
-            mLauncherApps.getApplicationInfo(SIMPLE_APP_PACKAGE, /* flags= */ 0, mUser);
-            fail("Missing exception");
-        } catch (PackageManager.NameNotFoundException e) {
-            // Expected.
-        }
-        assertFalse(mLauncherApps.isPackageEnabled(SIMPLE_APP_PACKAGE, mUser));
+        expectSecurityException(() -> mLauncherApps.getActivityList(null, mUser),
+                "getting activity list failed to throw security exception");
+        expectSecurityException(
+                () -> mLauncherApps.getApplicationInfo(SIMPLE_APP_PACKAGE, /* flags= */ 0, mUser),
+                "get applicationInfo failed to throw security exception");
+        expectSecurityException(() -> mLauncherApps.isPackageEnabled(SIMPLE_APP_PACKAGE, mUser),
+                "isPackageEnabled failed to throw security exception");
 
         final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.android.com/"));
-        assertNull(mLauncherApps.resolveActivity(intent, mUser));
+        expectSecurityException(() -> mLauncherApps.resolveActivity(intent, mUser),
+                "resolveActivity failed to throw security exception");
     }
 
     public void testGetProfiles_fromMainProfile() {
@@ -177,27 +170,17 @@
     }
 
     public void testLaunchNonExportActivityFails() throws Exception {
-        try {
-            mLauncherApps.startMainActivity(new ComponentName(
-                    SIMPLE_APP_PACKAGE,
-                    SIMPLE_APP_PACKAGE + ".NonExportedActivity"),
-                    mUser, null, null);
-            fail("starting non-exported activity failed to throw exception");
-        } catch (SecurityException e) {
-            // Expected.
-        }
+        expectSecurityException(() -> mLauncherApps.startMainActivity(new ComponentName(
+                SIMPLE_APP_PACKAGE, SIMPLE_APP_PACKAGE + ".NonExportedActivity"),
+                mUser, null, null),
+                "starting non-exported activity failed to throw exception");
     }
 
     public void testLaunchNonExportLauncherFails() throws Exception {
-        try {
-            mLauncherApps.startMainActivity(new ComponentName(
-                    SIMPLE_APP_PACKAGE,
-                    SIMPLE_APP_PACKAGE + ".NonLauncherActivity"),
-                    mUser, null, null);
-            fail("starting non-launcher activity failed to throw exception");
-        } catch (SecurityException e) {
-            // Expected.
-        }
+        expectSecurityException(() -> mLauncherApps.startMainActivity(new ComponentName(
+                SIMPLE_APP_PACKAGE, SIMPLE_APP_PACKAGE + ".NonLauncherActivity"),
+                mUser, null, null),
+                "starting non-launcher activity failed to throw exception");
     }
 
     public void testLaunchMainActivity() throws Exception {
@@ -213,6 +196,21 @@
         mInstrumentation.getContext().unregisterReceiver(receiver);
     }
 
+    private void expectSecurityException(ExceptionRunnable action, String failMessage)
+            throws Exception {
+        try {
+            action.run();
+            fail(failMessage);
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @FunctionalInterface
+    public interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+
     private UserHandle getUserHandleArgument(UserManager userManager, String key,
             Bundle arguments) throws Exception {
         String serial = arguments.getString(key);
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ImeCommandConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ImeCommandConstants.java
index 31368b0..e17a7e2 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ImeCommandConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ImeCommandConstants.java
@@ -52,12 +52,12 @@
     public static final String COMMAND_SWITCH_INPUT_METHOD = "switchInputMethod";
 
     /**
-     * This command has the mock IME call {@link android.inputmethodservice.InputMethodService#setInputMethodAndSubtype(String, InputMethodSubtype)} InputMethodService#setInputMethodAndSubtype(String imeId, InputMethodSubtype subtype)}.
+     * This command has the mock IME call {@link android.inputmethodservice.InputMethodService#switchInputMethod(String, InputMethodSubtype)} InputMethodService#switchInputMethod(String imeId, InputMethodSubtype subtype)}.
      * <ul>
      * <li>argument {@code imeId} needs to be specified by {@link #EXTRA_ARG_STRING1}.</li>
      * </ul>
      */
-    public static final String COMMAND_SET_INPUT_METHOD_AND_SUBTYPE = "setInputMethodAndSubtype";
+    public static final String COMMAND_SWITCH_INPUT_METHOD_WITH_SUBTYPE = "switchInputMethodWithSubtype";
 
     /**
      * This command has the mock IME call {@link android.inputmethodservice.InputMethodService#switchToNextInputMethod(boolean)} InputMethodService#switchToNextInputMethod(boolean onlyCurrentIme)}.
@@ -65,9 +65,9 @@
     public static final String COMMAND_SWITCH_TO_NEXT_INPUT = "switchToNextInput";
 
     /**
-     * This command has the mock IME call {@link android.inputmethodservice.InputMethodService#switchToLastInputMethod()} InputMethodService#switchToLastInputMethod()}.
+     * This command has the mock IME call {@link android.inputmethodservice.InputMethodService#switchToPreviousInputMethod()} InputMethodService#switchToPreviousInputMethod()}.
      */
-    public static final String COMMAND_SWITCH_TO_LAST_INPUT = "switchToLastInput";
+    public static final String COMMAND_SWITCH_TO_PREVIOUS_INPUT = "switchToPreviousInputMethod";
 
     /**
      * This command has the mock IME call {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)} InputMethodService#requestHideSelf(int flags)}.
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
index dae6db5..9c448e2 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
@@ -42,9 +42,9 @@
            "android.inputmethodservice.cts.devicetest.InputMethodServiceDeviceTest";
     public static final String TEST_CREATE_IME1 = "testCreateIme1";
     public static final String TEST_SWITCH_IME1_TO_IME2 = "testSwitchIme1ToIme2";
-    public static final String TEST_SET_INPUTMETHOD_AND_SUBTYPE = "testSetInputMethodAndSubtype";
+    public static final String TEST_SWITCH_INPUTMETHOD = "testSwitchInputMethod";
     public static final String TEST_SWITCH_NEXT_INPUT = "testSwitchToNextInputMethod";
-    public static final String TEST_SWITCH_LAST_INPUT = "testSwitchToLastInputMethod";
+    public static final String TEST_SWITCH_PREVIOUS_INPUT = "switchToPreviousInputMethod";
     public static final String TEST_INPUT_UNBINDS_ON_IME_STOPPED = "testInputUnbindsOnImeStopped";
     public static final String TEST_INPUT_UNBINDS_ON_APP_STOPPED = "testInputUnbindsOnAppStopped";
 }
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index 73410e4..ba5a1b1 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -29,10 +29,10 @@
         .ON_UNBIND_INPUT;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.ACTION_IME_COMMAND;
 import static android.inputmethodservice.cts.common.ImeCommandConstants
-        .COMMAND_SET_INPUT_METHOD_AND_SUBTYPE;
+        .COMMAND_SWITCH_INPUT_METHOD_WITH_SUBTYPE;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.COMMAND_SWITCH_INPUT_METHOD;
 import static android.inputmethodservice.cts.common.ImeCommandConstants
-        .COMMAND_SWITCH_TO_LAST_INPUT;
+        .COMMAND_SWITCH_TO_PREVIOUS_INPUT;
 import static android.inputmethodservice.cts.common.ImeCommandConstants
         .COMMAND_SWITCH_TO_NEXT_INPUT;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_ARG_STRING1;
@@ -127,9 +127,9 @@
     }
 
     @Test
-    public void testSetInputMethodAndSubtype() throws Throwable {
+    public void testSwitchInputMethod() throws Throwable {
         final TestHelper helper = new TestHelper(
-                getClass(), DeviceTestConstants.TEST_SET_INPUTMETHOD_AND_SUBTYPE);
+                getClass(), DeviceTestConstants.TEST_SWITCH_INPUTMETHOD);
         final long startActivityTime = SystemClock.uptimeMillis();
         helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
         pollingCheck(() -> helper.queryAllEvents()
@@ -142,7 +142,7 @@
         // call setInputMethodAndSubtype(IME2, null)
         helper.shell(ShellCommandUtils.broadcastIntent(
                 ACTION_IME_COMMAND, Ime1Constants.PACKAGE,
-                "-e", EXTRA_COMMAND, COMMAND_SET_INPUT_METHOD_AND_SUBTYPE,
+                "-e", EXTRA_COMMAND, COMMAND_SWITCH_INPUT_METHOD_WITH_SUBTYPE,
                 "-e", EXTRA_ARG_STRING1, Ime2Constants.IME_ID));
         pollingCheck(() -> helper.shell(ShellCommandUtils.getCurrentIme())
                         .equals(Ime2Constants.IME_ID),
@@ -177,9 +177,9 @@
     }
 
     @Test
-    public void testSwitchToLastInputMethod() throws Throwable {
+    public void switchToPreviousInputMethod() throws Throwable {
         final TestHelper helper = new TestHelper(
-                getClass(), DeviceTestConstants.TEST_SWITCH_LAST_INPUT);
+                getClass(), DeviceTestConstants.TEST_SWITCH_PREVIOUS_INPUT);
         final long startActivityTime = SystemClock.uptimeMillis();
         helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
         helper.findUiObject(R.id.text_entry).click();
@@ -192,7 +192,7 @@
                 TIMEOUT, "CtsInputMethod2.onStartInput is called");
         helper.shell(ShellCommandUtils.broadcastIntent(
                 ACTION_IME_COMMAND, Ime2Constants.PACKAGE,
-                "-e", EXTRA_COMMAND, COMMAND_SWITCH_TO_LAST_INPUT));
+                "-e", EXTRA_COMMAND, COMMAND_SWITCH_TO_PREVIOUS_INPUT));
         pollingCheck(() -> helper.shell(ShellCommandUtils.getCurrentIme())
                         .equals(initialIme),
                 TIMEOUT, initialIme + " is current IME");
diff --git a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/ImeCommandReceiver.java b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/ImeCommandReceiver.java
index c926b3c..dd53765 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/ImeCommandReceiver.java
+++ b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/ImeCommandReceiver.java
@@ -86,17 +86,18 @@
                 mIme.commandRequestHideSelf(flags);
                 return;
             }
-            case ImeCommandConstants.COMMAND_SET_INPUT_METHOD_AND_SUBTYPE: {
+            case ImeCommandConstants.COMMAND_SWITCH_INPUT_METHOD_WITH_SUBTYPE: {
                 final String imeId = getString1(intent);
-                mIme.setInputMethodAndSubtype(imeId, null);
+                // we don't support mock imes with subtypes yet.
+                mIme.switchInputMethod(imeId, null /* subtype*/);
                 return;
             }
             case ImeCommandConstants.COMMAND_SWITCH_TO_NEXT_INPUT: {
                 mIme.switchToNextInputMethod(false);
                 return;
             }
-            case ImeCommandConstants.COMMAND_SWITCH_TO_LAST_INPUT: {
-                mIme.switchToLastInputMethod();
+            case ImeCommandConstants.COMMAND_SWITCH_TO_PREVIOUS_INPUT: {
+                mIme.switchToPreviousInputMethod();
                 return;
             }
             default: {
diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
index 8badded..25fe8b1 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -106,10 +106,10 @@
     }
 
     @Test
-    public void testSetInputMethodAndSubtype() throws Exception {
+    public void testSwitchInputMethod() throws Exception {
         final TestInfo testSetInputMethod = new TestInfo(
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
-                DeviceTestConstants.TEST_SET_INPUTMETHOD_AND_SUBTYPE);
+                DeviceTestConstants.TEST_SWITCH_INPUTMETHOD);
         sendTestStartEvent(testSetInputMethod);
         installPackage(Ime1Constants.APK, "-r");
         installPackage(Ime2Constants.APK, "-r");
@@ -138,10 +138,10 @@
     }
 
     @Test
-    public void testSwitchToLastInput() throws Exception {
+    public void testSwitchToPreviousInput() throws Exception {
         final TestInfo testSwitchInputs = new TestInfo(
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
-                DeviceTestConstants.TEST_SWITCH_LAST_INPUT);
+                DeviceTestConstants.TEST_SWITCH_PREVIOUS_INPUT);
         sendTestStartEvent(testSwitchInputs);
         installPackage(Ime1Constants.APK, "-r");
         installPackage(Ime2Constants.APK, "-r");
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index e4dee54..7296513 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -66,16 +66,6 @@
     private static final String TAG = AtomTests.class.getSimpleName();
 
     @Test
-    public void testAppStart() throws Exception {
-        Context context = InstrumentationRegistry.getContext();
-        Intent intent = new Intent(context, StatsdCtsForegroundActivity.class);
-        intent.putExtra(StatsdCtsForegroundActivity.KEY_ACTION,
-                StatsdCtsForegroundActivity.ACTION_SLEEP_WHILE_TOP);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(intent);
-    }
-
-    @Test
     public void testAudioState() {
         // TODO: This should surely be getTargetContext(), here and everywhere, but test first.
         Context context = InstrumentationRegistry.getContext();
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index 14f3dba..615bbc0 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -38,6 +38,7 @@
     public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
     public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
     public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+    public static final String ACTION_CRASH = "action.crash";
 
     public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
     public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -65,6 +66,9 @@
             case ACTION_SHOW_APPLICATION_OVERLAY:
                 doShowApplicationOverlay();
                 break;
+            case ACTION_CRASH:
+                doCrash();
+                break;
             default:
                 Log.e(TAG, "Intent had invalid action " + action);
                 finish();
@@ -116,4 +120,8 @@
         AtomTests.sleep(SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY);
         finish();
     }
+
+    private void doCrash() {
+        Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
+    }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index a9d5163..d5e4db7 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -16,6 +16,8 @@
 package android.cts.statsd.atom;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
 
 import android.os.WakeLockLevelEnum;
 
@@ -30,6 +32,7 @@
 import com.android.os.AtomsProto.CameraStateChanged;
 import com.android.os.AtomsProto.CpuTimePerUid;
 import com.android.os.AtomsProto.CpuTimePerUidFreq;
+import com.android.os.AtomsProto.DropboxErrorChanged;
 import com.android.os.AtomsProto.FlashlightStateChanged;
 import com.android.os.AtomsProto.ForegroundServiceStateChanged;
 import com.android.os.AtomsProto.GpsScanStateChanged;
@@ -73,19 +76,17 @@
 
     public void testAppStartChanged() throws Exception {
         final int atomTag = Atom.APP_START_CHANGED_FIELD_NUMBER;
-        final String name = "testAppStart";
 
         createAndUploadConfig(atomTag, false);
         Thread.sleep(WAIT_TIME_SHORT);
 
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
+        runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top");
 
         // Sorted list of events in order in which they occurred.
         List<EventMetricData> data = getEventMetricDataList();
 
         AppStartChanged atom = data.get(0).getAtom().getAppStartChanged();
         assertEquals("com.android.server.cts.device.statsd", atom.getPkgName());
-        assertEquals(AppStartChanged.TransitionType.WARM, atom.getType());
         assertEquals("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity",
                 atom.getActivityName());
         assertFalse(atom.getIsInstantApp());
@@ -211,6 +212,8 @@
     }
 
     public void testCpuTimePerUidFreq() throws Exception {
+        // TODO: pending resolution on b/72505991 and b/74127576
+        if (1==1) return;
         StatsdConfig.Builder config = getPulledAndAnomalyConfig();
         FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
                 .setField(Atom.CPU_TIME_PER_UID_FREQ_FIELD_NUMBER)
@@ -236,13 +239,14 @@
         // implemented.
         boolean found = false;
         int uid = getUid();
+        long timeSpent = 0;
         for (Atom atom : atomList) {
             if (atom.getCpuTimePerUidFreq().getUid() == uid) {
                 found = true;
-                assertTrue(atom.getCpuTimePerUidFreq().getFreqIdx() >= 0);
-                assertTrue(atom.getCpuTimePerUidFreq().getTimeMillis() > 0);
+                timeSpent += atom.getCpuTimePerUidFreq().getTimeMillis();
             }
         }
+        assertTrue(timeSpent > 0);
         assertTrue("found uid " + uid, found);
     }
 
@@ -601,4 +605,20 @@
         assertStatesOccurred(stateSet, data, 1_000,
                 atom -> atom.getOverlayStateChanged().getState().getNumber());
     }
+
+    public void testDropboxErrorChanged() throws Exception {
+        final int atomTag = Atom.DROPBOX_ERROR_CHANGED_FIELD_NUMBER;
+        createAndUploadConfig(atomTag, false);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        runActivity("StatsdCtsForegroundActivity", "action", "action.crash");
+
+        Thread.sleep(WAIT_TIME_SHORT);
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        DropboxErrorChanged atom = data.get(0).getAtom().getDropboxErrorChanged();
+        assertTrue(atom.getIsInstantApp() == 0);
+        assertEquals("data_app_crash", atom.getTag());
+    }
 }
diff --git a/hostsidetests/sustainedperf/shadertoy_android/Android.mk b/hostsidetests/sustainedperf/shadertoy_android/Android.mk
index ba6bf64..2e1bbdd 100644
--- a/hostsidetests/sustainedperf/shadertoy_android/Android.mk
+++ b/hostsidetests/sustainedperf/shadertoy_android/Android.mk
@@ -24,7 +24,7 @@
 # Include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
 
-LOCAL_JNI_SHARED_LIBRARIES := libgltest libc++
+LOCAL_JNI_SHARED_LIBRARIES := libgltest
 #LOCAL_SHARED_LIBRARIES := libc++
 #LOCAL_STATIC_LIBRARIES := libc++_static
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/hostsidetests/theme/assets/26/280dpi.zip b/hostsidetests/theme/assets/26/280dpi.zip
old mode 100644
new mode 100755
index d5260d9..c2e2ed0
--- a/hostsidetests/theme/assets/26/280dpi.zip
+++ b/hostsidetests/theme/assets/26/280dpi.zip
Binary files differ
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 070b2eb..e313c4f 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -97,54 +97,65 @@
         }
     }
 
-    public void testOnlyPostPCanToggleAlarmsAndMediaTest() throws Exception {
+    public void testOnlyPostPCanToggleAlarmsMediaSystemTest() throws Exception {
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
-            // Post-P can toggle alarms and media
-            // toggle on alarms and media:
+            // Post-P can toggle alarms, media, system
+            // toggle on alarms, media, system:
             mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
                     NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
-                            | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, 0, 0));
+                            | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+                            | NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM, 0, 0));
             NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
             assertTrue((policy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) != 0);
             assertTrue((policy.priorityCategories
-                    & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0);
+                    & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0);
+            assertTrue((policy.priorityCategories
+                    & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) != 0);
 
-            // toggle off alarms and media
+            // toggle off alarms, media, system
             mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
             policy = mNotificationManager.getNotificationPolicy();
             assertTrue((policy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0);
             assertTrue((policy.priorityCategories &
-                    NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0);
+                    NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) == 0);
+            assertTrue((policy.priorityCategories &
+                    NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0);
         } else {
             // Pre-P cannot toggle alarms and media
             NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
             int alarmBit = origPolicy.priorityCategories & NotificationManager.Policy
                     .PRIORITY_CATEGORY_ALARMS;
             int mediaBit = origPolicy.priorityCategories & NotificationManager.Policy
-                    .PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER;
+                    .PRIORITY_CATEGORY_MEDIA;
+            int systemBit = origPolicy.priorityCategories & NotificationManager.Policy
+                    .PRIORITY_CATEGORY_SYSTEM;
 
-            // attempt to toggle off alarms and media:
+            // attempt to toggle off alarms, media, system:
             mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
             NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
             assertEquals(alarmBit, policy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS);
             assertEquals(mediaBit, policy.priorityCategories
-                    & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER);
+                    & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA);
+            assertEquals(systemBit, policy.priorityCategories
+                    & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM);
 
-            // attempt to toggle on alarms and media:
+            // attempt to toggle on alarms, media, system:
             mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
                     NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
-                            | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, 0, 0));
+                            | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA, 0, 0));
             policy = mNotificationManager.getNotificationPolicy();
             assertEquals(alarmBit, policy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS);
             assertEquals(mediaBit, policy.priorityCategories
-                    & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER);
+                    & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA);
+            assertEquals(systemBit, policy.priorityCategories
+                    & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM);
         }
     }
 
diff --git a/tests/app/src/android/app/cts/WallpaperColorsTest.java b/tests/app/src/android/app/cts/WallpaperColorsTest.java
index 105cae0..7b4c59e 100644
--- a/tests/app/src/android/app/cts/WallpaperColorsTest.java
+++ b/tests/app/src/android/app/cts/WallpaperColorsTest.java
@@ -21,6 +21,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
@@ -100,4 +101,13 @@
         Assert.assertNull(colors.getSecondaryColor());
     }
 
+    @Test
+    public void fromDrawableDoesntMutateBounds() {
+        ColorDrawable drawable = new ColorDrawable(Color.GREEN);
+        Rect initialBounds = drawable.copyBounds();
+
+        WallpaperColors.fromDrawable(drawable);
+
+        Assert.assertEquals(drawable.getBounds(), initialBounds);
+    }
 }
diff --git a/tests/autofillservice/res/layout/login_activity.xml b/tests/autofillservice/res/layout/login_activity.xml
index 2a971b1..1707153 100644
--- a/tests/autofillservice/res/layout/login_activity.xml
+++ b/tests/autofillservice/res/layout/login_activity.xml
@@ -41,7 +41,8 @@
             android:maxEms="5"
             android:maxLength="25"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"
+            android:imeOptions="flagNoFullscreen" />
     </LinearLayout>
 
     <LinearLayout
@@ -59,7 +60,8 @@
             android:id="@+id/password"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:inputType="textPassword"/>
+            android:inputType="textPassword"
+            android:imeOptions="flagNoFullscreen" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/tests/autofillservice/res/layout/simple_save_activity.xml b/tests/autofillservice/res/layout/simple_save_activity.xml
index 8eb5255..c48e0c3 100644
--- a/tests/autofillservice/res/layout/simple_save_activity.xml
+++ b/tests/autofillservice/res/layout/simple_save_activity.xml
@@ -36,12 +36,14 @@
         <EditText
             android:id="@+id/input"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"
+            android:imeOptions="flagNoFullscreen" />
         <EditText
             android:id="@+id/password"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:inputType="textPassword"/>
+            android:inputType="textPassword"
+            android:imeOptions="flagNoFullscreen" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
index 0eda6be..8b338de 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
@@ -62,7 +62,7 @@
                         if (timeout != null) {
                             long before = timeout.ms();
                             timeout.increase();
-                            Log.d(TAG, "Increased " + timeout.getName() + " from " + before + "ms "
+                            Log.d(TAG, "Increased " + timeout.getName() + " from " + before + "ms"
                                     + " to " + timeout.ms() + "ms");
                         }
                         caught = e;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index 6a2fe8f..d3613e8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -29,6 +29,7 @@
 import static android.autofillservice.cts.UiBot.PORTRAIT;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -39,6 +40,8 @@
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
 import android.view.autofill.AutofillValue;
 
 import org.junit.After;
@@ -379,4 +382,86 @@
         // Dataset should still be shown
         mUiBot.assertDatasets("dataset1");
     }
+
+    @Test
+    public void testDatasetGoesAwayWhenAutofilledAppIsKilled() throws Exception {
+        // Set service.
+        enableService();
+
+        // Start activity that is autofilled in a separate process so it can be killed
+        startAndWaitExternalActivity();
+
+        final CannedFillResponse response = new CannedFillResponse.Builder()
+                .addDataset(new CannedFillResponse.CannedDataset.Builder(
+                        createPresentation("dataset"))
+                                .setField(ID_USERNAME, "filled").build())
+                .build();
+        sReplier.addResponse(response);
+
+        // Trigger autofill on username
+        mUiBot.selectByRelativeId(ID_USERNAME);
+
+        // Wait for fill request to be processed
+        sReplier.getNextFillRequest();
+
+        // Wait until dataset is shown
+        mUiBot.assertDatasets("dataset");
+
+        // Kill activity
+        killOfProcessLoginActivityProcess();
+
+        // Make sure dataset is not shown anymore
+        // TODO: calling mUiBot.assertNoDatasets() fails with StaleObjectException because it
+        // takes a while for the UiDevice to return return when searching for
+        // UiBot.DATASET_PICKER_SELECTOR here - must investigate why
+        mUiBot.getDevice().wait(Until.gone(UiBot.DATASET_PICKER_SELECTOR),
+                Timeouts.DATASET_PICKER_NOT_SHOWN_NAPTIME_MS);
+
+        // Restart activity an make sure the dataset is still not shown
+        startAndWaitExternalActivity();
+        mUiBot.assertNoDatasets();
+    }
+
+    @Test
+    public void testSaveRemainsWhenAutofilledAppIsKilled() throws Exception {
+        // Set service.
+        enableService();
+
+        // Start activity that is autofilled in a separate process so it can be killed
+        startAndWaitExternalActivity();
+
+        final CannedFillResponse response = new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME, ID_USERNAME)
+                .build();
+        sReplier.addResponse(response);
+
+        // Trigger autofill on username
+        mUiBot.selectByRelativeId(ID_USERNAME);
+
+        // Wait for fill request to be processed
+        sReplier.getNextFillRequest();
+
+        // Wait until dataset is shown
+        mUiBot.assertNoDatasetsEver();
+
+        // Trigger save
+        mUiBot.setTextByRelativeId(ID_USERNAME, "dude");
+        mUiBot.selectByRelativeId(ID_LOGIN);
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_USERNAME);
+
+        // Kill activity
+        killOfProcessLoginActivityProcess();
+
+        // Make sure save is still showing
+        final UiObject2 saveSnackBar = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_USERNAME);
+
+        mUiBot.saveForAutofill(saveSnackBar, true);
+
+        final InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
+
+        // Make sure data is correctly saved
+        final AssistStructure.ViewNode username = findNodeByResourceId(saveRequest.structure,
+                ID_USERNAME);
+        assertTextAndValue(username, "dude");
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index ec60cc8..11bc53c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -91,8 +91,7 @@
     private static final String RESOURCE_STRING_SAVE_SNACKBAR_ACCESSIBILITY_TITLE =
             "autofill_save_accessibility_title";
 
-    private static final BySelector DATASET_PICKER_SELECTOR = By.res("android",
-            RESOURCE_ID_DATASET_PICKER);
+    static final BySelector DATASET_PICKER_SELECTOR = By.res("android", RESOURCE_ID_DATASET_PICKER);
     private static final BySelector SAVE_UI_SELECTOR = By.res("android", RESOURCE_ID_SAVE_SNACKBAR);
 
     private static final boolean DONT_DUMP_ON_ERROR = false;
@@ -129,6 +128,10 @@
         mOkToCallAssertNoDatasets = false;
     }
 
+    UiDevice getDevice() {
+        return mDevice;
+    }
+
     /**
      * Asserts the dataset picker is not shown anymore.
      *
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
index 54ac252..6446511 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
@@ -38,7 +38,6 @@
  * the Autofill APIs.
  */
 public class VirtualContainerActivityCompatModeTest extends VirtualContainerActivityTest {
-    private static final String TAG = "VirtualContainerActivityCompatModeTest";
     private static final Context sContext = InstrumentationRegistry.getContext();
 
     @ClassRule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 9d5e5fb..7e2947a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -59,6 +59,9 @@
  */
 public class VirtualContainerActivityTest extends AutoFillServiceTestCase {
 
+    // TODO(b/74256300): remove when fixed it :-)
+    private static final boolean BUG_74256300_FIXED = false;
+
     @Rule
     public final AutofillActivityTestRule<VirtualContainerActivity> mActivityRule =
             new AutofillActivityTestRule<VirtualContainerActivity>(VirtualContainerActivity.class) {
@@ -143,22 +146,24 @@
 
         // Set expectations.
         sReplier.addResponse(new CannedDataset.Builder()
-                .setField(ID_USERNAME, "dude")
-                .setField(ID_PASSWORD, "sweet")
-                .setPresentation(createPresentation("The Dude"))
+                .setField(ID_USERNAME, "dude", createPresentation("DUDE"))
+                .setField(ID_PASSWORD, "sweet", createPresentation("SWEET"))
                 .build());
         mActivity.expectAutoFill("dude", "sweet");
         mActivity.mCustomView.setSync(sync);
 
         // Trigger auto-fill.
         focusToUsername();
-        assertDatasetShown(mActivity.mUsername, "The Dude");
+        assertDatasetShown(mActivity.mUsername, "DUDE");
 
         // Play around with focus to make sure picker is properly drawn.
-        focusToPassword();
-        assertDatasetShown(mActivity.mPassword, "The Dude");
-        focusToUsername();
-        assertDatasetShown(mActivity.mUsername, "The Dude");
+        if (BUG_74256300_FIXED || !mCompatMode) {
+            focusToPassword();
+            assertDatasetShown(mActivity.mPassword, "SWEET");
+
+            focusToUsername();
+            assertDatasetShown(mActivity.mUsername, "DUDE");
+        }
 
         // Make sure input was sanitized.
         final FillRequest request = sReplier.getNextFillRequest();
@@ -206,7 +211,7 @@
         assertWithMessage("Password node is focused").that(password.isFocused()).isFalse();
 
         // Auto-fill it.
-        mUiBot.selectDataset("The Dude");
+        mUiBot.selectDataset("DUDE");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -451,7 +456,6 @@
 
     @Test
     public void testSave_submitButtonClicked() throws Throwable {
-        if (mCompatMode) return; // TODO(b/73649008): implement it
         saveTest(CommitType.SUBMIT_BUTTON_CLICKED);
     }
 
@@ -476,11 +480,16 @@
             case PARENT_VIEW_GONE:
                 response.setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE);
                 break;
+            case EXPLICIT_COMMIT:
+                // does nothing
+                break;
             case SUBMIT_BUTTON_CLICKED:
                 response
                     .setSaveInfoFlags(SaveInfo.FLAG_DONT_SAVE_ON_FINISH)
                     .setSaveTriggerId(mActivity.mCustomView.mLoginButtonId);
                 break;
+            default:
+                throw new IllegalArgumentException("invalid type: " + commitType);
         }
         sReplier.addResponse(response.build());
 
@@ -520,13 +529,6 @@
         final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
         final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
 
-        if (mCompatMode) {
-            // TODO(b/73557456): temporarily not checking values as it's not working
-            assertThat(username).isNotNull();
-            assertThat(password).isNotNull();
-            return;
-        }
-
         assertTextAndValue(username, "foo");
         assertTextAndValue(password, "bar");
     }
@@ -584,10 +586,6 @@
      * Asserts the dataset picker is properly displayed in a give line.
      */
     private void assertDatasetShown(Line line, String... expectedDatasets) throws Exception {
-        // TODO(b/73548352): temporary workaround until we figure out why there's a mismatch on the
-        // values reported while running on compat mode.
-        if (mCompatMode) return;
-
         final Rect pickerBounds = mUiBot.assertDatasets(expectedDatasets).getVisibleBounds();
         final Rect fieldBounds = line.getAbsCoordinates();
         assertWithMessage("vertical coordinates don't match; picker=%s, field=%s", pickerBounds,
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 625cfc2..a5a991b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -269,15 +269,12 @@
     void clickLogin() {
         Log.d(TAG, "clickLogin()");
         if (mCompatMode) {
-            // TODO(b/73649008): implement it
-            throw new IllegalArgumentException("clickLogin() on compat mode not implemented yet");
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED, LOGIN_BUTTON_VIRTUAL_ID);
         } else {
             mAfm.notifyViewClicked(this, LOGIN_BUTTON_VIRTUAL_ID);
         }
     }
 
-
-
     private Item getItem(int id) {
         final Item item = mItems.get(id);
         assertWithMessage("No item for id %s", id).that(item).isNotNull();
@@ -303,6 +300,15 @@
         return node;
     }
 
+    private AccessibilityNodeInfo onProvideAutofillCompatModeAccessibilityNodeInfoForLoginButton() {
+        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
+        node.setSource(this, LOGIN_BUTTON_VIRTUAL_ID);
+        node.setPackageName(getContext().getPackageName());
+        // TODO(b/72811561): ideally this button should be visible / drawn in the canvas and contain
+        // more properties like boundaries, class name, text etc...
+        return node;
+    }
+
     static void assertHtmlInfo(ViewNode node) {
         final String name = node.getText().toString();
         final HtmlInfo info = node.getHtmlInfo();
@@ -341,12 +347,16 @@
                 @Override
                 public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
                     Log.d(TAG, "createAccessibilityNodeInfo(): id=" + virtualViewId);
-                    if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) {
-                        return onProvideAutofillCompatModeAccessibilityNodeInfo();
+                    switch (virtualViewId) {
+                        case AccessibilityNodeProvider.HOST_VIEW_ID:
+                            return onProvideAutofillCompatModeAccessibilityNodeInfo();
+                        case LOGIN_BUTTON_VIRTUAL_ID:
+                            return onProvideAutofillCompatModeAccessibilityNodeInfoForLoginButton();
+                        default:
+                            final Item item = getItem(virtualViewId);
+                            return item.provideAccessibilityNodeInfo(VirtualContainerView.this,
+                                    getContext());
                     }
-                    final Item item = getItem(virtualViewId);
-                    return item.provideAccessibilityNodeInfo(VirtualContainerView.this,
-                            getContext());
                 }
 
                 @Override
@@ -377,6 +387,16 @@
         mOverrideDispatchProvideAutofillStructure = flag;
     }
 
+    private void sendAccessibilityEvent(int eventType, int virtualId) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setEventType(eventType);
+        event.setSource(VirtualContainerView.this, virtualId);
+        event.setEnabled(true);
+        event.setPackageName(getContext().getPackageName());
+        // TODO(b/72811561): recycle event?
+        getContext().getSystemService(AccessibilityManager.class).sendAccessibilityEvent(event);
+    }
+
     private static int nextId;
 
     final class Line {
@@ -385,6 +405,8 @@
         final Item text;
         // Boundaries of the text field, relative to the CustomView
         final Rect bounds = new Rect();
+        // Boundaries of the text field, relative to the screen
+        Rect absBounds;
 
         private boolean focused;
         private boolean visible = true;
@@ -396,20 +418,18 @@
 
         void changeFocus(boolean focused) {
             this.focused = focused;
+
+            if (focused) {
+                absBounds = getAbsCoordinates();
+                Log.v(TAG, "Setting absBounds for " + text.id + " on focus change: " + absBounds);
+            }
+
             if (mCompatMode) {
-                final AccessibilityEvent event = AccessibilityEvent.obtain();
-                event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                event.setSource(VirtualContainerView.this, text.id);
-                event.setEnabled(true);
-                event.setPackageName(getContext().getPackageName());
-                // TODO(b/72811561): recycle event?
-                getContext().getSystemService(AccessibilityManager.class)
-                    .sendAccessibilityEvent(event);
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED, text.id);
                 return;
             }
 
             if (focused) {
-                final Rect absBounds = getAbsCoordinates();
                 Log.d(TAG, "focus gained on " + text.id + "; absBounds=" + absBounds);
                 mAfm.notifyViewEntered(VirtualContainerView.this, text.id, absBounds);
             } else {
@@ -522,8 +542,9 @@
             node.setClassName(className);
             node.setEditable(editable);
             node.setViewIdResourceName(resourceId);
-            // TODO(b/73548352): ideally item should have its own bounds
-            node.setBoundsInScreen(line.bounds);
+            if (line.absBounds != null) {
+                node.setBoundsInScreen(line.absBounds);
+            }
             node.setText(text);
             return node;
         }
diff --git a/tests/framework/base/activitymanager/Android.mk b/tests/framework/base/activitymanager/Android.mk
index 797610a..19cd50c 100644
--- a/tests/framework/base/activitymanager/Android.mk
+++ b/tests/framework/base/activitymanager/Android.mk
@@ -37,8 +37,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    cts-amwm-util \
-    cts-display-service-app-util
+    cts-amwm-util
 
 LOCAL_CTS_TEST_PACKAGE := android.server
 
diff --git a/tests/framework/base/activitymanager/AndroidTest.xml b/tests/framework/base/activitymanager/AndroidTest.xml
index a2c6fa4..8a6c34f 100644
--- a/tests/framework/base/activitymanager/AndroidTest.xml
+++ b/tests/framework/base/activitymanager/AndroidTest.xml
@@ -28,7 +28,6 @@
         <option name="test-file-name" value="CtsDeviceDeprecatedSdkApp.apk" />
         <option name="test-file-name" value="CtsDeviceDisplaySizeApp.apk" />
         <option name="test-file-name" value="CtsDevicePrereleaseSdkApp.apk" />
-        <option name="test-file-name" value="CtsDisplayServiceApp.apk" />
         <option name="test-file-name" value="CtsDeviceTranslucentTestApp.apk" />
         <option name="test-file-name" value="CtsDeviceTranslucentTestApp26.apk" />
     </target_preparer>
diff --git a/tests/framework/base/activitymanager/app/AndroidManifest.xml b/tests/framework/base/activitymanager/app/AndroidManifest.xml
index 589c683..4847622 100755
--- a/tests/framework/base/activitymanager/app/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/app/AndroidManifest.xml
@@ -321,7 +321,8 @@
         </receiver>
 
         <activity android:name=".AssistantActivity"
-            android:exported="true" />
+            android:exported="true"
+            android:screenOrientation="locked" />
         <activity android:name=".TranslucentAssistantActivity"
             android:exported="true"
             android:theme="@style/Theme.Transparent" />
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
index 82b0218..e1a9f9d 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
@@ -20,9 +20,9 @@
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_DISPLAY_ID;
-import static android.server.am.Components.AssistantActivity.EXTRA_ENTER_PIP;
-import static android.server.am.Components.AssistantActivity.EXTRA_FINISH_SELF;
-import static android.server.am.Components.AssistantActivity.EXTRA_LAUNCH_NEW_TASK;
+import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_ENTER_PIP;
+import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_FINISH_SELF;
+import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_LAUNCH_NEW_TASK;
 
 import android.app.Activity;
 import android.app.ActivityOptions;
@@ -40,9 +40,9 @@
         setContentView(R.layout.assistant);
 
         // Launch the new activity if requested
-        if (getIntent().hasExtra(EXTRA_LAUNCH_NEW_TASK)) {
+        if (getIntent().hasExtra(EXTRA_ASSISTANT_LAUNCH_NEW_TASK)) {
             final ComponentName launchActivity = ComponentName.unflattenFromString(
-                    getIntent().getStringExtra(EXTRA_LAUNCH_NEW_TASK));
+                    getIntent().getStringExtra(EXTRA_ASSISTANT_LAUNCH_NEW_TASK));
             final Intent launchIntent = new Intent();
             launchIntent.setComponent(launchActivity)
                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -57,7 +57,7 @@
         }
 
         // Enter pip if requested
-        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+        if (getIntent().hasExtra(EXTRA_ASSISTANT_ENTER_PIP)) {
             try {
                 enterPictureInPictureMode();
             } catch (IllegalStateException e) {
@@ -67,7 +67,7 @@
         }
 
         // Finish this activity if requested
-        if (getIntent().hasExtra(EXTRA_FINISH_SELF)) {
+        if (getIntent().hasExtra(EXTRA_ASSISTANT_FINISH_SELF)) {
             finish();
         }
     }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
index de56159..0326894 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
@@ -16,8 +16,11 @@
 
 package android.server.am;
 
-import android.content.Context;
+import static android.server.am.Components.BottomActivity.EXTRA_BOTTOM_WALLPAPER;
+import static android.server.am.Components.BottomActivity.EXTRA_STOP_DELAY;
+
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -25,21 +28,14 @@
 
 public class BottomActivity extends AbstractLifecycleLogActivity {
 
-    private static final String TAG = BottomActivity.class.getSimpleName();
-
     private int mStopDelay;
     private View mFloatingWindow;
 
     @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
+        final boolean useWallpaper = getIntent().getBooleanExtra(EXTRA_BOTTOM_WALLPAPER, false);
         if (useWallpaper) {
             setTheme(R.style.WallpaperTheme);
         }
@@ -56,7 +52,7 @@
         // Note that if the test fails, we shouldn't try to change the app here to make
         // it pass. The test app is artificially made to simulate an failure case, but
         // it's not doing anything wrong.
-        mStopDelay = getIntent().getIntExtra("STOP_DELAY", 0);
+        mStopDelay = getIntent().getIntExtra(EXTRA_STOP_DELAY, 0);
         if (mStopDelay > 0) {
             LayoutInflater inflater = getLayoutInflater();
             mFloatingWindow = inflater.inflate(R.layout.floating, null);
@@ -72,16 +68,16 @@
 
     @Override
     public void onResume() {
-        Log.d(TAG, "onResume() E");
+        Log.d(getTag(), "onResume() E");
         super.onResume();
 
         if (mStopDelay > 0) {
             // Refresh floating window
-            Log.d(TAG, "Scheuling invalidate Floating Window in onResume()");
+            Log.d(getTag(), "Scheduling invalidate Floating Window in onResume()");
             mFloatingWindow.invalidate();
         }
 
-        Log.d(TAG, "onResume() X");
+        Log.d(getTag(), "onResume() X");
     }
 
     @Override
@@ -89,13 +85,11 @@
         super.onStop();
 
         if (mStopDelay > 0) {
-            try {
-                Log.d(TAG, "Stalling onStop() by " + mStopDelay + " ms...");
-                Thread.sleep(mStopDelay);
-            } catch(InterruptedException e) {}
+            Log.d(getTag(), "Stalling onStop() by " + mStopDelay + " ms...");
+            SystemClock.sleep(mStopDelay);
 
             // Refresh floating window
-            Log.d(TAG, "Scheuling invalidate Floating Window in onStop()");
+            Log.d(getTag(), "Scheduling invalidate Floating Window in onStop()");
             mFloatingWindow.invalidate();
         }
     }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
index 282d750..d9f7589 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
@@ -16,6 +16,12 @@
 
 package android.server.am;
 
+import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 
 import android.app.Activity;
@@ -31,8 +37,6 @@
  * Activity that registers broadcast receiver .
  */
 public class BroadcastReceiverActivity extends Activity {
-
-    public static final String ACTION_TRIGGER_BROADCAST = "trigger_broadcast";
     private static final String TAG = BroadcastReceiverActivity.class.getSimpleName();
 
     private TestBroadcastReceiver mBroadcastReceiver = new TestBroadcastReceiver();
@@ -62,19 +66,19 @@
             if (extras == null) {
                 return;
             }
-            if (extras.getBoolean("finish")) {
+            if (extras.getBoolean(EXTRA_FINISH_BROADCAST)) {
                 finish();
             }
-            if (extras.getBoolean("moveToBack")) {
+            if (extras.getBoolean(EXTRA_MOVE_BROADCAST_TO_BACK)) {
                 moveTaskToBack(true);
             }
-            if (extras.containsKey("orientation")) {
-                setRequestedOrientation(extras.getInt("orientation"));
+            if (extras.containsKey(EXTRA_BROADCAST_ORIENTATION)) {
+                setRequestedOrientation(extras.getInt(EXTRA_BROADCAST_ORIENTATION));
             }
-            if (extras.getBoolean("dismissKeyguard")) {
+            if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD)) {
                 getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
             }
-            if (extras.getBoolean("dismissKeyguardMethod")) {
+            if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD_METHOD)) {
                 getSystemService(KeyguardManager.class).requestDismissKeyguard(
                         BroadcastReceiverActivity.this, new KeyguardDismissLoggerCallback(context));
             }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
index a9973ba..037a75f 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
@@ -146,7 +146,7 @@
     public static class TestActivity {
         // Finishes the activity
         public static final String TEST_ACTIVITY_ACTION_FINISH_SELF =
-                TestActivity.class.getName() + ".finish_self";
+                "android.server.am.TestActivity.finish_self";
         // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
         public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
     }
@@ -160,16 +160,42 @@
      */
     public static class AssistantActivity {
         // Launches the given activity in onResume
-        public static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
-        // Finishes this activity in onResume, this happens after EXTRA_LAUNCH_NEW_TASK
-        public static final String EXTRA_FINISH_SELF = "finish_self";
+        public static final String EXTRA_ASSISTANT_LAUNCH_NEW_TASK = "launch_new_task";
+        // Finishes this activity in onResume, this happens after EXTRA_ASSISTANT_LAUNCH_NEW_TASK
+        public static final String EXTRA_ASSISTANT_FINISH_SELF = "finish_self";
         // Attempts to enter picture-in-picture in onResume
-        public static final String EXTRA_ENTER_PIP = "enter_pip";
+        public static final String EXTRA_ASSISTANT_ENTER_PIP = "enter_pip";
         // Display on which Assistant runs
         public static final String EXTRA_ASSISTANT_DISPLAY_ID = "assistant_display_id";
     }
 
     /**
+     * Extra key constants for {@link android.server.am.BottomActivity}.
+     *
+     * TODO(b/73346885): These constants should be in {@link android.server.am.BottomActivity}
+     * once the activity is moved to test APK.
+     */
+    public static class BottomActivity {
+        public static final String EXTRA_BOTTOM_WALLPAPER = "USE_WALLPAPER";
+        public static final String EXTRA_STOP_DELAY = "STOP_DELAY";
+    }
+
+    /**
+     * Extra key constants for {@link android.server.am.BroadcastReceiverActivity}.
+     *
+     * TODO(b/73346885): These constants should be in
+     * {@link android.server.am.BroadcastReceiverActivity} once the activity is moved to test APK.
+     */
+    public static class BroadcastReceiverActivity {
+        public static final String ACTION_TRIGGER_BROADCAST = "trigger_broadcast";
+        public static final String EXTRA_DISMISS_KEYGUARD = "dismissKeyguard";
+        public static final String EXTRA_DISMISS_KEYGUARD_METHOD = "dismissKeyguardMethod";
+        public static final String EXTRA_FINISH_BROADCAST = "finish";
+        public static final String EXTRA_MOVE_BROADCAST_TO_BACK = "moveToBack";
+        public static final String EXTRA_BROADCAST_ORIENTATION = "orientation";
+    }
+
+    /**
      * Extra key constants for {@link #LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK}.
      *
      * TODO(b/73346885): These constants should be in
@@ -178,7 +204,106 @@
      */
     public static class LaunchAssistantActivityIntoAssistantStack {
         // Launches the translucent assist activity
-        public static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
+        public static final String EXTRA_ASSISTANT_IS_TRANSLUCENT = "is_translucent";
+    }
+
+    /** Action and extra key constants for {@link android.server.am.PipActivity}.
+     *
+     * TODO(b/73346885): These constants should be in {@link android.server.am.PipActivity}
+     * once the activity is moved to test APK.
+     */
+    public static class PipActivity {
+        // Intent action that this activity dynamically registers to enter picture-in-picture
+        public static final String ACTION_ENTER_PIP = "android.server.am.PipActivity.enter_pip";
+        // Intent action that this activity dynamically registers to move itself to the back
+        public static final String ACTION_MOVE_TO_BACK =
+                "android.server.am.PipActivity.move_to_back";
+        // Intent action that this activity dynamically registers to expand itself.
+        // If EXTRA_SET_ASPECT_RATIO_WITH_DELAY is set, it will also attempt to apply the aspect
+        // ratio after a short delay.
+        public static final String ACTION_EXPAND_PIP = "android.server.am.PipActivity.expand_pip";
+        // Intent action that this activity dynamically registers to set requested orientation.
+        // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
+        public static final String ACTION_SET_REQUESTED_ORIENTATION =
+                "android.server.am.PipActivity.set_requested_orientation";
+        // Intent action that will finish this activity
+        public static final String ACTION_FINISH = "android.server.am.PipActivity.finish";
+
+        // Adds an assertion that we do not ever get onStop() before we enter picture in picture
+        public static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP =
+                "assert_no_on_stop_before_pip";
+        // Calls enterPictureInPicture() on creation
+        public static final String EXTRA_ENTER_PIP = "enter_pip";
+        // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
+        public static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
+                "enter_pip_aspect_ratio_numerator";
+        // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
+        public static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
+                "enter_pip_aspect_ratio_denominator";
+        // Calls requestAutoEnterPictureInPicture() with the value provided
+        public static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
+        // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
+        public static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+        // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
+        public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation";
+        // The amount to delay to artificially introduce in onPause()
+        // (before EXTRA_ENTER_PIP_ON_PAUSE is processed)
+        public static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
+        // Calls enterPictureInPicture() again after onPictureInPictureModeChanged(false) is called
+        public static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
+        // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
+        public static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR =
+                "set_aspect_ratio_denominator";
+        // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
+        public static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
+        // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
+        // fixed delay
+        public static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
+                "set_aspect_ratio_with_delay_numerator";
+        // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
+        // fixed delay
+        public static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
+                "set_aspect_ratio_with_delay_denominator";
+        // Shows this activity over the keyguard
+        public static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
+        // Starts the activity (component name) provided by the value at the end of onCreate
+        public static final String EXTRA_START_ACTIVITY = "start_activity";
+        // Adds a click listener to finish this activity when it is clicked
+        public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+    }
+
+    /**
+     * Extra key constants for {@link android.server.am.TopActivity} and
+     * {@link android.server.am.TranslucentTopActivity}.
+     *
+     * TODO(b/73346885): These constants should be in {@link android.server.am.TopActivity}
+     * once the activity is moved to test APK.
+     */
+    public static class TopActivity {
+        public static final String EXTRA_FINISH_DELAY = "FINISH_DELAY";
+        public static final String EXTRA_TOP_WALLPAPER = "USE_WALLPAPER";
+    }
+
+    /**
+     * Extra key constants for {@link android.server.am.VirtualDisplayActivity}.
+     *
+     * TODO(b/73346885): These constants should be in
+     * {@link android.server.am.VirtualDisplayActivity} once the activity is moved to test APK.
+     */
+    public static class VirtualDisplayActivity {
+        public static final String VIRTUAL_DISPLAY_PREFIX = "HostedVirtualDisplay";
+        public static final String KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD =
+                "can_show_with_insecure_keyguard";
+        public static final String KEY_COMMAND = "command";
+        public static final String KEY_COUNT = "count";
+        public static final String KEY_DENSITY_DPI = "density_dpi";
+        public static final String KEY_LAUNCH_TARGET_COMPONENT = "launch_target_component";
+        public static final String KEY_PUBLIC_DISPLAY = "public_display";
+        public static final String KEY_RESIZE_DISPLAY = "resize_display";
+        // Value constants of {@link #KEY_COMMAND}.
+        public static final String COMMAND_CREATE_DISPLAY = "create_display";
+        public static final String COMMAND_DESTROY_DISPLAY = "destroy_display";
+        public static final String COMMAND_RESIZE_DISPLAY = "resize_display";
     }
 
     private static ComponentName component(String className) {
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
index 0e73939..adb7f5f 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
@@ -20,9 +20,4 @@
  * Activity with DialogWhenLarge Theme.
  */
 public class DialogWhenLargeActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = DialogWhenLargeActivity.class.getSimpleName();
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
index d46ab38..7777d46 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
@@ -15,7 +15,6 @@
  */
 package android.server.am;
 
-import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.os.Bundle;
@@ -29,7 +28,6 @@
 import java.io.IOException;
 
 public class FontScaleActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = FontScaleActivity.class.getSimpleName();
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -67,10 +65,4 @@
         final int fontActivityDpi = getResources().getDisplayMetrics().densityDpi;
         Log.i(getTag(), "fontActivityDpi=" + fontActivityDpi);
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
index c7744b0..20b8a91 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package android.server.am;
 
 import android.content.res.Configuration;
@@ -24,9 +25,4 @@
         dumpActivityDpi();
         dumpFontSize();
     }
-
-    @Override
-    protected String getTag() {
-        return FontScaleNoRelaunchActivity.class.getSimpleName();
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
index 2d7ca6f..777beee 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
@@ -19,10 +19,6 @@
 import android.content.res.Configuration;
 
 public class LandscapeOrientationActivity extends AbstractLifecycleLogActivity {
-    @Override
-    protected String getTag() {
-        return "LandscapeOrientationActivity";
-    }
 
     @Override
     protected void onResume() {
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
index 19cc73b..c800360 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
@@ -17,7 +17,7 @@
 package android.server.am;
 
 import static android.server.am.Components.LaunchAssistantActivityIntoAssistantStack
-        .EXTRA_IS_TRANSLUCENT;
+        .EXTRA_ASSISTANT_IS_TRANSLUCENT;
 
 import android.app.Activity;
 import android.content.Intent;
@@ -38,7 +38,7 @@
     }
 
     private static boolean isTranslucent(Intent intent) {
-        return intent.hasExtra(EXTRA_IS_TRANSLUCENT)
-                && Boolean.valueOf(intent.getStringExtra(EXTRA_IS_TRANSLUCENT));
+        return intent.hasExtra(EXTRA_ASSISTANT_IS_TRANSLUCENT)
+                && Boolean.valueOf(intent.getStringExtra(EXTRA_ASSISTANT_IS_TRANSLUCENT));
     }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
index 6c37115..aea9bcd 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
@@ -24,8 +24,6 @@
  */
 public class MoveTaskToBackActivity extends AbstractLifecycleLogActivity {
 
-    private static final String TAG = MoveTaskToBackActivity.class.getSimpleName();
-
     private String mFinishPoint;
 
     @Override
@@ -53,10 +51,4 @@
             moveTaskToBack(true /* nonRoot */);
         }
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
index 7c73900..d6015ec 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
@@ -23,13 +23,6 @@
 /** Activity that changes UI mode on creation and handles corresponding configuration change. */
 public class NightModeActivity extends AbstractLifecycleLogActivity {
 
-    private static final String TAG = NightModeActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
index 3080fac..22cd061 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
@@ -20,10 +20,4 @@
  * An activity that has the noHistory flag set.
  */
 public class NoHistoryActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = NoHistoryActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
index b173f8d..063113d 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
@@ -17,11 +17,4 @@
 package android.server.am;
 
 public class NoRelaunchActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = NoRelaunchActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
index e3468e3..78c5d39 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
@@ -17,11 +17,4 @@
 package android.server.am;
 
 public class NonResizeableActivity extends AbstractLifecycleLogActivity {
-
-     private static final String TAG = NonResizeableActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
index f5397e6..ffa60b7 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
@@ -19,16 +19,37 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.am.Components.PipActivity.ACTION_ENTER_PIP;
+import static android.server.am.Components.PipActivity.ACTION_EXPAND_PIP;
+import static android.server.am.Components.PipActivity.ACTION_FINISH;
+import static android.server.am.Components.PipActivity.ACTION_MOVE_TO_BACK;
+import static android.server.am.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
+import static android.server.am.Components.PipActivity.EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ON_PAUSE;
+import static android.server.am.Components.PipActivity.EXTRA_FINISH_SELF_ON_RESUME;
+import static android.server.am.Components.PipActivity.EXTRA_ON_PAUSE_DELAY;
+import static android.server.am.Components.PipActivity.EXTRA_PIP_ORIENTATION;
+import static android.server.am.Components.PipActivity.EXTRA_REENTER_PIP_ON_EXIT;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_NUMERATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
+import static android.server.am.Components.PipActivity.EXTRA_START_ACTIVITY;
+import static android.server.am.Components.PipActivity.EXTRA_TAP_TO_FINISH;
 
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.app.PictureInPictureParams;
-import android.content.res.Configuration;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -39,63 +60,6 @@
 
 public class PipActivity extends AbstractLifecycleLogActivity {
 
-    private static final String TAG = "PipActivity";
-
-    // Intent action that this activity dynamically registers to enter picture-in-picture
-    private static final String ACTION_ENTER_PIP = "android.server.am.PipActivity.enter_pip";
-    // Intent action that this activity dynamically registers to move itself to the back
-    private static final String ACTION_MOVE_TO_BACK = "android.server.am.PipActivity.move_to_back";
-    // Intent action that this activity dynamically registers to expand itself.
-    // If EXTRA_SET_ASPECT_RATIO_WITH_DELAY is set, it will also attempt to apply the aspect ratio
-    // after a short delay.
-    private static final String ACTION_EXPAND_PIP = "android.server.am.PipActivity.expand_pip";
-    // Intent action that this activity dynamically registers to set requested orientation.
-    // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
-    private static final String ACTION_SET_REQUESTED_ORIENTATION =
-            "android.server.am.PipActivity.set_requested_orientation";
-    // Intent action that will finish this activity
-    private static final String ACTION_FINISH = "android.server.am.PipActivity.finish";
-
-    // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
-    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
-    // Calls enterPictureInPicture() on creation
-    private static final String EXTRA_ENTER_PIP = "enter_pip";
-    // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
-            "enter_pip_aspect_ratio_numerator";
-    // Used with EXTRA_AUTO_ENTER_PIP, value specifies the aspect ratio to enter PIP with
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
-            "enter_pip_aspect_ratio_denominator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
-    private static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value
-    private static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR = "set_aspect_ratio_denominator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
-    // fixed delay
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
-            "set_aspect_ratio_with_delay_numerator";
-    // Calls setPictureInPictureAspectRatio with the aspect ratio specified in the value with a
-    // fixed delay
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
-            "set_aspect_ratio_with_delay_denominator";
-    // Adds a click listener to finish this activity when it is clicked
-    private static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
-    // Calls requestAutoEnterPictureInPicture() with the value provided
-    private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
-    // Starts the activity (component name) provided by the value at the end of onCreate
-    private static final String EXTRA_START_ACTIVITY = "start_activity";
-    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
-    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
-    // Calls enterPictureInPicture() again after onPictureInPictureModeChanged(false) is called
-    private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
-    // Shows this activity over the keyguard
-    private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
-    // Adds an assertion that we do not ever get onStop() before we enter picture in picture
-    private static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP = "assert_no_on_stop_before_pip";
-    // The amount to delay to artificially introduce in onPause() (before EXTRA_ENTER_PIP_ON_PAUSE
-    // is processed)
-    private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
-
     private boolean mEnteredPictureInPicture;
 
     private Handler mHandler = new Handler();
@@ -132,7 +96,7 @@
                         break;
                     case ACTION_SET_REQUESTED_ORIENTATION:
                         setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
-                                EXTRA_FIXED_ORIENTATION)));
+                                EXTRA_PIP_ORIENTATION)));
                         break;
                     case ACTION_FINISH:
                         finish();
@@ -147,8 +111,8 @@
         super.onCreate(savedInstanceState);
 
         // Set the fixed orientation if requested
-        if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
-            final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
+        if (getIntent().hasExtra(EXTRA_PIP_ORIENTATION)) {
+            final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION));
             setRequestedOrientation(ori);
         }
 
@@ -252,7 +216,7 @@
         super.onStop();
 
         if (getIntent().hasExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP) && !mEnteredPictureInPicture) {
-            Log.w(TAG, "Unexpected onStop() called before entering picture-in-picture");
+            Log.w(getTag(), "Unexpected onStop() called before entering picture-in-picture");
             finish();
         }
     }
@@ -270,8 +234,8 @@
 
         // Fail early if the activity state does not match the dispatched state
         if (isInPictureInPictureMode() != isInPictureInPictureMode) {
-            Log.w(TAG, "Received onPictureInPictureModeChanged mode=" + isInPictureInPictureMode
-                    + " activityState=" + isInPictureInPictureMode());
+            Log.w(getTag(), "Received onPictureInPictureModeChanged mode="
+                    + isInPictureInPictureMode + " activityState=" + isInPictureInPictureMode());
             finish();
         }
 
@@ -323,11 +287,6 @@
         caller.startActivity(intent);
     }
 
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
     /**
      * @return a {@link Rational} aspect ratio from the given intent and extras.
      */
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
index a63e3b3..e322bc7 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
@@ -21,11 +21,6 @@
 public class PortraitOrientationActivity extends AbstractLifecycleLogActivity {
 
     @Override
-    protected String getTag() {
-        return "PortraitOrientationActivity";
-    }
-
-    @Override
     protected void onResume() {
         super.onResume();
         final Configuration config = getResources().getConfiguration();
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
index 31aa9f2..fc0110c 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
@@ -19,10 +19,6 @@
 import android.os.Bundle;
 
 public class ResizeableActivity extends AbstractLifecycleLogActivity {
-    @Override
-    protected String getTag() {
-        return "ResizeableActivity";
-    }
 
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
index 1fc7b74..b456aa8 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
@@ -16,7 +16,6 @@
 
 package android.server.am;
 
-import android.app.Activity;
 import android.os.Bundle;
 import android.view.WindowManager;
 
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
index 2814361..c5c7083 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
@@ -17,11 +17,4 @@
 package android.server.am;
 
 public class ShowWhenLockedAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = ShowWhenLockedAttrActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
index 414d021..9d303eb 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
@@ -16,20 +16,11 @@
 
 package android.server.am;
 
-import android.os.Bundle;
-
 public class ShowWhenLockedAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = ShowWhenLockedAttrRemoveAttrActivity.class.getSimpleName();
 
     @Override
     protected void onStop() {
         super.onStop();
         setShowWhenLocked(false);
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
index 8e2b781..a6bbd20 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
@@ -16,25 +16,16 @@
 
 package android.server.am;
 
-import android.app.Activity;
 import android.os.Bundle;
-import android.server.am.SwipeRefreshLayout;
-
 
 /**
  * An activity containing a SwipeRefreshLayout which prevents activity idle.
  */
 public class SwipeRefreshActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = SwipeRefreshActivity.class.getSimpleName();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(new SwipeRefreshLayout(this));
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
index d89d786..657ec5f 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
@@ -17,19 +17,12 @@
 package android.server.am;
 
 import android.app.PictureInPictureParams;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
 import android.os.Bundle;
-import android.util.Log;
 
 public class TestActivityWithSameAffinity extends TestActivity {
 
-    private static final String TAG = TestActivityWithSameAffinity.class.getSimpleName();
-
     // Calls enterPictureInPicture() on creation
     private static final String EXTRA_ENTER_PIP = "enter_pip";
     // Starts the activity (component name) provided by the value at the end of onCreate
@@ -64,9 +57,4 @@
             finish();
         }
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
index 7d69ef9..97948bb 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
@@ -16,37 +16,34 @@
 
 package android.server.am;
 
+import static android.server.am.Components.TopActivity.EXTRA_FINISH_DELAY;
+import static android.server.am.Components.TopActivity.EXTRA_TOP_WALLPAPER;
+
 import android.os.Handler;
 import android.os.Bundle;
 import android.util.Log;
 
 public class TopActivity extends AbstractLifecycleLogActivity {
 
-    private static final String TAG = TopActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
+    protected void setWallpaperTheme() {
+        setTheme(R.style.WallpaperTheme);
     }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
+        final boolean useWallpaper = getIntent().getBooleanExtra(EXTRA_TOP_WALLPAPER, false);
         if (useWallpaper) {
-            setTheme(R.style.WallpaperTheme);
+            setWallpaperTheme();
         }
 
-        final int finishDelay = getIntent().getIntExtra("FINISH_DELAY", 0);
+        final int finishDelay = getIntent().getIntExtra(EXTRA_FINISH_DELAY, 0);
         if (finishDelay > 0) {
             Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    Log.d(TAG, "Calling finish()");
+            handler.postDelayed(() -> {
+                    Log.d(getTag(), "Calling finish()");
                     finish();
-                }
             }, finishDelay);
         }
     }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
deleted file mode 100644
index 7391b04..0000000
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
+++ /dev/null
@@ -1,50 +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.server.am;
-
-import android.os.Bundle;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.content.Intent;
-
-public class TrampolineActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = TrampolineActivity.class.getSimpleName();
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // Add a delay here to expose more easily a failure case where the real target
-        // activity is visible before it's launched, because its task is being brought
-        // to foreground. We need to verify that 'am start' is unblocked correctly.
-        try {
-            Thread.sleep(2000);
-        } catch(InterruptedException e) {}
-        Intent intent = new Intent(this, SingleTaskActivity.class);
-        intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK);
-
-        startActivity(intent);
-        finish();
-    }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
index ba60f67..56fbd48 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
@@ -20,17 +20,10 @@
 
 public class TranslucentTestActivity extends TestActivity {
 
-    private static final String TAG = TranslucentTestActivity.class.getSimpleName();
-
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
         setContentView(R.layout.task_overlay);
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
index 5d639db..6161b46 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
@@ -16,38 +16,10 @@
 
 package android.server.am;
 
-import android.os.Handler;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TranslucentTopActivity extends AbstractLifecycleLogActivity {
-
-    private static final String TAG = TranslucentTopActivity.class.getSimpleName();
+public class TranslucentTopActivity extends TopActivity {
 
     @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final boolean useWallpaper = getIntent().getBooleanExtra("USE_WALLPAPER", false);
-        if (useWallpaper) {
-            setTheme(R.style.TranslucentWallpaperTheme);
-        }
-
-        final int finishDelay = getIntent().getIntExtra("FINISH_DELAY", 0);
-        if (finishDelay > 0) {
-            Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    Log.d(TAG, "Calling finish()");
-                    finish();
-                }
-            }, finishDelay);
-        }
+    protected void setWallpaperTheme() {
+        setTheme(R.style.TranslucentWallpaperTheme);
     }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
index f644eb2..3dd1aa3 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
@@ -17,10 +17,4 @@
 package android.server.am;
 
 public class TurnScreenOnAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnAttrActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
index acc92b6..7b72466 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
@@ -20,7 +20,6 @@
 import android.os.Bundle;
 
 public class TurnScreenOnAttrDismissKeyguardActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnAttrDismissKeyguardActivity.class.getSimpleName();
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -28,9 +27,4 @@
         ((KeyguardManager) getSystemService(KEYGUARD_SERVICE))
                 .requestDismissKeyguard(this, new KeyguardDismissLoggerCallback(this));
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
index 9ad7ddd..ee8506b 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
@@ -17,16 +17,10 @@
 package android.server.am;
 
 public class TurnScreenOnAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnAttrRemoveAttrActivity.class.getSimpleName();
 
     @Override
     protected void onStop() {
         super.onStop();
         setTurnScreenOn(false);
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
index 6113a23..83c6c08 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
@@ -17,10 +17,4 @@
 package android.server.am;
 
 public class TurnScreenOnShowOnLockActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnShowOnLockActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
index 2dddb9b..8740f6c 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
@@ -17,10 +17,4 @@
 package android.server.am;
 
 public class TurnScreenOnSingleTaskActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnSingleTaskActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
index a1ebbab..4315ed7 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
@@ -15,15 +15,10 @@
  */
 
 package android.server.am;
+
 import android.view.WindowManager;
 
 public class TurnScreenOnWithRelayoutActivity extends AbstractLifecycleLogActivity {
-    private static final String TAG = TurnScreenOnWithRelayoutActivity.class.getSimpleName();
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 
     @Override
     protected void onStart() {
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
index 9b8feeb..8b1cdb1 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
@@ -22,6 +22,17 @@
 import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
 import static android.server.am.ActivityLauncher.KEY_TARGET_COMPONENT;
 import static android.server.am.ComponentNameUtils.getActivityName;
+import static android.server.am.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_COMMAND;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_COUNT;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
 
 import android.app.Activity;
 import android.content.ComponentName;
@@ -41,25 +52,16 @@
  * Activity that is able to create and destroy a virtual display.
  */
 public class VirtualDisplayActivity extends Activity implements SurfaceHolder.Callback {
-    private static final String TAG = "VirtualDisplayActivity";
+    private static final String TAG = VirtualDisplayActivity.class.getSimpleName();
 
     private static final int DEFAULT_DENSITY_DPI = 160;
-    private static final String KEY_DENSITY_DPI = "density_dpi";
-    private static final String KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD
-            = "can_show_with_insecure_keyguard";
-    private static final String KEY_PUBLIC_DISPLAY = "public_display";
-    private static final String KEY_RESIZE_DISPLAY = "resize_display";
-    private static final String KEY_LAUNCH_TARGET_COMPONENT = "launch_target_component";
-    private static final String KEY_COUNT = "count";
-
-    private DisplayManager mDisplayManager;
 
     // Container for details about a pending virtual display creation request.
     private static class VirtualDisplayRequest {
-        public final SurfaceView surfaceView;
-        public final Bundle extras;
+        final SurfaceView surfaceView;
+        final Bundle extras;
 
-        public VirtualDisplayRequest(SurfaceView surfaceView, Bundle extras) {
+        VirtualDisplayRequest(SurfaceView surfaceView, Bundle extras) {
             this.surfaceView = surfaceView;
             this.extras = extras;
         }
@@ -67,12 +69,12 @@
 
     // Container to hold association between an active virtual display and surface view.
     private static class VirtualDisplayEntry {
-        public final VirtualDisplay display;
-        public final SurfaceView surfaceView;
-        public final boolean resizeDisplay;
-        public final int density;
+        final VirtualDisplay display;
+        final SurfaceView surfaceView;
+        final boolean resizeDisplay;
+        final int density;
 
-        public VirtualDisplayEntry(VirtualDisplay display, SurfaceView surfaceView, int density,
+        VirtualDisplayEntry(VirtualDisplay display, SurfaceView surfaceView, int density,
                 boolean resizeDisplay) {
             this.display = display;
             this.surfaceView = surfaceView;
@@ -81,16 +83,15 @@
         }
     }
 
-    private HashMap<Surface, VirtualDisplayRequest> mPendingDisplayRequests;
-    private HashMap<Surface, VirtualDisplayEntry> mVirtualDisplays;
+    private final HashMap<Surface, VirtualDisplayRequest> mPendingDisplayRequests = new HashMap<>();
+    private final HashMap<Surface, VirtualDisplayEntry> mVirtualDisplays = new HashMap<>();
+    private DisplayManager mDisplayManager;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.virtual_display_layout);
 
-        mVirtualDisplays = new HashMap<>();
-        mPendingDisplayRequests = new HashMap<>();
         mDisplayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
     }
 
@@ -102,15 +103,15 @@
             return;
         }
 
-        String command = extras.getString("command");
+        String command = extras.getString(KEY_COMMAND);
         switch (command) {
-            case "create_display":
+            case COMMAND_CREATE_DISPLAY:
                 createVirtualDisplay(extras);
                 break;
-            case "destroy_display":
+            case COMMAND_DESTROY_DISPLAY:
                 destroyVirtualDisplays();
                 break;
-            case "resize_display":
+            case COMMAND_RESIZE_DISPLAY:
                 resizeDisplay();
                 break;
         }
@@ -127,7 +128,7 @@
         Log.d(TAG, "createVirtualDisplays. requested count:" + requestedCount);
 
         for (int displayCount = 0; displayCount < requestedCount; ++displayCount) {
-            final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+            final ViewGroup root = findViewById(android.R.id.content);
             final SurfaceView surfaceView = new SurfaceView(this);
             surfaceView.setLayoutParams(new ViewGroup.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
@@ -140,7 +141,7 @@
 
     private void destroyVirtualDisplays() {
         Log.d(TAG, "destroyVirtualDisplays");
-        final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+        final ViewGroup root = findViewById(android.R.id.content);
 
         for (VirtualDisplayEntry entry : mVirtualDisplays.values()) {
             Log.d(TAG, "destroying:" + entry.display);
@@ -189,7 +190,7 @@
                 + ", publicDisplay=" + publicDisplay);
         try {
             VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(
-                    "HostedVirtualDisplay" + mVirtualDisplays.size(), width,
+                    VIRTUAL_DISPLAY_PREFIX + mVirtualDisplays.size(), width,
                     height, densityDpi, surface, flags);
             mVirtualDisplays.put(surface,
                     new VirtualDisplayEntry(virtualDisplay, entry.surfaceView, densityDpi,
@@ -203,11 +204,10 @@
                 launchActivity(targetActivity, displayId);
             }
         } catch (IllegalArgumentException e) {
-            final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+            final ViewGroup root = findViewById(android.R.id.content);
             // This is expected when trying to create show-when-locked public display.
             root.removeView(entry.surfaceView);
         }
-
     }
 
     @Override
diff --git a/tests/framework/base/activitymanager/app_base/src/android/server/am/AbstractLifecycleLogActivity.java b/tests/framework/base/activitymanager/app_base/src/android/server/am/AbstractLifecycleLogActivity.java
index 28e6576..63e801e 100644
--- a/tests/framework/base/activitymanager/app_base/src/android/server/am/AbstractLifecycleLogActivity.java
+++ b/tests/framework/base/activitymanager/app_base/src/android/server/am/AbstractLifecycleLogActivity.java
@@ -88,7 +88,9 @@
         Log.i(getTag(), "onUserLeaveHint");
     }
 
-    protected abstract String getTag();
+    protected final String getTag() {
+        return getClass().getSimpleName();
+    }
 
     protected void dumpConfiguration(Configuration config) {
         Log.i(getTag(), "Configuration: " + config);
diff --git a/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java b/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java
index 76e6e1a..1dbc81d 100644
--- a/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java
+++ b/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java
@@ -28,8 +28,6 @@
 
 public class TestActivity extends AbstractLifecycleLogActivity {
 
-    private static final String TAG = TestActivity.class.getSimpleName();
-
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -76,9 +74,4 @@
         dumpDisplaySize(newConfig);
         dumpConfiguration(newConfig);
     }
-
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
 }
diff --git a/tests/framework/base/activitymanager/displayserviceapp/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/Android.mk
deleted file mode 100644
index b798d87..0000000
--- a/tests/framework/base/activitymanager/displayserviceapp/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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.
-
-include $(call all-subdir-makefiles)
diff --git a/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml b/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
deleted file mode 100644
index 4051804..0000000
--- a/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          package="android.server.am.displayservice">
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <application android:label="CtsDisplayService">
-        <service android:name=".VirtualDisplayService"
-                 android:exported="true" />
-    </application>
-</manifest>
diff --git a/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java b/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
deleted file mode 100644
index bf6b87e..0000000
--- a/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.server.am.displayservice;
-
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.Surface;
-
-public class VirtualDisplayService extends Service {
-    private static final String NOTIFICATION_CHANNEL_ID = "cts/VirtualDisplayService";
-    private static final String TAG = "VirtualDisplayService";
-
-    private static final int FOREGROUND_ID = 1;
-
-    private static final int DENSITY = 160;
-    private static final int HEIGHT = 480;
-    private static final int WIDTH = 800;
-
-    private ImageReader mReader;
-    private VirtualDisplay mVirtualDisplay;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        NotificationManager notificationManager = getSystemService(NotificationManager.class);
-        notificationManager.createNotificationChannel(new NotificationChannel(
-            NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
-            NotificationManager.IMPORTANCE_DEFAULT));
-        Notification notif = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
-                .setSmallIcon(android.R.drawable.ic_dialog_alert)
-                .build();
-        startForeground(FOREGROUND_ID, notif);
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        String command = intent.getStringExtra("command");
-        Log.d(TAG, "Got command: " + command);
-
-        if ("create".equals(command)) {
-            createVirtualDisplay(intent);
-        } if ("off".equals(command)) {
-            mVirtualDisplay.setSurface(null);
-        } else if ("on".equals(command)) {
-            mVirtualDisplay.setSurface(mReader.getSurface());
-        } else if ("destroy".equals(command)) {
-            destroyVirtualDisplay();
-            stopSelf();
-        }
-
-        return START_NOT_STICKY;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-
-    private void createVirtualDisplay(Intent intent) {
-        mReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
-
-        final DisplayManager displayManager = getSystemService(DisplayManager.class);
-        final String name = "CtsVirtualDisplay";
-
-        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-        if (intent.getBooleanExtra("show_content_when_locked", false /* defaultValue */)) {
-            flags |= 1 << 5; // VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD
-        }
-        mVirtualDisplay = displayManager.createVirtualDisplay(
-                name, WIDTH, HEIGHT, DENSITY, mReader.getSurface(), flags);
-    }
-
-    private void destroyVirtualDisplay() {
-        mVirtualDisplay.release();
-        mReader.close();
-    }
-}
diff --git a/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java b/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java
deleted file mode 100644
index f541770..0000000
--- a/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.server.am.displayservice;
-
-import static junit.framework.Assert.assertTrue;
-
-import android.support.test.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import java.io.IOException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class DisplayHelper {
-    private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
-    private static final String VIRTUAL_DISPLAY_SERVICE =
-            "android.server.am.displayservice/.VirtualDisplayService";
-    private static final Pattern mDisplayDevicePattern = Pattern.compile(
-            ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
-
-    private boolean mCreated;
-
-    public DisplayHelper() {
-    }
-
-    public void createAndWaitForDisplay(boolean external, boolean requestShowWhenLocked)
-            {
-        StringBuilder command =
-                new StringBuilder("am startfgservice -n " + VIRTUAL_DISPLAY_SERVICE);
-        command.append(" --es command create");
-        if (external) {
-            command.append(" --ez external_display true");
-        }
-        if (requestShowWhenLocked) {
-            command.append(" --ez show_content_when_locked true");
-        }
-        executeShellCommand(command.toString());
-
-        waitForDisplayState(false /* default */, true /* exists */, true /* on */);
-        mCreated = true;
-    }
-
-    public void turnDisplayOff() {
-        executeShellCommand(
-                "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command off");
-        waitForDisplayState(false /* default */, true /* exists */, false /* on */);
-    }
-
-    public void turnDisplayOn() {
-        executeShellCommand(
-                "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command on");
-        waitForDisplayState(false /* default */, true /* exists */, true /* on */);
-    }
-
-    public void releaseDisplay() {
-        if (mCreated) {
-            executeShellCommand(
-                    "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command destroy");
-            waitForDisplayState(false /* default */, false /* exists */, true /* on */);
-        }
-        mCreated = false;
-    }
-
-    public static void waitForDefaultDisplayState(boolean wantOn) {
-        waitForDisplayState(true /* default */, true /* exists */, wantOn);
-    }
-
-    public static boolean getDefaultDisplayState() {
-        return getDisplayState(true);
-    }
-
-    private static void waitForDisplayState(boolean defaultDisplay, boolean wantExists, boolean wantOn) {
-        int tries = 0;
-        boolean done = false;
-        do {
-            if (tries > 0) {
-                try {
-                    Thread.sleep(500);
-                } catch (InterruptedException e) {
-                    // Oh well
-                }
-            }
-
-            Boolean state = getDisplayState(defaultDisplay);
-            done = (!wantExists && state == null)
-                    || (wantExists && state != null && state == wantOn);
-
-            tries++;
-        } while (tries < 10 && !done);
-
-        assertTrue(done);
-    }
-
-    private static Boolean getDisplayState(boolean defaultDisplay) {
-        String dump = executeShellCommand("dumpsys display");
-
-        boolean displayExists = false;
-        boolean displayOn = false;
-        for (String line : dump.split("\\n")) {
-            Matcher matcher = mDisplayDevicePattern.matcher(line);
-            if (matcher.matches()) {
-                if ((defaultDisplay && line.contains("FLAG_DEFAULT_DISPLAY"))
-                        || (!defaultDisplay && VIRTUAL_DISPLAY_NAME.equals(matcher.group(1)))) {
-                    return "ON".equals(matcher.group(2));
-                }
-            }
-        }
-        return null;
-    }
-
-    private static String executeShellCommand(String command) {
-        try {
-            return SystemUtil
-                    .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
-        } catch (IOException e) {
-            //bubble it up
-            throw new RuntimeException(e);
-        }
-    }
-}
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 7b76704..706e19a 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -28,6 +28,8 @@
 import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.am.Components.ALWAYS_FOCUSABLE_PIP_ACTIVITY;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
+import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
 import static android.server.am.Components.DOCKED_ACTIVITY;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
 import static android.server.am.Components.LAUNCH_PIP_ON_PIP_ACTIVITY;
@@ -62,6 +64,11 @@
  */
 public class ActivityManagerActivityVisibilityTests extends ActivityManagerTestBase {
 
+    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
+    // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
+
     @Rule
     public final DisableScreenDozeRule mDisableScreenDozeRule = new DisableScreenDozeRule();
 
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 834c7a6..15353a1 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -24,6 +24,10 @@
 import static android.server.am.ActivityManagerState.STATE_RESUMED;
 import static android.server.am.ComponentNameUtils.getWindowName;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
+import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
 import static android.server.am.Components.DIALOG_WHEN_LARGE_ACTIVITY;
 import static android.server.am.Components.LANDSCAPE_ORIENTATION_ACTIVITY;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
@@ -63,6 +67,17 @@
  */
 public class ActivityManagerAppConfigurationTests extends ActivityManagerTestBase {
 
+    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
+    // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
+    // Shell command to move {@link #BROADCAST_RECEIVER_ACTIVITY} task to back.
+    private static final String MOVE_TASK_TO_BACK_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_MOVE_BROADCAST_TO_BACK + " true";
+    // Shell command to request portrait orientation to {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String REQUEST_PORTRAIT_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ei " + EXTRA_BROADCAST_ORIENTATION + " 1";
+
     private static final int SMALL_WIDTH_DP = 426;
     private static final int SMALL_HEIGHT_DP = 320;
 
@@ -416,7 +431,7 @@
         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
 
         // Request portrait
-        executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
+        executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
         mAmWmState.waitForRotation(1);
 
         // Finish activity
@@ -445,7 +460,7 @@
         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
 
         // Request portrait
-        executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
+        executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
         mAmWmState.waitForRotation(1);
 
         // Finish activity
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
index 84c22d2..ad67958 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
@@ -23,26 +23,27 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
 import static android.server.am.ActivityManagerState.STATE_RESUMED;
 import static android.server.am.ComponentNameUtils.getActivityName;
 import static android.server.am.Components.ANIMATION_TEST_ACTIVITY;
 import static android.server.am.Components.ASSISTANT_ACTIVITY;
 import static android.server.am.Components.ASSISTANT_VOICE_INTERACTION_SERVICE;
 import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_DISPLAY_ID;
-import static android.server.am.Components.AssistantActivity.EXTRA_ENTER_PIP;
-import static android.server.am.Components.AssistantActivity.EXTRA_FINISH_SELF;
-import static android.server.am.Components.AssistantActivity.EXTRA_LAUNCH_NEW_TASK;
+import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_ENTER_PIP;
+import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_FINISH_SELF;
+import static android.server.am.Components.AssistantActivity.EXTRA_ASSISTANT_LAUNCH_NEW_TASK;
 import static android.server.am.Components.DOCKED_ACTIVITY;
 import static android.server.am.Components.LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION;
 import static android.server.am.Components.LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK;
 import static android.server.am.Components.LaunchAssistantActivityIntoAssistantStack
-        .EXTRA_IS_TRANSLUCENT;
+        .EXTRA_ASSISTANT_IS_TRANSLUCENT;
 import static android.server.am.Components.PIP_ACTIVITY;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP;
 import static android.server.am.Components.TEST_ACTIVITY;
 import static android.server.am.Components.TRANSLUCENT_ASSISTANT_ACTIVITY;
 import static android.server.am.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
 import static android.server.am.UiDeviceUtils.pressBackButton;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.hamcrest.Matchers.hasSize;
 import static org.junit.Assert.assertEquals;
@@ -66,13 +67,13 @@
 @FlakyTest(bugId = 71875631)
 public class ActivityManagerAssistantStackTests extends ActivityManagerTestBase {
 
-    private int mAssistantDisplayId = DEFAULT_DISPLAY_ID;
+    private int mAssistantDisplayId = DEFAULT_DISPLAY;
 
     public void setUp() throws Exception {
         super.setUp();
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK);
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK);
             waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             ActivityManagerState.ActivityStack assistantStack =
                     mAmWmState.getAmState().getStackByActivityType(ACTIVITY_TYPE_ASSISTANT);
@@ -87,7 +88,7 @@
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
             waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
 
             // Ensure that the activity launched in the fullscreen assistant stack
@@ -126,7 +127,7 @@
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
             waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             assertAssistantStackExists();
 
@@ -140,7 +141,7 @@
     @Test
     @Presubmit
     public void testAssistantStackLaunchNewTask() throws Exception {
-        assertAssistantStackCanLaunchAndReturnFromNewTask();
+        assertAssistantStackCanLaunchAndReturnFromNewTask(WINDOWING_MODE_FULLSCREEN);
     }
 
     @Test
@@ -158,28 +159,23 @@
         mAmWmState.assertContainsStack("Must contain docked stack.",
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
 
-        assertAssistantStackCanLaunchAndReturnFromNewTask();
+        assertAssistantStackCanLaunchAndReturnFromNewTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
     }
 
-    private void assertAssistantStackCanLaunchAndReturnFromNewTask() throws Exception {
-        final boolean inSplitScreenMode = mAmWmState.getAmState().containsStack(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
-
+    private void assertAssistantStackCanLaunchAndReturnFromNewTask(int expectedWindowingMode)
+            throws Exception {
         // Enable the assistant and launch an assistant activity which will launch a new task
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
-            launchActivityOnDisplay(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK, mAssistantDisplayId,
-                    EXTRA_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY),
+            launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK, mAssistantDisplayId,
+                    EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY),
                     EXTRA_ASSISTANT_DISPLAY_ID, Integer.toString(mAssistantDisplayId));
+            // Ensure that the fullscreen stack is on top and the test activity is now visible
+            waitForValidStateWithActivityTypeAndWindowingMode(
+                    TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, expectedWindowingMode);
         }
 
-        final int expectedWindowingMode = inSplitScreenMode
-                ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                : WINDOWING_MODE_FULLSCREEN;
-        // Ensure that the fullscreen stack is on top and the test activity is now visible
-        waitForValidStateWithActivityTypeAndWindowingMode(
-                TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, expectedWindowingMode);
         mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
         mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
                 expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
@@ -205,8 +201,10 @@
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_FINISH_SELF, "true");
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ASSISTANT_FINISH_SELF, "true");
+            mAmWmState.waitFor((amState, wmState) -> !amState.containsActivity(ASSISTANT_ACTIVITY),
+                    "Waiting for " + getActivityName(ASSISTANT_ACTIVITY) + " finished");
         }
         waitForValidStateWithActivityTypeAndWindowingMode(
                 TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
@@ -225,8 +223,8 @@
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_ENTER_PIP, "true");
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ASSISTANT_ENTER_PIP, "true");
         }
         waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
         mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
@@ -244,8 +242,8 @@
             removeStacksInWindowingModes(WINDOWING_MODE_FULLSCREEN,
                     WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             launchHomeActivity();
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_IS_TRANSLUCENT, "true");
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ASSISTANT_IS_TRANSLUCENT, "true");
             waitForValidStateWithActivityType(
                     TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             assertAssistantStackExists();
@@ -258,8 +256,8 @@
             // also visible
             removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
             launchActivityOnDisplay(TEST_ACTIVITY, mAssistantDisplayId);
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_IS_TRANSLUCENT, "true");
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ASSISTANT_IS_TRANSLUCENT, "true");
             waitForValidStateWithActivityType(
                     TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             assertAssistantStackExists();
@@ -269,9 +267,9 @@
             // back.Ensure home is visible.
             removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
             launchHomeActivity();
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_IS_TRANSLUCENT, "true",
-                    EXTRA_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY));
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ASSISTANT_IS_TRANSLUCENT, "true",
+                    EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY));
             waitForValidStateWithActivityTypeAndWindowingMode(
                     TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
             mAmWmState.assertHomeActivityVisible(false);
@@ -293,8 +291,8 @@
                         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
                 mAmWmState.assertContainsStack("Must contain docked stack.",
                         WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
-                launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                        EXTRA_IS_TRANSLUCENT, "true");
+                launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                        EXTRA_ASSISTANT_IS_TRANSLUCENT, "true");
                 waitForValidStateWithActivityType(
                         TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
                 assertAssistantStackExists();
@@ -312,7 +310,9 @@
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
             // Launch the assistant
-            launchActivityOnDisplay(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION, mAssistantDisplayId);
+            launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION,
+                    mAssistantDisplayId);
+            waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             assertAssistantStackExists();
             mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
             mAmWmState.assertFocusedStack("Expected assistant stack focused",
@@ -326,10 +326,14 @@
             // Launch a new fullscreen activity
             // Using Animation Test Activity because it is opaque on all devices.
             launchActivityOnDisplay(ANIMATION_TEST_ACTIVITY, mAssistantDisplayId);
+            // Wait for animation finished.
+            mAmWmState.waitForActivityState(ANIMATION_TEST_ACTIVITY, STATE_RESUMED);
             mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, false);
 
             // Launch the assistant again and ensure that it goes into the same task
-            launchActivityOnDisplay(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION, mAssistantDisplayId);
+            launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION,
+                    mAssistantDisplayId);
+            waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             assertAssistantStackExists();
             mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
             mAmWmState.assertFocusedStack("Expected assistant stack focused",
@@ -354,8 +358,8 @@
             // ensure that the test activity is still visible
             launchActivity(TEST_ACTIVITY);
             launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-            launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
-                    EXTRA_IS_TRANSLUCENT, String.valueOf(true));
+            launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                    EXTRA_ASSISTANT_IS_TRANSLUCENT, String.valueOf(true));
             waitForValidStateWithActivityType(
                     TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
             assertAssistantStackExists();
@@ -392,7 +396,7 @@
     // Any 2D Activity in VR mode is run on a special VR virtual display, so check if the Assistant
     // is going to run on the same display as other tasks.
     protected boolean assistantRunsOnPrimaryDisplay() {
-        return mAssistantDisplayId == DEFAULT_DISPLAY_ID;
+        return mAssistantDisplayId == DEFAULT_DISPLAY;
     }
 
     /** Helper class to save, set, and restore
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 837ae05..86f6458 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -17,16 +17,31 @@
 package android.server.am;
 
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
 import static android.server.am.ComponentNameUtils.getActivityName;
 import static android.server.am.Components.VIRTUAL_DISPLAY_ACTIVITY;
+import static android.server.am.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity
+        .KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_COMMAND;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_COUNT;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
+import static android.server.am.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
 import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logAlways;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.ComponentName;
 import android.content.res.Configuration;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.server.am.ActivityManagerState.ActivityDisplay;
 import android.server.am.settings.SettingsSession;
@@ -64,7 +79,7 @@
     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int width, int height,
             int dpi) {
         for (ActivityDisplay display : displays) {
-            if (display.mId == DEFAULT_DISPLAY_ID) {
+            if (display.mId == DEFAULT_DISPLAY) {
                 continue;
             }
             final Configuration config = display.mFullConfiguration;
@@ -203,54 +218,53 @@
         private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
                 new OverlayDisplayDevicesSession();
 
-        public VirtualDisplaySession setDensityDpi(int densityDpi) {
+        VirtualDisplaySession setDensityDpi(int densityDpi) {
             mDensityDpi = densityDpi;
             return this;
         }
 
-        public VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
+        VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
             mLaunchInSplitScreen = launchInSplitScreen;
             return this;
         }
 
-        public VirtualDisplaySession setCanShowWithInsecureKeyguard(
-                boolean canShowWithInsecureKeyguard) {
+        VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
             return this;
         }
 
-        public VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
+        VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
             mPublicDisplay = publicDisplay;
             return this;
         }
 
-        public VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
+        VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
             mResizeDisplay = resizeDisplay;
             return this;
         }
 
-        public VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
+        VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
             mLaunchActivity = launchActivity;
             return this;
         }
 
-        public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
+        VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
             mSimulateDisplay = simulateDisplay;
             return this;
         }
 
-        public VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
+        VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
             mMustBeCreated = mustBeCreated;
             return this;
         }
 
         @Nullable
-        public ActivityDisplay createDisplay() throws Exception {
+        ActivityDisplay createDisplay() throws Exception {
             return createDisplays(1).stream().findFirst().orElse(null);
         }
 
         @NonNull
-        public List<ActivityDisplay> createDisplays(int count) throws Exception {
+        List<ActivityDisplay> createDisplays(int count) throws Exception {
             if (mSimulateDisplay) {
                 return simulateDisplay();
             } else {
@@ -258,6 +272,11 @@
             }
         }
 
+        void resizeDisplay() {
+            executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
+                    + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY);
+        }
+
         @Override
         public void close() throws Exception {
             mOverlayDisplayDeviceSession.close();
@@ -301,7 +320,7 @@
          * @return A list of {@link ActivityDisplay} that represent newly created displays.
          * @throws Exception
          */
-        private List<ActivityDisplay> createVirtualDisplays(int displayCount) throws Exception {
+        private List<ActivityDisplay> createVirtualDisplays(int displayCount) {
             // Start an activity that is able to create virtual displays.
             if (mLaunchInSplitScreen) {
                 getLaunchActivityBuilder()
@@ -322,20 +341,20 @@
             final StringBuilder createVirtualDisplayCommand = new StringBuilder(
                     getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
                     .append(" -f 0x20000000")
-                    .append(" --es command create_display");
+                    .append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY);
             if (mDensityDpi != INVALID_DENSITY_DPI) {
                 createVirtualDisplayCommand
-                        .append(" --ei density_dpi ")
+                        .append(" --ei " + KEY_DENSITY_DPI + " ")
                         .append(mDensityDpi);
             }
-            createVirtualDisplayCommand.append(" --ei count ").append(displayCount)
-                    .append(" --ez can_show_with_insecure_keyguard ")
+            createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount)
+                    .append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
                     .append(mCanShowWithInsecureKeyguard)
-                    .append(" --ez public_display ").append(mPublicDisplay)
-                    .append(" --ez resize_display ").append(mResizeDisplay);
+                    .append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
+                    .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay);
             if (mLaunchActivity != null) {
                 createVirtualDisplayCommand
-                        .append(" --es launch_target_component ")
+                        .append(" --es " + KEY_LAUNCH_TARGET_COMPONENT + " ")
                         .append(getActivityName(mLaunchActivity));
             }
             executeShellCommand(createVirtualDisplayCommand.toString());
@@ -347,38 +366,29 @@
         /**
          * Destroy existing virtual display.
          */
-        void destroyVirtualDisplays() throws Exception {
+        void destroyVirtualDisplays() {
             final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
                     + " -f 0x20000000"
-                    + " --es command destroy_display";
+                    + " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY;
             executeShellCommand(destroyVirtualDisplayCommand);
             waitForDisplaysDestroyed();
         }
 
-        private void waitForDisplaysDestroyed() throws Exception {
-            int tries = 0;
-            boolean done;
-            do {
-                done = !isHostedVirtualDisplayPresent();
-                if (!done && tries > 0) {
-                    log("Waiting for hosted displays destruction");
-                    try {
-                        Thread.sleep(500);
-                    } catch (InterruptedException e) {
-                        // Oh well
-                    }
+        private void waitForDisplaysDestroyed() {
+            for (int retry = 1; retry <= 5; retry++) {
+                if (!isHostedVirtualDisplayPresent()) {
+                    return;
                 }
-
-                tries++;
-            } while (tries < 10 && !done);
-
-            assertTrue(done);
+                logAlways("Waiting for hosted displays destruction... retry=" + retry);
+                SystemClock.sleep(500);
+            }
+            fail("Waiting for hosted displays destruction failed.");
         }
 
-        private boolean isHostedVirtualDisplayPresent() throws Exception {
+        private boolean isHostedVirtualDisplayPresent() {
             mAmWmState.computeState(true);
             return mAmWmState.getWmState().getDisplays().stream().anyMatch(
-                    d -> d.getName() != null && d.getName().contains("HostedVirtualDisplay"));
+                    d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX));
         }
 
         /**
@@ -388,7 +398,7 @@
          * @return list of new displays, empty list if no new display is created.
          */
         private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
-                List<ActivityDisplay> originalDS) throws Exception {
+                List<ActivityDisplay> originalDS) {
             final int originalDisplayCount = originalDS.size();
 
             // Wait for the display(s) to be created and get 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 6a0291f..4bef8fe 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -16,9 +16,9 @@
 
 package android.server.am;
 
-import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
 import static android.server.am.ComponentNameUtils.getActivityName;
 import static android.server.am.Components.TEST_ACTIVITY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeFalse;
@@ -43,8 +43,7 @@
     @Test
     public void testDefaultDisplayOverrideConfiguration() throws Exception {
         final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
-        final ActivityDisplay primaryDisplay = getDisplayState(reportedDisplays,
-                DEFAULT_DISPLAY_ID);
+        final ActivityDisplay primaryDisplay = getDisplayState(reportedDisplays, DEFAULT_DISPLAY);
         assertEquals("Primary display's configuration should be equal to global configuration.",
                 primaryDisplay.mOverrideConfiguration, primaryDisplay.mFullConfiguration);
         assertEquals("Primary display's configuration should be equal to global configuration.",
@@ -87,13 +86,13 @@
                     TEST_ACTIVITY);
 
             // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
             final ActivityManagerState.ActivityStack frontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Launched activity must be resumed",
                     getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
-            assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
-                    frontStack.mDisplayId);
+            assertEquals("Front stack must be on the default display",
+                    DEFAULT_DISPLAY, frontStack.mDisplayId);
             mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
         }
     }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
index fa88fb0..fde03cc 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -19,18 +19,18 @@
 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.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
 import static android.server.am.ActivityLauncher.KEY_NEW_TASK;
 import static android.server.am.ActivityLauncher.KEY_TARGET_COMPONENT;
-import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics
-        .getDisplayMetrics;
+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.ComponentNameUtils.getActivityName;
 import static android.server.am.ComponentNameUtils.getWindowName;
 import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
+import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
 import static android.server.am.Components.LAUNCH_BROADCAST_ACTION;
 import static android.server.am.Components.LAUNCH_BROADCAST_RECEIVER;
@@ -48,6 +48,7 @@
 import static android.server.am.second.Components.SECOND_LAUNCH_BROADCAST_RECEIVER;
 import static android.server.am.second.Components.SECOND_NO_EMBEDDING_ACTIVITY;
 import static android.server.am.third.Components.THIRD_ACTIVITY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -60,7 +61,6 @@
 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;
 
@@ -77,6 +77,15 @@
  */
 public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase {
 
+    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
+    // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
+    // Shell command to launch activity via {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String LAUNCH_ACTIVITY_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + KEY_LAUNCH_ACTIVITY + " true --ez "
+            + KEY_NEW_TASK + " true --es " + KEY_TARGET_COMPONENT + " ";
+
     @Before
     @Override
     public void setUp() throws Exception {
@@ -139,7 +148,7 @@
                     NON_RESIZEABLE_ACTIVITY);
 
             // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
             final ActivityManagerState.ActivityStack frontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Launched activity must be on the primary display and resumed",
@@ -174,7 +183,7 @@
                     NON_RESIZEABLE_ACTIVITY);
 
             // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
             final ActivityManagerState.ActivityStack frontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Launched activity must be on the primary display and resumed",
@@ -212,7 +221,7 @@
 
             // Check that activity is in the same stack
             final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(
-                    DEFAULT_DISPLAY_ID);
+                    DEFAULT_DISPLAY);
             final ActivityManagerState.ActivityStack defaultFrontStack =
                     mAmWmState.getAmState().getStackById(defaultFrontStackId);
             assertEquals("Launched activity must be on the primary display and resumed",
@@ -250,9 +259,8 @@
             mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
 
             // Launch non-resizeable activity from secondary display.
-            executeShellCommand("am broadcast -a trigger_broadcast --ez " + KEY_LAUNCH_ACTIVITY
-                    + " true --ez " + KEY_NEW_TASK + " true --es " + KEY_TARGET_COMPONENT + " "
-                    + getActivityName(NON_RESIZEABLE_ACTIVITY));
+            executeShellCommand(
+                    LAUNCH_ACTIVITY_BROADCAST + getActivityName(NON_RESIZEABLE_ACTIVITY));
             mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
 
             // Check that non-resizeable activity is on the secondary display, because of the
@@ -395,13 +403,13 @@
             // 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);
+            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Launched activity must be resumed in front stack",
                     getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
             assertEquals("Front stack must be on primary display",
-                    DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+                    DEFAULT_DISPLAY, frontStack.mDisplayId);
         }
     }
 
@@ -434,8 +442,8 @@
             mAmWmState.assertFocusedActivity("Launched activity must be focused",
                     TEST_ACTIVITY);
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack frontStack
-                    = mAmWmState.getAmState().getStackById(frontStackId);
+            final ActivityManagerState.ActivityStack frontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Launched activity must be resumed in front stack",
                     getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
         }
@@ -630,8 +638,8 @@
             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);
+            assertEquals("Focus must remain on primary display",
+                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
@@ -648,8 +656,8 @@
             mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY);
             focusedStackId = mAmWmState.getAmState().getFocusedStackId();
             focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-            assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
-                    focusedStack.mDisplayId);
+            assertEquals("Focus must return to primary display",
+                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
         }
     }
 
@@ -701,7 +709,7 @@
         // Start an activity on default display to determine default stack.
         launchActivity(BROADCAST_RECEIVER_ACTIVITY);
         final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode(
-                DEFAULT_DISPLAY_ID);
+                DEFAULT_DISPLAY);
         // Finish probing activity.
         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
 
@@ -768,7 +776,7 @@
             // 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);
+            final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
@@ -835,8 +843,8 @@
             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);
+            assertEquals("Focus must remain on primary display",
+                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
@@ -878,8 +886,8 @@
             mAmWmState.assertFocusedActivity(
                     "Focus must be on newly launched app", SECOND_ACTIVITY);
             final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            ActivityManagerState.ActivityStack focusedStack
-                    = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+            ActivityManagerState.ActivityStack focusedStack =
+                    mAmWmState.getAmState().getStackById(externalFocusedStackId);
             assertEquals("Focused stack must be on secondary display", newDisplay.mId,
                     focusedStack.mDisplayId);
 
@@ -942,10 +950,10 @@
             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);
+            ActivityManagerState.ActivityStack focusedStack =
+                    mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+            assertEquals("Focus must remain on primary display",
+                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
 
             // Launch other activity with different uid on secondary display.
             final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
@@ -991,8 +999,8 @@
             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);
+            assertEquals("Focus must remain on primary display",
+                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
@@ -1135,7 +1143,7 @@
 
             // Resize the docked stack, so that activity with virtual display will also be resized.
             final LogSeparator logSeparator = clearLogcat();
-            executeShellCommand(getResizeVirtualDisplayCommand());
+            virtualDisplaySession.resizeDisplay();
 
             mAmWmState.waitForWithAmState(amState -> {
                 try {
@@ -1190,7 +1198,7 @@
             launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
             mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
 
-            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
                     .mStacks.size();
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
             final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
@@ -1220,7 +1228,7 @@
             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)
+            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
                     .mStacks.size();
             assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
                     stackNumFinal);
@@ -1245,8 +1253,7 @@
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
             mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
 
-            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
-                    .mStacks.size();
+            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY).mStacks.size();
 
             // Launch activity on new secondary display.
             // Using custom command here, because normally we add flags
@@ -1273,7 +1280,7 @@
             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)
+            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
                     .mStacks.size();
             assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
                     stackNumFinal);
@@ -1362,7 +1369,7 @@
             // 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);
+                    DEFAULT_DISPLAY);
             final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
                     mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
             assertEquals("Activity launched on default display must be resumed",
@@ -1376,8 +1383,8 @@
 
             // 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);
+            final ActivityManagerState.ActivityStack secondFrontStack =
+                    mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
             mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
@@ -1457,16 +1464,14 @@
 
             // 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);
+            final ActivityManagerState.ActivityStack focusedStack =
+                    mAmWmState.getAmState().getStackById(focusedStackId);
             assertEquals("Activity launched on default display must be resumed",
                     getActivityName(TEST_ACTIVITY), focusedStack.mResumedActivity);
-            assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
-                    focusedStack.mDisplayId);
+            assertEquals("Focus must be on primary display",
+                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
 
-            executeShellCommand("am broadcast -a trigger_broadcast --ez " + KEY_LAUNCH_ACTIVITY
-                    + " true --ez " + KEY_NEW_TASK + " true --es " + KEY_TARGET_COMPONENT + " "
-                    + getActivityName(LAUNCHING_ACTIVITY));
+            executeShellCommand(LAUNCH_ACTIVITY_BROADCAST + getActivityName(LAUNCHING_ACTIVITY));
 
             // Check that the third activity ends up in a new task in the same stack as the
             // first activity
@@ -1513,7 +1518,7 @@
     public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY_ID,
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                 "Activity launched on primary display must be resumed");
 
         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
@@ -1547,7 +1552,7 @@
     public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY_ID,
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                 "Activity launched on primary display must be resumed");
 
         try (final PrimaryDisplayStateSession displayStateSession =
@@ -1609,7 +1614,7 @@
     public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY_ID,
+        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                 "Activity launched on primary display must be resumed");
 
         try (final LockScreenSession lockScreenSession = new LockScreenSession();
@@ -1642,7 +1647,7 @@
             executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
 
             // Check that the activity on the primary display is resumed
-            waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY_ID,
+            waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                     "Activity launched on primary display must be resumed");
             assertEquals("Unexpected resumed activity",
                     1, mAmWmState.getAmState().getResumedActivitiesCount());
@@ -1703,8 +1708,8 @@
     /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
     private void assertMovedToDisplay(ComponentName componentName, LogSeparator logSeparator)
             throws Exception {
-        final ActivityLifecycleCounts lifecycleCounts
-                = new ActivityLifecycleCounts(componentName, logSeparator);
+        final ActivityLifecycleCounts lifecycleCounts =
+                new ActivityLifecycleCounts(componentName, logSeparator);
         if (lifecycleCounts.mDestroyCount != 0) {
             fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
                     + " time(s), wasn't expecting any");
@@ -1722,15 +1727,10 @@
         }
     }
 
-    private static String getResizeVirtualDisplayCommand() {
-        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
-                " --es command resize_display";
-    }
-
     private class ExternalDisplaySession implements AutoCloseable {
 
         @Nullable
-        private DisplayHelper mExternalDisplayHelper;
+        private VirtualDisplayHelper mExternalDisplayHelper;
 
         /**
          * Creates a private virtual display with the external and show with insecure
@@ -1741,9 +1741,8 @@
             final List<ActivityDisplay> originalDS = getDisplaysStates();
             final int originalDisplayCount = originalDS.size();
 
-            mExternalDisplayHelper = new DisplayHelper();
-            mExternalDisplayHelper.createAndWaitForDisplay(true /* external */,
-                    showContentWhenLocked);
+            mExternalDisplayHelper = new VirtualDisplayHelper();
+            mExternalDisplayHelper.createAndWaitForDisplay(showContentWhenLocked);
 
             // Wait for the virtual display to be created and get configurations.
             final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
@@ -1778,7 +1777,7 @@
         }
     }
 
-    private class PrimaryDisplayStateSession implements AutoCloseable {
+    private static class PrimaryDisplayStateSession implements AutoCloseable {
 
         void turnScreenOff() {
             setPrimaryDisplayState(false);
@@ -1796,7 +1795,7 @@
             } else {
                 pressSleepButton();
             }
-            DisplayHelper.waitForDefaultDisplayState(wantOn);
+            VirtualDisplayHelper.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 28fb44e..6f3c1c2 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -22,7 +22,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
 import static android.server.am.ActivityManagerState.STATE_DESTROYED;
 import static android.server.am.ActivityManagerState.STATE_RESUMED;
 import static android.server.am.ActivityManagerState.STATE_STOPPED;
@@ -38,12 +37,35 @@
 import static android.server.am.Components.PIP_ACTIVITY2;
 import static android.server.am.Components.PIP_ACTIVITY_WITH_SAME_AFFINITY;
 import static android.server.am.Components.PIP_ON_STOP_ACTIVITY;
+import static android.server.am.Components.PipActivity.ACTION_ENTER_PIP;
+import static android.server.am.Components.PipActivity.ACTION_EXPAND_PIP;
+import static android.server.am.Components.PipActivity.ACTION_FINISH;
+import static android.server.am.Components.PipActivity.ACTION_MOVE_TO_BACK;
+
+import static android.server.am.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
+import static android.server.am.Components.PipActivity.EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR;
+import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ON_PAUSE;
+import static android.server.am.Components.PipActivity.EXTRA_FINISH_SELF_ON_RESUME;
+import static android.server.am.Components.PipActivity.EXTRA_ON_PAUSE_DELAY;
+import static android.server.am.Components.PipActivity.EXTRA_PIP_ORIENTATION;
+import static android.server.am.Components.PipActivity.EXTRA_REENTER_PIP_ON_EXIT;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_NUMERATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
+import static android.server.am.Components.PipActivity.EXTRA_START_ACTIVITY;
+import static android.server.am.Components.PipActivity.EXTRA_TAP_TO_FINISH;
 import static android.server.am.Components.RESUME_WHILE_PAUSING_ACTIVITY;
 import static android.server.am.Components.TEST_ACTIVITY;
 import static android.server.am.Components.TEST_ACTIVITY_WITH_SAME_AFFINITY;
 import static android.server.am.Components.TRANSLUCENT_TEST_ACTIVITY;
+import static android.server.am.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
 import static android.server.am.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
 import static android.server.am.UiDeviceUtils.pressWindowButton;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.hamcrest.Matchers.lessThan;
 import static org.junit.Assert.assertEquals;
@@ -64,6 +86,7 @@
 import android.support.test.filters.FlakyTest;
 import android.util.Size;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -78,37 +101,6 @@
 @FlakyTest(bugId = 71792368)
 public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
 
-    private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
-    private static final String EXTRA_ENTER_PIP = "enter_pip";
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR =
-            "enter_pip_aspect_ratio_numerator";
-    private static final String EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR =
-            "enter_pip_aspect_ratio_denominator";
-    private static final String EXTRA_SET_ASPECT_RATIO_NUMERATOR = "set_aspect_ratio_numerator";
-    private static final String EXTRA_SET_ASPECT_RATIO_DENOMINATOR = "set_aspect_ratio_denominator";
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR =
-            "set_aspect_ratio_with_delay_numerator";
-    private static final String EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR =
-            "set_aspect_ratio_with_delay_denominator";
-    private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
-    private static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
-    private static final String EXTRA_START_ACTIVITY = "start_activity";
-    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
-    private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
-    private static final String EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP = "assert_no_on_stop_before_pip";
-    private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
-
-    private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
-            "android.server.am.PipActivity.enter_pip";
-    private static final String PIP_ACTIVITY_ACTION_MOVE_TO_BACK =
-            "android.server.am.PipActivity.move_to_back";
-    private static final String PIP_ACTIVITY_ACTION_EXPAND_PIP =
-            "android.server.am.PipActivity.expand_pip";
-    private static final String PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION =
-            "android.server.am.PipActivity.set_requested_orientation";
-    private static final String PIP_ACTIVITY_ACTION_FINISH =
-            "android.server.am.PipActivity.finish";
-
     private static final String APP_OPS_OP_ENTER_PICTURE_IN_PICTURE = "PICTURE_IN_PICTURE";
     private static final int APP_OPS_MODE_ALLOWED = 0;
     private static final int APP_OPS_MODE_IGNORED = 1;
@@ -160,7 +152,8 @@
                 true /* moveTopToPinnedStack */, false /* isFocusable */);
     }
 
-    // This test is back-listed in cts-known-failures.xml.
+    // This test is black-listed in cts-known-failures.xml (b/35314835).
+    @Ignore
     @Test
     public void testAlwaysFocusablePipActivity() throws Exception {
         pinnedStackTester(getAmStartCmd(ALWAYS_FOCUSABLE_PIP_ACTIVITY),
@@ -168,7 +161,8 @@
                 false /* moveTopToPinnedStack */, true /* isFocusable */);
     }
 
-    // This test is back-listed in cts-known-failures.xml.
+    // This test is black-listed in cts-known-failures.xml (b/35314835).
+    @Ignore
     @Presubmit
     @Test
     public void testLaunchIntoPinnedStack() throws Exception {
@@ -323,10 +317,10 @@
                 EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT));
         // Launch the PiP activity fixed as landscape
         launchActivity(PIP_ACTIVITY,
-                EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_LANDSCAPE));
+                EXTRA_PIP_ORIENTATION, String.valueOf(ORIENTATION_LANDSCAPE));
         // Enter PiP, and assert that the PiP is within bounds now that the device is back in
         // portrait
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
@@ -593,10 +587,10 @@
         // Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
         launchActivity(PIP_ACTIVITY);
         mAmWmState.waitForWithAmState(amState ->
-                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_FULLSCREEN,
+                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY) == WINDOWING_MODE_FULLSCREEN,
                 "Waiting for PIP to exit to fullscreen");
         mAmWmState.waitForWithAmState(amState ->
-                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_PINNED,
+                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY) == WINDOWING_MODE_PINNED,
                 "Waiting to re-enter PIP");
         mAmWmState.assertHomeActivityVisible(true);
     }
@@ -617,10 +611,10 @@
         // Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
         launchActivity(PIP_ACTIVITY);
         mAmWmState.waitForWithAmState(amState ->
-                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_FULLSCREEN,
+                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY) == WINDOWING_MODE_FULLSCREEN,
                 "Waiting for PIP to exit to fullscreen");
         mAmWmState.waitForWithAmState(amState ->
-                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_PINNED,
+                amState.getFrontStackWindowingMode(DEFAULT_DISPLAY) == WINDOWING_MODE_PINNED,
                 "Waiting to re-enter PIP");
         mAmWmState.assertVisibility(TEST_ACTIVITY, true);
     }
@@ -694,7 +688,7 @@
 
         // Remove the stack and ensure that the task is now in the fullscreen stack (when no
         // fullscreen stack existed before)
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        executeShellCommand("am broadcast -a " + ACTION_MOVE_TO_BACK);
         assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
     }
@@ -712,7 +706,7 @@
 
         // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
         // top fullscreen activity
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        executeShellCommand("am broadcast -a " + ACTION_MOVE_TO_BACK);
         assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
     }
@@ -732,7 +726,7 @@
 
         // Remove the stack and ensure that the task is placed on top of the hidden fullscreen
         // stack, but that the home stack is still focused
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        executeShellCommand("am broadcast -a " + ACTION_MOVE_TO_BACK);
         assertPinnedStackStateOnMoveToFullscreen(
                 PIP_ACTIVITY, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
     }
@@ -832,7 +826,7 @@
         // Lock the task and ensure that we can't enter picture-in-picture both explicitly and
         // when paused
         executeShellCommand("am task lock " + task.mTaskId);
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
@@ -853,7 +847,7 @@
         // configuration change happened after the picture-in-picture and multi-window callbacks
         launchActivity(PIP_ACTIVITY);
         LogSeparator logSeparator = clearLogcat();
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
@@ -964,7 +958,7 @@
 
         // Trigger it to go back to fullscreen and try to set the aspect ratio, and ensure that the
         // call to set the aspect ratio did not prevent the PiP from returning to fullscreen
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_EXPAND_PIP
+        executeShellCommand("am broadcast -a " + ACTION_EXPAND_PIP
                 + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR + " 123456789"
                 + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR + " 100000000");
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
@@ -980,14 +974,15 @@
 
         // Launch the PiP activity fixed as portrait, and enter picture-in-picture
         launchActivity(PIP_ACTIVITY,
-                EXTRA_FIXED_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT),
+                EXTRA_PIP_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT),
                 EXTRA_ENTER_PIP, "true");
         assertPinnedStackExists();
 
         // Request that the orientation is set to landscape
         executeShellCommand("am broadcast -a "
-                + PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION + " -e "
-                + EXTRA_FIXED_ORIENTATION + " " + String.valueOf(ORIENTATION_LANDSCAPE));
+                + ACTION_SET_REQUESTED_ORIENTATION + " -e "
+                + EXTRA_PIP_ORIENTATION + " "
+                + String.valueOf(ORIENTATION_LANDSCAPE));
 
         // Launch the activity back into fullscreen and ensure that it is now in landscape
         launchActivity(PIP_ACTIVITY);
@@ -1030,7 +1025,7 @@
         launchPinnedActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId);
 
         // Finish the PiP activity and ensure that there is no pinned stack
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_FINISH);
+        executeShellCommand("am broadcast -a " + ACTION_FINISH);
         mAmWmState.waitForWithAmState(amState -> getPinnedStack() == null,
                 "Waiting for pinned stack to be removed...");
         assertPinnedStackDoesNotExist();
@@ -1255,12 +1250,12 @@
 
         // Expand the PiP back to fullscreen and back into PiP and ensure that it is in the same
         // position as before we expanded (and that the default bounds reflect that)
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_EXPAND_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_EXPAND_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
                 .build());
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
@@ -1275,7 +1270,7 @@
 
         // Expand the PiP, then launch an activity in a new task, and ensure that the PiP goes back
         // to the default position (and not the saved position) the next time it is launched
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_EXPAND_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_EXPAND_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
@@ -1283,7 +1278,7 @@
         launchActivity(TEST_ACTIVITY);
         executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
         mAmWmState.waitForActivityState(PIP_ACTIVITY, STATE_RESUMED);
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(PIP_ACTIVITY)
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
@@ -1315,7 +1310,7 @@
                 offsetStackBounds.bottom);
 
         // Finish the activity
-        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_FINISH);
+        executeShellCommand("am broadcast -a " + ACTION_FINISH);
         mAmWmState.waitForActivityState(PIP_ACTIVITY, STATE_DESTROYED);
         mAmWmState.waitForWithAmState(amState -> getPinnedStack() == null,
                 "Waiting for pinned stack to be removed...");
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 925f9a4..aa19459 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
@@ -17,8 +17,12 @@
 package android.server.am;
 
 import static android.server.am.Components.BOTTOM_ACTIVITY;
+import static android.server.am.Components.BottomActivity.EXTRA_BOTTOM_WALLPAPER;
+import static android.server.am.Components.BottomActivity.EXTRA_STOP_DELAY;
 import static android.server.am.Components.TOP_ACTIVITY;
 import static android.server.am.Components.TRANSLUCENT_TOP_ACTIVITY;
+import static android.server.am.Components.TopActivity.EXTRA_FINISH_DELAY;
+import static android.server.am.Components.TopActivity.EXTRA_TOP_WALLPAPER;
 import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_CLOSE;
 import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
 import static android.server.am.WindowManagerState.TRANSIT_TASK_CLOSE;
@@ -31,6 +35,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.ComponentName;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.FlakyTest;
 
@@ -54,37 +59,37 @@
 
     // Test activity open/close under normal timing
     @Test
-    public void testOpenActivity_NeitherWallpaper() throws Exception {
+    public void testOpenActivity_NeitherWallpaper() {
         testOpenActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_ACTIVITY_OPEN);
     }
 
     @Test
-    public void testCloseActivity_NeitherWallpaper() throws Exception {
+    public void testCloseActivity_NeitherWallpaper() {
         testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
     }
 
     @Test
-    public void testOpenActivity_BottomWallpaper() throws Exception {
+    public void testOpenActivity_BottomWallpaper() {
         testOpenActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
     }
 
     @Test
-    public void testCloseActivity_BottomWallpaper() throws Exception {
+    public void testCloseActivity_BottomWallpaper() {
         testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
     }
 
     @Test
-    public void testOpenActivity_BothWallpaper() throws Exception {
+    public void testOpenActivity_BothWallpaper() {
         testOpenActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
     }
 
     @Test
-    public void testCloseActivity_BothWallpaper() throws Exception {
+    public void testCloseActivity_BothWallpaper() {
         testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
     }
@@ -93,38 +98,38 @@
 
     // Test task open/close under normal timing
     @Test
-    public void testOpenTask_NeitherWallpaper() throws Exception {
+    public void testOpenTask_NeitherWallpaper() {
         testOpenTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_TASK_OPEN);
     }
 
     @FlakyTest(bugId = 71792333)
     @Test
-    public void testCloseTask_NeitherWallpaper() throws Exception {
+    public void testCloseTask_NeitherWallpaper() {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_TASK_CLOSE);
     }
 
     @Test
-    public void testOpenTask_BottomWallpaper() throws Exception {
+    public void testOpenTask_BottomWallpaper() {
         testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
     }
 
     @Test
-    public void testCloseTask_BottomWallpaper() throws Exception {
+    public void testCloseTask_BottomWallpaper() {
         testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
     }
 
     @Test
-    public void testOpenTask_BothWallpaper() throws Exception {
+    public void testOpenTask_BothWallpaper() {
         testOpenTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
     }
 
     @Test
-    public void testCloseTask_BothWallpaper() throws Exception {
+    public void testCloseTask_BothWallpaper() {
         testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
     }
@@ -135,19 +140,19 @@
     // These simulate the case where the bottom activity is resumed
     // before AM receives its activitiyStopped
     @Test
-    public void testCloseActivity_NeitherWallpaper_SlowStop() throws Exception {
+    public void testCloseActivity_NeitherWallpaper_SlowStop() {
         testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 true /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
     }
 
     @Test
-    public void testCloseActivity_BottomWallpaper_SlowStop() throws Exception {
+    public void testCloseActivity_BottomWallpaper_SlowStop() {
         testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
     }
 
     @Test
-    public void testCloseActivity_BothWallpaper_SlowStop() throws Exception {
+    public void testCloseActivity_BothWallpaper_SlowStop() {
         testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
     }
@@ -159,19 +164,19 @@
     // before AM receives its activitiyStopped
     @FlakyTest(bugId = 71792333)
     @Test
-    public void testCloseTask_NeitherWallpaper_SlowStop() throws Exception {
+    public void testCloseTask_NeitherWallpaper_SlowStop() {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 true /*slowStop*/, TRANSIT_TASK_CLOSE);
     }
 
     @Test
-    public void testCloseTask_BottomWallpaper_SlowStop() throws Exception {
+    public void testCloseTask_BottomWallpaper_SlowStop() {
         testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
     }
 
     @Test
-    public void testCloseTask_BothWallpaper_SlowStop() throws Exception {
+    public void testCloseTask_BothWallpaper_SlowStop() {
         testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
     }
@@ -180,38 +185,38 @@
 
     /// Test closing of translucent activity/task
     @Test
-    public void testCloseActivity_NeitherWallpaper_Translucent() throws Exception {
+    public void testCloseActivity_NeitherWallpaper_Translucent() {
         testCloseActivityTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 TRANSIT_ACTIVITY_CLOSE);
     }
 
     @Test
-    public void testCloseActivity_BottomWallpaper_Translucent() throws Exception {
+    public void testCloseActivity_BottomWallpaper_Translucent() {
         testCloseActivityTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 TRANSIT_WALLPAPER_OPEN);
     }
 
     @Test
-    public void testCloseActivity_BothWallpaper_Translucent() throws Exception {
+    public void testCloseActivity_BothWallpaper_Translucent() {
         testCloseActivityTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 TRANSIT_WALLPAPER_INTRA_CLOSE);
     }
 
     @Test
-    public void testCloseTask_NeitherWallpaper_Translucent() throws Exception {
+    public void testCloseTask_NeitherWallpaper_Translucent() {
         testCloseTaskTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
                 TRANSIT_TASK_CLOSE);
     }
 
     @FlakyTest(bugId = 71792333)
     @Test
-    public void testCloseTask_BottomWallpaper_Translucent() throws Exception {
+    public void testCloseTask_BottomWallpaper_Translucent() {
         testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
                 TRANSIT_WALLPAPER_OPEN);
     }
 
     @Test
-    public void testCloseTask_BothWallpaper_Translucent() throws Exception {
+    public void testCloseTask_BothWallpaper_Translucent() {
         testCloseTaskTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
                 TRANSIT_WALLPAPER_INTRA_CLOSE);
     }
@@ -219,38 +224,38 @@
     //------------------------------------------------------------------------//
 
     private void testOpenActivity(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+            boolean topWallpaper, boolean slowStop, String expectedTransit) {
         testTransitionSelection(true /*testOpen*/, false /*testNewTask*/,
                 bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
     }
 
     private void testCloseActivity(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+            boolean topWallpaper, boolean slowStop, String expectedTransit) {
         testTransitionSelection(false /*testOpen*/, false /*testNewTask*/,
                 bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
     }
 
     private void testOpenTask(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+            boolean topWallpaper, boolean slowStop, String expectedTransit) {
         testTransitionSelection(true /*testOpen*/, true /*testNewTask*/,
                 bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
     }
 
     private void testCloseTask(boolean bottomWallpaper,
-            boolean topWallpaper, boolean slowStop, String expectedTransit) throws Exception {
+            boolean topWallpaper, boolean slowStop, String expectedTransit) {
         testTransitionSelection(false /*testOpen*/, true /*testNewTask*/,
                 bottomWallpaper, topWallpaper, false /*topTranslucent*/, slowStop, expectedTransit);
     }
 
     private void testCloseActivityTranslucent(boolean bottomWallpaper,
-            boolean topWallpaper, String expectedTransit) throws Exception {
+            boolean topWallpaper, String expectedTransit) {
         testTransitionSelection(false /*testOpen*/, false /*testNewTask*/,
                 bottomWallpaper, topWallpaper, true /*topTranslucent*/,
                 false /*slowStop*/, expectedTransit);
     }
 
     private void testCloseTaskTranslucent(boolean bottomWallpaper,
-            boolean topWallpaper, String expectedTransit) throws Exception {
+            boolean topWallpaper, String expectedTransit) {
         testTransitionSelection(false /*testOpen*/, true /*testNewTask*/,
                 bottomWallpaper, topWallpaper, true /*topTranslucent*/,
                 false /*slowStop*/, expectedTransit);
@@ -261,13 +266,13 @@
     private void testTransitionSelection(
             boolean testOpen, boolean testNewTask,
             boolean bottomWallpaper, boolean topWallpaper, boolean topTranslucent,
-            boolean testSlowStop, String expectedTransit) throws Exception {
+            boolean testSlowStop, String expectedTransit) {
         String bottomStartCmd = getAmStartCmd(BOTTOM_ACTIVITY);
         if (bottomWallpaper) {
-            bottomStartCmd += " --ez USE_WALLPAPER true";
+            bottomStartCmd += " --ez " + EXTRA_BOTTOM_WALLPAPER + " true";
         }
         if (testSlowStop) {
-            bottomStartCmd += " --ei STOP_DELAY 3000";
+            bottomStartCmd += " --ei " + EXTRA_STOP_DELAY + " 3000";
         }
         executeShellCommand(bottomStartCmd);
 
@@ -279,14 +284,14 @@
             topStartCmd += " -f 0x18000000";
         }
         if (topWallpaper) {
-            topStartCmd += " --ez USE_WALLPAPER true";
+            topStartCmd += " --ez " + EXTRA_TOP_WALLPAPER + " true";
         }
         if (!testOpen) {
-            topStartCmd += " --ei FINISH_DELAY 1000";
+            topStartCmd += " --ei " + EXTRA_FINISH_DELAY + " 1000";
         }
         executeShellCommand(topStartCmd);
 
-        Thread.sleep(5000);
+        SystemClock.sleep(5000);
         if (testOpen) {
             mAmWmState.computeState(topActivity);
         } else {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
index 82576e8..6a89e26 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
@@ -16,12 +16,12 @@
 
 package android.server.am;
 
-import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
 import static android.server.am.ComponentNameUtils.getActivityName;
 import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
 import static android.server.am.Components.RESIZEABLE_ACTIVITY;
 import static android.server.am.Components.VR_TEST_ACTIVITY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -220,12 +220,12 @@
 
         // Check that activity is launched in focused stack on primary display.
         mAmWmState.assertFocusedActivity("Launched activity must be focused", RESIZEABLE_ACTIVITY);
-        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
         final ActivityManagerState.ActivityStack frontStack
                 = mAmWmState.getAmState().getStackById(frontStackId);
         assertEquals("Launched activity must be resumed in front stack",
                 getActivityName(RESIZEABLE_ACTIVITY), frontStack.mResumedActivity);
         assertEquals("Front stack must be on primary display",
-                DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+                DEFAULT_DISPLAY, frontStack.mDisplayId);
     }
 }
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 3c2bac9..5904bb8 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -18,9 +18,13 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
 import static android.server.am.Components.DISMISS_KEYGUARD_ACTIVITY;
 import static android.server.am.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
 import static android.server.am.Components.PIP_ACTIVITY;
+import static android.server.am.Components.PipActivity.ACTION_ENTER_PIP;
+import static android.server.am.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ACTIVITY;
 import static android.server.am.UiDeviceUtils.pressBackButton;
 
@@ -37,8 +41,10 @@
  */
 public class KeyguardLockedTests extends KeyguardTestBase {
 
-    private static final String ACTION_ENTER_PIP = "android.server.am.PipActivity.enter_pip";
-    private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
+    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
+    // Shell command to dismiss keyguard via {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String DISMISS_KEYGUARD_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_DISMISS_KEYGUARD + " true";
 
     @Before
     @Override
@@ -107,7 +113,7 @@
             launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-            executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
+            executeShellCommand(DISMISS_KEYGUARD_BROADCAST);
             lockScreenSession.enterAndConfirmLockCredential();
 
             // Make sure we stay on Keyguard.
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 56e7111..07b5535 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -20,6 +20,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.server.am.ComponentNameUtils.getWindowName;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
+import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
 import static android.server.am.Components.DISMISS_KEYGUARD_ACTIVITY;
 import static android.server.am.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
 import static android.server.am.Components.KEYGUARD_LOCK_ACTIVITY;
@@ -53,6 +57,17 @@
  */
 public class KeyguardTests extends KeyguardTestBase {
 
+    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
+    // Shell command to dismiss keyguard via {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String DISMISS_KEYGUARD_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_DISMISS_KEYGUARD + " true";
+    // Shell command to dismiss keyguard via {@link #BROADCAST_RECEIVER_ACTIVITY} method.
+    private static final String DISMISS_KEYGUARD_METHOD_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_DISMISS_KEYGUARD_METHOD + " true";
+    // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
+    private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
+            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
+
     @Before
     @Override
     public void setUp() throws Exception {
@@ -246,8 +261,7 @@
             assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
             launchActivity(BROADCAST_RECEIVER_ACTIVITY);
             launchActivity(TEST_ACTIVITY);
-            executeShellCommand(
-                    "am broadcast -a trigger_broadcast --ez dismissKeyguardMethod true");
+            executeShellCommand(DISMISS_KEYGUARD_METHOD_BROADCAST);
             assertOnDismissErrorInLogcat(logSeparator);
         }
     }
@@ -278,7 +292,7 @@
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertKeyguardShowingAndOccluded();
-            executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
+            executeShellCommand(DISMISS_KEYGUARD_BROADCAST);
             mAmWmState.assertKeyguardShowingAndOccluded();
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
         }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java b/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java
new file mode 100644
index 0000000..1e1cb27
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java
@@ -0,0 +1,145 @@
+/*
+ * 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.server.am;
+
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.server.am.StateLogger.logAlways;
+import static android.support.test.InstrumentationRegistry.getContext;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper class to create virtual display.
+ */
+class VirtualDisplayHelper {
+
+    private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
+    /** See {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD}. */
+    private static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
+
+    private static final Pattern DISPLAY_DEVICE_PATTERN = Pattern.compile(
+            ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
+    private static final int DENSITY = 160;
+    private static final int HEIGHT = 480;
+    private static final int WIDTH = 800;
+
+    private ImageReader mReader;
+    private VirtualDisplay mVirtualDisplay;
+    private boolean mCreated;
+
+    void createAndWaitForDisplay(boolean requestShowWhenLocked) {
+        createVirtualDisplay(requestShowWhenLocked);
+        waitForDisplayState(false /* default */, true /* on */);
+        mCreated = true;
+    }
+
+    void turnDisplayOff() {
+        mVirtualDisplay.setSurface(null);
+        waitForDisplayState(false /* default */, false /* on */);
+    }
+
+    void turnDisplayOn() {
+        mVirtualDisplay.setSurface(mReader.getSurface());
+        waitForDisplayState(false /* default */, true /* on */);
+    }
+
+    void releaseDisplay() {
+        if (mCreated) {
+            mVirtualDisplay.release();
+            mReader.close();
+            waitForDisplayCondition(false /* defaultDisplay */, Objects::isNull,
+                    "Waiting for virtual display destroy");
+        }
+        mCreated = false;
+    }
+
+    private void createVirtualDisplay(boolean requestShowWhenLocked) {
+        mReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
+
+        final DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
+
+        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+        if (requestShowWhenLocked) {
+            flags |= VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+        }
+        mVirtualDisplay = displayManager.createVirtualDisplay(
+                VIRTUAL_DISPLAY_NAME, WIDTH, HEIGHT, DENSITY, mReader.getSurface(), flags);
+    }
+
+    static void waitForDefaultDisplayState(boolean wantOn) {
+        waitForDisplayState(true /* default */, wantOn);
+    }
+
+    private static void waitForDisplayState(boolean defaultDisplay, boolean wantOn) {
+        waitForDisplayCondition(defaultDisplay, state -> state != null && state == wantOn,
+                "Waiting for " + (defaultDisplay ? "default" : "virtual") + " display "
+                        + (wantOn ? "on" : "off"));
+    }
+
+    private static void waitForDisplayCondition(boolean defaultDisplay,
+            Predicate<Boolean> condition, String message) {
+        for (int retry = 1; retry <= 10; retry++) {
+            if (condition.test(getDisplayState(defaultDisplay))) {
+                return;
+            }
+            logAlways(message + "... retry=" + retry);
+            SystemClock.sleep(500);
+        }
+        fail(message + " failed");
+    }
+
+    @Nullable
+    private static Boolean getDisplayState(boolean defaultDisplay) {
+        final String dump = executeShellCommand("dumpsys display");
+        final Predicate<Matcher> displayNameMatcher = defaultDisplay
+                ? m -> m.group(0).contains("FLAG_DEFAULT_DISPLAY")
+                : m -> m.group(1).equals(VIRTUAL_DISPLAY_NAME);
+        for (final String line : dump.split("\\n")) {
+            final Matcher matcher = DISPLAY_DEVICE_PATTERN.matcher(line);
+            if (matcher.matches() && displayNameMatcher.test(matcher)) {
+                return "ON".equals(matcher.group(2));
+            }
+        }
+        return null;
+    }
+
+    private static String executeShellCommand(String command) {
+        try {
+            return SystemUtil.runShellCommand(getInstrumentation(), command);
+        } catch (IOException e) {
+            //bubble it up
+            throw new RuntimeException(e);
+        }
+    }
+}
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 def3457..747228a 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
@@ -28,6 +28,8 @@
 import static android.server.am.StateLogger.log;
 import static android.server.am.StateLogger.logAlways;
 import static android.server.am.StateLogger.logE;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.lessThan;
@@ -63,12 +65,6 @@
  */
 public class ActivityAndWindowManagersState {
 
-    // Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
-    // (Needed in host-side tests to convert dp to px.)
-    private static final int DISPLAY_DENSITY_DEFAULT = 160;
-    // TODO: Change to use framework constant.
-    public static final int DEFAULT_DISPLAY_ID = 0;
-
     // Default minimal size of resizable task, used if none is set explicitly.
     // Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base.
     private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220;
@@ -509,23 +505,23 @@
     }
 
     void assertFrontStack(String msg, int stackId) {
-        assertEquals(msg, stackId, mAmState.getFrontStackId(DEFAULT_DISPLAY_ID));
-        assertEquals(msg, stackId, mWmState.getFrontStackId(DEFAULT_DISPLAY_ID));
+        assertEquals(msg, stackId, mAmState.getFrontStackId(DEFAULT_DISPLAY));
+        assertEquals(msg, stackId, mWmState.getFrontStackId(DEFAULT_DISPLAY));
     }
 
     void assertFrontStack(String msg, int windowingMode, int activityType) {
         if (windowingMode != WINDOWING_MODE_UNDEFINED) {
             assertEquals(msg, windowingMode,
-                    mAmState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID));
+                    mAmState.getFrontStackWindowingMode(DEFAULT_DISPLAY));
         }
         if (activityType != ACTIVITY_TYPE_UNDEFINED) {
-            assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+            assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY));
         }
     }
 
     void assertFrontStackActivityType(String msg, int activityType) {
-        assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
-        assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+        assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY));
+        assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY));
     }
 
     @Deprecated
@@ -595,8 +591,8 @@
      */
     void assertDeviceDefaultDisplaySize(String errorMessage) {
         computeState(true);
-        final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY_ID);
-        final Display display = getWmState().getDisplay(DEFAULT_DISPLAY_ID);
+        final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY);
+        final Display display = getWmState().getDisplay(DEFAULT_DISPLAY);
         final Rect displayRect = display.getDisplayRect();
         if (Math.min(displayRect.width(), displayRect.height()) < minTaskSizePx) {
             fail(errorMessage);
@@ -825,16 +821,14 @@
                         } else {
                             // Minimal dimensions affect task size, so bounds of task and stack must
                             // be different - will compare dimensions instead.
-                            int targetWidth = (int) Math.max(aTaskMinWidth,
-                                    aStackBounds.width());
+                            int targetWidth = Math.max(aTaskMinWidth, aStackBounds.width());
                             assertEquals("Task width must be set according to minimal width"
                                             + " taskId=" + taskId + ", stackId=" + stackId,
-                                    targetWidth, (int) wTaskBounds.width());
-                            int targetHeight = (int) Math.max(aTaskMinHeight,
-                                    aStackBounds.height());
+                                    targetWidth, wTaskBounds.width());
+                            int targetHeight = Math.max(aTaskMinHeight, aStackBounds.height());
                             assertEquals("Task height must be set according to minimal height"
                                             + " taskId=" + taskId + ", stackId=" + stackId,
-                                    targetHeight, (int) wTaskBounds.height());
+                                    targetHeight, wTaskBounds.height());
                         }
                     }
                 }
@@ -863,7 +857,7 @@
     }
 
     static int dpToPx(float dp, int densityDpi) {
-        return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f);
+        return (int) (dp * densityDpi / DENSITY_DEFAULT + 0.5f);
     }
 
     private int defaultMinimalTaskSize(int displayId) {
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 c8d3db5..5e45919 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
@@ -61,6 +61,8 @@
 import static android.server.am.UiDeviceUtils.pressUnlockButton;
 import static android.server.am.UiDeviceUtils.pressWakeupButton;
 import static android.server.am.UiDeviceUtils.waitForDeviceIdle;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -72,14 +74,12 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.server.am.settings.SettingsSession;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
-import android.view.Display;
 
 import com.android.compatibility.common.util.SystemUtil;
 
@@ -89,7 +89,6 @@
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -129,14 +128,6 @@
     private static final String AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND_FORMAT =
             "am stack move-top-activity-to-pinned-stack %1d 0 0 500 500";
 
-    /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */
-    static final String FINISH_ACTIVITY_BROADCAST
-            = "am broadcast -a trigger_broadcast --ez finish true";
-
-    /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */
-    static final String MOVE_TASK_TO_BACK_BROADCAST
-            = "am broadcast -a trigger_broadcast --ez moveToBack true";
-
     private static final String AM_RESIZE_DOCKED_STACK = "am stack resize-docked-stack ";
     private static final String AM_RESIZE_STACK = "am stack resize ";
 
@@ -146,8 +137,6 @@
 
     private static final String LOCK_CREDENTIAL = "1234";
 
-    private static final int INVALID_DISPLAY_ID = Display.INVALID_DISPLAY;
-
     private static final int UI_MODE_TYPE_MASK = 0x0f;
     private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
 
@@ -220,10 +209,6 @@
         return String.format(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND_FORMAT, stackId);
     }
 
-    protected static String getOrientationBroadcast(int orientation) {
-        return "am broadcast -a trigger_broadcast --ei orientation " + orientation;
-    }
-
     protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
 
     @Before
@@ -301,8 +286,8 @@
     @Deprecated
     protected int launchActivityInNewDynamicStack(ComponentName activityName) {
         HashSet<Integer> stackIds = getStackIds();
-        executeShellCommand("am stack start " + ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID
-                + " " + getActivityName(activityName));
+        executeShellCommand("am stack start " + DEFAULT_DISPLAY + " "
+                + getActivityName(activityName));
         HashSet<Integer> newStackIds = getStackIds();
         newStackIds.removeAll(stackIds);
         if (newStackIds.isEmpty()) {
@@ -342,11 +327,15 @@
                 .build());
     }
 
-    protected void launchActivityOnDisplay(ComponentName targetActivityName, int displayId,
+    protected void launchActivityOnDisplay(ComponentName activityName, int displayId,
             String... keyValuePairs) {
-        executeShellCommand(getAmStartCmd(targetActivityName, displayId, keyValuePairs));
+        launchActivityOnDisplayNoWait(activityName, displayId, keyValuePairs);
+        mAmWmState.waitForValidState(activityName);
+    }
 
-        mAmWmState.waitForValidState(new WaitForValidActivityState(targetActivityName));
+    protected void launchActivityOnDisplayNoWait(ComponentName activityName, int displayId,
+            String... keyValuePairs) {
+        executeShellCommand(getAmStartCmd(activityName, displayId, keyValuePairs));
     }
 
     /**
@@ -1258,7 +1247,7 @@
         private boolean mRandomData;
         private boolean mNewTask;
         private boolean mMultipleTask;
-        private int mDisplayId = INVALID_DISPLAY_ID;
+        private int mDisplayId = INVALID_DISPLAY;
         // A proxy activity that launches other activities including mTargetActivityName
         private ComponentName mLaunchingActivity = LAUNCHING_ACTIVITY;
         private boolean mReorderToFront;
@@ -1423,7 +1412,7 @@
             if (mReorderToFront) {
                 commandBuilder.append(" --ez " + KEY_REORDER_TO_FRONT + " true");
             }
-            if (mDisplayId != INVALID_DISPLAY_ID) {
+            if (mDisplayId != INVALID_DISPLAY) {
                 commandBuilder.append(" --ei " + KEY_DISPLAY_ID + " ").append(mDisplayId);
             }
 
diff --git a/tests/libcore/luni/Android.mk b/tests/libcore/luni/Android.mk
index 7dfcb61..619962b 100644
--- a/tests/libcore/luni/Android.mk
+++ b/tests/libcore/luni/Android.mk
@@ -38,6 +38,10 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_DX_FLAGS := --multi-dex
 
+# Exclude apache harmony tests from coverage instrumentation, since it breaks
+# the tests of reflection APIs by adding fields and methods to the test classes.
+LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := org.apache.harmony.tests.*
+
 LOCAL_PROGUARD_ENABLED := disabled
 # Keep META-INF/ resources from LOCAL_STATIC_JAVA_LIBRARIES. http://b/62341677
 LOCAL_DONT_DELETE_JAR_META_INF := true
diff --git a/tests/signature/Android.mk b/tests/signature/Android.mk
index fec47b6..84d7cec 100644
--- a/tests/signature/Android.mk
+++ b/tests/signature/Android.mk
@@ -24,6 +24,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
+
 LOCAL_MODULE := cts-signature-common
 
 LOCAL_SDK_VERSION := current
@@ -36,7 +38,7 @@
 include $(CLEAR_VARS)
 
 # These files are for device-side only, so filter-out for host library
-LOCAL_DEVICE_ONLY_SOURCES := %/CurrentApi.java %/ApiDocumentParser.java
+LOCAL_DEVICE_ONLY_SOURCES := %/CurrentApi.java %/ApiDocumentParser.java %/DexMemberChecker.java
 
 LOCAL_SRC_FILES := $(filter-out $(LOCAL_DEVICE_ONLY_SOURCES), $(call all-java-files-under, src))
 
diff --git a/tests/signature/api-check/Android.mk b/tests/signature/api-check/Android.mk
index 4462393..94b59ba 100644
--- a/tests/signature/api-check/Android.mk
+++ b/tests/signature/api-check/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_MODULE := cts-api-signature-test
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     cts-signature-common \
diff --git a/tests/framework/base/activitymanager/displayserviceapp/app/Android.mk b/tests/signature/api-check/hidden-api-killswitch/Android.mk
similarity index 60%
rename from tests/framework/base/activitymanager/displayserviceapp/app/Android.mk
rename to tests/signature/api-check/hidden-api-killswitch/Android.mk
index c9cb7ef..2e1ee05 100644
--- a/tests/framework/base/activitymanager/displayserviceapp/app/Android.mk
+++ b/tests/signature/api-check/hidden-api-killswitch/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 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,22 +12,19 @@
 # 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)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := test_current
+LOCAL_PACKAGE_NAME := CtsHiddenApiKillswitchTestCases
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_MULTILIB := both
+LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
+LOCAL_NDK_STL_VARIANT := c++_static
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := cts-api-signature-test
 
-LOCAL_PACKAGE_NAME := CtsDisplayServiceApp
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml b/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml
new file mode 100644
index 0000000..8699d18
--- /dev/null
+++ b/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.signature.cts.api.killswitch">
+
+    <application/>
+
+    <instrumentation android:name="repackaged.android.test.InstrumentationTestRunner"
+                     android:targetPackage="android.signature.cts.api.killswitch"
+                     android:label="Hidden API Killswitch Test"/>
+</manifest>
diff --git a/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml b/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml
new file mode 100644
index 0000000..2d99a73
--- /dev/null
+++ b/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?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 Hidden API Signature test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <!-- Enable the killswitch before running the test, then disable it afterwards. The test
+             is intended to verify the behaviour when the killswitch is enabled. -->
+        <option name="run-command" value="settings put global hidden_api_blacklist_exemptions \*" />
+        <option name="teardown-command" value="settings delete global hidden_api_blacklist_exemptions" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsHiddenApiKillswitchTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.signature.cts.api.killswitch" />
+        <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
+        <option name="class" value="android.signature.cts.api.KillswitchTest" />
+        <option name="runtime-hint" value="30s" />
+    </test>
+</configuration>
diff --git a/tests/signature/api-check/hidden-api/Android.mk b/tests/signature/api-check/hidden-api/Android.mk
index a5bc737..f039032 100644
--- a/tests/signature/api-check/hidden-api/Android.mk
+++ b/tests/signature/api-check/hidden-api/Android.mk
@@ -24,18 +24,8 @@
 $(eval $(call copy-one-file,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST),$(LOCAL_BUILT_MODULE)))
 
 include $(CLEAR_VARS)
-LOCAL_MODULE := libcts_hiddenapi
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := hidden-api.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := CtsHiddenApiDiscoveryTestCases
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_SIGNATURE_API_FILES := blacklist.api
-LOCAL_JNI_SHARED_LIBRARIES := libcts_hiddenapi
+LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
 include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
index e1da5ac..9673fd2 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
@@ -21,13 +21,17 @@
 import android.signature.cts.DexApiDocumentParser.DexField;
 import android.signature.cts.DexApiDocumentParser.DexMember;
 import android.signature.cts.DexApiDocumentParser.DexMethod;
+import android.signature.cts.DexMemberChecker;
 import android.signature.cts.FailureType;
+import android.signature.cts.ResultObserver;
+
 import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Stream;
 import java.text.ParseException;
 
@@ -52,136 +56,59 @@
      * Will check the entire API, and then report the complete list of failures
      */
     public void testSignature() {
-        System.loadLibrary("cts_hiddenapi");
+        DexMemberChecker.init();
         runWithTestResultObserver(resultObserver -> {
+            DexMemberChecker.Observer observer = new DexMemberChecker.Observer() {
+                @Override
+                public void classAccessible(boolean accessible, DexMember member) {
+                }
+
+                @Override
+                public void fieldAccessibleViaReflection(boolean accessible, DexField field) {
+                    if (accessible) {
+                        resultObserver.notifyFailure(
+                                FailureType.EXTRA_FIELD,
+                                field.toString(),
+                                "Hidden field accessible through reflection");
+                    }
+                }
+
+                @Override
+                public void fieldAccessibleViaJni(boolean accessible, DexField field) {
+                    if (accessible) {
+                        resultObserver.notifyFailure(
+                                FailureType.EXTRA_FIELD,
+                                field.toString(),
+                                "Hidden field accessible through JNI");
+                    }
+                }
+
+                @Override
+                public void methodAccessibleViaReflection(boolean accessible, DexMethod method) {
+                    if (accessible) {
+                        resultObserver.notifyFailure(
+                                FailureType.EXTRA_METHOD,
+                                method.toString(),
+                                "Hidden method accessible through reflection");
+                    }
+                }
+
+                @Override
+                public void methodAccessibleViaJni(boolean accessible, DexMethod method) {
+                    if (accessible) {
+                        resultObserver.notifyFailure(
+                                FailureType.EXTRA_METHOD,
+                                method.toString(),
+                                "Hidden method accessible through JNI");
+                    }
+                }
+            };
             parseDexApiFilesAsStream(hiddenApiFiles).forEach(dexMember -> {
-                checkSingleMember(dexMember, resultObserver);
+                DexMemberChecker.checkSingleMember(dexMember, observer);
             });
         });
     }
 
-    /**
-     * Check that a DexMember cannot be discovered with reflection or JNI, and
-     * record results in the result
-     */
-    private void checkSingleMember(DexMember dexMember, TestResultObserver resultObserver) {
-        Class<?> klass = findClass(dexMember);
-        if (klass == null) {
-            // Class not found. Therefore its members are not visible.
-            return;
-        }
-
-        if (dexMember instanceof DexField) {
-            if (hasMatchingField_Reflection(klass, (DexField) dexMember)) {
-                resultObserver.notifyFailure(
-                        FailureType.EXTRA_FIELD,
-                        dexMember.toString(),
-                        "Hidden field accessible through reflection");
-            }
-            if (hasMatchingField_JNI(klass, (DexField) dexMember)) {
-                resultObserver.notifyFailure(
-                        FailureType.EXTRA_FIELD,
-                        dexMember.toString(),
-                        "Hidden field accessible through JNI");
-            }
-        } else if (dexMember instanceof DexMethod) {
-            if (hasMatchingMethod_Reflection(klass, (DexMethod) dexMember)) {
-                resultObserver.notifyFailure(
-                        FailureType.EXTRA_METHOD,
-                        dexMember.toString(),
-                        "Hidden method accessible through reflection");
-            }
-            if (hasMatchingMethod_JNI(klass, (DexMethod) dexMember)) {
-                resultObserver.notifyFailure(
-                        FailureType.EXTRA_METHOD,
-                        dexMember.toString(),
-                        "Hidden method accessible through JNI");
-            }
-        } else {
-            throw new IllegalStateException("Unexpected type of dex member");
-        }
-    }
-
-    private boolean typesMatch(Class<?>[] classes, List<String> typeNames) {
-        if (classes.length != typeNames.size()) {
-            return false;
-        }
-        for (int i = 0; i < classes.length; ++i) {
-            if (!classes[i].getTypeName().equals(typeNames.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private Class<?> findClass(DexMember dexMember) {
-        Class<?> klass = null;
-        try {
-            return Class.forName(dexMember.getJavaClassName());
-        } catch (ClassNotFoundException ex) {
-            return null;
-        }
-    }
-
-    private static boolean hasMatchingField_Reflection(Class<?> klass, DexField dexField) {
-        try {
-            klass.getDeclaredField(dexField.getName());
-            return true;
-        } catch (NoSuchFieldException ex) {
-            return false;
-        }
-    }
-
-    private static boolean hasMatchingField_JNI(Class<?> klass, DexField dexField) {
-        try {
-            getField_JNI(klass, dexField.getName(), dexField.getDexType());
-            return true;
-        } catch (NoSuchFieldError ex) {
-        }
-        try {
-            getStaticField_JNI(klass, dexField.getName(), dexField.getDexType());
-            return true;
-        } catch (NoSuchFieldError ex) {
-        }
-        return false;
-    }
-
-    private boolean hasMatchingMethod_Reflection(Class<?> klass, DexMethod dexMethod) {
-        List<String> methodParams = dexMethod.getJavaParameterTypes();
-
-        if (dexMethod.isConstructor()) {
-            for (Constructor constructor : klass.getDeclaredConstructors()) {
-                if (typesMatch(constructor.getParameterTypes(), methodParams)) {
-                    return true;
-                }
-            }
-        } else {
-            for (Method method : klass.getDeclaredMethods()) {
-                if (method.getName().equals(dexMethod.getName())
-                        && typesMatch(method.getParameterTypes(), methodParams)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private static boolean hasMatchingMethod_JNI(Class<?> klass, DexMethod dexMethod) {
-        try {
-            getMethod_JNI(klass, dexMethod.getName(), dexMethod.getDexSignature());
-            return true;
-        } catch (NoSuchMethodError ex) {
-        }
-        if (!dexMethod.isConstructor()) {
-            try {
-                getStaticMethod_JNI(klass, dexMethod.getName(), dexMethod.getDexSignature());
-                return true;
-            } catch (NoSuchMethodError ex) {
-            }
-        }
-        return false;
-    }
-
     private static Stream<DexMember> parseDexApiFilesAsStream(String[] apiFiles) {
         DexApiDocumentParser dexApiDocumentParser = new DexApiDocumentParser();
         return Stream.of(apiFiles)
@@ -190,9 +117,4 @@
                 .flatMap(stream -> dexApiDocumentParser.parseAsStream(stream));
     }
 
-    private static native boolean getField_JNI(Class<?> klass, String name, String type);
-    private static native boolean getStaticField_JNI(Class<?> klass, String name, String type);
-    private static native boolean getMethod_JNI(Class<?> klass, String name, String signature);
-    private static native boolean getStaticMethod_JNI(Class<?> klass, String name,
-            String signature);
 }
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java
new file mode 100644
index 0000000..37ff746
--- /dev/null
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.signature.cts.api;
+
+import android.provider.Settings;
+import android.signature.cts.DexApiDocumentParser;
+import android.signature.cts.DexMemberChecker;
+import android.signature.cts.FailureType;
+
+public class KillswitchTest extends AbstractApiTest {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        DexMemberChecker.init();
+        // precondition check: make sure global setting has been configured properly.
+        // This should be done via an adb command, configured in AndroidTest.xml.
+        assertEquals("Global setting " + Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
+                "*",
+                Settings.Global.getString(
+                        getInstrumentation().getContext().getContentResolver(),
+                        Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS));
+    }
+
+    private static final String ERROR_MESSAGE_APPENDIX =
+            " when global setting hidden_api_blacklist_exemptions is set to *";
+
+    public void testKillswitch() {
+        // for now, just verify that we can access something *not* on the light grey or white list
+        DexApiDocumentParser.DexMember member = new DexApiDocumentParser.DexField(
+                "Ldalvik/system/VMRuntime;", "THE_ONE", "Ldalvik/system/VMRuntime;");
+        runWithTestResultObserver(resultObserver ->
+                DexMemberChecker.checkSingleMember(member, new DexMemberChecker.Observer() {
+                    @Override
+                    public void classAccessible(boolean accessible,
+                            DexApiDocumentParser.DexMember member) {
+                        if (!accessible) {
+                            resultObserver.notifyFailure(
+                                    FailureType.MISSING_CLASS,
+                                    member.toString(),
+                                    "Class from boot classpath is not accessible"
+                                            + ERROR_MESSAGE_APPENDIX);
+                        }
+                    }
+
+                    @Override
+                    public void fieldAccessibleViaReflection(boolean accessible,
+                            DexApiDocumentParser.DexField field) {
+                        if (!accessible) {
+                            resultObserver.notifyFailure(
+                                    FailureType.MISSING_FIELD,
+                                    field.toString(),
+                                    "Field from boot classpath is not accessible via reflection"
+                                            + ERROR_MESSAGE_APPENDIX);
+                        }
+                    }
+
+                    @Override
+                    public void fieldAccessibleViaJni(boolean accessible,
+                            DexApiDocumentParser.DexField field) {
+                        if (!accessible) {
+                            resultObserver.notifyFailure(
+                                    FailureType.MISSING_FIELD,
+                                    field.toString(),
+                                    "Field from boot classpath is not accessible via JNI"
+                                            + ERROR_MESSAGE_APPENDIX);
+                        }
+                    }
+
+                    @Override
+                    public void methodAccessibleViaReflection(boolean accessible,
+                            DexApiDocumentParser.DexMethod method) {
+                        if (!accessible) {
+                            resultObserver.notifyFailure(
+                                    FailureType.MISSING_METHOD,
+                                    method.toString(),
+                                    "Method from boot classpath is not accessible via reflection"
+                                            + ERROR_MESSAGE_APPENDIX);
+                        }
+                    }
+
+                    @Override
+                    public void methodAccessibleViaJni(boolean accessible,
+                            DexApiDocumentParser.DexMethod method) {
+                        if (!accessible) {
+                            resultObserver.notifyFailure(
+                                    FailureType.MISSING_METHOD,
+                                    method.toString(),
+                                    "Method from boot classpath is not accessible via JNI"
+                                            + ERROR_MESSAGE_APPENDIX);
+                        }
+                    }
+
+                }));
+    }
+}
diff --git a/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk b/tests/signature/dex-checker/Android.mk
similarity index 62%
rename from tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
rename to tests/signature/dex-checker/Android.mk
index 613888a..8a86a36 100644
--- a/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
+++ b/tests/signature/dex-checker/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 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.
@@ -15,19 +15,11 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    android-support-test
-
-LOCAL_MODULE := cts-display-service-app-util
-
+LOCAL_MODULE := libcts_dexchecker
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := dex-checker.cpp
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 LOCAL_SDK_VERSION := current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_NDK_STL_VARIANT := c++_static
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/signature/api-check/hidden-api/hidden-api.cpp b/tests/signature/dex-checker/dex-checker.cpp
similarity index 90%
rename from tests/signature/api-check/hidden-api/hidden-api.cpp
rename to tests/signature/dex-checker/dex-checker.cpp
index fd2a94d..dc6757e 100644
--- a/tests/signature/api-check/hidden-api/hidden-api.cpp
+++ b/tests/signature/dex-checker/dex-checker.cpp
@@ -43,7 +43,7 @@
 };
 
 extern "C" JNIEXPORT void JNICALL
-Java_android_signature_cts_api_HiddenApiTest_getField_1JNI(
+Java_android_signature_cts_DexMemberChecker_getField_1JNI(
     JNIEnv* env, jclass, jclass klass, jstring name, jstring type) {
   ScopedUtfChars utf_name(env, name);
   ScopedUtfChars utf_type(env, type);
@@ -53,7 +53,7 @@
 }
 
 extern "C" JNIEXPORT void JNICALL
-Java_android_signature_cts_api_HiddenApiTest_getStaticField_1JNI(
+Java_android_signature_cts_DexMemberChecker_getStaticField_1JNI(
     JNIEnv* env, jclass, jclass klass, jstring name, jstring type) {
   ScopedUtfChars utf_name(env, name);
   ScopedUtfChars utf_type(env, type);
@@ -63,7 +63,7 @@
 }
 
 extern "C" JNIEXPORT void JNICALL
-Java_android_signature_cts_api_HiddenApiTest_getMethod_1JNI(
+Java_android_signature_cts_DexMemberChecker_getMethod_1JNI(
     JNIEnv* env, jclass, jclass klass, jstring name, jstring signature) {
   ScopedUtfChars utf_name(env, name);
   ScopedUtfChars utf_signature(env, signature);
@@ -73,7 +73,7 @@
 }
 
 extern "C" JNIEXPORT void JNICALL
-Java_android_signature_cts_api_HiddenApiTest_getStaticMethod_1JNI(
+Java_android_signature_cts_DexMemberChecker_getStaticMethod_1JNI(
     JNIEnv* env, jclass, jclass klass, jstring name, jstring signature) {
   ScopedUtfChars utf_name(env, name);
   ScopedUtfChars utf_signature(env, signature);
diff --git a/tests/signature/runSignatureTests.sh b/tests/signature/runSignatureTests.sh
index 1350297..d3f1546 100755
--- a/tests/signature/runSignatureTests.sh
+++ b/tests/signature/runSignatureTests.sh
@@ -4,6 +4,8 @@
 #
 # Builds and runs signature APK tests.
 
+set -e
+
 if [ -z "$ANDROID_BUILD_TOP" ]; then
     echo "Missing environment variables. Did you run build/envsetup.sh and lunch?" >&2
     exit 1
@@ -24,6 +26,7 @@
 CtsSystemApiAnnotationTestCases
 
 CtsHiddenApiDiscoveryTestCases
+CtsHiddenApiKillswitchTestCases
 "
 else
     PACKAGES=${1+"$@"}
diff --git a/tests/signature/src/android/signature/cts/DexMemberChecker.java b/tests/signature/src/android/signature/cts/DexMemberChecker.java
new file mode 100644
index 0000000..3800682
--- /dev/null
+++ b/tests/signature/src/android/signature/cts/DexMemberChecker.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.signature.cts;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.List;
+
+public class DexMemberChecker {
+
+    public interface Observer {
+        void classAccessible(boolean accessible, DexApiDocumentParser.DexMember member);
+        void fieldAccessibleViaReflection(boolean accessible, DexApiDocumentParser.DexField field);
+        void fieldAccessibleViaJni(boolean accessible, DexApiDocumentParser.DexField field);
+        void methodAccessibleViaReflection(boolean accessible,
+                DexApiDocumentParser.DexMethod method);
+        void methodAccessibleViaJni(boolean accessible, DexApiDocumentParser.DexMethod method);
+    }
+
+    public static void init() {
+        System.loadLibrary("cts_dexchecker");
+    }
+
+    public static void checkSingleMember(DexApiDocumentParser.DexMember dexMember,
+            DexMemberChecker.Observer observer) {
+        Class<?> klass = findClass(dexMember);
+        if (klass == null) {
+            // Class not found. Therefore its members are not visible.
+            observer.classAccessible(false, dexMember);
+            return;
+        }
+        observer.classAccessible(true, dexMember);
+
+        StringBuilder error = new StringBuilder("Hidden ");
+        if (dexMember instanceof DexApiDocumentParser.DexField) {
+            DexApiDocumentParser.DexField field = (DexApiDocumentParser.DexField) dexMember;
+            observer.fieldAccessibleViaReflection(
+                    hasMatchingField_Reflection(klass, field),
+                    field);
+            observer.fieldAccessibleViaJni(
+                    hasMatchingField_JNI(klass, field),
+                    field);
+        } else if (dexMember instanceof DexApiDocumentParser.DexMethod) {
+            DexApiDocumentParser.DexMethod method = (DexApiDocumentParser.DexMethod) dexMember;
+            observer.methodAccessibleViaReflection(
+                    hasMatchingMethod_Reflection(klass, method),
+                    method);
+            observer.methodAccessibleViaJni(
+                    hasMatchingMethod_JNI(klass, method),
+                    method);
+        } else {
+            throw new IllegalStateException("Unexpected type of dex member");
+        }
+    }
+
+    private static boolean typesMatch(Class<?>[] classes, List<String> typeNames) {
+        if (classes.length != typeNames.size()) {
+            return false;
+        }
+        for (int i = 0; i < classes.length; ++i) {
+            if (!classes[i].getTypeName().equals(typeNames.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static Class<?> findClass(DexApiDocumentParser.DexMember dexMember) {
+        Class<?> klass = null;
+        try {
+            return Class.forName(dexMember.getJavaClassName());
+        } catch (ClassNotFoundException ex) {
+            return null;
+        }
+    }
+
+    private static boolean hasMatchingField_Reflection(Class<?> klass,
+            DexApiDocumentParser.DexField dexField) {
+        try {
+            klass.getDeclaredField(dexField.getName());
+            return true;
+        } catch (NoSuchFieldException ex) {
+            return false;
+        }
+    }
+
+    private static boolean hasMatchingField_JNI(Class<?> klass,
+            DexApiDocumentParser.DexField dexField) {
+        try {
+            getField_JNI(klass, dexField.getName(), dexField.getDexType());
+            return true;
+        } catch (NoSuchFieldError ex) {
+        }
+        try {
+            getStaticField_JNI(klass, dexField.getName(), dexField.getDexType());
+            return true;
+        } catch (NoSuchFieldError ex) {
+        }
+        return false;
+    }
+
+    private static boolean hasMatchingMethod_Reflection(Class<?> klass,
+            DexApiDocumentParser.DexMethod dexMethod) {
+        List<String> methodParams = dexMethod.getJavaParameterTypes();
+
+        if (dexMethod.isConstructor()) {
+            for (Constructor constructor : klass.getDeclaredConstructors()) {
+                if (typesMatch(constructor.getParameterTypes(), methodParams)) {
+                    return true;
+                }
+            }
+        } else {
+            for (Method method : klass.getDeclaredMethods()) {
+                if (method.getName().equals(dexMethod.getName())
+                        && typesMatch(method.getParameterTypes(), methodParams)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasMatchingMethod_JNI(Class<?> klass,
+            DexApiDocumentParser.DexMethod dexMethod) {
+        try {
+            getMethod_JNI(klass, dexMethod.getName(), dexMethod.getDexSignature());
+            return true;
+        } catch (NoSuchMethodError ex) {
+        }
+        if (!dexMethod.isConstructor()) {
+            try {
+                getStaticMethod_JNI(klass, dexMethod.getName(), dexMethod.getDexSignature());
+                return true;
+            } catch (NoSuchMethodError ex) {
+            }
+        }
+        return false;
+    }
+
+    private static native boolean getField_JNI(Class<?> klass, String name, String type);
+    private static native boolean getStaticField_JNI(Class<?> klass, String name, String type);
+    private static native boolean getMethod_JNI(Class<?> klass, String name, String signature);
+    private static native boolean getStaticMethod_JNI(Class<?> klass, String name,
+            String signature);
+
+}
diff --git a/tests/tests/appcomponentfactory/Android.mk b/tests/tests/appcomponentfactory/Android.mk
index 5b20611..c3c9939 100644
--- a/tests/tests/appcomponentfactory/Android.mk
+++ b/tests/tests/appcomponentfactory/Android.mk
@@ -17,6 +17,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsAppComponentFactoryTestCases
+LOCAL_SDK_VERSION := current
 
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
diff --git a/tests/tests/car/Android.mk b/tests/tests/car/Android.mk
index 07cbd83..7310085 100644
--- a/tests/tests/car/Android.mk
+++ b/tests/tests/car/Android.mk
@@ -33,6 +33,8 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_SDK_VERSION := current
+#TODO(b/72620511) Apps should not use platform APIs directly
+#LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
index f191801..a620071 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.database.Cursor;
+import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteCursor;
 import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
@@ -290,6 +291,15 @@
         }
     }
 
+    public void testSetWriteAheadLoggingDisablesCompatibilityWal() {
+        // Verify that compatibility WAL is not enabled, if an application explicitly disables WAL
+
+        mOpenHelper.setWriteAheadLoggingEnabled(false);
+        String journalMode = DatabaseUtils
+                .stringForQuery(mOpenHelper.getWritableDatabase(), "PRAGMA journal_mode", null);
+        assertFalse("Default journal mode should not be WAL", "WAL".equalsIgnoreCase(journalMode));
+    }
+
     private MockOpenHelper getOpenHelper() {
         return new MockOpenHelper(mContext, TEST_DATABASE_NAME, mFactory, TEST_VERSION);
     }
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
index 7b1e371..5651f47 100644
--- a/tests/tests/display/AndroidManifest.xml
+++ b/tests/tests/display/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <!-- For testing brightness slider tracking. -->
     <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <!-- For testing pushing brightness curves. -->
+    <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/display/src/android/display/cts/BrightnessTest.java b/tests/tests/display/src/android/display/cts/BrightnessTest.java
index 62617d6..65fcb6e 100644
--- a/tests/tests/display/src/android/display/cts/BrightnessTest.java
+++ b/tests/tests/display/src/android/display/cts/BrightnessTest.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.DisplayManager;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
@@ -49,8 +50,16 @@
                 InstrumentationRegistry.getContext().getSystemService(DisplayManager.class);
         PowerManager pm =
                 InstrumentationRegistry.getContext().getSystemService(PowerManager.class);
+        // Fail early if screen isn't on as wakelock won't wake it up.
+        assertTrue(pm.isInteractive());
+
         mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "BrightnessTest");
         mWakeLock.acquire();
+
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.BRIGHTNESS_SLIDER_USAGE");
     }
 
     @Override
@@ -111,6 +120,151 @@
         }
     }
 
+    public void testNoTrackingForManualBrightness() throws IOException, InterruptedException {
+        if (!systemAppWithPermission("android.permission.BRIGHTNESS_SLIDER_USAGE",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to access slider usage.
+            return;
+        }
+        int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+        int previousBrightnessMode =
+                getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        try {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+            int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+            assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, mode);
+
+            runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                    + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+
+            // Setup and remember some initial state.
+            recordSliderEvents();
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+            assertTrue(getNewEvents().isEmpty());
+
+            // Then change the brightness
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 80);
+            Thread.sleep(200);
+            // There shouldn't be any events.
+            assertTrue(getNewEvents().isEmpty());
+        } finally {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+        }
+    }
+
+    public void testSliderUsagePermission() throws IOException, InterruptedException {
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+
+        try {
+            mDisplayManager.getBrightnessEvents();
+        } catch (SecurityException e) {
+            // Expected
+            return;
+        }
+        fail();
+    }
+
+    public void testConfigureBrightnessPermission() throws IOException, InterruptedException {
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+        BrightnessConfiguration config =
+            new BrightnessConfiguration.Builder(
+                    new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+                .setDescription("some test").build();
+
+        try {
+            mDisplayManager.setBrightnessConfiguration(config);
+        } catch (SecurityException e) {
+            // Expected
+            return;
+        }
+        fail();
+    }
+
+    public void testPushSimpleCurves() throws IOException, InterruptedException {
+        if (!systemAppWithPermission("android.permission.CONFIGURE_DISPLAY_BRIGHTNESS",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to push curves.
+            return;
+        }
+        runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+        BrightnessConfiguration config =
+                new BrightnessConfiguration.Builder(
+                        new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+                        .setDescription("some test").build();
+        mDisplayManager.setBrightnessConfiguration(config);
+        mDisplayManager.setBrightnessConfiguration(null);
+    }
+
+    public void testSliderEventsReflectCurves() throws IOException, InterruptedException {
+        if (!systemAppWithPermission("android.permission.BRIGHTNESS_SLIDER_USAGE",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to access slider usage.
+            return;
+        }
+        if (!systemAppWithPermission("android.permission.CONFIGURE_DISPLAY_BRIGHTNESS",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to push curves.
+            return;
+        }
+
+        BrightnessConfiguration config =
+                new BrightnessConfiguration.Builder(
+                        new float[]{0.0f, 10000.0f},new float[]{15.0f, 400.0f})
+                        .setDescription("model:8").build();
+
+        int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+        int previousBrightnessMode =
+                getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        try {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+            int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+            assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode);
+
+            runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                    + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+            runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                    + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+            // Setup and remember some initial state.
+            recordSliderEvents();
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+            getNewEvents(1);
+
+            // Update brightness while we have a custom curve.
+            mDisplayManager.setBrightnessConfiguration(config);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60);
+
+            // Check we got a slider event for the change.
+            List<BrightnessChangeEvent> newEvents = getNewEvents(1);
+            assertEquals(1, newEvents.size());
+            BrightnessChangeEvent firstEvent = newEvents.get(0);
+            assertValidLuxData(firstEvent);
+            assertFalse(firstEvent.isDefaultBrightnessConfig);
+
+            // Update brightness again now with default curve.
+            mDisplayManager.setBrightnessConfiguration(null);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200);
+
+            // Check we get a second slider event.
+            newEvents = getNewEvents(1);
+            assertEquals(1, newEvents.size());
+            BrightnessChangeEvent secondEvent = newEvents.get(0);
+            assertValidLuxData(secondEvent);
+            assertTrue(secondEvent.isDefaultBrightnessConfig);
+        } finally {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+        }
+    }
+
     private void assertValidLuxData(BrightnessChangeEvent event) {
         assertNotNull(event.luxTimestamps);
         assertNotNull(event.luxValues);
@@ -142,16 +296,22 @@
             if (i != 0) {
                 Thread.sleep(100);
             }
-            List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
-            for (BrightnessChangeEvent event : events) {
-                if (!mLastReadEvents.containsKey(event.timeStamp)) {
-                    newEvents.add(event);
-                }
+            newEvents.addAll(getNewEvents());
+        }
+        return newEvents;
+    }
+
+    private List<BrightnessChangeEvent> getNewEvents() {
+        List<BrightnessChangeEvent> newEvents = new ArrayList<>();
+        List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
+        for (BrightnessChangeEvent event : events) {
+            if (!mLastReadEvents.containsKey(event.timeStamp)) {
+                newEvents.add(event);
             }
-            mLastReadEvents = new HashMap<>();
-            for (BrightnessChangeEvent event : events) {
-                mLastReadEvents.put(event.timeStamp, event);
-            }
+        }
+        mLastReadEvents = new HashMap<>();
+        for (BrightnessChangeEvent event : events) {
+            mLastReadEvents.put(event.timeStamp, event);
         }
         return newEvents;
     }
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 12e57470..d76a68b 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -728,6 +728,55 @@
     }
 
     @Test
+    public void testResizeTransparency() {
+        ImageDecoder.Source src = mCreators[0].apply(R.drawable.animated);
+        Drawable dr = null;
+        try {
+            dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                decoder.setResize(info.getSize().getWidth() - 5, info.getSize().getHeight() - 5);
+            });
+        } catch (IOException e) {
+            fail("Failed with exception " + e);
+        }
+
+        final int width = dr.getIntrinsicWidth();
+        final int height = dr.getIntrinsicHeight();
+
+        // Draw to a fully transparent Bitmap. Pixels that are transparent in the image will be
+        // transparent.
+        Bitmap normal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        {
+            Canvas canvas = new Canvas(normal);
+            dr.draw(canvas);
+        }
+
+        // Draw to a BLUE Bitmap. Any pixels that are transparent in the image remain BLUE.
+        Bitmap blended = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        {
+            Canvas canvas = new Canvas(blended);
+            canvas.drawColor(Color.BLUE);
+            dr.draw(canvas);
+        }
+
+        boolean hasTransparency = false;
+        for (int i = 0; i < width; ++i) {
+            for (int j = 0; j < height; ++j) {
+                int normalColor = normal.getPixel(i, j);
+                int blendedColor = blended.getPixel(i, j);
+                if (normalColor == Color.TRANSPARENT) {
+                    hasTransparency = true;
+                    assertEquals(Color.BLUE, blendedColor);
+                } else if (Color.alpha(normalColor) == 255) {
+                    assertEquals(normalColor, blendedColor);
+                }
+            }
+        }
+
+        // Verify that the image has transparency. Otherwise the test is not useful.
+        assertTrue(hasTransparency);
+    }
+
+    @Test
     public void testOnPartialImage() {
         class PartialImageCallback implements ImageDecoder.OnPartialImageListener {
             public boolean wasCalled;
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java
index e2254a7..8def08f 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AdaptiveIconDrawableTest.java
@@ -31,13 +31,15 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.Arrays;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class AdaptiveIconDrawableTest {
@@ -215,12 +217,23 @@
     public void testSetAlpha() {
         AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(
             new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE));
-        iconDrawable.setAlpha(1);
-        iconDrawable.setAlpha(-1);
+        iconDrawable.setBounds(0, 0, 100, 100);
 
-        iconDrawable.setAlpha(0);
-        iconDrawable.setAlpha(Integer.MAX_VALUE);
-        iconDrawable.setAlpha(Integer.MIN_VALUE);
+        Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+
+        iconDrawable.draw(canvas);
+        assertEquals(255, Color.alpha(bitmap.getPixel(50, 50)));
+
+        iconDrawable.setAlpha(200);
+        bitmap.eraseColor(Color.TRANSPARENT);
+        iconDrawable.draw(canvas);
+        assertEquals(200, Color.alpha(bitmap.getPixel(50, 50)));
+
+        iconDrawable.setAlpha(100);
+        bitmap.eraseColor(Color.TRANSPARENT);
+        iconDrawable.draw(canvas);
+        assertEquals(100, Color.alpha(bitmap.getPixel(50, 50)));
     }
 
     @Test
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index 0d2fd02..fe52159 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -42,6 +42,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
 import android.widget.ImageView;
 
 import com.android.compatibility.common.util.BitmapUtils;
@@ -567,6 +568,51 @@
         assertTrue(drawable.isAutoMirrored());
     }
 
+    private void drawAndCompare(Bitmap expected, Drawable drawable) {
+        Bitmap test = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(test);
+        drawable.draw(canvas);
+        BitmapUtils.compareBitmaps(expected, test);
+    }
+
+    @Test
+    public void testAutoMirroredDrawing() {
+        AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
+        assertFalse(drawable.isAutoMirrored());
+
+        final int width = drawable.getIntrinsicWidth();
+        final int height = drawable.getIntrinsicHeight();
+        Bitmap normal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        {
+            Canvas canvas = new Canvas(normal);
+            drawable.draw(canvas);
+        }
+
+        Bitmap flipped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        {
+            Canvas canvas = new Canvas(flipped);
+            canvas.translate(width, 0);
+            canvas.scale(-1, 1);
+            drawable.draw(canvas);
+        }
+
+        for (int i = 0; i < width; ++i) {
+            for (int j = 0; j < height; ++j) {
+                assertEquals(normal.getPixel(i, j), flipped.getPixel(width - 1 - i, j));
+            }
+        }
+
+        drawable.setAutoMirrored(true);
+        drawAndCompare(normal, drawable);
+
+        drawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+        drawAndCompare(flipped, drawable);
+
+        drawable.setAutoMirrored(false);
+        drawAndCompare(normal, drawable);
+    }
+
     @Test
     public void testRepeatCountFromXml() throws XmlPullParserException, IOException {
         XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count);
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 44af14d..d387a99 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -87,6 +87,7 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
             </intent-filter>
         </activity>
+        <activity android:name="android.media.cts.MockActivity" />
         <service android:name="android.media.cts.RemoteVirtualDisplayService"
             android:process=":remoteService" >
             <intent-filter>
@@ -98,6 +99,20 @@
                 <action android:name="android.media.browse.MediaBrowserService" />
             </intent-filter>
         </service>
+        <!-- Keep the test services synced together with the TestUtils.java -->
+        <service android:name="android.media.cts.MockMediaSessionService2">
+            <intent-filter>
+                <action android:name="android.media.MediaSessionService2" />
+            </intent-filter>
+            <meta-data android:name="android.media.session" android:value="TestSession" />
+        </service>
+        <!-- Keep the test services synced together with the MockMediaLibraryService -->
+        <service android:name="android.media.cts.MockMediaLibraryService2">
+            <intent-filter>
+                <action android:name="android.media.MediaLibraryService2" />
+            </intent-filter>
+            <meta-data android:name="android.media.session" android:value="TestLibrary" />
+        </service>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 59e2361..5d2ee79 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -28,6 +28,7 @@
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_ACCESSIBILITY;
 import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_RING;
 import static android.media.AudioManager.USE_DEFAULT_STREAM_TYPE;
 import static android.media.AudioManager.VIBRATE_SETTING_OFF;
 import static android.media.AudioManager.VIBRATE_SETTING_ON;
@@ -62,6 +63,7 @@
 public class AudioManagerTest extends InstrumentationTestCase {
     private final static String TAG = "AudioManagerTest";
 
+    private final static long MUTE_DELAY = 50;
     private final static int MP3_TO_PLAY = R.raw.testmp3;
     private final static long TIME_TO_PLAY = 2000;
     private final static String APPOPS_OP_STR = "android:write_settings";
@@ -83,6 +85,8 @@
     private final static int ASYNC_TIMING_TOLERANCE_MS = 50;
     private int mOriginalRingerMode;
     private Map<Integer, Integer> mOriginalStreamVolumes = new HashMap<>();
+    private NotificationManager.Policy mOriginalNotificationPolicy;
+    private int mOriginalZen;
 
     @Override
     protected void setUp() throws Exception {
@@ -120,6 +124,19 @@
         for (int streamType : streamTypes) {
             mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
         }
+
+        if (mSupportNotificationPolicyAccess) {
+            try {
+                Utils.toggleNotificationPolicyAccess(
+                        mContext.getPackageName(), getInstrumentation(), true);
+                mOriginalNotificationPolicy = mNm.getNotificationPolicy();
+                mOriginalZen = mNm.getCurrentInterruptionFilter();
+            } finally {
+                setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+                Utils.toggleNotificationPolicyAccess(
+                        mContext.getPackageName(), getInstrumentation(), false);
+            }
+        }
     }
 
     @Override
@@ -130,6 +147,8 @@
         try {
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), true);
+            mNm.setNotificationPolicy(mOriginalNotificationPolicy);
+            setInterruptionFilter(mOriginalZen);
 
             // Recover the volume and the ringer mode that the test may have overwritten.
             for (Map.Entry<Integer, Integer> e : mOriginalStreamVolumes.entrySet()) {
@@ -1015,8 +1034,8 @@
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), true);
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
-            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALARMS);
 
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALARMS);
             int musicVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
             mAudioManager.adjustStreamVolume(
                     AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0);
@@ -1039,12 +1058,12 @@
                     mContext.getPackageName(), getInstrumentation(), true);
             mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+
             setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
 
             int musicVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 7, 0);
             assertEquals(musicVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
-
             mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 7, 0);
             assertEquals(7, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
         } finally {
@@ -1065,7 +1084,6 @@
 
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 3, 0);
             assertEquals(3, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
-
             mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 7, 0);
             assertEquals(7, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
         } finally {
@@ -1079,33 +1097,33 @@
         }
         Utils.toggleNotificationPolicyAccess(
                 mContext.getPackageName(), getInstrumentation(), true);
-        NotificationManager.Policy previousPolicy = mNm.getNotificationPolicy();
 
         try {
-            // disallow all sounds in priority only
-            mNm.setNotificationPolicy(new NotificationManager.Policy(0,
-                    0 , 0));
+            // turn off zen, set stream volumes to check for later
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+
+            final int testRingerVol = getTestRingerVol();
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
             mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
-            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
-
-            // saved volumes before trying to adjust volume
             int musicVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
             int alarmVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
 
-            // check new volumes, cannot adjust music or alarm
+            // disallow all sounds in priority only, turn on priority only DND, try to change volume
+            mNm.setNotificationPolicy(new NotificationManager.Policy(0, 0 , 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+            Thread.sleep(MUTE_DELAY); // delay for streams to get into correct mute states
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 3, 0);
-            assertEquals(musicVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
-
             mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 5, 0);
-            assertEquals(alarmVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM));
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, testRingerVol, 0);
 
-            // can set stream volume (this could trigger exiting of dnd mode)
-            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 7, 0);
-            assertEquals(7, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
+            // Turn off zen and make sure stream levels are still the same prior to zen
+            // aside from ringer since ringer could potentially cause exit of dnd
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            assertEquals(musicVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
+            assertEquals(alarmVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM));
+            assertEquals(testRingerVol, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
         } finally {
             setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
-            mNm.setNotificationPolicy(previousPolicy);
         }
     }
 
@@ -1116,36 +1134,198 @@
 
         Utils.toggleNotificationPolicyAccess(
                 mContext.getPackageName(), getInstrumentation(), true);
-        NotificationManager.Policy previousPolicy = mNm.getNotificationPolicy();
         try {
-            // disallow all sounds in priority only, turn on priority only DND
-            mNm.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
+            // turn off zen, set stream volumes to check for later
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
             mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
             mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
-            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
-
-            // save volumes before trying to adjust volume
             int ringVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
             int musicVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
             int alarmVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
 
-            // Ensure - we should not be able to adjust stream volumes (ie from volume buttons)
+            // disallow all sounds in priority only, turn on priority only DND, try to change volume
+            mNm.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+            Thread.sleep(MUTE_DELAY); // delay for streams to be get into correct mute states
             mAudioManager.adjustStreamVolume(
                     AudioManager.STREAM_RING, AudioManager.ADJUST_RAISE, 0);
-            assertEquals(ringVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
-
             mAudioManager.adjustStreamVolume(
                     AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0);
-            assertEquals(musicVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
-
             mAudioManager.adjustStreamVolume(
                     AudioManager.STREAM_ALARM, AudioManager.ADJUST_RAISE, 0);
+
+            // Turn off zen and make sure stream levels are still the same prior to zen
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            assertEquals(musicVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
             assertEquals(alarmVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM));
+            assertEquals(ringVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+        }
+    }
+
+    public void testPriorityOnlyMuteAll() throws Exception {
+        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+            return;
+        }
+
+        Utils.toggleNotificationPolicyAccess(
+                mContext.getPackageName(), getInstrumentation(), true);
+        try {
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            // disallow all sounds in priority only, turn on priority only DND
+            mNm.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+
+            Thread.sleep(MUTE_DELAY); // delay for streams to be get into correct mute states
+            assertTrue("Music (media) stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertTrue("Alarm stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+            assertTrue("Ringer stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+        }
+    }
+
+    public void testPriorityOnlyMediaAllowed() throws Exception {
+        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+            return;
+        }
+        Utils.toggleNotificationPolicyAccess(
+                mContext.getPackageName(), getInstrumentation(), true);
+        try {
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            // allow only media in priority only
+            mNm.setNotificationPolicy(new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+
+            Thread.sleep(MUTE_DELAY); // delay for streams to be get into correct mute states
+            assertFalse("Music (media) stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertTrue("Alarm stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+            assertTrue("Ringer stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+        }
+    }
+
+    public void testPriorityOnlySystemAllowed() throws Exception {
+        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+            return;
+        }
+
+        Utils.toggleNotificationPolicyAccess(
+                mContext.getPackageName(), getInstrumentation(), true);
+        try {
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            // allow only system in priority only
+            mNm.setNotificationPolicy(new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+
+            Thread.sleep(MUTE_DELAY); // delay for streams to be get into correct mute states
+            assertTrue("Music (media) stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertFalse("System stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertTrue("Alarm stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+            assertTrue("Ringer stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+        }
+    }
+
+    public void testPriorityOnlyAlarmsAllowed() throws Exception {
+        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+            return;
+        }
+
+        Utils.toggleNotificationPolicyAccess(
+                mContext.getPackageName(), getInstrumentation(), true);
+        try {
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            // allow only alarms in priority only
+            mNm.setNotificationPolicy(new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+
+            Thread.sleep(MUTE_DELAY); // delay for streams to be get into correct mute states
+            assertTrue("Music (media) stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertFalse("Alarm stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+            assertTrue("Ringer stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+        }
+    }
+
+    public void testPriorityOnlyRingerAllowed() throws Exception {
+        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+            return;
+        }
+
+        Utils.toggleNotificationPolicyAccess(
+                mContext.getPackageName(), getInstrumentation(), true);
+        try {
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            // allow only reminders in priority only
+            mNm.setNotificationPolicy(new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+
+            Thread.sleep(MUTE_DELAY); // delay for streams to be muted
+            assertTrue("Music (media) stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertTrue("Alarm stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+            assertFalse("Ringer stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
 
         } finally {
             setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
-            mNm.setNotificationPolicy(previousPolicy);
         }
     }
 
@@ -1293,4 +1473,14 @@
         return 1;
     }
 
+    private int getTestRingerVol() {
+        final int currentRingVol = mAudioManager.getStreamVolume(STREAM_RING);
+        final int maxRingVol = mAudioManager.getStreamMaxVolume(STREAM_RING);
+        if (currentRingVol != maxRingVol) {
+            return maxRingVol;
+        } else {
+            return maxRingVol - 1;
+        }
+    }
+
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowser2Test.java b/tests/tests/media/src/android/media/cts/MediaBrowser2Test.java
new file mode 100644
index 0000000..9619e94
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowser2Test.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaBrowser2;
+import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaController2;
+import android.media.MediaItem2;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.PlaybackState2;
+import android.media.SessionToken2;
+import android.media.cts.TestServiceRegistry.SessionCallbackProxy;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.os.Process;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link MediaBrowser2}.
+ * <p>
+ * This test inherits {@link MediaController2Test} to ensure that inherited APIs from
+ * {@link MediaController2} works cleanly.
+ */
+// TODO(jaewan): Implement host-side test so browser and service can run in different processes.
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaBrowser2Test extends MediaController2Test {
+    private static final String TAG = "MediaBrowser2Test";
+
+    @Override
+    TestControllerInterface onCreateController(@NonNull SessionToken2 token,
+            @Nullable TestControllerCallbackInterface callback) {
+        if (callback == null) {
+            callback = new TestBrowserCallbackInterface() {};
+        }
+        return new TestMediaBrowser(mContext, token, new TestBrowserCallback(callback));
+    }
+
+    interface TestBrowserCallbackInterface extends TestControllerCallbackInterface {
+        // Browser specific callbacks
+        default void onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+        default void onGetItemDone(String mediaId, MediaItem2 result) {}
+        default void onChildrenChanged(String parentId, int itemCount, Bundle extras) {}
+        default void onGetChildrenDone(String parentId, int page, int pageSize,
+                List<MediaItem2> result, Bundle extras) {}
+        default void onSearchResultChanged(String query, int itemCount, Bundle extras) {}
+        default void onGetSearchResultDone(String query, int page, int pageSize,
+                List<MediaItem2> result, Bundle extras) {}
+    }
+
+    @Test
+    public void testGetLibraryRoot() throws InterruptedException {
+        final Bundle param = new Bundle();
+        param.putString(TAG, TAG);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onGetLibraryRootDone(Bundle rootHints, String rootMediaId,
+                    Bundle rootExtra) {
+                assertTrue(TestUtils.equals(param, rootHints));
+                assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
+                assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRAS, rootExtra));
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser =
+                (MediaBrowser2) createController(token,true, callback);
+        browser.getLibraryRoot(param);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetItem() throws InterruptedException {
+        final String mediaId = MockMediaLibraryService2.MEDIA_ID_GET_ITEM;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onGetItemDone(String mediaIdOut, MediaItem2 result) {
+                assertEquals(mediaId, mediaIdOut);
+                assertNotNull(result);
+                assertEquals(mediaId, result.getMediaId());
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getItem(mediaId);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetItemNullResult() throws InterruptedException {
+        final String mediaId = "random_media_id";
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onGetItemDone(String mediaIdOut, MediaItem2 result) {
+                assertEquals(mediaId, mediaIdOut);
+                assertNull(result);
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getItem(mediaId);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetChildren() throws InterruptedException {
+        final String parentId = MockMediaLibraryService2.PARENT_ID;
+        final int page = 4;
+        final int pageSize = 10;
+        final Bundle extras = new Bundle();
+        extras.putString(TAG, TAG);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onGetChildrenDone(String parentIdOut, int pageOut, int pageSizeOut,
+                    List<MediaItem2> result, Bundle extrasOut) {
+                assertEquals(parentId, parentIdOut);
+                assertEquals(page, pageOut);
+                assertEquals(pageSize, pageSizeOut);
+                assertTrue(TestUtils.equals(extras, extrasOut));
+                assertNotNull(result);
+
+                int fromIndex = (page - 1) * pageSize;
+                int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT);
+
+                // Compare the given results with originals.
+                for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+                    int relativeIndex = originalIndex - fromIndex;
+                    Assert.assertEquals(
+                            MockMediaLibraryService2.GET_CHILDREN_RESULT.get(originalIndex)
+                                    .getMediaId(),
+                            result.get(relativeIndex).getMediaId());
+                }
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getChildren(parentId, page, pageSize, extras);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetChildrenEmptyResult() throws InterruptedException {
+        final String parentId = MockMediaLibraryService2.PARENT_ID_NO_CHILDREN;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onGetChildrenDone(String parentIdOut, int pageOut, int pageSizeOut,
+                    List<MediaItem2> result, Bundle extrasOut) {
+                assertNotNull(result);
+                assertEquals(0, result.size());
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getChildren(parentId, 1, 1, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetChildrenNullResult() throws InterruptedException {
+        final String parentId = MockMediaLibraryService2.PARENT_ID_ERROR;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onGetChildrenDone(String parentIdOut, int pageOut, int pageSizeOut,
+                    List<MediaItem2> result, Bundle extrasOut) {
+                assertNull(result);
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getChildren(parentId, 1, 1, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Ignore
+    @Test
+    public void testSearch() throws InterruptedException {
+        final String query = MockMediaLibraryService2.SEARCH_QUERY;
+        final int page = 4;
+        final int pageSize = 10;
+        final Bundle extras = new Bundle();
+        extras.putString(TAG, TAG);
+
+        final CountDownLatch latchForSearch = new CountDownLatch(1);
+        final CountDownLatch latchForGetSearchResult = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+                assertEquals(query, queryOut);
+                assertTrue(TestUtils.equals(extras, extrasOut));
+                assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+                latchForSearch.countDown();
+            }
+
+            @Override
+            public void onGetSearchResultDone(String queryOut, int pageOut, int pageSizeOut,
+                    List<MediaItem2> result, Bundle extrasOut) {
+                assertEquals(query, queryOut);
+                assertEquals(page, pageOut);
+                assertEquals(pageSize, pageSizeOut);
+                assertTrue(TestUtils.equals(extras, extrasOut));
+                assertNotNull(result);
+
+                int fromIndex = (page - 1) * pageSize;
+                int toIndex = Math.min(
+                        page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT);
+
+                // Compare the given results with originals.
+                for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+                    int relativeIndex = originalIndex - fromIndex;
+                    Assert.assertEquals(
+                            MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(),
+                            result.get(relativeIndex).getMediaId());
+                }
+                latchForGetSearchResult.countDown();
+            }
+        };
+
+        // Request the search.
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.search(query, extras);
+        assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+        // Get the search result.
+        browser.getSearchResult(query, page, pageSize, extras);
+        assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testSearchTakesTime() throws InterruptedException {
+        final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME;
+        final Bundle extras = new Bundle();
+        extras.putString(TAG, TAG);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+                assertEquals(query, queryOut);
+                assertTrue(TestUtils.equals(extras, extrasOut));
+                assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.search(query, extras);
+        assertTrue(latch.await(
+                MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testSearchEmptyResult() throws InterruptedException {
+        final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT;
+        final Bundle extras = new Bundle();
+        extras.putString(TAG, TAG);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+                assertEquals(query, queryOut);
+                assertTrue(TestUtils.equals(extras, extrasOut));
+                assertEquals(0, itemCount);
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.search(query, extras);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testSubscribe() throws InterruptedException {
+        final String testParentId = "testSubscribeId";
+        final Bundle testExtras = new Bundle();
+        testExtras.putString(testParentId, testParentId);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+                if (Process.myUid() == info.getUid()) {
+                    assertEquals(testParentId, parentId);
+                    assertTrue(TestUtils.equals(testExtras, extras));
+                    latch.countDown();
+                }
+            }
+        };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token);
+        browser.subscribe(testParentId, testExtras);
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Ignore
+    @Test
+    public void testUnsubscribe() throws InterruptedException {
+        final String testParentId = "testUnsubscribeId";
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public void onUnsubscribe(ControllerInfo info, String parentId) {
+                if (Process.myUid() == info.getUid()) {
+                    assertEquals(testParentId, parentId);
+                    latch.countDown();
+                }
+            }
+        };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token);
+        browser.unsubscribe(testParentId);
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testBrowserCallback_notifyChildrenChanged() throws InterruptedException {
+        // TODO(jaewan): Add test for the notifyChildrenChanged itself.
+        final String testParentId1 = "testBrowserCallback_notifyChildrenChanged_unexpectedParent";
+        final String testParentId2 = "testBrowserCallback_notifyChildrenChanged";
+        final int testChildrenCount = 101;
+        final Bundle testExtras = new Bundle();
+        testExtras.putString(testParentId1, testParentId1);
+
+        final CountDownLatch latch = new CountDownLatch(3);
+        final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public CommandGroup onConnect(ControllerInfo controller) {
+                final MockMediaLibraryService2 service = (MockMediaLibraryService2)
+                        TestServiceRegistry.getInstance().getServiceInstance();
+                final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
+                // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+                // subscribed.
+                session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
+                session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
+                return super.onConnect(controller);
+            }
+
+            @Override
+            public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+                if (Process.myUid() == info.getUid()) {
+                    final MediaLibrarySession session =  (MediaLibrarySession) mSession;
+                    session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
+                    session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
+                            testExtras);
+                }
+            }
+        };
+        final TestBrowserCallbackInterface controllerCallbackProxy =
+                new TestBrowserCallbackInterface() {
+                    @Override
+                    public void onChildrenChanged(String parentId, int itemCount, Bundle extras) {
+                        switch ((int) latch.getCount()) {
+                            case 3:
+                                assertEquals(testParentId2, parentId);
+                                assertEquals(testChildrenCount, itemCount);
+                                assertNull(extras);
+                                latch.countDown();
+                                break;
+                            case 2:
+                                assertEquals(testParentId2, parentId);
+                                assertEquals(testChildrenCount, itemCount);
+                                assertTrue(TestUtils.equals(testExtras, extras));
+                                latch.countDown();
+                                break;
+                            default:
+                                // Unexpected call.
+                                fail();
+                        }
+                    }
+                };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(sessionCallbackProxy);
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        final MediaBrowser2 browser = (MediaBrowser2) createController(
+                token, true, controllerCallbackProxy);
+        final MockMediaLibraryService2 service =
+                (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
+        if (mSession != null) {
+            mSession.close();
+        }
+        mSession = service.getSession();
+        assertTrue(mSession instanceof MediaLibrarySession);
+        browser.subscribe(testParentId2, null);
+        // This ensures that onChildrenChanged() is only called for the expected reasons.
+        assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    public static class TestBrowserCallback extends BrowserCallback
+            implements WaitForConnectionInterface {
+        private final TestControllerCallbackInterface mCallbackProxy;
+        public final CountDownLatch connectLatch = new CountDownLatch(1);
+        public final CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+        TestBrowserCallback(TestControllerCallbackInterface callbackProxy) {
+            if (callbackProxy == null) {
+                throw new IllegalArgumentException("Callback proxy shouldn't be null. Test bug");
+            }
+            mCallbackProxy = callbackProxy;
+        }
+
+        @CallSuper
+        @Override
+        public void onConnected(MediaController2 controller, CommandGroup commands) {
+            connectLatch.countDown();
+        }
+
+        @CallSuper
+        @Override
+        public void onDisconnected(MediaController2 controller) {
+            disconnectLatch.countDown();
+        }
+
+        @Override
+        public void onPlaybackStateChanged(MediaController2 controller, PlaybackState2 state) {
+            mCallbackProxy.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void onPlaylistParamsChanged(MediaController2 controller, PlaylistParams params) {
+            mCallbackProxy.onPlaylistParamsChanged(params);
+        }
+
+        @Override
+        public void onPlaybackInfoChanged(MediaController2 controller,
+                MediaController2.PlaybackInfo info) {
+            mCallbackProxy.onPlaybackInfoChanged(info);
+        }
+
+        @Override
+        public void onCustomCommand(MediaController2 controller, Command command, Bundle args,
+                ResultReceiver receiver) {
+            mCallbackProxy.onCustomCommand(command, args, receiver);
+        }
+
+
+        @Override
+        public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
+            mCallbackProxy.onCustomLayoutChanged(layout);
+        }
+
+        @Override
+        public void onGetLibraryRootDone(MediaBrowser2 browser, Bundle rootHints,
+                String rootMediaId, Bundle rootExtra) {
+            super.onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onGetLibraryRootDone(rootHints, rootMediaId, rootExtra);
+            }
+        }
+
+        @Override
+        public void onGetItemDone(MediaBrowser2 browser, String mediaId, MediaItem2 result) {
+            super.onGetItemDone(browser, mediaId, result);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy).onGetItemDone(mediaId, result);
+            }
+        }
+
+        @Override
+        public void onGetChildrenDone(MediaBrowser2 browser, String parentId, int page,
+                int pageSize, List<MediaItem2> result, Bundle extras) {
+            super.onGetChildrenDone(browser, parentId, page, pageSize, result, extras);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onGetChildrenDone(parentId, page, pageSize, result, extras);
+            }
+        }
+
+        @Override
+        public void onSearchResultChanged(MediaBrowser2 browser, String query, int itemCount,
+                Bundle extras) {
+            super.onSearchResultChanged(browser, query, itemCount, extras);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onSearchResultChanged(query, itemCount, extras);
+            }
+        }
+
+        @Override
+        public void onGetSearchResultDone(MediaBrowser2 browser, String query, int page,
+                int pageSize, List<MediaItem2> result, Bundle extras) {
+            super.onGetSearchResultDone(browser, query, page, pageSize, result, extras);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onGetSearchResultDone(query, page, pageSize, result, extras);
+            }
+        }
+
+        @Override
+        public void onChildrenChanged(MediaBrowser2 browser, String parentId, int itemCount,
+                Bundle extras) {
+            super.onChildrenChanged(browser, parentId, itemCount, extras);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onChildrenChanged(parentId, itemCount, extras);
+            }
+        }
+
+        @Override
+        public void waitForConnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+
+        @Override
+        public void waitForDisconnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+    }
+
+    public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface {
+        private final BrowserCallback mCallback;
+
+        public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken2 token,
+                @NonNull ControllerCallback callback) {
+            super(context, token, sHandlerExecutor, (BrowserCallback) callback);
+            mCallback = (BrowserCallback) callback;
+        }
+
+        @Override
+        public BrowserCallback getCallback() {
+            return mCallback;
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaController2Test.java b/tests/tests/media/src/android/media/cts/MediaController2Test.java
new file mode 100644
index 0000000..17c69e5
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaController2Test.java
@@ -0,0 +1,863 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaController2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.MediaSession2.SessionCallback;
+import android.media.PlaybackState2;
+import android.media.Rating2;
+import android.media.SessionToken2;
+import android.media.VolumeProvider2;
+import android.media.cts.TestServiceRegistry.SessionCallbackProxy;
+import android.media.cts.TestUtils.SyncHandler;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.media.cts.TestUtils.ensurePlaylistParamsModeEquals;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link MediaController2}.
+ */
+// TODO(jaewan): Implement host-side test so controller and session can run in different processes.
+// TODO(jaewan): Fix flaky failure -- see MediaController2Impl.getController()
+// TODO(jaeawn): Revisit create/close session in the sHandler. It's no longer necessary.
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest
+public class MediaController2Test extends MediaSession2TestBase {
+    private static final String TAG = "MediaController2Test";
+
+    PendingIntent mIntent;
+    MediaSession2 mSession;
+    MediaController2 mController;
+    MockPlayer mPlayer;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        final Intent sessionActivity = new Intent(mContext, MockActivity.class);
+        // Create this test specific MediaSession2 to use our own Handler.
+        mIntent = PendingIntent.getActivity(mContext, 0, sessionActivity, 0);
+
+        mPlayer = new MockPlayer(1);
+        mSession = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {})
+                .setSessionActivity(mIntent)
+                .setId(TAG).build();
+        mController = createController(mSession.getToken());
+        TestServiceRegistry.getInstance().setHandler(sHandler);
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        if (mSession != null) {
+            mSession.close();
+        }
+        TestServiceRegistry.getInstance().cleanUp();
+    }
+
+    @Test
+    public void testPlay() throws InterruptedException {
+        mController.play();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mPlayCalled);
+    }
+
+    @Test
+    public void testPause() throws InterruptedException {
+        mController.pause();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mPauseCalled);
+    }
+
+    @Ignore
+    @Test
+    public void testSkipToPreviousItem() throws InterruptedException {
+        mController.skipToPreviousItem();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSkipToPreviousCalled);
+    }
+
+    @Ignore
+    @Test
+    public void testSkipToNextItem() throws InterruptedException {
+        mController.skipToNextItem();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSkipToNextCalled);
+    }
+
+    @Ignore
+    @Test
+    public void testStop() throws InterruptedException {
+        mController.stop();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mStopCalled);
+    }
+
+    @Test
+    public void testPrepare() throws InterruptedException {
+        mController.prepare();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mPrepareCalled);
+    }
+
+    @Ignore
+    @Test
+    public void testFastForward() throws InterruptedException {
+        mController.fastForward();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mFastForwardCalled);
+    }
+
+    @Ignore
+    @Test
+    public void testRewind() throws InterruptedException {
+        mController.rewind();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mRewindCalled);
+    }
+
+    @Test
+    public void testSeekTo() throws InterruptedException {
+        final long seekPosition = 12125L;
+        mController.seekTo(seekPosition);
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSeekToCalled);
+        assertEquals(seekPosition, mPlayer.mSeekPosition);
+    }
+
+    // TODO(jaewan): Re-enable this test
+    /*
+    @Test
+    public void testSetCurrentPlaylistItem() throws InterruptedException {
+        final
+        final int itemIndex = 9;
+        mController.skipToPlaylistItem(itemIndex);
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSetCurrentPlaylistCalled);
+        assertEquals(itemIndex, mPlayer.mCurrentItem);
+    }
+    */
+
+    @Test
+    public void testGetSessionActivity() throws InterruptedException {
+        PendingIntent sessionActivity = mController.getSessionActivity();
+        assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
+        assertEquals(Process.myUid(), sessionActivity.getCreatorUid());
+    }
+
+    @Ignore
+    @Test
+    public void testGetSetPlaylistParams() throws Exception {
+        final PlaylistParams params = new PlaylistParams(mContext,
+                PlaylistParams.REPEAT_MODE_ALL,
+                PlaylistParams.SHUFFLE_MODE_ALL,
+                null /* PlaylistMetadata */);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistParamsChanged(PlaylistParams givenParams) {
+                ensurePlaylistParamsModeEquals(params, givenParams);
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        controller.setPlaylistParams(params);
+
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        ensurePlaylistParamsModeEquals(params, mSession.getPlaylistParams());
+        ensurePlaylistParamsModeEquals(params, controller.getPlaylistParams());
+    }
+
+    @Test
+    public void testSetVolumeTo() throws Exception {
+        final int maxVolume = 100;
+        final int currentVolume = 23;
+        final int volumeControlType = VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+        TestVolumeProvider volumeProvider =
+                new TestVolumeProvider(mContext, volumeControlType, maxVolume, currentVolume);
+
+        mSession.updatePlayer(new MockPlayer(0), null, volumeProvider);
+        final MediaController2 controller = createController(mSession.getToken(), true, null);
+
+        final int targetVolume = 50;
+        controller.setVolumeTo(targetVolume, 0 /* flags */);
+        assertTrue(volumeProvider.mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertTrue(volumeProvider.mSetVolumeToCalled);
+        assertEquals(targetVolume, volumeProvider.mVolume);
+    }
+
+    @Test
+    public void testAdjustVolume() throws Exception {
+        final int maxVolume = 100;
+        final int currentVolume = 23;
+        final int volumeControlType = VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+        TestVolumeProvider volumeProvider =
+                new TestVolumeProvider(mContext, volumeControlType, maxVolume, currentVolume);
+
+        mSession.updatePlayer(new MockPlayer(0), null, volumeProvider);
+        final MediaController2 controller = createController(mSession.getToken(), true, null);
+
+        final int direction = AudioManager.ADJUST_RAISE;
+        controller.adjustVolume(direction, 0 /* flags */);
+        assertTrue(volumeProvider.mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertTrue(volumeProvider.mAdjustVolumeCalled);
+        assertEquals(direction, volumeProvider.mDirection);
+    }
+
+    @Test
+    public void testGetPackageName() {
+        assertEquals(mContext.getPackageName(), mController.getSessionToken().getPackageName());
+    }
+
+    // This also tests getPlaybackState().
+    @Ignore
+    @Test
+    public void testControllerCallback_onPlaybackStateChanged() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaybackStateChanged(PlaybackState2 state) {
+                // Called only once when the player's playback state is changed after this.
+                assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
+                latch.countDown();
+            }
+        };
+        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
+        mController = createController(mSession.getToken(), true, callback);
+        assertEquals(PlaybackState2.STATE_PLAYING, mController.getPlaybackState().getState());
+        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(PlaybackState2.STATE_PAUSED, mController.getPlaybackState().getState());
+    }
+
+    @Test
+    public void testSendCustomCommand() throws InterruptedException {
+        // TODO(jaewan): Need to revisit with the permission.
+        final Command testCommand =
+                new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
+        final Bundle testArgs = new Bundle();
+        testArgs.putString("args", "testSendCustomAction");
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onCustomCommand(MediaSession2 session, ControllerInfo controller,
+                    Command customCommand, Bundle args, ResultReceiver cb) {
+                super.onCustomCommand(session, controller, customCommand, args, cb);
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(testCommand, customCommand);
+                assertTrue(TestUtils.equals(testArgs, args));
+                assertNull(cb);
+                latch.countDown();
+            }
+        };
+        mSession.close();
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
+        final MediaController2 controller = createController(mSession.getToken());
+        controller.sendCustomCommand(testCommand, testArgs, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testControllerCallback_onConnected() throws InterruptedException {
+        // createController() uses controller callback to wait until the controller becomes
+        // available.
+        MediaController2 controller = createController(mSession.getToken());
+        assertNotNull(controller);
+    }
+
+    @Test
+    public void testControllerCallback_sessionRejects() throws InterruptedException {
+        final MediaSession2.SessionCallback sessionCallback = new SessionCallback(mContext) {
+            @Override
+            public MediaSession2.CommandGroup onConnect(MediaSession2 session,
+                    ControllerInfo controller) {
+                return null;
+            }
+        };
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                    .setSessionCallback(sHandlerExecutor, sessionCallback).build();
+        });
+        MediaController2 controller =
+                createController(mSession.getToken(), false, null);
+        assertNotNull(controller);
+        waitForConnect(controller, false);
+        waitForDisconnect(controller, true);
+    }
+
+    @Test
+    public void testControllerCallback_releaseSession() throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            mSession.close();
+        });
+        waitForDisconnect(mController, true);
+    }
+
+    @Test
+    public void testControllerCallback_release() throws InterruptedException {
+        mController.close();
+        waitForDisconnect(mController, true);
+    }
+
+    @Test
+    public void testPlayFromSearch() throws InterruptedException {
+        final String request = "random query";
+        final Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onPlayFromSearch(MediaSession2 session, ControllerInfo controller,
+                    String query, Bundle extras) {
+                super.onPlayFromSearch(session, controller, query, extras);
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(request, query);
+                assertTrue(TestUtils.equals(bundle, extras));
+                latch.countDown();
+            }
+        };
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testPlayFromSearch").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.playFromSearch(request, bundle);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testPlayFromUri() throws InterruptedException {
+        final Uri request = Uri.parse("foo://boo");
+        final Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onPlayFromUri(MediaSession2 session, ControllerInfo controller, Uri uri,
+                    Bundle extras) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(request, uri);
+                assertTrue(TestUtils.equals(bundle, extras));
+                latch.countDown();
+            }
+        };
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testPlayFromUri").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.playFromUri(request, bundle);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testPlayFromMediaId() throws InterruptedException {
+        final String request = "media_id";
+        final Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onPlayFromMediaId(MediaSession2 session, ControllerInfo controller,
+                    String mediaId, Bundle extras) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(request, mediaId);
+                assertTrue(TestUtils.equals(bundle, extras));
+                latch.countDown();
+            }
+        };
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testPlayFromMediaId").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.playFromMediaId(request, bundle);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+
+    @Test
+    public void testPrepareFromSearch() throws InterruptedException {
+        final String request = "random query";
+        final Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onPrepareFromSearch(MediaSession2 session, ControllerInfo controller,
+                    String query, Bundle extras) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(request, query);
+                assertTrue(TestUtils.equals(bundle, extras));
+                latch.countDown();
+            }
+        };
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testPrepareFromSearch").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.prepareFromSearch(request, bundle);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testPrepareFromUri() throws InterruptedException {
+        final Uri request = Uri.parse("foo://boo");
+        final Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onPrepareFromUri(MediaSession2 session, ControllerInfo controller, Uri uri,
+                    Bundle extras) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(request, uri);
+                assertTrue(TestUtils.equals(bundle, extras));
+                latch.countDown();
+            }
+        };
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testPrepareFromUri").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.prepareFromUri(request, bundle);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testPrepareFromMediaId() throws InterruptedException {
+        final String request = "media_id";
+        final Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onPrepareFromMediaId(MediaSession2 session, ControllerInfo controller,
+                    String mediaId, Bundle extras) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(request, mediaId);
+                assertTrue(TestUtils.equals(bundle, extras));
+                latch.countDown();
+            }
+        };
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testPrepareFromMediaId").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.prepareFromMediaId(request, bundle);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testSetRating() throws InterruptedException {
+        final int ratingType = Rating2.RATING_5_STARS;
+        final float ratingValue = 3.5f;
+        final Rating2 rating = Rating2.newStarRating(mContext, ratingType, ratingValue);
+        final String mediaId = "media_id";
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onSetRating(MediaSession2 session, ControllerInfo controller,
+                    String mediaIdOut, Rating2 ratingOut) {
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(mediaId, mediaIdOut);
+                assertEquals(rating, ratingOut);
+                latch.countDown();
+            }
+        };
+
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback)
+                .setId("testSetRating").build()) {
+            MediaController2 controller = createController(session.getToken());
+            controller.setRating(mediaId, rating);
+            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testIsConnected() throws InterruptedException {
+        assertTrue(mController.isConnected());
+        sHandler.postAndSync(()->{
+            mSession.close();
+        });
+        // postAndSync() to wait until the disconnection is propagated.
+        sHandler.postAndSync(()->{
+            assertFalse(mController.isConnected());
+        });
+    }
+
+    /**
+     * Test potential deadlock for calls between controller and session.
+     */
+    @Test
+    public void testDeadlock() throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = null;
+        });
+
+        // Two more threads are needed not to block test thread nor test wide thread (sHandler).
+        final HandlerThread sessionThread = new HandlerThread("testDeadlock_session");
+        final HandlerThread testThread = new HandlerThread("testDeadlock_test");
+        sessionThread.start();
+        testThread.start();
+        final SyncHandler sessionHandler = new SyncHandler(sessionThread.getLooper());
+        final Handler testHandler = new Handler(testThread.getLooper());
+        final CountDownLatch latch = new CountDownLatch(1);
+        try {
+            final MockPlayer player = new MockPlayer(0);
+            sessionHandler.postAndSync(() -> {
+                mSession = new MediaSession2.Builder(mContext)
+                        .setPlayer(mPlayer)
+                        .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {})
+                        .setId("testDeadlock").build();
+            });
+            final MediaController2 controller = createController(mSession.getToken());
+            testHandler.post(() -> {
+                final PlaybackState2 state = createPlaybackState(PlaybackState2.STATE_ERROR);
+                for (int i = 0; i < 100; i++) {
+                    // triggers call from session to controller.
+                    player.notifyPlaybackState(state);
+                    // triggers call from controller to session.
+                    controller.play();
+
+                    // Repeat above
+                    player.notifyPlaybackState(state);
+                    controller.pause();
+                    player.notifyPlaybackState(state);
+                    controller.stop();
+                    player.notifyPlaybackState(state);
+                    controller.skipToNextItem();
+                    player.notifyPlaybackState(state);
+                    controller.skipToPreviousItem();
+                }
+                // This may hang if deadlock happens.
+                latch.countDown();
+            });
+            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } finally {
+            if (mSession != null) {
+                sessionHandler.postAndSync(() -> {
+                    // Clean up here because sessionHandler will be removed afterwards.
+                    mSession.close();
+                    mSession = null;
+                });
+            }
+            if (sessionThread != null) {
+                sessionThread.quitSafely();
+            }
+            if (testThread != null) {
+                testThread.quitSafely();
+            }
+        }
+    }
+
+    @Test
+    public void testGetServiceToken() {
+        SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
+        assertNotNull(token);
+        assertEquals(mContext.getPackageName(), token.getPackageName());
+        assertEquals(MockMediaSessionService2.ID, token.getId());
+        assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+    }
+
+    private void connectToService(SessionToken2 token) throws InterruptedException {
+        if (mSession != null) {
+            mSession.close();
+        }
+        mController = createController(token);
+        mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
+        mPlayer = (MockPlayer) mSession.getPlayer();
+    }
+
+    @Test
+    public void testConnectToService_sessionService() throws InterruptedException {
+        testConnectToService(MockMediaSessionService2.ID);
+    }
+
+    @Ignore
+    @Test
+    public void testConnectToService_libraryService() throws InterruptedException {
+        testConnectToService(MockMediaLibraryService2.ID);
+    }
+
+    public void testConnectToService(String id) throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public CommandGroup onConnect(ControllerInfo controller) {
+                if (Process.myUid() == controller.getUid()) {
+                    assertEquals(mContext.getPackageName(), controller.getPackageName());
+                    assertFalse(controller.isTrusted());
+                    latch.countDown();
+                }
+                return super.onConnect(controller);
+            }
+        };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
+        connectToService(TestUtils.getServiceToken(mContext, id));
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        // Test command from controller to session service
+        mController.play();
+        assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertTrue(mPlayer.mPlayCalled);
+
+        // Test command from session service to controller
+        // TODO(jaewan): Add equivalent tests again
+        /*
+        final CountDownLatch latch = new CountDownLatch(1);
+        mController.registerPlayerEventCallback((state) -> {
+            assertNotNull(state);
+            assertEquals(PlaybackState.STATE_REWINDING, state.getState());
+            latch.countDown();
+        }, sHandler);
+        mPlayer.notifyPlaybackState(
+                TestUtils.createPlaybackState(PlaybackState.STATE_REWINDING));
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        */
+    }
+
+    @Test
+    public void testControllerAfterSessionIsGone_session() throws InterruptedException {
+        testControllerAfterSessionIsGone(mSession.getToken().getId());
+    }
+
+    @Ignore
+    @Test
+    public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
+        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
+        testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
+    }
+
+    @Test
+    public void testClose_beforeConnected() throws InterruptedException {
+        MediaController2 controller =
+                createController(mSession.getToken(), false, null);
+        controller.close();
+    }
+
+    @Test
+    public void testClose_twice() throws InterruptedException {
+        mController.close();
+        mController.close();
+    }
+
+    @Test
+    public void testClose_session() throws InterruptedException {
+        final String id = mSession.getToken().getId();
+        mController.close();
+        // close is done immediately for session.
+        testNoInteraction();
+
+        // Test whether the controller is notified about later close of the session or
+        // re-creation.
+        testControllerAfterSessionIsGone(id);
+    }
+
+    @Test
+    public void testClose_sessionService() throws InterruptedException {
+        testCloseFromService(MockMediaSessionService2.ID);
+    }
+
+    @Test
+    public void testClose_libraryService() throws InterruptedException {
+        testCloseFromService(MockMediaLibraryService2.ID);
+    }
+
+    private void testCloseFromService(String id) throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public void onServiceDestroyed() {
+                latch.countDown();
+            }
+        };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
+        mController = createController(TestUtils.getServiceToken(mContext, id));
+        mController.close();
+        // Wait until close triggers onDestroy() of the session service.
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertNull(TestServiceRegistry.getInstance().getServiceInstance());
+        testNoInteraction();
+
+        // Test whether the controller is notified about later close of the session or
+        // re-creation.
+        testControllerAfterSessionIsGone(id);
+    }
+
+    private void testControllerAfterSessionIsGone(final String id) throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            // TODO(jaewan): Use Session.close later when we add the API.
+            mSession.close();
+        });
+        waitForDisconnect(mController, true);
+        testNoInteraction();
+
+        // Ensure that the controller cannot use newly create session with the same ID.
+        sHandler.postAndSync(() -> {
+            // Recreated session has different session stub, so previously created controller
+            // shouldn't be available.
+            mSession = new MediaSession2.Builder(mContext)
+                    .setPlayer(mPlayer)
+                    .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {})
+                    .setId(id).build();
+        });
+        testNoInteraction();
+    }
+
+    private void testNoInteraction() throws InterruptedException {
+        // TODO: Uncomment
+        /*
+        final CountDownLatch latch = new CountDownLatch(1);
+        final PlayerEventCallback callback = new PlayerEventCallback() {
+            @Override
+            public void onPlaybackStateChanged(PlaybackState2 state) {
+                fail("Controller shouldn't be notified about change in session after the close.");
+                latch.countDown();
+            }
+        };
+        */
+
+        // TODO(jaewan): Add equivalent tests again
+        /*
+        mController.registerPlayerEventCallback(playbackListener, sHandler);
+        mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
+        assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        mController.unregisterPlayerEventCallback(playbackListener);
+        */
+    }
+
+    // TODO(jaewan): Add  test for service connect rejection, when we differentiate session
+    //               active/inactive and connection accept/refuse
+
+    class TestVolumeProvider extends VolumeProvider2 {
+        final CountDownLatch mLatch = new CountDownLatch(1);
+        boolean mSetVolumeToCalled;
+        boolean mAdjustVolumeCalled;
+        int mVolume;
+        int mDirection;
+
+        public TestVolumeProvider(Context context, int controlType, int maxVolume,
+                int currentVolume) {
+            super(context, controlType, maxVolume, currentVolume);
+        }
+
+        @Override
+        public void onSetVolumeTo(int volume) {
+            mSetVolumeToCalled = true;
+            mVolume = volume;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onAdjustVolume(int direction) {
+            mAdjustVolumeCalled = true;
+            mDirection = direction;
+            mLatch.countDown();
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadata2Test.java b/tests/tests/media/src/android/media/cts/MediaMetadata2Test.java
new file mode 100644
index 0000000..6317eda
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaMetadata2Test.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaMetadata2;
+import android.media.MediaMetadata2.Builder;
+import android.media.Rating2;
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaMetadata2Test {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testBuilder() {
+        final Bundle extras = new Bundle();
+        extras.putString("MediaMetadata2Test", "testBuilder");
+        final String title = "title";
+        final long discNumber = 10;
+        final Rating2 rating = Rating2.newThumbRating(mContext, true);
+
+        Builder builder = new Builder(mContext);
+        builder.setExtras(extras);
+        builder.putString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE, title);
+        builder.putLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER, discNumber);
+        builder.putRating(MediaMetadata2.METADATA_KEY_USER_RATING, rating);
+
+        MediaMetadata2 metadata = builder.build();
+        assertTrue(TestUtils.equals(extras, metadata.getExtras()));
+        assertEquals(title, metadata.getString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE));
+        assertEquals(discNumber, metadata.getLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER));
+        assertEquals(rating, metadata.getRating(MediaMetadata2.METADATA_KEY_USER_RATING));
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2Test.java b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
new file mode 100644
index 0000000..c993a78
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
+import static android.media.cts.TestUtils.ensurePlaylistParamsModeEquals;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaController2;
+import android.media.MediaController2.PlaybackInfo;
+import android.media.MediaItem2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Builder;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.MediaSession2.SessionCallback;
+import android.media.PlaybackState2;
+import android.media.VolumeProvider2;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link MediaSession2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaSession2Test extends MediaSession2TestBase {
+    private static final String TAG = "MediaSession2Test";
+
+    private MediaSession2 mSession;
+    private MockPlayer mPlayer;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mPlayer = new MockPlayer(0);
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {}).build();
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        mSession.close();
+    }
+
+    @Ignore
+    @Test
+    public void testBuilder() throws Exception {
+        try {
+            MediaSession2.Builder builder = new Builder(mContext);
+            fail("null player shouldn't be allowed");
+        } catch (IllegalArgumentException e) {
+            // expected. pass-through
+        }
+        MediaSession2.Builder builder = new Builder(mContext).setPlayer(mPlayer);
+        try {
+            builder.setId(null);
+            fail("null id shouldn't be allowed");
+        } catch (IllegalArgumentException e) {
+            // expected. pass-through
+        }
+    }
+
+    @Test
+    public void testUpdatePlayer() throws Exception {
+        MockPlayer player = new MockPlayer(0);
+        // Test if setPlayer doesn't crash with various situations.
+        mSession.updatePlayer(mPlayer, null, null);
+        mSession.updatePlayer(player, null, null);
+        mSession.close();
+    }
+
+    @Test
+    public void testSetPlayer_playbackInfo() throws Exception {
+        MockPlayer player = new MockPlayer(0);
+        AudioAttributes attrs = new AudioAttributes.Builder()
+                .setContentType(CONTENT_TYPE_MUSIC)
+                .build();
+        player.setAudioAttributes(attrs);
+
+        final int maxVolume = 100;
+        final int currentVolume = 23;
+        final int volumeControlType = VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+        VolumeProvider2 volumeProvider =
+                new VolumeProvider2(mContext, volumeControlType, maxVolume, currentVolume) { };
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaybackInfoChanged(PlaybackInfo info) {
+                Assert.assertEquals(MediaController2.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
+                        info.getPlaybackType());
+                assertEquals(attrs, info.getAudioAttributes());
+                assertEquals(volumeControlType, info.getPlaybackType());
+                assertEquals(maxVolume, info.getMaxVolume());
+                assertEquals(currentVolume, info.getCurrentVolume());
+                latch.countDown();
+            }
+        };
+
+        mSession.updatePlayer(player, null, null);
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        PlaybackInfo info = controller.getPlaybackInfo();
+        assertNotNull(info);
+        assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
+        assertEquals(attrs, info.getAudioAttributes());
+        AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        int localVolumeControlType = manager.isVolumeFixed()
+                ? VolumeProvider2.VOLUME_CONTROL_FIXED : VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+        assertEquals(localVolumeControlType, info.getControlType());
+        assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume());
+        assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume());
+
+        mSession.updatePlayer(player, null, volumeProvider);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+        info = controller.getPlaybackInfo();
+        assertNotNull(info);
+        assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+        assertEquals(attrs, info.getAudioAttributes());
+        assertEquals(volumeControlType, info.getControlType());
+        assertEquals(maxVolume, info.getMaxVolume());
+        assertEquals(currentVolume, info.getCurrentVolume());
+    }
+
+    @Test
+    public void testPlay() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.play();
+            assertTrue(mPlayer.mPlayCalled);
+        });
+    }
+
+    @Test
+    public void testPause() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.pause();
+            assertTrue(mPlayer.mPauseCalled);
+        });
+    }
+
+    @Ignore
+    @Test
+    public void testStop() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.stop();
+            assertTrue(mPlayer.mStopCalled);
+        });
+    }
+
+    @Ignore
+    @Test
+    public void testSkipToNextItem() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.skipToNextItem();
+            assertTrue(mPlayer.mSkipToNextCalled);
+        });
+    }
+
+    @Ignore
+    @Test
+    public void testSkipToPreviousItem() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.skipToPreviousItem();
+            assertTrue(mPlayer.mSkipToPreviousCalled);
+        });
+    }
+
+    @Ignore
+    @Test
+    public void testSetPlaylist() throws Exception {
+        final List<MediaItem2> playlist = new ArrayList<>();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistChanged(List<MediaItem2> givenList) {
+                assertMediaItemListEquals(playlist, givenList);
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        mSession.setPlaylist(playlist);
+
+        assertTrue(mPlayer.mSetPlaylistCalled);
+        assertMediaItemListEquals(playlist, mPlayer.mPlaylist);
+        assertMediaItemListEquals(playlist, mSession.getPlaylist());
+
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertMediaItemListEquals(playlist, controller.getPlaylist());
+    }
+
+    @Ignore
+    @Test
+    public void testSetPlaylistParams() throws Exception {
+        final PlaylistParams params = new PlaylistParams(mContext,
+                PlaylistParams.REPEAT_MODE_ALL,
+                PlaylistParams.SHUFFLE_MODE_ALL,
+                null /* PlaylistMetadata */);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistParamsChanged(PlaylistParams givenParams) {
+                ensurePlaylistParamsModeEquals(params, givenParams);
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        mSession.setPlaylistParams(params);
+        assertTrue(mPlayer.mSetPlaylistParamsCalled);
+        ensurePlaylistParamsModeEquals(params, mPlayer.mPlaylistParams);
+        ensurePlaylistParamsModeEquals(params, mSession.getPlaylistParams());
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Ignore
+    @Test
+    public void testRegisterEventCallback() throws InterruptedException {
+        final int testWhat = 1001;
+        final MockPlayer player = new MockPlayer(0);
+        final CountDownLatch playbackLatch = new CountDownLatch(3);
+        final CountDownLatch errorLatch = new CountDownLatch(1);
+        // TODO: Uncomment or remove
+        /*
+        final PlayerEventCallback callback = new PlayerEventCallback() {
+            @Override
+            public void onPlaybackStateChanged(PlaybackState2 state) {
+                assertEquals(sHandler.getLooper(), Looper.myLooper());
+                switch ((int) playbackLatch.getCount()) {
+                    case 3:
+                        assertNull(state);
+                        break;
+                    case 2:
+                        assertNotNull(state);
+                        assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
+                        break;
+                    case 1:
+                        assertNotNull(state);
+                        assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
+                        break;
+                    case 0:
+                        fail();
+                }
+                playbackLatch.countDown();
+            }
+
+            @Override
+            public void onError(String mediaId, int what, int extra) {
+                assertEquals(testWhat, what);
+                errorLatch.countDown();
+            }
+        };
+        */
+        player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
+        // EventCallback will be notified with the mPlayer's playback state (null)
+        // TODO: Uncomment or remove
+        //mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
+        // When the player is set, EventCallback will be notified about the new player's state.
+        mSession.updatePlayer(player, null, null);
+        // When the player is set, EventCallback will be notified about the new player's state.
+        player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
+        assertTrue(playbackLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        player.notifyError(testWhat);
+        assertTrue(errorLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testBadPlayer() throws InterruptedException {
+        // TODO(jaewan): Add equivalent tests again
+        final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
+        final BadPlayer player = new BadPlayer(0);
+        // TODO: Uncomment or remove
+        /*
+        mSession.registerPlayerEventCallback(sHandlerExecutor, new PlayerEventCallback() {
+            @Override
+            public void onPlaybackStateChanged(PlaybackState2 state) {
+                // This will be called for every setPlayer() calls, but no more.
+                assertNull(state);
+                latch.countDown();
+            }
+        });
+        */
+        mSession.updatePlayer(player, null, null);
+        mSession.updatePlayer(mPlayer, null, null);
+        player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
+        assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    private static class BadPlayer extends MockPlayer {
+        public BadPlayer(int count) {
+            super(count);
+        }
+
+        @Override
+        public void unregisterPlayerEventCallback(@NonNull MediaPlayerBase.PlayerEventCallback listener) {
+            // No-op. This bad player will keep push notification to the listener that is previously
+            // registered by session.setPlayer().
+        }
+    }
+
+    @Test
+    public void testOnCommandCallback() throws InterruptedException {
+        final MockOnCommandCallback callback = new MockOnCommandCallback();
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mPlayer = new MockPlayer(1);
+            mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                    .setSessionCallback(sHandlerExecutor, callback).build();
+        });
+        MediaController2 controller = createController(mSession.getToken());
+        controller.pause();
+        assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertFalse(mPlayer.mPauseCalled);
+        assertEquals(1, callback.commands.size());
+        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE,
+                (long) callback.commands.get(0).getCommandCode());
+        // TODO(jaewan): uncomment followings once skipToNextItem is implemented (b/74090741)
+//        controller.skipToNextItem();
+//        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+//        assertTrue(mPlayer.mSkipToNextCalled);
+//        assertFalse(mPlayer.mPauseCalled);
+//        assertEquals(2, callback.commands.size());
+//        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
+//                (long) callback.commands.get(1).getCommandCode());
+    }
+
+    @Test
+    public void testOnConnectCallback() throws InterruptedException {
+        final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+                    .setSessionCallback(sHandlerExecutor, sessionCallback).build();
+        });
+        MediaController2 controller =
+                createController(mSession.getToken(), false, null);
+        assertNotNull(controller);
+        waitForConnect(controller, false);
+        waitForDisconnect(controller, true);
+    }
+
+    @Test
+    public void testSetCustomLayout() throws InterruptedException {
+        final List<CommandButton> buttons = new ArrayList<>();
+        buttons.add(new CommandButton.Builder(mContext)
+                .setCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PLAY))
+                .setDisplayName("button").build());
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback sessionCallback = new SessionCallback(mContext) {
+            @Override
+            public CommandGroup onConnect(MediaSession2 session,
+                    ControllerInfo controller) {
+                if (mContext.getPackageName().equals(controller.getPackageName())) {
+                    mSession.setCustomLayout(controller, buttons);
+                }
+                return super.onConnect(session, controller);
+            }
+        };
+
+        try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
+                .setId("testSetCustomLayout")
+                .setSessionCallback(sHandlerExecutor, sessionCallback)
+                .build()) {
+            if (mSession != null) {
+                mSession.close();
+                mSession = session;
+            }
+            final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+                @Override
+                public void onCustomLayoutChanged(List<CommandButton> layout) {
+                    assertEquals(layout.size(), buttons.size());
+                    for (int i = 0; i < layout.size(); i++) {
+                        assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand());
+                        assertEquals(layout.get(i).getDisplayName(),
+                                buttons.get(i).getDisplayName());
+                    }
+                    latch.countDown();
+                }
+            };
+            final MediaController2 controller =
+                    createController(session.getToken(), true, callback);
+            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
+    public void testSendCustomAction() throws InterruptedException {
+        final Command testCommand =
+                new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
+        final Bundle testArgs = new Bundle();
+        testArgs.putString("args", "testSendCustomAction");
+
+        final CountDownLatch latch = new CountDownLatch(2);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
+                assertEquals(testCommand, command);
+                assertTrue(TestUtils.equals(testArgs, args));
+                assertNull(receiver);
+                latch.countDown();
+            }
+        };
+        final MediaController2 controller =
+                createController(mSession.getToken(), true, callback);
+        // TODO(jaewan): Test with multiple controllers
+        mSession.sendCustomCommand(testCommand, testArgs);
+
+        ControllerInfo controllerInfo = getTestControllerInfo();
+        assertNotNull(controllerInfo);
+        // TODO(jaewan): Test receivers as well.
+        mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    private ControllerInfo getTestControllerInfo() {
+        List<ControllerInfo> controllers = mSession.getConnectedControllers();
+        assertNotNull(controllers);
+        final String packageName = mContext.getPackageName();
+        for (int i = 0; i < controllers.size(); i++) {
+            if (TextUtils.equals(packageName, controllers.get(i).getPackageName())) {
+                return controllers.get(i);
+            }
+        }
+        fail("Fails to get custom command");
+        return null;
+    }
+
+    public class MockOnConnectCallback extends SessionCallback {
+        public MockOnConnectCallback() {
+            super(mContext);
+        }
+
+        @Override
+        public MediaSession2.CommandGroup onConnect(MediaSession2 session,
+                ControllerInfo controllerInfo) {
+            if (Process.myUid() != controllerInfo.getUid()) {
+                return null;
+            }
+            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
+            assertEquals(Process.myUid(), controllerInfo.getUid());
+            assertFalse(controllerInfo.isTrusted());
+            // Reject all
+            return null;
+        }
+    }
+
+    public class MockOnCommandCallback extends SessionCallback {
+        public final ArrayList<MediaSession2.Command> commands = new ArrayList<>();
+
+        public MockOnCommandCallback() {
+            super(mContext);
+        }
+
+        @Override
+        public boolean onCommandRequest(MediaSession2 session, ControllerInfo controllerInfo,
+                MediaSession2.Command command) {
+            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
+            assertEquals(Process.myUid(), controllerInfo.getUid());
+            assertFalse(controllerInfo.isTrusted());
+            commands.add(command);
+            if (command.getCommandCode() == MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) {
+        if (a == null || b == null) {
+            assertEquals(a, b);
+        }
+        assertEquals(a.size(), b.size());
+
+        for (int i = 0; i < a.size(); i++) {
+            MediaItem2 aItem = a.get(i);
+            MediaItem2 bItem = b.get(i);
+
+            if (aItem == null || bItem == null) {
+                assertEquals(aItem, bItem);
+                continue;
+            }
+
+            assertEquals(aItem.getMediaId(), bItem.getMediaId());
+            assertEquals(aItem.getFlags(), bItem.getFlags());
+            TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());
+
+            // Note: Here it does not check whether DataSourceDesc are equal,
+            // since there DataSourceDec is not comparable.
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2TestBase.java b/tests/tests/media/src/android/media/cts/MediaSession2TestBase.java
new file mode 100644
index 0000000..01f5a93
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSession2TestBase.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaItem2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.PlaybackState2;
+import android.media.SessionToken2;
+import android.os.Bundle;
+import android.os.HandlerThread;
+import android.os.ResultReceiver;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * Base class for session test.
+ */
+abstract class MediaSession2TestBase {
+    // Expected success
+    static final int WAIT_TIME_MS = 1000;
+
+    // Expected timeout
+    static final int TIMEOUT_MS = 500;
+
+    static TestUtils.SyncHandler sHandler;
+    static Executor sHandlerExecutor;
+
+    Context mContext;
+    private List<MediaController2> mControllers = new ArrayList<>();
+
+    interface TestControllerInterface {
+        ControllerCallback getCallback();
+    }
+
+    // Any change here should be also reflected to the TestControllerCallback and
+    // TestBrowserCallback
+    interface TestControllerCallbackInterface {
+        // Add methods in ControllerCallback that you want to test.
+        default void onPlaylistChanged(List<MediaItem2> playlist) {}
+        default void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {}
+        default void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {}
+        default void onPlaybackStateChanged(PlaybackState2 state) {}
+        default void onCustomLayoutChanged(List<CommandButton> layout) {}
+        default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
+    }
+
+    interface WaitForConnectionInterface {
+        void waitForConnect(boolean expect) throws InterruptedException;
+        void waitForDisconnect(boolean expect) throws InterruptedException;
+    }
+
+    @BeforeClass
+    public static void setUpThread() {
+        if (sHandler == null) {
+            HandlerThread handlerThread = new HandlerThread("MediaSession2TestBase");
+            handlerThread.start();
+            sHandler = new TestUtils.SyncHandler(handlerThread.getLooper());
+            sHandlerExecutor = (runnable) -> {
+                sHandler.post(runnable);
+            };
+        }
+    }
+
+    @AfterClass
+    public static void cleanUpThread() {
+        if (sHandler != null) {
+            sHandler.getLooper().quitSafely();
+            sHandler = null;
+            sHandlerExecutor = null;
+        }
+    }
+
+    @CallSuper
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @CallSuper
+    public void cleanUp() throws Exception {
+        for (int i = 0; i < mControllers.size(); i++) {
+            mControllers.get(i).close();
+        }
+    }
+
+    /**
+     * Creates a {@link android.media.session.PlaybackState} with the given state.
+     *
+     * @param state one of the PlaybackState.STATE_xxx.
+     * @return a PlaybackState
+     */
+    public PlaybackState2 createPlaybackState(int state) {
+        return new PlaybackState2(mContext, state, 0, 0, 1.0f, 0, 0);
+    }
+
+    final MediaController2 createController(SessionToken2 token) throws InterruptedException {
+        return createController(token, true, null);
+    }
+
+    final MediaController2 createController(@NonNull SessionToken2 token,
+            boolean waitForConnect, @Nullable TestControllerCallbackInterface callback)
+            throws InterruptedException {
+        TestControllerInterface instance = onCreateController(token, callback);
+        if (!(instance instanceof MediaController2)) {
+            throw new RuntimeException("Test has a bug. Expected MediaController2 but returned "
+                    + instance);
+        }
+        MediaController2 controller = (MediaController2) instance;
+        mControllers.add(controller);
+        if (waitForConnect) {
+            waitForConnect(controller, true);
+        }
+        return controller;
+    }
+
+    private static WaitForConnectionInterface getWaitForConnectionInterface(
+            MediaController2 controller) {
+        if (!(controller instanceof TestControllerInterface)) {
+            throw new RuntimeException("Test has a bug. Expected controller implemented"
+                    + " TestControllerInterface but got " + controller);
+        }
+        ControllerCallback callback = ((TestControllerInterface) controller).getCallback();
+        if (!(callback instanceof WaitForConnectionInterface)) {
+            throw new RuntimeException("Test has a bug. Expected controller with callback "
+                    + " implemented WaitForConnectionInterface but got " + controller);
+        }
+        return (WaitForConnectionInterface) callback;
+    }
+
+    public static void waitForConnect(MediaController2 controller, boolean expected)
+            throws InterruptedException {
+        getWaitForConnectionInterface(controller).waitForConnect(expected);
+    }
+
+    public static void waitForDisconnect(MediaController2 controller, boolean expected)
+            throws InterruptedException {
+        getWaitForConnectionInterface(controller).waitForDisconnect(expected);
+    }
+
+    TestControllerInterface onCreateController(@NonNull SessionToken2 token,
+            @Nullable TestControllerCallbackInterface callback) {
+        if (callback == null) {
+            callback = new TestControllerCallbackInterface() {};
+        }
+        return new TestMediaController(mContext, token, new TestControllerCallback(callback));
+    }
+
+    public static class TestControllerCallback extends MediaController2.ControllerCallback
+            implements WaitForConnectionInterface {
+        public final TestControllerCallbackInterface mCallbackProxy;
+        public final CountDownLatch connectLatch = new CountDownLatch(1);
+        public final CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+        TestControllerCallback(@NonNull TestControllerCallbackInterface callbackProxy) {
+            if (callbackProxy == null) {
+                throw new IllegalArgumentException("Callback proxy shouldn't be null. Test bug");
+            }
+            mCallbackProxy = callbackProxy;
+        }
+
+        @CallSuper
+        @Override
+        public void onConnected(MediaController2 controller, CommandGroup commands) {
+            connectLatch.countDown();
+        }
+
+        @CallSuper
+        @Override
+        public void onDisconnected(MediaController2 controller) {
+            disconnectLatch.countDown();
+        }
+
+        @Override
+        public void onPlaybackStateChanged(MediaController2 controller, PlaybackState2 state) {
+            mCallbackProxy.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void onCustomCommand(MediaController2 controller, Command command, Bundle args,
+                ResultReceiver receiver) {
+            mCallbackProxy.onCustomCommand(command, args, receiver);
+        }
+
+        @Override
+        public void waitForConnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+
+        @Override
+        public void waitForDisconnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+
+        @Override
+        public void onPlaylistChanged(MediaController2 controller, List<MediaItem2> params) {
+            mCallbackProxy.onPlaylistChanged(params);
+        }
+
+        @Override
+        public void onPlaylistParamsChanged(MediaController2 controller,
+                MediaSession2.PlaylistParams params) {
+            mCallbackProxy.onPlaylistParamsChanged(params);
+        }
+
+        @Override
+        public void onPlaybackInfoChanged(MediaController2 controller,
+                MediaController2.PlaybackInfo info) {
+            mCallbackProxy.onPlaybackInfoChanged(info);
+        }
+
+        @Override
+        public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
+            mCallbackProxy.onCustomLayoutChanged(layout);
+        }
+    }
+
+    public class TestMediaController extends MediaController2 implements TestControllerInterface {
+        private final ControllerCallback mCallback;
+
+        public TestMediaController(@NonNull Context context, @NonNull SessionToken2 token,
+                @NonNull ControllerCallback callback) {
+            super(context, token, sHandlerExecutor, callback);
+            mCallback = callback;
+        }
+
+        @Override
+        public ControllerCallback getCallback() {
+            return mCallback;
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2_PermissionTest.java b/tests/tests/media/src/android/media/cts/MediaSession2_PermissionTest.java
new file mode 100644
index 0000000..bd2da7a
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSession2_PermissionTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static android.media.MediaSession2.*;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.SessionCallback;
+import android.net.Uri;
+import android.os.Process;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests whether {@link MediaSession2} receives commands that hasn't allowed.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class MediaSession2_PermissionTest extends MediaSession2TestBase {
+    private static final String SESSION_ID = "MediaSession2Test_permission";
+
+    private MockPlayer mPlayer;
+    private MediaSession2 mSession;
+    private MediaSession2.SessionCallback mCallback;
+
+    private MediaSession2 matchesSession() {
+        return argThat((session) -> session == mSession);
+    }
+
+    private static ControllerInfo matchesCaller() {
+        return argThat((controllerInfo) -> controllerInfo.getUid() == Process.myUid());
+    }
+
+    private static Command matches(final int commandCode) {
+        return argThat((command) -> command.getCommandCode() == commandCode);
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        if (mSession != null) {
+            mSession.close();
+            mSession = null;
+        }
+        mPlayer = null;
+        mCallback = null;
+    }
+
+    private MediaSession2 createSessionWithAllowedActions(CommandGroup commands) {
+        mPlayer = new MockPlayer(0);
+        if (commands == null) {
+            commands = new CommandGroup(mContext);
+        }
+        mCallback = mock(SessionCallback.class);
+        when(mCallback.onConnect(any(), any())).thenReturn(commands);
+        if (mSession != null) {
+            mSession.close();
+        }
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer).setId(SESSION_ID)
+                .setSessionCallback(sHandlerExecutor, mCallback).build();
+        return mSession;
+    }
+
+    private CommandGroup createCommandGroupWith(int commandCode) {
+        CommandGroup commands = new CommandGroup(mContext);
+        commands.addCommand(new Command(mContext, commandCode));
+        return commands;
+    }
+
+    private CommandGroup createCommandGroupWithout(int commandCode) {
+        CommandGroup commands = new CommandGroup(mContext);
+        commands.addAllPredefinedCommands();
+        commands.removeCommand(new Command(mContext, commandCode));
+        return commands;
+    }
+
+    @Test
+    public void testPlay() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_PLAY));
+        createController(mSession.getToken()).play();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_PLAY));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_PLAY));
+        createController(mSession.getToken()).play();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testPause() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_PAUSE));
+        createController(mSession.getToken()).pause();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_PAUSE));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_PAUSE));
+        createController(mSession.getToken()).pause();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testStop() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_STOP));
+        createController(mSession.getToken()).stop();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_STOP));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_STOP));
+        createController(mSession.getToken()).stop();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testSkipToNext() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+        createController(mSession.getToken()).skipToNextItem();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+        createController(mSession.getToken()).skipToNextItem();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testSkipToPrevious() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        createController(mSession.getToken()).skipToPreviousItem();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        createController(mSession.getToken()).skipToPreviousItem();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testFastForward() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
+        createController(mSession.getToken()).fastForward();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
+        createController(mSession.getToken()).fastForward();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testRewind() throws InterruptedException {
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_REWIND));
+        createController(mSession.getToken()).rewind();
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_REWIND));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_REWIND));
+        createController(mSession.getToken()).rewind();
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testSeekTo() throws InterruptedException {
+        final long position = 10;
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SEEK_TO));
+        createController(mSession.getToken()).seekTo(position);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SEEK_TO));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SEEK_TO));
+        createController(mSession.getToken()).seekTo(position);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    // TODO(jaewan): Uncomment when we implement skipToPlaylistItem()
+    /*
+    @Test
+    public void testSkipToPlaylistItem() throws InterruptedException {
+        final Uri uri = Uri.parse("set://current.playlist.item");
+        final DataSourceDesc dsd = new DataSourceDesc.Builder()
+                .setDataSource(mContext, uri).build();
+        final MediaItem2 item = new MediaItem2.Builder(mContext, MediaItem2.FLAG_PLAYABLE)
+                .setDataSourceDesc(dsd).build();
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+        createController(mSession.getToken()).skipToPlaylistItem(item);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(matchesCaller(),
+                matches(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+        createController(mSession.getToken()).skipToPlaylistItem(item);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any());
+    }
+    */
+
+    @Test
+    public void testSetPlaylistParams() throws InterruptedException {
+        final PlaylistParams param = new PlaylistParams(mContext,
+                PlaylistParams.REPEAT_MODE_ALL, PlaylistParams.SHUFFLE_MODE_ALL, null);
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+        createController(mSession.getToken()).setPlaylistParams(param);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(),
+                matches(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+        createController(mSession.getToken()).setPlaylistParams(param);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testSetVolume() throws InterruptedException {
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_VOLUME));
+        createController(mSession.getToken()).setVolumeTo(0, 0);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SET_VOLUME));
+
+        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_VOLUME));
+        createController(mSession.getToken()).setVolumeTo(0, 0);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
+    }
+
+    @Test
+    public void testPlayFromMediaId() throws InterruptedException {
+        final String mediaId = "testPlayFromMediaId";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAY_FROM_MEDIA_ID));
+        createController(mSession.getToken()).playFromMediaId(mediaId, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPlayFromMediaId(
+                matchesSession(), matchesCaller(), eq(mediaId), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAY_FROM_MEDIA_ID));
+        createController(mSession.getToken()).playFromMediaId(mediaId, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPlayFromMediaId(
+                any(), any(), any(), any());
+    }
+
+    @Test
+    public void testPlayFromUri() throws InterruptedException {
+        final Uri uri = Uri.parse("play://from.uri");
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAY_FROM_URI));
+        createController(mSession.getToken()).playFromUri(uri, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPlayFromUri(
+                matchesSession(), matchesCaller(), eq(uri), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAY_FROM_URI));
+        createController(mSession.getToken()).playFromUri(uri, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPlayFromUri(any(), any(), any(), any());
+    }
+
+    @Test
+    public void testPlayFromSearch() throws InterruptedException {
+        final String query = "testPlayFromSearch";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PLAY_FROM_SEARCH));
+        createController(mSession.getToken()).playFromSearch(query, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPlayFromSearch(
+                matchesSession(), matchesCaller(), eq(query), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAY_FROM_SEARCH));
+        createController(mSession.getToken()).playFromSearch(query, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPlayFromSearch(any(), any(), any(), any());
+    }
+
+    @Test
+    public void testPrepareFromMediaId() throws InterruptedException {
+        final String mediaId = "testPrepareFromMediaId";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_MEDIA_ID));
+        createController(mSession.getToken()).prepareFromMediaId(mediaId, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromMediaId(
+                matchesSession(), matchesCaller(), eq(mediaId), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_MEDIA_ID));
+        createController(mSession.getToken()).prepareFromMediaId(mediaId, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromMediaId(
+                any(), any(), any(), any());
+    }
+
+    @Test
+    public void testPrepareFromUri() throws InterruptedException {
+        final Uri uri = Uri.parse("prepare://from.uri");
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_URI));
+        createController(mSession.getToken()).prepareFromUri(uri, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromUri(
+                matchesSession(), matchesCaller(), eq(uri), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_URI));
+        createController(mSession.getToken()).prepareFromUri(uri, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromUri(any(), any(), any(), any());
+    }
+
+    @Test
+    public void testPrepareFromSearch() throws InterruptedException {
+        final String query = "testPrepareFromSearch";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_SEARCH));
+        createController(mSession.getToken()).prepareFromSearch(query, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromSearch(
+                matchesSession(), matchesCaller(), eq(query), isNull());
+
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_SEARCH));
+        createController(mSession.getToken()).prepareFromSearch(query, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
+                any(), any(), any(), any());
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManager_MediaSession2Test.java b/tests/tests/media/src/android/media/cts/MediaSessionManager_MediaSession2Test.java
new file mode 100644
index 0000000..75025b5
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSessionManager_MediaSession2Test.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.Context;
+import android.media.MediaController2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.SessionToken2;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnSessionTokensChangedListener;
+import android.media.session.PlaybackState;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.UUID;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link MediaSessionManager} with {@link MediaSession2} specific APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaSessionManager_MediaSession2Test extends MediaSession2TestBase {
+    private static final String TAG = "MediaSessionManager_MediaSession2Test";
+
+    private MediaSessionManager mManager;
+    private MediaSession2 mSession;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+
+        // Specify TAG here so {@link MediaSession2.getInstance()} doesn't complaint about
+        // per test thread differs across the {@link MediaSession2} with the same TAG.
+        final MockPlayer player = new MockPlayer(1);
+        mSession = new MediaSession2.Builder(mContext)
+                .setPlayer(player)
+                .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) { })
+                .setId(TAG)
+                .build();
+        ensureChangeInSession();
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        sHandler.removeCallbacksAndMessages(null);
+        mSession.close();
+    }
+
+    // TODO(jaewan): Make this host-side test to see per-user behavior.
+    @Ignore
+    @Test
+    public void testGetMediaSession2Tokens_hasMediaController() throws InterruptedException {
+        final MockPlayer player = (MockPlayer) mSession.getPlayer();
+        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_STOPPED));
+
+        MediaController2 controller = null;
+        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
+        assertNotNull(tokens);
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken2 token = tokens.get(i);
+            if (mContext.getPackageName().equals(token.getPackageName())
+                    && TAG.equals(token.getId())) {
+                assertNull(controller);
+                controller = createController(token);
+            }
+        }
+        assertNotNull(controller);
+
+        // Test if the found controller is correct one.
+        assertEquals(PlaybackState.STATE_STOPPED, controller.getPlaybackState().getState());
+        controller.play();
+
+        assertTrue(player.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertTrue(player.mPlayCalled);
+    }
+
+    /**
+     * Test if server recognizes a session even if the session refuses the connection from server.
+     *
+     * @throws InterruptedException
+     */
+    @Test
+    public void testGetSessionTokens_sessionRejected() throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = new MediaSession2.Builder(mContext).setPlayer(new MockPlayer(0))
+                    .setId(TAG).setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {
+                        @Override
+                        public MediaSession2.CommandGroup onConnect(
+                                MediaSession2 session, ControllerInfo controller) {
+                            // Reject all connection request.
+                            return null;
+                        }
+                    }).build();
+        });
+        ensureChangeInSession();
+
+        boolean foundSession = false;
+        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
+        assertNotNull(tokens);
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken2 token = tokens.get(i);
+            if (mContext.getPackageName().equals(token.getPackageName())
+                    && TAG.equals(token.getId())) {
+                assertFalse(foundSession);
+                foundSession = true;
+            }
+        }
+        assertTrue(foundSession);
+    }
+
+    @Test
+    public void testGetMediaSession2Tokens_playerRemoved() throws InterruptedException {
+        // Release
+        sHandler.postAndSync(() -> {
+            mSession.close();
+        });
+        ensureChangeInSession();
+
+        // When the mSession's player becomes null, it should lose binder connection between server.
+        // So server will forget the session.
+        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken2 token = tokens.get(i);
+            assertFalse(mContext.getPackageName().equals(token.getPackageName())
+                    && TAG.equals(token.getId()));
+        }
+    }
+
+    @Test
+    public void testGetMediaSessionService2Token() throws InterruptedException {
+        boolean foundTestSessionService = false;
+        boolean foundTestLibraryService = false;
+        List<SessionToken2> tokens = mManager.getSessionServiceTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken2 token = tokens.get(i);
+            if (mContext.getPackageName().equals(token.getPackageName())
+                    && MockMediaSessionService2.ID.equals(token.getId())) {
+                assertFalse(foundTestSessionService);
+                assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+                foundTestSessionService = true;
+            } else if (mContext.getPackageName().equals(token.getPackageName())
+                    && MockMediaLibraryService2.ID.equals(token.getId())) {
+                assertFalse(foundTestLibraryService);
+                assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+                foundTestLibraryService = true;
+            }
+        }
+        assertTrue(foundTestSessionService);
+        assertTrue(foundTestLibraryService);
+    }
+
+    @Test
+    public void testGetAllSessionTokens() throws InterruptedException {
+        boolean foundTestSession = false;
+        boolean foundTestSessionService = false;
+        boolean foundTestLibraryService = false;
+        List<SessionToken2> tokens = mManager.getAllSessionTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken2 token = tokens.get(i);
+            if (!mContext.getPackageName().equals(token.getPackageName())) {
+                continue;
+            }
+            switch (token.getId()) {
+                case TAG:
+                    assertFalse(foundTestSession);
+                    foundTestSession = true;
+                    break;
+                case MockMediaSessionService2.ID:
+                    assertFalse(foundTestSessionService);
+                    foundTestSessionService = true;
+                    assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+                    break;
+                case MockMediaLibraryService2.ID:
+                    assertFalse(foundTestLibraryService);
+                    assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+                    foundTestLibraryService = true;
+                    break;
+                default:
+                    fail("Unexpected session " + token + " exists in the package");
+            }
+        }
+        assertTrue(foundTestSession);
+        assertTrue(foundTestSessionService);
+        assertTrue(foundTestLibraryService);
+    }
+
+    @Test
+    public void testAddOnSessionTokensChangedListener() throws InterruptedException {
+        TokensChangedListener listener = new TokensChangedListener();
+        mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+        listener.reset();
+        MediaSession2 session1 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+        assertTrue(listener.findToken(session1.getToken()));
+
+        listener.reset();
+        session1.close();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+
+        listener.reset();
+        MediaSession2 session2 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertTrue(listener.findToken(session2.getToken()));
+
+        listener.reset();
+        MediaSession2 session3 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertTrue(listener.findToken(session2.getToken()));
+        assertTrue(listener.findToken(session3.getToken()));
+
+        listener.reset();
+        session2.close();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertFalse(listener.findToken(session2.getToken()));
+        assertTrue(listener.findToken(session3.getToken()));
+
+        listener.reset();
+        session3.close();
+        assertTrue(listener.await());
+        assertFalse(listener.findToken(session1.getToken()));
+        assertFalse(listener.findToken(session2.getToken()));
+        assertFalse(listener.findToken(session3.getToken()));
+
+        mManager.removeOnSessionTokensChangedListener(listener);
+    }
+
+    @Test
+    public void testRemoveOnSessionTokensChangedListener() throws InterruptedException {
+        TokensChangedListener listener = new TokensChangedListener();
+        mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+        listener.reset();
+        MediaSession2 session1 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertTrue(listener.await());
+
+        mManager.removeOnSessionTokensChangedListener(listener);
+
+        listener.reset();
+        session1.close();
+        assertFalse(listener.await());
+
+        listener.reset();
+        MediaSession2 session2 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertFalse(listener.await());
+
+        listener.reset();
+        MediaSession2 session3 = new MediaSession2.Builder(mContext)
+                .setPlayer(new MockPlayer(0))
+                .setId(UUID.randomUUID().toString())
+                .build();
+        assertFalse(listener.await());
+
+        listener.reset();
+        session2.close();
+        assertFalse(listener.await());
+
+        listener.reset();
+        session3.close();
+        assertFalse(listener.await());
+    }
+
+    // Ensures if the session creation/release is notified to the server.
+    private void ensureChangeInSession() throws InterruptedException {
+        // TODO(jaewan): Wait by listener.
+        Thread.sleep(WAIT_TIME_MS);
+    }
+
+    private class TokensChangedListener implements OnSessionTokensChangedListener {
+        private CountDownLatch mLatch;
+        private List<SessionToken2> mTokens;
+
+        private void reset() {
+            mLatch = new CountDownLatch(1);
+            mTokens = null;
+        }
+
+        private boolean await() throws InterruptedException {
+            return mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+        }
+
+        private boolean findToken(SessionToken2 token) {
+            return mTokens.contains(token);
+        }
+
+        @Override
+        public void onSessionTokensChanged(List<SessionToken2> tokens) {
+            mTokens = tokens;
+            mLatch.countDown();
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MockActivity.java b/tests/tests/media/src/android/media/cts/MockActivity.java
new file mode 100644
index 0000000..028cfae
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.app.Activity;
+
+public class MockActivity extends Activity {
+}
diff --git a/tests/tests/media/src/android/media/cts/MockMediaLibraryService2.java b/tests/tests/media/src/android/media/cts/MockMediaLibraryService2.java
new file mode 100644
index 0000000..96e0d7f
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockMediaLibraryService2.java
@@ -0,0 +1,264 @@
+/*
+* Copyright 2018 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.media.cts;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaLibraryService2;
+import android.media.MediaMetadata2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
+import android.media.SessionToken2;
+import android.media.cts.TestServiceRegistry.SessionCallbackProxy;
+import android.media.cts.TestUtils.SyncHandler;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Mock implementation of {@link MediaLibraryService2} for testing.
+ */
+public class MockMediaLibraryService2 extends MediaLibraryService2 {
+    // Keep in sync with the AndroidManifest.xml
+    public static final String ID = "TestLibrary";
+
+    public static final String ROOT_ID = "rootId";
+    public static final Bundle EXTRAS = new Bundle();
+
+    public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
+
+    public static final String PARENT_ID = "parent_id";
+    public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
+    public static final String PARENT_ID_ERROR = "parent_id_error";
+
+    public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+    public static final int CHILDREN_COUNT = 100;
+
+    public static final String SEARCH_QUERY = "search_query";
+    public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
+    public static final int SEARCH_TIME_IN_MS = 5000;
+    public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
+
+    public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
+    public static final int SEARCH_RESULT_COUNT = 50;
+
+    private static final DataSourceDesc DATA_SOURCE_DESC =
+            new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
+
+    private static final String TAG = "MockMediaLibrarySvc2";
+
+    static {
+        EXTRAS.putString(ROOT_ID, ROOT_ID);
+    }
+    @GuardedBy("MockMediaLibraryService2.class")
+    private static SessionToken2 sToken;
+
+    private MediaLibrarySession mSession;
+
+    public MockMediaLibraryService2() {
+        super();
+        GET_CHILDREN_RESULT.clear();
+        String getChildrenMediaIdPrefix = "get_children_media_id_";
+        for (int i = 0; i < CHILDREN_COUNT; i++) {
+            GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
+        }
+
+        SEARCH_RESULT.clear();
+        String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
+        for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
+            SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        TestServiceRegistry.getInstance().setServiceInstance(this);
+    }
+
+    @Override
+    public MediaLibrarySession onCreateSession(String sessionId) {
+        final MockPlayer player = new MockPlayer(1);
+        final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        final Executor executor = (runnable) -> handler.post(runnable);
+        SessionCallbackProxy sessionCallbackProxy = TestServiceRegistry.getInstance()
+                .getSessionCallbackProxy();
+        if (sessionCallbackProxy == null) {
+            // Ensures non-null
+            sessionCallbackProxy = new SessionCallbackProxy(this) {};
+        }
+        TestLibrarySessionCallback callback =
+                new TestLibrarySessionCallback(sessionCallbackProxy);
+        mSession = new MediaLibrarySession.Builder(MockMediaLibraryService2.this, executor,
+                callback).setPlayer(player).setId(sessionId).build();
+        return mSession;
+    }
+
+    @Override
+    public void onDestroy() {
+        TestServiceRegistry.getInstance().cleanUp();
+        super.onDestroy();
+    }
+
+    public static SessionToken2 getToken(Context context) {
+        synchronized (MockMediaLibraryService2.class) {
+            if (sToken == null) {
+                sToken = new SessionToken2(context, context.getPackageName(),
+                        MockMediaLibraryService2.class.getName());
+                assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, sToken.getType());
+            }
+            return sToken;
+        }
+    }
+
+    private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
+        private final SessionCallbackProxy mCallbackProxy;
+
+        public TestLibrarySessionCallback(SessionCallbackProxy callbackProxy) {
+            super(MockMediaLibraryService2.this);
+            mCallbackProxy = callbackProxy;
+        }
+
+        @Override
+        public CommandGroup onConnect(MediaSession2 session,
+                ControllerInfo controller) {
+            return mCallbackProxy.onConnect(controller);
+        }
+
+        @Override
+        public LibraryRoot onGetLibraryRoot(MediaLibrarySession session, ControllerInfo controller,
+                Bundle rootHints) {
+            return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRAS);
+        }
+
+        @Override
+        public MediaItem2 onGetItem(MediaLibrarySession session, ControllerInfo controller,
+                String mediaId) {
+            if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
+                return createMediaItem(mediaId);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public List<MediaItem2> onGetChildren(MediaLibrarySession session,
+                ControllerInfo controller, String parentId, int page, int pageSize, Bundle extras) {
+            if (PARENT_ID.equals(parentId)) {
+                return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
+            } else if (PARENT_ID_ERROR.equals(parentId)) {
+                return null;
+            }
+            // Includes the case of PARENT_ID_NO_CHILDREN.
+            return new ArrayList<>();
+        }
+
+        @Override
+        public void onSearch(MediaLibrarySession session, ControllerInfo controllerInfo,
+                String query, Bundle extras) {
+            if (SEARCH_QUERY.equals(query)) {
+                mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
+                        extras);
+            } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
+                // Searching takes some time. Notify after 5 seconds.
+                Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+                    @Override
+                    public void run() {
+                        mSession.notifySearchResultChanged(
+                                controllerInfo, query, SEARCH_RESULT_COUNT, extras);
+                    }
+                }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+            } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
+                mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
+            } else {
+                // TODO: For the error case, how should we notify the browser?
+            }
+        }
+
+        @Override
+        public List<MediaItem2> onGetSearchResult(MediaLibrarySession session,
+                ControllerInfo controllerInfo, String query, int page, int pageSize,
+                Bundle extras) {
+            if (SEARCH_QUERY.equals(query)) {
+                return getPaginatedResult(SEARCH_RESULT, page, pageSize);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public void onSubscribe(MediaLibrarySession session, ControllerInfo controller,
+                String parentId, Bundle extras) {
+            mCallbackProxy.onSubscribe(controller, parentId, extras);
+        }
+
+        @Override
+        public void onUnsubscribe(MediaLibrarySession session, ControllerInfo controller,
+                String parentId) {
+            mCallbackProxy.onUnsubscribe(controller, parentId);
+        }
+    }
+
+    private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
+        if (items == null) {
+            return null;
+        } else if (items.size() == 0) {
+            return new ArrayList<>();
+        }
+
+        final int totalItemCount = items.size();
+        int fromIndex = (page - 1) * pageSize;
+        int toIndex = Math.min(page * pageSize, totalItemCount);
+
+        List<MediaItem2> paginatedResult = new ArrayList<>();
+        try {
+            // The case of (fromIndex >= totalItemCount) will throw exception below.
+            paginatedResult = items.subList(fromIndex, toIndex);
+        } catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
+            Log.d(TAG, "Result is empty for given pagination arguments: totalItemCount="
+                    + totalItemCount + ", page=" + page + ", pageSize=" + pageSize, ex);
+        }
+        return paginatedResult;
+    }
+
+    private MediaItem2 createMediaItem(String mediaId) {
+        Context context = MockMediaLibraryService2.this;
+        return new MediaItem2.Builder(context, 0 /* Flags */)
+                .setMediaId(mediaId)
+                .setDataSourceDesc(DATA_SOURCE_DESC)
+                .setMetadata(new MediaMetadata2.Builder(context)
+                                .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
+                                .build())
+                .build();
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MockMediaSessionService2.java b/tests/tests/media/src/android/media/cts/MockMediaSessionService2.java
new file mode 100644
index 0000000..96f2b89
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockMediaSessionService2.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.fail;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.media.MediaSession2;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.MediaSessionService2;
+import android.media.cts.TestServiceRegistry.SessionCallbackProxy;
+import android.media.cts.TestUtils.SyncHandler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Mock implementation of {@link android.media.MediaSessionService2} for testing.
+ */
+public class MockMediaSessionService2 extends MediaSessionService2 {
+    // Keep in sync with the AndroidManifest.xml
+    public static final String ID = "TestSession";
+
+    private static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
+    private static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
+
+    private NotificationChannel mDefaultNotificationChannel;
+    private MediaSession2 mSession;
+    private NotificationManager mNotificationManager;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        TestServiceRegistry.getInstance().setServiceInstance(this);
+        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    @Override
+    public MediaSession2 onCreateSession(String sessionId) {
+        final MockPlayer player = new MockPlayer(1);
+        final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        final Executor executor = (runnable) -> handler.post(runnable);
+        SessionCallbackProxy sessionCallbackProxy = TestServiceRegistry.getInstance()
+                .getSessionCallbackProxy();
+        if (sessionCallbackProxy == null) {
+            // Ensures non-null
+            sessionCallbackProxy = new SessionCallbackProxy(this) {};
+        }
+        TestSessionServiceCallback callback =
+                new TestSessionServiceCallback(sessionCallbackProxy);
+        mSession = new MediaSession2.Builder(this)
+                .setPlayer(player)
+                .setSessionCallback(executor, callback)
+                .setId(sessionId).build();
+        return mSession;
+    }
+
+    @Override
+    public void onDestroy() {
+        TestServiceRegistry.getInstance().cleanUp();
+        super.onDestroy();
+    }
+
+    @Override
+    public MediaNotification onUpdateNotification() {
+        if (mDefaultNotificationChannel == null) {
+            mDefaultNotificationChannel = new NotificationChannel(
+                    DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
+                    DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
+                    NotificationManager.IMPORTANCE_DEFAULT);
+            mNotificationManager.createNotificationChannel(mDefaultNotificationChannel);
+        }
+        Notification notification = new Notification.Builder(
+                this, DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID)
+                .setContentTitle(getPackageName())
+                .setContentText("Dummt test notification")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+        return new MediaNotification(this, DEFAULT_MEDIA_NOTIFICATION_ID, notification);
+    }
+
+    private class TestSessionServiceCallback extends SessionCallback {
+        private final SessionCallbackProxy mCallbackProxy;
+
+        public TestSessionServiceCallback(SessionCallbackProxy callbackProxy) {
+            super(MockMediaSessionService2.this);
+            mCallbackProxy = callbackProxy;
+        }
+
+        @Override
+        public CommandGroup onConnect(MediaSession2 session,
+                ControllerInfo controller) {
+            return mCallbackProxy.onConnect(controller);
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MockPlayer.java b/tests/tests/media/src/android/media/cts/MockPlayer.java
new file mode 100644
index 0000000..56813c0
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockPlayer.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.media.AudioAttributes;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.PlaybackState2;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.ArrayMap;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * A mock implementation of {@link MediaPlayerBase} for testing.
+ */
+public class MockPlayer extends MediaPlayerBase {
+    public final CountDownLatch mCountDownLatch;
+
+    public boolean mPlayCalled;
+    public boolean mPauseCalled;
+    public boolean mStopCalled;
+    public boolean mSkipToPreviousCalled;
+    public boolean mSkipToNextCalled;
+    public boolean mPrepareCalled;
+    public boolean mFastForwardCalled;
+    public boolean mRewindCalled;
+    public boolean mSeekToCalled;
+    public long mSeekPosition;
+    public boolean mSetCurrentPlaylistItemCalled;
+    public MediaItem2 mCurrentItem;
+    public boolean mSetPlaylistCalled;
+    public boolean mSetPlaylistParamsCalled;
+
+    public ArrayMap<PlayerEventCallback, Executor> mCallbacks = new ArrayMap<>();
+    public List<MediaItem2> mPlaylist;
+    public PlaylistParams mPlaylistParams;
+
+    private PlaybackState2 mLastPlaybackState;
+    private AudioAttributes mAudioAttributes;
+
+    public MockPlayer(int count) {
+        mCountDownLatch = (count > 0) ? new CountDownLatch(count) : null;
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    @Override
+    public void reset() {
+        // no-op
+    }
+
+    @Override
+    public void play() {
+        mPlayCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void pause() {
+        mPauseCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void stop() {
+        mStopCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void skipToPrevious() {
+        mSkipToPreviousCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+    */
+
+    @Override
+    public void skipToNext() {
+        mSkipToNextCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void prepare() {
+        mPrepareCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void fastForward() {
+        mFastForwardCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void rewind() {
+        mRewindCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+    */
+
+    @Override
+    public void seekTo(long pos) {
+        mSeekToCalled = true;
+        mSeekPosition = pos;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void setCurrentPlaylistItem(MediaItem2 item) {
+        mSetCurrentPlaylistItemCalled = true;
+        mCurrentItem = item;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Nullable
+    @Override
+    public PlaybackState2 getPlaybackState() {
+        return mLastPlaybackState;
+    }
+    */
+
+    @Override
+    public int getPlayerState() {
+        return mLastPlaybackState.getState();
+    }
+
+    @Override
+    public int getBufferingState() {
+        // TODO: implement this
+        return -1;
+    }
+
+    @Override
+    public void registerPlayerEventCallback(@NonNull Executor executor,
+            @NonNull PlayerEventCallback callback) {
+        mCallbacks.put(callback, executor);
+    }
+
+    @Override
+    public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    public void notifyPlaybackState(final PlaybackState2 state) {
+        mLastPlaybackState = state;
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            final PlayerEventCallback callback = mCallbacks.keyAt(i);
+            final Executor executor = mCallbacks.valueAt(i);
+            // TODO: Uncomment or remove
+            //executor.execute(() -> callback.onPlaybackStateChanged(state));
+        }
+    }
+
+    public void notifyError(int what) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            final PlayerEventCallback callback = mCallbacks.keyAt(i);
+            final Executor executor = mCallbacks.valueAt(i);
+            // TODO: Uncomment or remove
+            //executor.execute(() -> callback.onError(null, what, 0));
+        }
+    }
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void setPlaylistParams(PlaylistParams params) {
+        mSetPlaylistParamsCalled = true;
+        mPlaylistParams = params;
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void addPlaylistItem(int index, MediaItem2 item) {
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void removePlaylistItem(MediaItem2 item) {
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public PlaylistParams getPlaylistParams() {
+        return mPlaylistParams;
+    }
+    */
+
+    @Override
+    public void setAudioAttributes(AudioAttributes attributes) {
+        mAudioAttributes = attributes;
+    }
+
+    @Override
+    public AudioAttributes getAudioAttributes() {
+        return mAudioAttributes;
+    }
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public void setPlaylist(List<MediaItem2> playlist) {
+        mSetPlaylistCalled = true;
+        mPlaylist = playlist;
+    }
+    */
+
+    // TODO: Uncomment or remove
+    /*
+    @Override
+    public List<MediaItem2> getPlaylist() {
+        return mPlaylist;
+    }
+    */
+
+    @Override
+    public void setDataSource(@NonNull DataSourceDesc dsd) {
+        // TODO: Implement this
+    }
+
+    @Override
+    public void setNextDataSource(@NonNull DataSourceDesc dsd) {
+        // TODO: Implement this
+    }
+
+    @Override
+    public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
+        // TODO: Implement this
+    }
+
+    @Override
+    public DataSourceDesc getCurrentDataSource() {
+        // TODO: Implement this
+        return null;
+    }
+
+    @Override
+    public void loopCurrent(boolean loop) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void setPlaybackSpeed(float speed) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void setPlayerVolume(float volume) {
+        // TODO: implement this
+    }
+
+    @Override
+    public float getPlayerVolume() {
+        // TODO: implement this
+        return -1;
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/SessionToken2Test.java b/tests/tests/media/src/android/media/cts/SessionToken2Test.java
new file mode 100644
index 0000000..1400079
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/SessionToken2Test.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
+import android.media.SessionToken2;
+import android.os.Process;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link SessionToken2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SessionToken2Test {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testConstructor_sessionService() {
+        SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+                MockMediaSessionService2.class.getCanonicalName());
+        assertEquals(MockMediaSessionService2.ID, token.getId());
+        assertEquals(mContext.getPackageName(), token.getPackageName());
+        assertEquals(Process.myUid(), token.getUid());
+        assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+    }
+
+    @Test
+    public void testConstructor_libraryService() {
+        SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+                MockMediaLibraryService2.class.getCanonicalName());
+        assertEquals(MockMediaLibraryService2.ID, token.getId());
+        assertEquals(mContext.getPackageName(), token.getPackageName());
+        assertEquals(Process.myUid(), token.getUid());
+        assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/TestServiceRegistry.java b/tests/tests/media/src/android/media/cts/TestServiceRegistry.java
new file mode 100644
index 0000000..7d6b882
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/TestServiceRegistry.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.media.MediaLibraryService2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSessionService2;
+import android.media.cts.TestUtils.SyncHandler;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Process;
+import android.support.annotation.GuardedBy;
+
+/**
+ * Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
+ * a way to control them in one place.
+ * <p>
+ * It only support only one service at a time.
+ */
+public class TestServiceRegistry {
+    /**
+     * Proxy for both {@link MediaSession2.SessionCallback} and
+     * {@link MediaLibraryService2.MediaLibrarySession.SessionCallback}.
+     */
+    public static abstract class SessionCallbackProxy {
+        private final Context mContext;
+
+        /**
+         * Constructor
+         */
+        public SessionCallbackProxy(Context context) {
+            mContext = context;
+        }
+
+        public final Context getContext() {
+            return mContext;
+        }
+
+        /**
+         * @param controller
+         * @return
+         */
+        public CommandGroup onConnect(ControllerInfo controller) {
+            if (Process.myUid() == controller.getUid()) {
+                CommandGroup commands = new CommandGroup(mContext);
+                commands.addAllPredefinedCommands();
+                return commands;
+            }
+            return null;
+        }
+
+        /**
+         * Called when enclosing service is created.
+         */
+        public void onServiceCreated(MediaSessionService2 service) { }
+
+        /**
+         * Called when enclosing service is destroyed.
+         */
+        public void onServiceDestroyed() { }
+
+        public void onSubscribe(ControllerInfo info, String parentId, Bundle extra) { }
+        public void onUnsubscribe(ControllerInfo info, String parentId) { }
+    }
+
+    @GuardedBy("TestServiceRegistry.class")
+    private static TestServiceRegistry sInstance;
+    @GuardedBy("TestServiceRegistry.class")
+    private MediaSessionService2 mService;
+    @GuardedBy("TestServiceRegistry.class")
+    private SyncHandler mHandler;
+    @GuardedBy("TestServiceRegistry.class")
+    private SessionCallbackProxy mCallbackProxy;
+
+    public static TestServiceRegistry getInstance() {
+        synchronized (TestServiceRegistry.class) {
+            if (sInstance == null) {
+                sInstance = new TestServiceRegistry();
+            }
+            return sInstance;
+        }
+    }
+
+    public void setHandler(Handler handler) {
+        synchronized (TestServiceRegistry.class) {
+            mHandler = new SyncHandler(handler.getLooper());
+        }
+    }
+
+    public Handler getHandler() {
+        synchronized (TestServiceRegistry.class) {
+            return mHandler;
+        }
+    }
+
+    public void setSessionCallbackProxy(SessionCallbackProxy callbackProxy) {
+        synchronized (TestServiceRegistry.class) {
+            mCallbackProxy = callbackProxy;
+        }
+    }
+
+    public SessionCallbackProxy getSessionCallbackProxy() {
+        synchronized (TestServiceRegistry.class) {
+            return mCallbackProxy;
+        }
+    }
+
+    public void setServiceInstance(MediaSessionService2 service) {
+        synchronized (TestServiceRegistry.class) {
+            if (mService != null) {
+                fail("Previous service instance is still running. Clean up manually to ensure"
+                        + " previoulsy running service doesn't break current test");
+            }
+            mService = service;
+            if (mCallbackProxy != null) {
+                mCallbackProxy.onServiceCreated(service);
+            }
+        }
+    }
+
+    public MediaSessionService2 getServiceInstance() {
+        synchronized (TestServiceRegistry.class) {
+            return mService;
+        }
+    }
+
+    public void cleanUp() {
+        synchronized (TestServiceRegistry.class) {
+            final SessionCallbackProxy callbackProxy = mCallbackProxy;
+            if (mService != null) {
+                mService.getSession().close();
+                // stopSelf() would not kill service while the binder connection established by
+                // bindService() exists, and close() above will do the job instead.
+                // So stopSelf() isn't really needed, but just for sure.
+                mService.stopSelf();
+                mService = null;
+            }
+            if (mHandler != null) {
+                mHandler.removeCallbacksAndMessages(null);
+            }
+            mCallbackProxy = null;
+
+            if (callbackProxy != null) {
+                callbackProxy.onServiceDestroyed();
+            }
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/TestUtils.java b/tests/tests/media/src/android/media/cts/TestUtils.java
new file mode 100644
index 0000000..dd91c00
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/TestUtils.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.SessionToken2;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utilities for tests.
+ */
+public final class TestUtils {
+    private static final int WAIT_TIME_MS = 1000;
+    private static final int WAIT_SERVICE_TIME_MS = 5000;
+
+    /**
+     * Finds the session with id in this test package.
+     *
+     * @param context
+     * @param id
+     * @return
+     */
+    // TODO(jaewan): Currently not working.
+    public static SessionToken2 getServiceToken(Context context, String id) {
+        MediaSessionManager manager =
+                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        List<SessionToken2> tokens = manager.getSessionServiceTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken2 token = tokens.get(i);
+            if (context.getPackageName().equals(token.getPackageName())
+                    && id.equals(token.getId())) {
+                return token;
+            }
+        }
+        fail("Failed to find service");
+        return null;
+    }
+
+    /**
+     * Compares contents of two bundles.
+     *
+     * @param a a bundle
+     * @param b another bundle
+     * @return {@code true} if two bundles are the same. {@code false} otherwise. This may be
+     *     incorrect if any bundle contains a bundle.
+     */
+    public static boolean equals(Bundle a, Bundle b) {
+        if (a == b) {
+            return true;
+        }
+        if (a == null || b == null) {
+            return false;
+        }
+        if (!a.keySet().containsAll(b.keySet())
+                || !b.keySet().containsAll(a.keySet())) {
+            return false;
+        }
+        for (String key : a.keySet()) {
+            if (!Objects.equals(a.get(key), b.get(key))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static void ensurePlaylistParamsModeEquals(PlaylistParams a, PlaylistParams b) {
+        assertEquals(a.getRepeatMode(), b.getRepeatMode());
+        assertEquals(a.getShuffleMode(), b.getShuffleMode());
+    }
+
+    /**
+     * Handler that always waits until the Runnable finishes.
+     */
+    public static class SyncHandler extends Handler {
+        public SyncHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void postAndSync(Runnable runnable) throws InterruptedException {
+            final CountDownLatch latch = new CountDownLatch(1);
+            if (getLooper() == Looper.myLooper()) {
+                runnable.run();
+            } else {
+                post(()->{
+                    runnable.run();
+                    latch.countDown();
+                });
+                assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+    }
+}
diff --git a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
index 16db0fe..9c1a339 100644
--- a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
+++ b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
@@ -19,4 +19,5 @@
 interface IIsolatedService {
     String[] getCachedSystemServices();
     IBinder getSystemService(String serviceName);
+    boolean getProcessIsIsolated();
 }
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index cddcd76..363ed47 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -39,16 +39,7 @@
 		libselinux \
 		libc++ \
 		libpcre2 \
-		libpackagelistparser \
-		libpowermanager \
-		libbase \
-		libunwind \
-		libhardware \
-		libsync \
-		libcamera_metadata \
-		libspeexresampler \
-		liblzma \
-		libstagefright_foundation
+		libpackagelistparser
 
 LOCAL_C_INCLUDES += ndk/sources/cpufeatures
 LOCAL_STATIC_LIBRARIES := cpufeatures
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
index 56e3e13..2be37bb 100644
--- a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.platform.test.annotations.SecurityTest;
 import android.security.cts.IIsolatedService;
@@ -31,7 +32,6 @@
 import java.util.concurrent.TimeUnit;
 import junit.framework.Assert;
 
-@SecurityTest
 public class IsolatedProcessTest extends AndroidTestCase {
     static final String TAG = IsolatedProcessTest.class.getSimpleName();
 
@@ -74,6 +74,7 @@
                 mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
+    @SecurityTest
     public void testGetCachedServicesFromIsolatedService() throws RemoteException {
         String[] cachedServices = mService.getCachedSystemServices();
         for (String serviceName : cachedServices) {
@@ -82,6 +83,7 @@
         }
     }
 
+    @SecurityTest
     public void testGetServiceFromIsolatedService() throws RemoteException {
         for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
             IBinder service = mService.getSystemService(serviceName);
@@ -90,6 +92,11 @@
         }
     }
 
+    public void testGetProcessIsIsolated() throws RemoteException {
+        Assert.assertFalse(Process.isIsolated());
+        Assert.assertTrue(mService.getProcessIsIsolated());
+    }
+
     @Override
     public void tearDown() {
         mContext.unbindService(mServiceConnection);
diff --git a/tests/tests/security/src/android/security/cts/IsolatedService.java b/tests/tests/security/src/android/security/cts/IsolatedService.java
index a033113..0245105 100644
--- a/tests/tests/security/src/android/security/cts/IsolatedService.java
+++ b/tests/tests/security/src/android/security/cts/IsolatedService.java
@@ -19,6 +19,7 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
+import android.os.Process;
 import android.platform.test.annotations.SecurityTest;
 import android.util.Log;
 import java.lang.reflect.Field;
@@ -75,6 +76,11 @@
         public IBinder getSystemService(String serviceName) {
             return getServiceFromServiceManager(serviceName);
         }
+
+        public boolean getProcessIsIsolated() {
+            return Process.isIsolated();
+        }
+
     };
 
     @Override
diff --git a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
index a2ca765..41dba19 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
@@ -27,7 +27,6 @@
 import android.app.Instrumentation;
 import android.app.slice.Slice;
 import android.app.slice.SliceManager;
-import android.app.slice.SliceManager.SliceCallback;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -113,37 +112,6 @@
     }
 
     @Test
-    public void testRegisterPin() {
-        SliceCallback callback = mock(SliceCallback.class);
-
-        mSliceManager.registerSliceCallback(BASE_URI, Collections.emptyList(), callback);
-        verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
-
-        mSliceManager.unregisterSliceCallback(BASE_URI, callback);
-        verify(LocalSliceProvider.sProxy, timeout(2000)).onSliceUnpinned(eq(BASE_URI));
-    }
-
-    @Test
-    public void testCallback() {
-        SliceCallback callback = mock(SliceCallback.class);
-
-        mSliceManager.registerSliceCallback(BASE_URI, Collections.emptyList(),
-                command -> command.run(), callback);
-        verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
-
-        try {
-            Slice s = new Slice.Builder(BASE_URI).build();
-            when(LocalSliceProvider.sProxy.onBindSlice(any(), any())).thenReturn(s);
-
-            mContext.getContentResolver().notifyChange(BASE_URI, null);
-            verify(callback, new Timeout(2000, atLeastOnce())).onSliceUpdated(any());
-        } finally {
-            mSliceManager.unregisterSliceCallback(BASE_URI, callback);
-            verify(LocalSliceProvider.sProxy, timeout(2000)).onSliceUnpinned(eq(BASE_URI));
-        }
-    }
-
-    @Test
     public void testMapIntentToUri() {
         Intent intent = new Intent("android.slice.cts.action.TEST_ACTION");
         intent.setPackage("android.slice.cts");
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index 7f588bd..94f6772 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -115,6 +115,40 @@
     }
 
     @Test
+    public void testLayerPaintAlphaChanged() {
+        final CountDownLatch fence = new CountDownLatch(1);
+        createTest()
+            .addLayout(R.layout.frame_layout, view -> {
+                FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+                View child = new View(view.getContext());
+                child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                child.setAlpha(0.0f);
+                // add rendering content
+                child.setBackgroundColor(Color.RED);
+                root.addView(child, new FrameLayout.LayoutParams(TEST_WIDTH, TEST_HEIGHT,
+                        Gravity.TOP | Gravity.LEFT));
+
+                // Post non-zero alpha a few frames in, so that the initial layer draw completes.
+                root.getViewTreeObserver().addOnPreDrawListener(
+                        new ViewTreeObserver.OnPreDrawListener() {
+                            int mDrawCount = 0;
+                            @Override
+                            public boolean onPreDraw() {
+                                if (mDrawCount++ == 5) {
+                                    root.getChildAt(0).setAlpha(1.00f);
+                                    root.getViewTreeObserver().removeOnPreDrawListener(this);
+                                    root.post(fence::countDown);
+                                } else {
+                                    root.postInvalidate();
+                                }
+                                return true;
+                            }
+                        });
+            }, true, fence)
+            .runWithVerifier(new ColorVerifier(Color.RED));
+    }
+
+    @Test
     public void testLayerPaintColorFilter() {
         // Red, fully desaturated. Note that it's not 255/3 in each channel.
         // See ColorMatrix#setSaturation()
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 54ce087..b8ba73b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -189,8 +189,16 @@
     }
 
     private class DrawCounterListener implements ViewTreeObserver.OnDrawListener {
+        private static final int DEBUG_REQUIRE_EXTRA_FRAMES = 1;
+        private int mDrawCount = 0;
+
         @Override
         public void onDraw() {
+            if (++mDrawCount <= DEBUG_REQUIRE_EXTRA_FRAMES) {
+                mView.postInvalidate();
+                return;
+            }
+
             long vsyncMillis = mView.getDrawingTime();
 
             mView.post(() -> mView.getViewTreeObserver().removeOnDrawListener(this));
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
new file mode 100644
index 0000000..282ee5f
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.text.Layout;
+import android.text.PrecomputedText;
+import android.text.PrecomputedText.Params;
+import android.text.PrecomputedText.Params.Builder;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.widget.TextView;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * Tests for TextView with precomputed text.
+ */
+@RunWith(Parameterized.class)
+public class TextViewPrecomputedTextTest {
+    private static final String TEXT = "Hello, World!";
+
+    @Parameterized.Parameter(0)
+    public boolean differentTextSize;
+    @Parameterized.Parameter(1)
+    public boolean differentScaleX;
+    @Parameterized.Parameter(2)
+    public boolean differentSkewX;
+    @Parameterized.Parameter(3)
+    public boolean differentLetterSpacing;
+    @Parameterized.Parameter(4)
+    public boolean differentTextLocale;
+    @Parameterized.Parameter(5)
+    public boolean differentTypeface;
+    @Parameterized.Parameter(6)
+    public boolean differentFontVariationSettings;
+    @Parameterized.Parameter(7)
+    public boolean differentElegantTextHeight;
+    @Parameterized.Parameter(8)
+    public boolean differentBreakStrategy;
+    @Parameterized.Parameter(9)
+    public boolean differentHyphenationFrequency;
+    @Parameterized.Parameter(10)
+    public boolean differentTextDir;
+
+    // text size from the default value.
+    private Pair<Params, String[]> makeDifferentParams(Params params) {
+        final TextPaint paint = new TextPaint(params.getTextPaint());
+        ArrayList<String> differenceList = new ArrayList();
+
+        if (differentTextSize) {
+            paint.setTextSize(paint.getTextSize() * 2.0f + 1.0f);
+            differenceList.add("Text Size");
+        }
+        if (differentScaleX) {
+            paint.setTextScaleX(paint.getTextScaleX() * 2.0f + 1.0f);
+            differenceList.add("Text Scale X");
+        }
+        if (differentSkewX) {
+            paint.setTextSkewX(paint.getTextSkewX() * 2.0f + 1.0f);
+            differenceList.add("Text Skew X");
+        }
+        if (differentLetterSpacing) {
+            paint.setLetterSpacing(paint.getLetterSpacing() * 2.0f + 1.0f);
+            differenceList.add("Letter Spacing");
+        }
+        if (differentTextLocale) {
+            paint.setTextLocale(Locale.US.equals(paint.getTextLocale()) ? Locale.JAPAN : Locale.US);
+        }
+        if (differentTypeface) {
+            final Typeface tf = paint.getTypeface();
+            if (tf == null || tf == Typeface.DEFAULT) {
+                paint.setTypeface(Typeface.SERIF);
+            } else {
+                paint.setTypeface(Typeface.DEFAULT);
+            }
+            differenceList.add("Typeface");
+        }
+        if (differentFontVariationSettings) {
+            final String wght = "'wght' 700";
+            final String wdth = "'wdth' 100";
+
+            final String varSettings = paint.getFontVariationSettings();
+            if (varSettings == null || varSettings.equals(wght)) {
+                paint.setFontVariationSettings(wdth);
+            } else {
+                paint.setFontVariationSettings(wght);
+            }
+            differenceList.add("Font variation settings");
+        }
+        if (differentElegantTextHeight) {
+            paint.setElegantTextHeight(!paint.isElegantTextHeight());
+            differenceList.add("Elegant Text Height");
+        }
+
+        int strategy = params.getBreakStrategy();
+        if (differentBreakStrategy) {
+            strategy = strategy == Layout.BREAK_STRATEGY_SIMPLE
+                    ?  Layout.BREAK_STRATEGY_HIGH_QUALITY : Layout.BREAK_STRATEGY_SIMPLE;
+            differenceList.add("Break strategy");
+        }
+
+        int hyFreq = params.getHyphenationFrequency();
+        if (differentHyphenationFrequency) {
+            hyFreq = hyFreq == Layout.HYPHENATION_FREQUENCY_NONE
+                    ? Layout.HYPHENATION_FREQUENCY_FULL : Layout.HYPHENATION_FREQUENCY_NONE;
+            differenceList.add("Hyphenation Frequency");
+        }
+
+        TextDirectionHeuristic dir = params.getTextDirection();
+        if (differentTextDir) {
+            dir = dir == TextDirectionHeuristics.LTR
+                    ?  TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR;
+            differenceList.add("Text Direction");
+        }
+
+        final Params outParams = new Builder(paint).setBreakStrategy(strategy)
+                .setHyphenationFrequency(hyFreq).setTextDirection(dir).build();
+        return new Pair(outParams, differenceList.toArray(new String[differenceList.size()]));
+    }
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> getParameters() {
+        ArrayList<Object[]> allParams = new ArrayList<>();
+
+        // Compute the powerset except for all false case.
+        int allParameterCount = 11;
+        for (int bits = 1; bits < (1 << allParameterCount); ++bits) {
+            Object[] param = new Object[allParameterCount];
+            for (int j = 0; j < allParameterCount; ++j) {
+                param[j] = new Boolean((bits & (1 << j)) != 0);
+            }
+            allParams.add(param);
+        }
+        return allParams;
+    }
+
+    @SmallTest
+    @Test
+    public void setText() {
+        final TextView tv = new TextView(getContext());
+        final Params tvParams = tv.getTextMetricsParams();
+        final Pair<Params, String[]> testConfig = makeDifferentParams(tvParams);
+        final Params pctParams = testConfig.first;
+
+        final PrecomputedText pct = PrecomputedText.create(TEXT, pctParams);
+        try {
+            tv.setText(pct);
+            fail("Test Case: {" + TextUtils.join(",", testConfig.second) + "}, "
+                    + tvParams.toString() + " vs " + pctParams.toString());
+        } catch (IllegalArgumentException e) {
+            // pass
+        }
+
+        tv.setTextMetricsParams(pctParams);
+        tv.setText(pct);
+    }
+}