Merge "ITS: Check the video profile file format." into tm-dev
diff --git a/apps/CameraITS/utils/lighting_control_utils.py b/apps/CameraITS/utils/lighting_control_utils.py
index a78c658..ad5c521 100644
--- a/apps/CameraITS/utils/lighting_control_utils.py
+++ b/apps/CameraITS/utils/lighting_control_utils.py
@@ -15,7 +15,9 @@
 
 
 import logging
+import select
 import struct
+import sys
 import time
 import sensor_fusion_utils
 
@@ -24,6 +26,8 @@
 ARDUINO_BRIGHTNESS_MIN = 0
 ARDUINO_LIGHT_START_BYTE = 254
 
+KEYBOARD_ENTRY_WAIT_TIME = 20  # seconds to wait for keyboard entry
+
 
 def set_light_brightness(ch, brightness, serial_port, delay=0):
   """Turn on light to specified brightness.
@@ -96,5 +100,6 @@
   if arduino_serial_port:
     set_light_brightness(lighting_ch, level, arduino_serial_port, delay=1)
   else:
-    _ = input(f'Turn {state} lights in rig and hit ENTER to continue.')
+    print(f'Turn {state} lights in rig and hit <ENTER> to continue.')
+    _, _, _ = select.select([sys.stdin], [], [], KEYBOARD_ENTRY_WAIT_TIME)
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
index d3b1866..cfd7b07 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
@@ -16,11 +16,6 @@
 
 package com.android.cts.verifier.bluetooth;
 
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -32,6 +27,9 @@
 import android.view.View.OnClickListener;
 import android.widget.ToggleButton;
 
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
 /**
  * Activity for testing that Bluetooth can be disabled and enabled properly. The activity shows
  * a button that toggles Bluetooth by disabling it via {@link BluetoothAdapter#disable()} and
@@ -42,13 +40,12 @@
     private static final String TAG = BluetoothToggleActivity.class.getName();
 
     private static final int START_ENABLE_BLUETOOTH_REQUEST = 1;
+    private static final int START_DISABLE_BLUETOOTH_REQUEST = 2;
 
     private BluetoothAdapter mBluetoothAdapter;
 
     private BluetoothBroadcastReceiver mReceiver;
 
-    private ProgressDialog mDisablingDialog;
-
     private ToggleButton mToggleButton;
 
     private int mNumDisabledTimes = 0;
@@ -65,9 +62,6 @@
         IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         registerReceiver(mReceiver, filter);
 
-        mDisablingDialog = new ProgressDialog(this);
-        mDisablingDialog.setMessage(getString(R.string.bt_disabling));
-
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 
         getPassButton().setEnabled(false);
@@ -87,12 +81,17 @@
     }
 
     private void enableBluetooth() {
-        mDisablingDialog.dismiss();
         mToggleButton.setEnabled(false);
         Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
         startActivityForResult(intent, START_ENABLE_BLUETOOTH_REQUEST);
     }
 
+    private void disableBluetooth() {
+        mToggleButton.setEnabled(false);
+        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISABLE);
+        startActivityForResult(intent, START_DISABLE_BLUETOOTH_REQUEST);
+    }
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
@@ -102,22 +101,14 @@
                 mToggleButton.setChecked(enabledBluetooth);
                 mToggleButton.setEnabled(true);
                 break;
+            case START_DISABLE_BLUETOOTH_REQUEST:
+                boolean disabledBluetooth = RESULT_OK == resultCode;
+                mToggleButton.setChecked(!disabledBluetooth);
+                mToggleButton.setEnabled(true);
+                break;
         }
     }
 
-    private void disableBluetooth() {
-        mDisablingDialog.show();
-        mToggleButton.setEnabled(false);
-        if (!mBluetoothAdapter.disable()) {
-            mDisablingDialog.dismiss();
-            mToggleButton.setEnabled(true);
-            new AlertDialog.Builder(this)
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(R.string.bt_disabling_error)
-                .setPositiveButton(android.R.string.ok, null)
-                .show();
-        }
-    }
 
     class BluetoothBroadcastReceiver extends BroadcastReceiver {
         @Override
@@ -138,11 +129,6 @@
                 mNumEnabledTimes++;
             }
 
-            if (BluetoothAdapter.STATE_OFF == newState) {
-                mDisablingDialog.dismiss();
-                mToggleButton.setEnabled(true);
-            }
-
             mToggleButton.setChecked(mBluetoothAdapter.isEnabled());
             getPassButton().setEnabled(mNumDisabledTimes > 0 &&  mNumEnabledTimes > 0);
         }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index 203dc68..f8ec3108 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -197,14 +197,18 @@
 
     private static final String TV_PROFILE_TYPE_NAME = "com.android.tv.profile";
 
-    // We are allowed 11 minutes before the entire test run fails
-    private static final Duration MAX_TEST_DURATION = Duration.ofMinutes(10);
+    // We timeout 10 seconds before the infra would timeout
+    private static final Duration MAX_TEST_DURATION =
+            Duration.ofMillis(
+                    Long.parseLong(TestApis.instrumentation().arguments().getString(
+                            "timeout_msec", "600000")) - 10000);
     private final ExecutorService mTestExecutor = Executors.newSingleThreadExecutor();
     private Thread mTestThread;
 
     private final Logger mLogger = Logger.forInstance(this);
 
     public DeviceState() {
+
         mLogger.constructor(() -> {
             Future<Thread> testThreadFuture = mTestExecutor.submit(Thread::currentThread);
 
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java
index b2530e8..79eb2b8 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java
@@ -43,6 +43,7 @@
         try {
             ShellCommand.builder("input keyevent")
                     .addOperand("KEYCODE_WAKEUP")
+                    .allowEmptyOutput(true)
                     .validate(String::isEmpty)
                     .execute();
         } catch (AdbException e) {
diff --git a/hostsidetests/angle/OWNERS b/hostsidetests/angle/OWNERS
index 949f3a7..21522ca 100644
--- a/hostsidetests/angle/OWNERS
+++ b/hostsidetests/angle/OWNERS
@@ -1,7 +1,10 @@
 # Bug component: 452972
-timvp@google.com
-alanward@google.com
+abdolrashidi@google.com
+cclao@google.com
+chrisforbes@google.com
 cnorthrop@google.com
-courtneygo@google.com
 ianelliott@google.com
-tobine@google.com
+lfy@google.com
+romanl@google.com
+vantablack@google.com
+yuxinhu@google.com
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index deb05ed..8720426 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -951,6 +951,11 @@
                                 Classpaths.getClassDefsFromJar(getDevice(), apk).stream()
                                         .map(ClassDef::getType)
                                         .collect(ImmutableSet.toImmutableSet());
+                        // b/226559955: The directory paths containing APKs contain the build ID,
+                        // so strip out the @BUILD_ID portion.
+                        // e.g. /apex/com.android.bluetooth/app/Bluetooth@SC-DEV/Bluetooth.apk ->
+                        //      /apex/com.android.bluetooth/app/Bluetooth/Bluetooth.apk
+                        apk = apk.replaceFirst("@[^/]*", "");
                         final ImmutableSet<String> burndownClasses =
                                 FULL_APK_IN_APEX_BURNDOWN.getOrDefault(apk, ImmutableSet.of());
                         final Multimap<String, String> duplicates =
diff --git a/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java b/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java
index e41bbc0..8f5c3ed 100644
--- a/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java
+++ b/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java
@@ -16,33 +16,47 @@
 
 package android.car.cts.builtin;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.cts.builtin.user.CarInitialUserCommand;
+import android.car.cts.builtin.user.InitializedUsersCommand;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class ActivityManagerHelperHostTest extends CarBuiltinApiHostCtsBase {
+    private static final int SYSTEM_USER = 0;
 
+    // The startUserInForeground, startUserInBackground and unlockUser ActivityManagerHelper
+    // APIs are called by InitialUserSetter during device boot.Checking the default users
+    // are properly initialized covers the API testing.
     @Test
-    public void testStartUser() throws Exception {
-        // TODO (b/201005730): implement the test case to test the startUserInBackground()
-        // and startUserInForeground() APIs
+    public void testDefaultUserInitialization() throws Exception {
+        InitializedUsersCommand initUsersCommand = new InitializedUsersCommand(getDevice());
+        initUsersCommand.executeWith();
+        List<Integer> initUsers = initUsersCommand.getInitializedUsers();
+
+        ArrayList<Integer> defaultUsers = new ArrayList<>();
+        defaultUsers.add(SYSTEM_USER);
+        if (initUsersCommand.hasHeadlessUser()) {
+            CarInitialUserCommand initialUserCommand = new CarInitialUserCommand(getDevice());
+            initialUserCommand.executeWith();
+            int initialUser = initialUserCommand.getCarInitialUser();
+            defaultUsers.add(initialUser);
+        }
+
+        assertThat(initUsers).containsAtLeastElementsIn(defaultUsers);
     }
 
     @Test
     public void testStopUserWithDelayedLocking() throws Exception {
-        // TODO (b/201005730): implement the test case to test
-        // the stopUserWithDelayedLocking() API.
-    }
-
-    @Test
-    public void testUnlockUser() throws Exception {
-        // TODO (b/201005730): implement the test case to test the unlockUser() API.
-    }
-
-    @Test
-    public void testStopAllTaskForUser() throws Exception {
-        // TODO (b/201005730): implement the test case to test the stopAllTasksForUser() API.
+        // CtsCarHostTestCases:CarGarageModeAtomTests covers the stopUserWithDelayedLocking
+        // API call.
     }
 }
diff --git a/hostsidetests/car_builtin/src/android/car/cts/builtin/user/CarInitialUserCommand.java b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/CarInitialUserCommand.java
new file mode 100644
index 0000000..f96f04e
--- /dev/null
+++ b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/CarInitialUserCommand.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cts.builtin.user;
+
+import android.car.cts.builtin.CtsCarShellCommand;
+
+import com.android.tradefed.device.ITestDevice;
+
+public final class CarInitialUserCommand extends CtsCarShellCommand {
+
+    private static final String COMMAND_NAME = "cmd car_service get-initial-user";
+
+    // the value from UserHandler.USER_NULL
+    private static final int INVALID_USER_ID = -10_000;
+
+    private int mCarInitialUser = INVALID_USER_ID;
+
+    public CarInitialUserCommand(ITestDevice device) {
+        super(COMMAND_NAME, device);
+    }
+
+    public int getCarInitialUser() {
+        return mCarInitialUser;
+    }
+
+    @Override
+    protected void parseCommandReturn() throws Exception {
+        mCarInitialUser = Integer.parseInt(mCommandReturn.trim());
+    }
+}
diff --git a/hostsidetests/car_builtin/src/android/car/cts/builtin/user/InitializedUsersCommand.java b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/InitializedUsersCommand.java
new file mode 100644
index 0000000..bb79b35
--- /dev/null
+++ b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/InitializedUsersCommand.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cts.builtin.user;
+
+import static org.junit.Assert.fail;
+
+import android.car.cts.builtin.CtsCarShellCommand;
+
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class InitializedUsersCommand extends CtsCarShellCommand {
+
+    private static final String COMMAND_NAME = "cmd user list -v ";
+    private static final int  USER_INFO_MIN_STRING_LENGTH = 20;
+
+    private static final Pattern USER_PATTERN =
+            Pattern.compile(".*id=(\\d+).*type=([^\\s]+).*");
+    private static final int USER_PATTERN_GROUP_ID = 1;
+    private static final int USER_PATTERN_GROUP_TYPE = 2;
+
+    private List<Integer> mInitializedUsers;
+    private boolean mHasHeadlessUser;
+
+    public InitializedUsersCommand(ITestDevice device) {
+        super(COMMAND_NAME, device);
+    }
+
+    public List<Integer> getInitializedUsers() {
+        return mInitializedUsers;
+    }
+
+    public boolean hasHeadlessUser() {
+        return mHasHeadlessUser;
+    }
+
+    @Override
+    protected void parseCommandReturn() throws Exception {
+        mInitializedUsers = new ArrayList<Integer>();
+        mHasHeadlessUser = false;
+        String[] userInfoList = mCommandReturn.split("\n");
+        for (int i = 0; i < userInfoList.length; i++) {
+            if (userInfoList[i].length() > USER_INFO_MIN_STRING_LENGTH) {
+                if (userInfoList[i].contains("INITIALIZED")) {
+                    Matcher matcher = USER_PATTERN.matcher(userInfoList[i]);
+                    if (!matcher.find()) {
+                        fail("parseCommandReturn: no match was found in: " + userInfoList[i]);
+                    }
+                    int userId = Integer.parseInt(matcher.group(USER_PATTERN_GROUP_ID));
+                    mInitializedUsers.add(userId);
+                    String type = matcher.group(USER_PATTERN_GROUP_TYPE);
+                    if (!mHasHeadlessUser && type.contains("system.HEADLESS")) {
+                        mHasHeadlessUser = true;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
index 3c4f75b..319a138 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
@@ -102,10 +102,8 @@
             .add("setDefaultSmsApplication")
             .add("getPermittedInputMethods")
             .add("setPermittedInputMethods")
-            .add("getDrawable")
-            .add("getDrawableForDensity")
             .add("getDevicePolicyManagementRoleHolderPackage")
-            .add("getDrawableAsIcon")
+            .add("getResources")
             .build();
 
     private static final String LOG_TAG = "ParentProfileTest";
diff --git a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
index 8167e5f..29d9132 100644
--- a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
+++ b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
@@ -74,6 +74,14 @@
                 }
                 Log.i(TAG, "Volume at " + percentageVolume + "%");
                 break;
+            case "android.hdmicec.app.RAISE_VOLUME":
+                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
+                        AudioManager.ADJUST_RAISE, 0);
+                break;
+            case "android.hdmicec.app.LOWER_VOLUME":
+                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
+                        AudioManager.ADJUST_LOWER, 0);
+                break;
             case "android.hdmicec.app.SET_VOLUME":
                 int percentVolume = getIntent().getIntExtra("volumePercent", 50);
                 int volume = minVolume + ((maxVolume - minVolume) * percentVolume / 100);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java
index 88091c6..49b7964 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java
@@ -16,13 +16,13 @@
 
 package android.hdmicec.cts;
 
+import com.android.tradefed.device.ITestDevice;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
-import com.android.tradefed.device.ITestDevice;
-
 /** Helper class to get DUT audio status using Audio manager app */
 public final class AudioManagerHelper {
 
@@ -66,6 +66,14 @@
         hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.REPORT_AUDIO_STATUS);
     }
 
+    public static void unmuteDevice(ITestDevice device)
+            throws Exception {
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND + "android.hdmicec.app.UNMUTE");
+    }
+
     public static boolean isDeviceMuted(ITestDevice device) throws Exception {
         // Clear activity
         device.executeShellCommand(CLEAR_COMMAND);
@@ -76,6 +84,20 @@
         return (LogHelper.parseDutVolume(device, CLASS) >= 128);
     }
 
+    public static void lowerVolume(ITestDevice device) throws Exception {
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Start the APK and wait for it to complete
+        device.executeShellCommand(START_COMMAND + "android.hdmicec.app.LOWER_VOLUME");
+    }
+
+    public static void raiseVolume(ITestDevice device) throws Exception {
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Start the APK and wait for it to complete
+        device.executeShellCommand(START_COMMAND + "android.hdmicec.app.RAISE_VOLUME");
+    }
+
     public static void setDeviceVolume(ITestDevice device, int percentVolume) throws Exception {
         // Clear activity
         device.executeShellCommand(CLEAR_COMMAND);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecAbsoluteVolumeControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecAbsoluteVolumeControlTest.java
new file mode 100644
index 0000000..c8235d0
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecAbsoluteVolumeControlTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2022 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.hdmicec.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Abstract base class for tests for Absolute Volume Control (AVC). Subclasses must call
+ * this class's constructor to specify the device type of the DUT and the System Audio device.
+ * The three valid pairs of (DUT type, System Audio device type) for AVC are as follows:
+ * (Playback, TV); (Playback, Audio System); (TV, Audio System).
+ *
+ * Currently, it is only feasible to test the case where the DUT is a playback device and the
+ * System Audio device is a TV. This is because the CEC adapter responds <Feature Abort> to
+ * <Set Audio Volume Level> when it is started as an Audio System.
+ */
+public abstract class BaseHdmiCecAbsoluteVolumeControlTest extends BaseHdmiCecCtsTest {
+
+    /**
+     * Constructor. The test device type and client params (determining the client's device type)
+     * passed in here determine the behavior of the tests.
+     */
+    public BaseHdmiCecAbsoluteVolumeControlTest(@HdmiCecConstants.CecDeviceType int testDeviceType,
+            String... clientParams) {
+        super(testDeviceType, clientParams);
+    }
+
+    /**
+     * Returns the audio output device being used.
+     */
+    public int getAudioOutputDevice() {
+        if (mTestDeviceType == HdmiCecConstants.CEC_DEVICE_TYPE_TV) {
+            return HdmiCecConstants.DEVICE_OUT_HDMI_ARC;
+        } else {
+            return HdmiCecConstants.DEVICE_OUT_HDMI;
+        }
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // This setting must be enabled to use AVC. Start with it disabled to ensure that we can
+        // control when the AVC initiation process starts.
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_DISABLED);
+
+        // Disable and enable CEC on the DUT to clear its knowledge of device feature support.
+        // If the DUT isn't a TV, simulate a connected sink as well.
+        if (mTestDeviceType == HdmiCecConstants.CEC_DEVICE_TYPE_TV) {
+            getDevice().executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
+            waitForCondition(() -> !isCecAvailable(getDevice()), "Could not disable CEC");
+            getDevice().executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
+            waitForCondition(() -> isCecAvailable(getDevice()), "Could not enable CEC");
+        } else {
+            simulateCecSinkConnected(getDevice(), getTargetLogicalAddress());
+        }
+
+        // Full volume behavior is a prerequisite for AVC. However, we cannot control this
+        // condition from CTS tests or shell due to missing permissions. Therefore, we run these
+        // tests only if it is already true.
+        assumeTrue(isFullVolumeDevice(getAudioOutputDevice()));
+    }
+
+    /**
+     * Requires the device to be able to adopt CEC 2.0 so that it sends <Give Features>.
+     *
+     * Tests that the DUT enables and disables AVC in response to changes in the System Audio
+     * device's support for <Set Audio Volume Level>. In this test, this support status is
+     * communicated through <Report Features> messages.
+     */
+    @Test
+    public void testEnableDisableAvc_cec20_triggeredByReportFeatures() throws Exception {
+        // Enable CEC 2.0
+        setCec20();
+
+        // Enable CEC volume
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+        // Enable System Audio Mode if the System Audio device is an Audio System
+        enableSystemAudioModeIfApplicable();
+
+        // Since CEC 2.0 is enabled, DUT should also use <Give Features> to query AVC support
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_FEATURES);
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.SET_AUDIO_VOLUME_LEVEL);
+
+        // System Audio device reports support for <Set Audio Volume Level> via <Report Features>
+        sendReportFeatures(true);
+
+        // DUT queries audio status
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+        checkAbsoluteVolumeControlStatus(false);
+
+        // DUT receives audio status
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(50));
+        checkAbsoluteVolumeControlStatus(true);
+
+        // System Audio device reports no support for <Set Audio Volume Level>
+        sendReportFeatures(false);
+        checkAbsoluteVolumeControlStatus(false);
+    }
+
+    /**
+     * Tests that the DUT enables and disables AVC in response to changes in the System Audio
+     * device's support for <Set Audio Volume Level>. In this test, this support status is
+     * communicated through (the lack of) <Feature Abort> responses to <Set Audio Volume Level>.
+     */
+    @Test
+    public void testEnableDisableAvc_triggeredByAvcSupportChanged() throws Exception {
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+        enableSystemAudioModeIfApplicable();
+
+        // DUT queries AVC support by sending <Set Audio Volume Level>
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.SET_AUDIO_VOLUME_LEVEL);
+
+        // System Audio device does not respond with <Feature Abort>. DUT queries audio status
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+        checkAbsoluteVolumeControlStatus(false);
+
+        // DUT receives audio status
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(50));
+        checkAbsoluteVolumeControlStatus(true);
+
+        // System Audio device responds to <Set Audio Volume Level> with
+        // <Feature Abort>[Unrecognized opcode]
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.FEATURE_ABORT,
+                CecMessage.formatParams(CecOperand.SET_AUDIO_VOLUME_LEVEL + "00"));
+        checkAbsoluteVolumeControlStatus(false);
+    }
+
+    /**
+     * Tests that the DUT enables and disables AVC in response to CEC volume control being
+     * enabled or disabled.
+     */
+    @Test
+    public void testEnableAndDisableAVC_triggeredByVolumeControlSettingChange() throws Exception {
+        enableSystemAudioModeIfApplicable();
+
+        // System audio device reports support for <Set Audio Volume Level>
+        sendReportFeatures(true);
+
+        // Enable CEC volume
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+        // DUT queries audio status
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+        checkAbsoluteVolumeControlStatus(false);
+
+        // DUT receives audio status
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(50));
+        checkAbsoluteVolumeControlStatus(true);
+
+        // CEC volume control is disabled on the DUT
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_DISABLED);
+        checkAbsoluteVolumeControlStatus(false);
+    }
+
+    /**
+     * Tests that the DUT enables and disables AVC in response to System Audio mode being
+     * enabled or disabled.
+     *
+     * Only valid when the System Audio device is an Audio System.
+     */
+    @Test
+    public void testEnableDisableAvc_triggeredBySystemAudioModeChange() throws Exception {
+        assumeTrue("Skipping this test for this setup because the System Audio device "
+                        + "is not an Audio System.",
+                hdmiCecClient.getSelfDevice().getDeviceType()
+                        == HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM);
+
+        // System audio device reports support for <Set Audio Volume Level>
+        sendReportFeatures(true);
+
+        // CEC volume control is enabled on the DUT
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+        // Enable System Audio Mode
+        broadcastSystemAudioModeMessage(true);
+
+        // DUT queries audio status
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+        checkAbsoluteVolumeControlStatus(false);
+
+        // DUT receives audio status
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(50));
+        checkAbsoluteVolumeControlStatus(true);
+
+        // System Audio Mode is disabled
+        broadcastSystemAudioModeMessage(false);
+        checkAbsoluteVolumeControlStatus(false);
+    }
+
+    /**
+     * Tests that the DUT sends the correct CEC messages when AVC is enabled and Android
+     * initiates volume changes.
+     */
+    @Test
+    public void testOutgoingVolumeUpdates() throws Exception {
+        // Enable AVC
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+        enableSystemAudioModeIfApplicable();
+        sendReportFeatures(true);
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(50));
+        checkAbsoluteVolumeControlStatus(true);
+
+        // Calling AudioManager#setStreamVolume should cause the DUT to send
+        // <Set Audio Volume Level> with the new volume level as a parameter
+        AudioManagerHelper.setDeviceVolume(getDevice(), 80);
+        String setAudioVolumeLevelMessage = hdmiCecClient.checkExpectedOutput(
+                hdmiCecClient.getSelfDevice(), CecOperand.SET_AUDIO_VOLUME_LEVEL);
+        assertThat(CecMessage.getParams(setAudioVolumeLevelMessage)).isEqualTo(80);
+
+        // Calling AudioManager#adjustStreamVolume should cause the DUT to send
+        // <User Control Pressed>, <User Control Released>, and <Give Audio Status>
+        AudioManagerHelper.raiseVolume(getDevice());
+        String userControlPressedMessage = hdmiCecClient.checkExpectedOutput(
+                hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_PRESSED);
+        assertThat(CecMessage.getParams(userControlPressedMessage))
+                .isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_UP);
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.USER_CONTROL_RELEASED);
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+    }
+
+    /**
+     * Tests that the DUT notifies AudioManager when it receives <Report Audio Status> from the
+     * System Audio device.
+     */
+    @Test
+    public void testIncomingVolumeUpdates() throws Exception {
+        // Enable AVC
+        setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+                HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+        enableSystemAudioModeIfApplicable();
+        sendReportFeatures(true);
+        hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+                CecOperand.GIVE_AUDIO_STATUS);
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(50)); // Volume 50, mute off
+        checkAbsoluteVolumeControlStatus(true);
+
+        // Volume and mute status should match the initial <Report Audio Status>
+        assertApproximateDeviceVolumeAndMute(50, false);
+
+        // Test an incoming <Report Audio Status> that does not mute the device
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(90)); // Volume 90, mute off
+        assertApproximateDeviceVolumeAndMute(90, false);
+
+        // Test an incoming <Report Audio Status> that mutes the device
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+                CecMessage.formatParams(70 + 0b1000_0000)); // Volume 70, mute on
+        assertApproximateDeviceVolumeAndMute(0, true);
+    }
+
+    /**
+     * Enables System Audio Mode if the System Audio device is an Audio System.
+     */
+    private void enableSystemAudioModeIfApplicable() throws Exception {
+        if (hdmiCecClient.getSelfDevice() == LogicalAddress.AUDIO_SYSTEM) {
+            broadcastSystemAudioModeMessage(true);
+        }
+    }
+
+    /**
+     * Has the CEC client broadcast a message enabling or disabling System Audio Mode.
+     */
+    private void broadcastSystemAudioModeMessage(boolean val) throws Exception {
+        hdmiCecClient.sendCecMessage(
+                hdmiCecClient.getSelfDevice(),
+                LogicalAddress.BROADCAST,
+                CecOperand.SET_SYSTEM_AUDIO_MODE,
+                CecMessage.formatParams(val ? 1 : 0));
+    }
+
+    /**
+     * Has the CEC client send a <Report Features> message expressing support or lack of support for
+     * <Set Audio Volume Level>.
+     */
+    private void sendReportFeatures(boolean setAudioVolumeLevelSupport) throws Exception {
+        String deviceTypeNibble = hdmiCecClient.getSelfDevice() == LogicalAddress.TV
+                ? "80" : "08";
+        String featureSupportNibble = setAudioVolumeLevelSupport ? "01" : "00";
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_FEATURES,
+                "06:" + deviceTypeNibble + ":00:" + featureSupportNibble);
+    }
+
+    /**
+     * Checks that the status of Absolute Volume Control on the DUT to match the expected status.
+     * Waits and rechecks a limited number of times if the status does not currently match.
+     */
+    private void checkAbsoluteVolumeControlStatus(boolean enabledStatus) throws Exception {
+        String expectedStatus = enabledStatus ? "enabled" : "disabled";
+        waitForCondition(() -> getAbsoluteVolumeControlStatus() == enabledStatus,
+                "Absolute Volume Control was not " + expectedStatus + " when expected");
+    }
+
+    /**
+     * Returns the state of Absolute Volume Control on the DUT. This is determined by the
+     * volume behavior of the audio output device being used for HDMI audio.
+     */
+    private boolean getAbsoluteVolumeControlStatus() throws Exception {
+        return getDevice()
+                .executeShellCommand("dumpsys hdmi_control | grep mIsAbsoluteVolumeControlEnabled:")
+                .replace("mIsAbsoluteVolumeControlEnabled:", "").trim()
+                .equals("true");
+    }
+
+    /**
+     * Asserts that the DUT's volume (scale: [0, 100]) is within 5 points of an expected volume.
+     * This accounts for small differences due to rounding when converting between volume scales.
+     * Also asserts that the DUT's mute status is equal to {@code expectedMute}.
+     *
+     * Asserting both volume and mute at the same time saves a shell command because both are
+     * conveyed in a single log message.
+     */
+    private void assertApproximateDeviceVolumeAndMute(int expectedVolume, boolean expectedMute)
+            throws Exception {
+        // Raw output is equal to volume out of 100, plus 128 if muted
+        // In practice, if the stream is muted, volume equals 0, so this will be at most 128
+        int rawOutput = AudioManagerHelper.getDutAudioVolume(getDevice());
+
+        int actualVolume = rawOutput % 128;
+        assertWithMessage("Expected DUT to have volume " + expectedVolume
+                + " but was actually " + actualVolume)
+                .that(Math.abs(expectedVolume - actualVolume) <= 5)
+                .isTrue();
+
+        boolean actualMute = rawOutput >= 128;
+        String expectedMuteString = expectedMute ? "muted" : "unmuted";
+        String actualMuteString = actualMute ? "muted" : "unmuted";
+        assertWithMessage("Expected DUT to be " + expectedMuteString
+                + "but was actually " + actualMuteString)
+                .that(expectedMute)
+                .isEqualTo(actualMute);
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
index 2bd8281..83525e9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
@@ -37,6 +37,7 @@
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -503,6 +504,25 @@
                 .isEqualTo(wakefulness);
     }
 
+    /**
+     * Checks a given condition once every {@link HdmiCecConstants.SLEEP_TIMESTEP_SECONDS} seconds
+     * until it is true, or {@link HdmiCecConstants.MAX_SLEEP_TIME_SECONDS} seconds have passed.
+     * Triggers an assertion failure if the condition remains false after the time limit.
+     * @param condition Callable that returns whether the condition is met
+     * @param errorMessage The message to print if the condition is false
+     */
+    public void waitForCondition(Callable<Boolean> condition, String errorMessage)
+            throws Exception {
+        int waitTimeSeconds = 0;
+        boolean conditionState;
+        do {
+            TimeUnit.SECONDS.sleep(HdmiCecConstants.SLEEP_TIMESTEP_SECONDS);
+            waitTimeSeconds += HdmiCecConstants.SLEEP_TIMESTEP_SECONDS;
+            conditionState = condition.call();
+        } while (!conditionState && waitTimeSeconds <= HdmiCecConstants.MAX_SLEEP_TIME_SECONDS);
+        assertWithMessage(errorMessage).that(conditionState).isTrue();
+    }
+
     public void sendOtp() throws Exception {
         ITestDevice device = getDevice();
         device.executeShellCommand("cmd hdmi_control onetouchplay");
@@ -551,6 +571,7 @@
             throws Exception {
         hdmiCecClient.clearClientOutput();
         device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
+        waitForCondition(() -> !isCecAvailable(device), "Could not disable CEC");
         device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
         // When a CEC device has just become available, the CEC adapter isn't able to send it
         // messages right away. Therefore we let the first <Give Power Status> message time-out, and
@@ -560,20 +581,36 @@
         hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
         hdmiCecClient.sendCecMessage(LogicalAddress.TV, source, CecOperand.REPORT_POWER_STATUS,
                 CecMessage.formatParams(HdmiCecConstants.CEC_POWER_STATUS_STANDBY));
-        checkIsCecAvailable(device);
+        waitForCondition(() -> isCecAvailable(device),
+                "Simulating that a sink is connected, failed.");
     }
 
-    private void checkIsCecAvailable(ITestDevice device) throws Exception {
-        boolean isCecAvailable;
-        int waitTimeSeconds = 0;
-        do {
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.SLEEP_TIMESTEP_SECONDS);
-            waitTimeSeconds += HdmiCecConstants.SLEEP_TIMESTEP_SECONDS;
-            isCecAvailable =
-                    device.executeShellCommand("dumpsys hdmi_control | grep mIsCecAvailable:")
-                            .replace("mIsCecAvailable:", "").trim().equals("true");
-        } while (!isCecAvailable && waitTimeSeconds <= HdmiCecConstants.MAX_SLEEP_TIME_SECONDS);
-        assertWithMessage("Simulating that a sink is connected, failed.")
-                .that(isCecAvailable).isTrue();
+    boolean isCecAvailable(ITestDevice device) throws Exception {
+        return device.executeShellCommand("dumpsys hdmi_control | grep mIsCecAvailable:")
+                .replace("mIsCecAvailable:", "").trim().equals("true");
+    }
+
+    /**
+     * Returns whether an audio output device is using full volume behavior by checking if it is in
+     * the "mFullVolumeDevices" line in audio dumpsys. Example: "mFullVolumeDevices=0x400,0x40001".
+     */
+    public boolean isFullVolumeDevice(int audioOutputDevice) throws Exception {
+        String[] splitLine = getDevice().executeShellCommand(
+                "dumpsys audio | grep mFullVolumeDevices").split("=");
+        if (splitLine.length < 2) {
+            // No full volume devices
+            return false;
+        }
+        String[] deviceStrings = splitLine[1].trim().split(",");
+        for (String deviceString : deviceStrings) {
+            try {
+                if (Integer.decode(deviceString) == audioOutputDevice) {
+                    return true;
+                }
+            } catch (NumberFormatException e) {
+                // Ignore this device and continue
+            }
+        }
+        return false;
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
index 88362d8..a5be8bf 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
@@ -42,6 +42,7 @@
     SYSTEM_AUDIO_MODE_REQUEST(0x70),
     GIVE_AUDIO_STATUS(0x71),
     SET_SYSTEM_AUDIO_MODE(0x72),
+    SET_AUDIO_VOLUME_LEVEL(0x73),
     REPORT_AUDIO_STATUS(0x7a),
     GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
     SYSTEM_AUDIO_MODE_STATUS(0x7e),
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index f7c8e14..f1b22f9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -156,6 +156,7 @@
 
     // CEC Settings Values
     public static final String VOLUME_CONTROL_ENABLED = "1";
+    public static final String VOLUME_CONTROL_DISABLED = "0";
 
     // Power Control Modes for source devices
     public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
@@ -208,4 +209,15 @@
     // CEC 2.0 Report Feature Bits
     public static final int FEATURES_SINK_SUPPORTS_ARC_TX_BIT = 0x4;
     public static final int FEATURES_SINK_SUPPORTS_ARC_RX_BIT = 0x2;
+
+    // Audio device types from AudioDeviceInfo
+    public static final int DEVICE_OUT_HDMI = 0x400;
+    public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
+    public static final int DEVICE_OUT_HDMI_EARC = 0x40001;
+
+    // Volume behavior constants from AudioManager
+    public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
+    public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
+    public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
+    public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecAvcToTvTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecAvcToTvTest.java
new file mode 100644
index 0000000..f601312
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecAvcToTvTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.hdmicec.cts.playback;
+
+import android.hdmicec.cts.BaseHdmiCecAbsoluteVolumeControlTest;
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.HdmiCecConstants;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for Absolute Volume Control where the DUT is a Playback device and the
+ * System Audio device is a TV.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class HdmiCecAvcToTvTest extends BaseHdmiCecAbsoluteVolumeControlTest {
+
+    /**
+     * No need to pass in client parameters because the client is started as TV as long as the
+     * DUT is not a TV.
+     */
+    public HdmiCecAvcToTvTest() {
+        super(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
+    }
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(BaseHdmiCecCtsTest.CecRules.requiresCec(this))
+                    .around(BaseHdmiCecCtsTest.CecRules.requiresLeanback(this))
+                    .around(
+                            BaseHdmiCecCtsTest.CecRules.requiresDeviceType(
+                                    this, HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE))
+                    .around(hdmiCecClient);
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAbsoluteVolumeControlFollowerTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAbsoluteVolumeControlFollowerTest.java
new file mode 100644
index 0000000..77dbcb2
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAbsoluteVolumeControlFollowerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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.hdmicec.cts.tv;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.hdmicec.cts.AudioManagerHelper;
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests TV behavior when it receives <Set Audio Volume Level>.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class HdmiCecAbsoluteVolumeControlFollowerTest extends BaseHdmiCecCtsTest {
+    public HdmiCecAbsoluteVolumeControlFollowerTest() {
+        super(HdmiCecConstants.CEC_DEVICE_TYPE_TV, "-t", "p", "-t", "a");
+    }
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(CecRules.requiresCec(this))
+                    .around(CecRules.requiresLeanback(this))
+                    .around(CecRules.requiresDeviceType(this, HdmiCecConstants.CEC_DEVICE_TYPE_TV))
+                    .around(hdmiCecClient);
+
+    /**
+     * Tests that if System Audio Mode is enabled, the TV responds to <Set Audio Volume Level>
+     * with <Feature Abort>[Not in correct mode to respond]
+     */
+    @Test
+    public void testSystemAudioModeOn_respondsFeatureAbort() throws Exception {
+        AudioManagerHelper.unmuteDevice(getDevice());
+
+        int initialDeviceVolume = AudioManagerHelper.getDutAudioVolume(getDevice());
+
+        getDevice().executeShellCommand("cmd hdmi_control setsam on");
+
+        hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1,
+                CecOperand.SET_AUDIO_VOLUME_LEVEL,
+                CecMessage.formatParams((initialDeviceVolume + 50) % 101));
+
+        // Check that the DUT sent
+        // <Feature Abort>[Set Audio Volume Level, Not in correct mode to respond]
+        String featureAbort = hdmiCecClient.checkExpectedOutput(
+                LogicalAddress.PLAYBACK_1, CecOperand.FEATURE_ABORT);
+        assertThat(CecOperand.getOperand(CecMessage.getParams(featureAbort, 0, 2)))
+                .isEqualTo(CecOperand.SET_AUDIO_VOLUME_LEVEL);
+        assertThat(CecMessage.getParams(featureAbort, 2, 4)).isEqualTo(1);
+
+        // Check that volume did not change
+        assertThat(AudioManagerHelper.getDutAudioVolume(getDevice()))
+                .isEqualTo(initialDeviceVolume);
+    }
+
+    /**
+     * Tests that if System Audio Mode is disabled, the TV updates its volume after receiving
+     * <Set Audio Volume Level>
+     */
+    @Test
+    public void testSystemAudioModeOff_updatesVolume() throws Exception {
+        // Wait for CEC adapter to enable System Audio Mode before turning it off
+        hdmiCecClient.checkExpectedMessageFromClient(LogicalAddress.AUDIO_SYSTEM,
+                LogicalAddress.TV, CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+
+        getDevice().executeShellCommand("cmd hdmi_control setsam off");
+
+        AudioManagerHelper.unmuteDevice(getDevice());
+
+        int initialDeviceVolume = AudioManagerHelper.getDutAudioVolume(getDevice());
+        try {
+            int volumeToSet = (initialDeviceVolume + 50) % 101;
+            hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1,
+                    CecOperand.SET_AUDIO_VOLUME_LEVEL,
+                    CecMessage.formatParams(volumeToSet));
+
+            // Check that no <Feature Abort> was sent
+            hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.PLAYBACK_1,
+                    CecOperand.FEATURE_ABORT);
+
+            // Check that device volume is within 5 points of the expected volume.
+            // This accounts for rounding errors due to volume scale conversions.
+            int deviceVolume = AudioManagerHelper.getDutAudioVolume(getDevice());
+            assertWithMessage("Expected DUT to have volume " + volumeToSet
+                    + " but was actually " + deviceVolume)
+                    .that(Math.abs(volumeToSet - deviceVolume) <= 5)
+                    .isTrue();
+        } finally {
+            AudioManagerHelper.setDeviceVolume(getDevice(), initialDeviceVolume);
+        }
+    }
+}
diff --git a/tests/ServiceKillTest/Android.bp b/tests/ServiceKillTest/Android.bp
new file mode 100644
index 0000000..a9f32c5
--- /dev/null
+++ b/tests/ServiceKillTest/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "CtsServiceKillTestCases",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "ub-uiautomator",
+        "compatibility-device-util-axt",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "app/src/**/*.java",
+    ],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    platform_apis: true,
+}
diff --git a/tests/ServiceKillTest/AndroidManifest.xml b/tests/ServiceKillTest/AndroidManifest.xml
new file mode 100644
index 0000000..e32d385
--- /dev/null
+++ b/tests/ServiceKillTest/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.cts.servicekilltest" >
+
+    <queries>
+        <package android:name="com.android.cts.servicekilltestapp" />
+    </queries>
+
+    <application android:label="Cts Service Kill Test">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:functionalTest="true"
+                     android:targetPackage="com.android.cts.servicekilltest"
+                     android:label="Service Kill Tests"/>
+</manifest>
diff --git a/tests/ServiceKillTest/AndroidTest.xml b/tests/ServiceKillTest/AndroidTest.xml
new file mode 100644
index 0000000..29247c4
--- /dev/null
+++ b/tests/ServiceKillTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 Service Kill test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsServiceKillTestApp.apk" />
+        <option name="test-file-name" value="CtsServiceKillTestCases.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.cts.servicekilltest" />
+        <option name="runtime-hint" value="180m" />
+        <option name="test-timeout" value="14400000" />
+    </test>
+
+</configuration>
diff --git a/tests/ServiceKillTest/OWNERS b/tests/ServiceKillTest/OWNERS
new file mode 100644
index 0000000..553f8d0
--- /dev/null
+++ b/tests/ServiceKillTest/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316234
+suprabh@google.com
diff --git a/tests/ServiceKillTest/app/Android.bp b/tests/ServiceKillTest/app/Android.bp
new file mode 100644
index 0000000..834ce40
--- /dev/null
+++ b/tests/ServiceKillTest/app/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "CtsServiceKillTestApp",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    srcs: ["src/**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/tests/ServiceKillTest/app/AndroidManifest.xml b/tests/ServiceKillTest/app/AndroidManifest.xml
new file mode 100644
index 0000000..e3f45df
--- /dev/null
+++ b/tests/ServiceKillTest/app/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.servicekilltestapp">
+
+    <queries>
+        <package android:name="com.android.cts.servicekilltest" />
+    </queries>
+
+    <application>
+        <service android:name=".ServiceKillTestService" android:exported="true"/>
+    </application>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java b/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
new file mode 100644
index 0000000..56384ad
--- /dev/null
+++ b/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2021 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.servicekilltestapp;
+
+import android.annotation.SuppressLint;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.util.Log;
+import android.os.SystemClock;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class ServiceKillTestService extends Service {
+
+    /**
+     * Execution times for each measure
+     */
+    public static final long HOUR_IN_MS = TimeUnit.HOURS.toMillis(1);
+    public static final long ALARM_REPEAT_MS = TimeUnit.MINUTES.toMillis(10);
+    public static final long ALARM_REPEAT_MARGIN_MS = TimeUnit.SECONDS.toMillis(30);
+    public static final long PERSIST_BENCHMARK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(30);
+    public static final long WORK_REPEAT_MS = TimeUnit.SECONDS.toMillis(10);
+    public static final long MAIN_REPEAT_MS = TimeUnit.SECONDS.toMillis(10);
+
+    /**
+     * Actions and extras
+     */
+    public static final String TEST_CASE_PACKAGE_NAME = "com.android.cts.servicekilltest";
+    public static final String TEST_APP_PACKAGE_NAME = TEST_CASE_PACKAGE_NAME + "app";
+    public static final String ACTION_START = TEST_CASE_PACKAGE_NAME + ".ACTION_START";
+    public static final String ACTION_STOP = TEST_CASE_PACKAGE_NAME + ".ACTION_STOP";
+    public static final String ACTION_RESULT = TEST_CASE_PACKAGE_NAME + ".ACTION_RESULT";
+    private static final String ACTION_ALARM = TEST_CASE_PACKAGE_NAME + ".ACTION_ALARM";
+    public static final String EXTRA_TEST_ID = "test_id";
+
+
+    public static final String APP = "CTSServiceKillTest";
+    public static final String TAG = "ServiceKillTest";
+
+    public static String NOTIFICATION_CHANNEL_FOREGROUND = "foreground";
+
+    private PowerManager.WakeLock mWakeLock;
+    private Handler mHandler;
+    private ScheduledExecutorService mScheduledExecutor;
+    private ExecutorService mExecutor;
+
+
+    private Benchmark mCurrentBenchmark;
+
+    private boolean mStarted = false;
+
+    private ScheduledFuture<?> mScheduledFuture;
+
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent != null && ACTION_ALARM.equals(intent.getAction())) {
+                logDebug("Alarm");
+                mCurrentBenchmark.addEvent(Benchmark.Measure.ALARM, SystemClock.elapsedRealtime());
+                scheduleAlarm();
+                saveBenchmarkIfRequired(mCurrentBenchmark);
+            }
+
+        }
+    };
+
+    private Runnable mMainRunnable = new Runnable() {
+        @Override
+        public void run() {
+            logDebug("Main");
+            mCurrentBenchmark.addEvent(Benchmark.Measure.MAIN, SystemClock.elapsedRealtime());
+            mHandler.postDelayed(this, MAIN_REPEAT_MS);
+            saveBenchmarkIfRequired(mCurrentBenchmark);
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        logDebug("onCreate()");
+        PowerManager powerManager = getSystemService(PowerManager.class);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, APP + "::" + TAG);
+        mWakeLock.acquire();
+        mExecutor = Executors.newSingleThreadExecutor();
+        mHandler = new Handler();
+        startForeground();
+        loadBenchmarkAsync(benchmark -> {
+            logDebug("Loading Benchmark " + benchmark);
+
+            if (benchmark == null || !benchmark.isRunning()) {
+                mCurrentBenchmark = new Benchmark();
+                logDebug("New Benchmark " + mCurrentBenchmark);
+            } else {
+                mCurrentBenchmark = benchmark;
+            }
+            startBenchmark();
+        });
+
+    }
+
+    private String getTestId(Intent i) {
+        return i == null ? null : i.getStringExtra(EXTRA_TEST_ID);
+    }
+
+    private boolean isAction(Intent i, String action) {
+        if (i != null && action != null) {
+            return action.equals(i.getAction());
+        }
+        return i == null && action == null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        startForeground();
+
+        final String id = getTestId(intent);
+        if (id != null) {
+            logDebug("onStartCommand TEST " + id + " action " + intent.getAction());
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mCurrentBenchmark != null) {
+                        if (isAction(intent, ACTION_START)) {
+                            logDebug("Starting TEST " + id);
+                            mCurrentBenchmark.startTest(id);
+                        } else if (isAction(intent, ACTION_STOP)) {
+                            logDebug("Stopping TEST " + id);
+                            sendResult(id, mCurrentBenchmark.finishTest(id));
+
+                            if (!mCurrentBenchmark.isRunning()) {
+                                logDebug("No TEST running, stopping benchmark");
+                                saveBenchmarkAsync(mCurrentBenchmark, () -> {
+                                            logDebug("No TEST running, stopping service");
+                                            stopSelf();
+                                        });
+                            }
+                        } else if (isAction(intent, ACTION_RESULT)) {
+                            logDebug("Getting results for TEST " + id);
+                            sendResult(id, mCurrentBenchmark.getAllResults(id));
+                        }
+                    } else {
+                        mHandler.postDelayed(this, 1000);
+                    }
+                }
+            });
+        } else {
+            Log.w(TAG, "Ignoring start request without test ID");
+        }
+        return START_STICKY;
+    }
+
+    private void sendResult(String testId, Map<Benchmark.Measure, Float> result) {
+        logDebug("Sending result");
+        Intent intent = new Intent(ACTION_RESULT);
+        intent.putExtra(EXTRA_TEST_ID, testId);
+        intent.setPackage(TEST_CASE_PACKAGE_NAME);
+        if (result != null) {
+            for (Benchmark.Measure measure : result.keySet()) {
+                intent.putExtra(measure.name(), result.get(measure));
+                logDebug("Result " + measure.name() + "=" + result.get(measure));
+            }
+        }
+        sendBroadcast(intent);
+    }
+
+    private void startForeground() {
+        NotificationManager notificationManager = getSystemService(NotificationManager.class);
+
+        NotificationChannel notificationChannel =
+                new NotificationChannel(NOTIFICATION_CHANNEL_FOREGROUND, TAG,
+                        NotificationManager.IMPORTANCE_LOW);
+        notificationManager.createNotificationChannel(notificationChannel);
+        startForeground(12, new Notification.Builder(this, NOTIFICATION_CHANNEL_FOREGROUND)
+                .setSmallIcon(android.R.drawable.ic_media_play)
+                .setChannelId(NOTIFICATION_CHANNEL_FOREGROUND)
+                .setContentText("Foreground Service Kill Test Running").build());
+    }
+
+    private PendingIntent getAlarmIntent() {
+        Intent i = new Intent(ACTION_ALARM);
+        i.setPackage(getPackageName());
+        return PendingIntent.getBroadcast(this, 0, i,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    private void scheduleAlarm() {
+        long alarmTime = SystemClock.elapsedRealtime() + ALARM_REPEAT_MS;
+        logDebug(String.format("Scheduling alarm at %d", alarmTime));
+        AlarmManager alarmManager = getSystemService(AlarmManager.class);
+        if (alarmManager.canScheduleExactAlarms()) {
+            alarmManager.setExactAndAllowWhileIdle(
+                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    alarmTime,
+                    getAlarmIntent()
+            );
+        } else {
+            Log.w(TAG, "Cannot schedule exact alarms");
+        }
+    }
+
+    private void cancelAlarm() {
+        logDebug("Cancel alarm ");
+        AlarmManager alarmManager = getSystemService(AlarmManager.class);
+        alarmManager.cancel(getAlarmIntent());
+    }
+
+
+    private void startBenchmark() {
+        mHandler.post(mMainRunnable);
+        scheduleAlarm();
+        registerReceiver(mAlarmReceiver, new IntentFilter(ACTION_ALARM));
+        mScheduledExecutor = Executors.newScheduledThreadPool(1);
+        mScheduledFuture = mScheduledExecutor.scheduleAtFixedRate(() -> {
+            try {
+                logDebug("Work");
+                long now = SystemClock.elapsedRealtime();
+                mHandler.post(() -> {
+                    mCurrentBenchmark.addEvent(Benchmark.Measure.WORK, now);
+                    saveBenchmarkIfRequired(mCurrentBenchmark);
+                });
+            } catch (Throwable t) {
+                Log.e(TAG, "Error in scheduled execution ", t);
+            }
+        }, WORK_REPEAT_MS, WORK_REPEAT_MS, TimeUnit.MILLISECONDS);
+
+        mStarted = true;
+    }
+
+    private void stopBenchmark() {
+        try {
+            unregisterReceiver(mAlarmReceiver);
+        } catch (Exception e) {
+            Log.w(TAG, "Receiver not registered", e);
+        }
+        cancelAlarm();
+        mHandler.removeCallbacks(mMainRunnable);
+        if (mScheduledExecutor != null) {
+            mScheduledExecutor.shutdown();
+            if (mScheduledFuture.isDone()) {
+                try {
+                    mScheduledFuture.get();
+                } catch (CancellationException e) {
+                } catch (Exception e) {
+                    Log.e(TAG, "Error in scheduled execution ", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        logDebug("onDestroy()");
+        if (mStarted) {
+            stopBenchmark();
+        }
+        mExecutor.shutdown();
+        mWakeLock.release();
+    }
+
+    private static Intent getServiceIntent(Context context) {
+        return new Intent(context, ServiceKillTestService.class);
+    }
+
+    private void loadBenchmarkAsync(Consumer<Benchmark> consumer) {
+        mExecutor.execute(() -> {
+            final Benchmark benchmark = loadBenchmark();
+            mHandler.post(() -> {
+                consumer.accept(benchmark);
+            });
+        });
+    }
+
+    public interface Consumer<T> {
+        void accept(T consumable);
+    }
+
+    private synchronized Benchmark loadBenchmark() {
+        ObjectInputStream in = null;
+        try {
+            in = new ObjectInputStream(openFileInput(TAG));
+            return (Benchmark) in.readObject();
+        } catch (FileNotFoundException e) {
+            logDebug("File not found");
+        } catch (ClassNotFoundException e) {
+            Log.e(TAG, "Class no found", e);
+        } catch (IOException e) {
+            Log.e(TAG, "I/O error", e);
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Cannot close benchmark file", e);
+            }
+        }
+        return null;
+    }
+
+
+    private synchronized void clearBenchmark() {
+        deleteFile(TAG);
+    }
+
+    private void saveBenchmarkIfRequired(Benchmark benchmark) {
+        if (SystemClock.elapsedRealtime() - benchmark.lastPersisted > PERSIST_BENCHMARK_TIMEOUT_MS) {
+            saveBenchmarkAsync(benchmark, null);
+        }
+    }
+
+
+    private void saveBenchmarkAsync(Benchmark benchmark, Runnable runnable) {
+        final byte[] bytes = benchmark.toBytes();
+
+        mExecutor.execute(() -> {
+            save(bytes);
+            mHandler.post(() -> {
+                logDebug("SAVED " + benchmark);
+                benchmark.setPersisted();
+                if (runnable != null) {
+                    runnable.run();
+                }
+            });
+        });
+    }
+
+    private synchronized void save(byte[] bytes) {
+        FileOutputStream fileOut = null;
+        try {
+            fileOut = openFileOutput(TAG, MODE_PRIVATE);
+            fileOut.write(bytes);
+            fileOut.flush();
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "File not found", e);
+        } catch (IOException e) {
+            Log.e(TAG, "I/O error", e);
+        } finally {
+            try {
+                if (fileOut != null) {
+                    fileOut.close();
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Cannot close benchmark file", e);
+            }
+        }
+    }
+
+    private static class Range {
+        public final long from;
+        public final long to;
+
+        public Range(long from, long to) {
+            if (to < from || to < 0 || from < 0) {
+                throw new IllegalArgumentException("FROM: " + from + " before TO: " + to);
+            }
+            this.from = from;
+            this.to = to;
+        }
+
+        public boolean inRange(long timestamp) {
+            return timestamp >= from && timestamp <= to;
+        }
+
+        public long getDuration() {
+            return to - from + 1;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[%d-%d]", from, to);
+        }
+    }
+
+    public static class Benchmark implements Serializable {
+
+        public enum Measure {
+            TOTAL,
+            WORK(WORK_REPEAT_MS),
+            MAIN(MAIN_REPEAT_MS),
+            ALARM(ALARM_REPEAT_MS + ALARM_REPEAT_MARGIN_MS);
+
+            private final long interval;
+
+            Measure() {
+                interval = -1;
+            }
+
+            Measure(long interval) {
+                this.interval = interval;
+            }
+        }
+
+        private static final long serialVersionUID = -2939643983335136263L;
+
+        private long lastPersisted = -1;
+
+        private long startTime;
+
+        public Benchmark() {
+            startTime = SystemClock.elapsedRealtime();
+        }
+
+        private final Map<Measure, List<Long>> eventMap = new HashMap<>();
+        private final Map<String, Long> tests = new HashMap<>();
+
+        public boolean isRunning() {
+            return tests.size() > 0;
+        }
+
+        public void startTest(String id) {
+            if (!tests.containsKey(id)) {
+                tests.put(id, SystemClock.elapsedRealtime());
+            }
+        }
+
+        public Map<Measure, Float> finishTest(String id) {
+            if (tests.containsKey(id)) {
+                Long startTime = tests.remove(id);
+                return getAllResults(new Range(startTime, SystemClock.elapsedRealtime()));
+            }
+            Log.w(TAG, "Missing results for test " + id);
+            return null;
+        }
+
+        public Map<Measure, Float> getAllResults(String id) {
+            if (tests.containsKey(id)) {
+                Long startTime = tests.get(id);
+                return getAllResults(new Range(startTime, SystemClock.elapsedRealtime()));
+            }
+            return null;
+        }
+
+        private Map<Measure, Float> getAllResults(Range range) {
+            Map<Measure, Float> results = new HashMap<>();
+            for (Measure measure : Measure.values()) {
+                results.put(measure, getResult(measure, range));
+            }
+            return results;
+        }
+
+        public long getLastPersisted() {
+            return lastPersisted;
+        }
+
+        public void setPersisted() {
+            this.lastPersisted = SystemClock.elapsedRealtime();
+        }
+
+        private List<Long> filter(List<Long> source, Range range) {
+            List<Long> result = new ArrayList<>(source);
+
+            if (range == null) {
+                return source;
+            }
+
+            Iterator<Long> i = result.iterator();
+            while (i.hasNext()) {
+                if (!range.inRange(i.next())) {
+                    i.remove();
+                }
+            }
+            return result;
+        }
+
+        public void addEvent(Measure measure, long timestamp) {
+            List<Long> events = getEvents(measure);
+            events.add(timestamp);
+            if (!eventMap.containsKey(measure)) {
+                eventMap.put(measure, events);
+            }
+        }
+
+        public void addEvent(Measure measure) {
+            addEvent(measure, SystemClock.elapsedRealtime());
+        }
+
+        private List<Long> getEvents(Measure measure) {
+            List<Long> events = eventMap.get(measure);
+            return events == null ? new ArrayList<>() : events;
+        }
+
+        public float getResult(Measure measure) {
+            return getResult(measure, null);
+        }
+
+        public float getResult(Measure measure, Range range) {
+
+            if (measure == Measure.TOTAL) {
+                return (getResult(Measure.WORK, range) + (2 * getResult(Measure.ALARM, range)) +
+                        getResult(Measure.MAIN, range)) / 4f;
+            }
+
+            List<Long> events = filter(getEvents(measure), range);
+
+            return Math
+                    .min(1, events.size() / (getDuration(range) / (float) measure.interval));
+        }
+
+        private long getDuration() {
+            return SystemClock.elapsedRealtime() - startTime;
+        }
+
+        private long getDuration(Range range) {
+            if (range == null) {
+                return getDuration();
+            }
+            return range.getDuration();
+        }
+
+        private byte[] toBytes() {
+            try {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                ObjectOutputStream out = new ObjectOutputStream(bos);
+                out.writeObject(this);
+                out.flush();
+                out.close();
+                bos.close();
+                return bos.toByteArray();
+            } catch (IOException e) {
+                Log.e(TAG, "Cannot serialize benchmark: " + this, e);
+                return null;
+            }
+        }
+
+        @SuppressLint("DefaultLocale")
+        @Override
+        public String toString() {
+            return toReportString().replaceAll("\n", "");
+        }
+
+        @SuppressLint("DefaultLocale")
+        public String toReportString() {
+            return String
+                    .format("Benchmark TIME: %tT TESTS: %d \n\nMAIN:\n%.1f%% %d \n\nWORK:\n%.1f%%" +
+                                    " %d \n\nALARM:\n%.1f%% %d \n\n%s",
+                            getDuration() - TimeZone.getDefault().getOffset(0),
+                            tests.size(), getResult(Measure.MAIN) * 100,
+                            getEvents(Measure.MAIN).size(), getResult(Measure.WORK) * 100,
+                            getEvents(Measure.WORK).size(), getResult(Measure.ALARM) * 100,
+                            getEvents(Measure.ALARM).size(), isRunning() ? "RUNNING..." :
+                                    getResult(Measure.TOTAL) >= 0.9f ? "TEST PASSED!" :
+                                            "TEST FAILED!");
+        }
+    }
+
+    public static void logDebug(String s) {
+        if (DEBUG) {
+            Log.d(TAG, s);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/ServiceKillTest/src/com/android/cts/servicekilltest/ServiceKillTests.java b/tests/ServiceKillTest/src/com/android/cts/servicekilltest/ServiceKillTests.java
new file mode 100644
index 0000000..91922dd
--- /dev/null
+++ b/tests/ServiceKillTest/src/com/android/cts/servicekilltest/ServiceKillTests.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2021 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.servicekilltest;
+
+import static com.android.cts.servicekilltestapp.ServiceKillTestService.Benchmark.Measure.*;
+import static com.android.cts.servicekilltestapp.ServiceKillTestService.logDebug;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.runner.RunWith;
+import org.junit.After;
+
+import com.android.cts.servicekilltestapp.ServiceKillTestService;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.Before;
+
+import android.support.test.uiautomator.UiDevice;
+
+@AppModeFull
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ServiceKillTests {
+
+    private static final boolean REQUIRE_UNPLUGGED = true;
+    private static final float DEFAULT_THRESHOLD = 0.9f;
+    private static final long DEFAULT_DURATION = TimeUnit.HOURS.toMinutes(3);
+
+    @Before
+    public void setUp() throws Exception {
+        if (REQUIRE_UNPLUGGED) {
+            setChargingAndCheck(false);
+        } else {
+            setCharging(false);
+        }
+    }
+
+    @Test
+    public void testAll() throws Exception {
+        test("testAll", DEFAULT_DURATION, TOTAL);
+    }
+
+    /**
+     * Examples on how you can configure the tests for different purposes
+     */
+    @Test
+    @Ignore
+    public void testAlarmStrict() throws Exception {
+        test("testAlarm", DEFAULT_DURATION, ALARM, 0.99f);
+    }
+
+    @Test
+    @Ignore
+    public void testMainLong() throws Exception {
+        test("testMain", DEFAULT_DURATION * 2, MAIN);
+    }
+
+    @Test
+    @Ignore
+    public void testWork() throws Exception {
+        test("testWork", DEFAULT_DURATION, WORK);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        setCharging(true);
+    }
+
+
+    private void test(String testName, long runTimeMinutes,
+            ServiceKillTestService.Benchmark.Measure measure) throws Exception {
+        test(testName, runTimeMinutes, measure, DEFAULT_THRESHOLD);
+    }
+
+    private void test(String testName, long runTimeMinutes,
+            ServiceKillTestService.Benchmark.Measure measure, float passThreshold)
+            throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+        final long startTime = SystemClock.elapsedRealtime();
+
+        final String testId = generateTestId(testName);
+
+        logDebug("Testing " + testId + " " + runTimeMinutes + "min " + measure + " th " +
+                passThreshold);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent != null) {
+                    logDebug("onReceive testId " +
+                            intent.getStringExtra(ServiceKillTestService.EXTRA_TEST_ID) + " " +
+                            intent.getAction());
+                    if (intent.hasExtra(ServiceKillTestService.EXTRA_TEST_ID) && testId.equals(
+                            intent.getStringExtra(ServiceKillTestService.EXTRA_TEST_ID))) {
+                        float result = intent.getFloatExtra(measure.name(), 0);
+                        logDebug("result " + result);
+                        assertTrue("Test '" + testName + "' for '" + measure + "' did not reach " +
+                                passThreshold, result >= passThreshold);
+                        latch.countDown();
+                    }
+                }
+            }
+        };
+        context.registerReceiver(receiver, new IntentFilter(ServiceKillTestService.ACTION_RESULT));
+
+        startTest(context, testId);
+
+        try {
+            logDebug("waiting " + runTimeMinutes + "min ");
+            while (SystemClock.elapsedRealtime() - startTime <
+                    TimeUnit.MINUTES.toMillis(runTimeMinutes)) {
+                latch.await(5, TimeUnit.MINUTES);
+                if (REQUIRE_UNPLUGGED) {
+                    setChargingAndCheck(false);
+                }
+            }
+        } catch (InterruptedException e) {
+            stopTest(context, testId);
+            throw e;
+        }
+
+        stopTest(context, testId);
+
+        logDebug("Waiting max 1 more minutes");
+        latch.await(1, TimeUnit.MINUTES);
+
+        if (latch.getCount() > 0) {
+            fail("Did not get test result from ServiceKillTestService");
+        }
+
+        context.unregisterReceiver(receiver);
+    }
+
+    private Intent getServiceIntent(String action, String testId) {
+        Intent i = new Intent(action);
+        i.putExtra(ServiceKillTestService.EXTRA_TEST_ID, testId);
+        i.setComponent(new ComponentName(ServiceKillTestService.TEST_APP_PACKAGE_NAME,
+                ServiceKillTestService.class.getName()));
+        return i;
+    }
+
+    private void startService(Context context, String testId, String action) {
+        logDebug("service " + testId + " " + action);
+        context.startForegroundService(getServiceIntent(action, testId));
+    }
+
+    private void startTest(Context context, String testId) {
+        startService(context, testId, ServiceKillTestService.ACTION_START);
+    }
+
+    private void stopTest(Context context, String testId) {
+        startService(context, testId, ServiceKillTestService.ACTION_STOP);
+    }
+
+    private String generateTestId(String testName) {
+        return testName + ":" + this.hashCode() + ":" + SystemClock.elapsedRealtime();
+    }
+
+
+    private void setChargingAndCheck(final boolean charging) throws Exception {
+        final BatteryManager bm =
+                InstrumentationRegistry.getTargetContext().getSystemService(BatteryManager.class);
+        logDebug("isCharging " + bm.isCharging() + " target " + charging);
+
+        if (charging != bm.isCharging()) {
+            setCharging(charging);
+            PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(20),
+                    () -> charging == bm.isCharging(), "Setting charging timeout");
+        }
+    }
+
+    private void setCharging(final boolean charging) throws Exception {
+        if (charging) {
+            logDebug("Setting CHARGING ");
+            executeAndLog("dumpsys battery reset");
+        } else {
+            logDebug("Setting UNPLUG ");
+            executeAndLog("dumpsys battery unplug");
+            executeAndLog("dumpsys battery set status "
+                    + BatteryManager.BATTERY_STATUS_DISCHARGING);
+        }
+    }
+
+    private String executeAndLog(String command) throws IOException {
+        final String output =
+                UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+                        .executeShellCommand(command).trim();
+        logDebug("command: '" + command + "', output: '" + output + "'");
+        return output;
+    }
+
+}
\ No newline at end of file
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
index 4fc8ca2..e9fb255 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
@@ -151,6 +151,9 @@
                                 return editorInfo != null ? editorInfo.privateImeOptions : null;
                             })));
 
+            assertTrue(RunOnMainUtils.getOnMain(sInstrumentation,
+                    inputMethod::getCurrentInputStarted));
+
             final InputMethod.AccessibilityInputConnection connection =
                     inputMethod.getCurrentInputConnection();
             assertNotNull(connection);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityInputConnectionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityInputConnectionTest.java
new file mode 100644
index 0000000..ea512f3
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityInputConnectionTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts;
+
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.InputMethod;
+import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
+import android.accessibilityservice.cts.utils.AsyncUtils;
+import android.accessibilityservice.cts.utils.InputConnectionSplitter;
+import android.accessibilityservice.cts.utils.NoOpInputConnection;
+import android.accessibilityservice.cts.utils.RunOnMainUtils;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.LargeTest;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests for {@link InputMethod.AccessibilityInputConnection}.
+ */
+@LargeTest
+@AppModeFull
+@RunWith(AndroidJUnit4.class)
+public final class AccessibilityInputConnectionTest {
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
+
+    private static StubImeAccessibilityService sStubImeAccessibilityService;
+
+    private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+            new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    private AtomicReference<InputConnection> mLastInputConnectionSpy = new AtomicReference<>();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
+    @BeforeClass
+    public static void oneTimeSetup() throws Exception {
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation();
+        sInstrumentation
+                .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        sStubImeAccessibilityService = enableService(StubImeAccessibilityService.class);
+    }
+
+    @AfterClass
+    public static void postTestTearDown() {
+        sStubImeAccessibilityService.disableSelfAndRemove();
+        sUiAutomation.destroy();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        final String markerValue = "Test-" + SystemClock.elapsedRealtimeNanos();
+        final CountDownLatch startInputLatch = new CountDownLatch(1);
+        sStubImeAccessibilityService.setOnStartInputCallback(((editorInfo, restarting) -> {
+            if (editorInfo != null && TextUtils.equals(markerValue, editorInfo.privateImeOptions)) {
+                startInputLatch.countDown();
+            }
+        }));
+
+        final AccessibilityEndToEndActivity activity = launchActivityAndWaitForItToBeOnscreen(
+                sInstrumentation, sUiAutomation, mActivityRule);
+
+        final LinearLayout layout = (LinearLayout) activity.findViewById(R.id.edittext).getParent();
+        sInstrumentation.runOnMainSync(() -> {
+            final EditText editText = new EditText(activity) {
+                @Override
+                public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+                    final InputConnection ic = super.onCreateInputConnection(editorInfo);
+                    // For some reasons, Mockito.spy() for real Framework classes did not work...
+                    // Use NoOpInputConnection/InputConnectionSplitter instead.
+                    final InputConnection spy = Mockito.spy(new NoOpInputConnection());
+                    mLastInputConnectionSpy.set(spy);
+                    return new InputConnectionSplitter(ic, spy);
+                }
+            };
+            editText.setPrivateImeOptions(markerValue);
+            layout.addView(editText);
+            editText.requestFocus();
+        });
+
+        // Wait until EditorInfo#privateImeOptions becomes the expected marker value.
+        assertTrue("time out waiting for input to start",
+                startInputLatch.await(AsyncUtils.DEFAULT_TIMEOUT_MS, MILLISECONDS));
+    }
+
+    private InputMethod.AccessibilityInputConnection getInputConnection() {
+        return RunOnMainUtils.getOnMain(
+                sInstrumentation,
+                () -> sStubImeAccessibilityService.getInputMethod().getCurrentInputConnection());
+    }
+
+    private InputConnection resetAndGetLastInputConnectionSpy() {
+        final InputConnection spy = mLastInputConnectionSpy.get();
+        Mockito.reset(spy);
+        return spy;
+    }
+
+    @Test
+    public void testCommitText() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.commitText("test", 1, null);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .commitText("test", 1, null);
+    }
+
+    @Test
+    public void testSetSelection() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.setSelection(1, 2);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS)).setSelection(1, 2);
+    }
+
+    @Test
+    public void testGetSurroundingText() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.getSurroundingText(1, 2, InputConnection.GET_TEXT_WITH_STYLES);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .getSurroundingText(1, 2, InputConnection.GET_TEXT_WITH_STYLES);
+    }
+
+    @Test
+    public void testDeleteSurroundingText() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.deleteSurroundingText(2, 1);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .deleteSurroundingText(2, 1);
+    }
+
+    @Test
+    public void testSendKeyEvent() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        final long eventTime = SystemClock.uptimeMillis();
+        final KeyEvent keyEvent = new KeyEvent(eventTime, eventTime,
+                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 0, 0,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
+
+        ic.sendKeyEvent(keyEvent);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .sendKeyEvent(keyEvent);
+    }
+
+    @Test
+    public void testPerformEditorAction() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
+    }
+
+    @Test
+    public void testPerformContextMenuAction() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.performContextMenuAction(android.R.id.selectAll);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .performContextMenuAction(android.R.id.selectAll);
+    }
+
+    @Test
+    public void testGetCursorCapsMode() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.getCursorCapsMode(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .getCursorCapsMode(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
+    }
+
+    @Test
+    public void testClearMetaKeyStates() {
+        final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+        final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+        ic.clearMetaKeyStates(KeyEvent.META_SHIFT_ON);
+        Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+                .clearMetaKeyStates(KeyEvent.META_SHIFT_ON);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
index 6bfc66b..77e194e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
@@ -38,6 +38,17 @@
     private CountDownLatch mSelectionChangeLatch = null;
     private int mSelectionTarget = INVALID;
 
+    @FunctionalInterface
+    public interface OnStartInputCallback {
+        void onStartInput(EditorInfo editorInfo, boolean restarting);
+    }
+
+    private OnStartInputCallback mOnStartInputCallback;
+
+    public void setOnStartInputCallback(OnStartInputCallback callback) {
+        mOnStartInputCallback = callback;
+    }
+
     public void setStartInputCountDownLatch(CountDownLatch latch) {
         mStartInputLatch = latch;
     }
@@ -77,6 +88,9 @@
                 mStartInputLatch.countDown();
                 mStartInputLatch = null;
             }
+            if (mOnStartInputCallback != null) {
+                mOnStartInputCallback.onStartInput(attribute, restarting);
+            }
         }
     }
 
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/InputConnectionSplitter.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/InputConnectionSplitter.java
new file mode 100644
index 0000000..4e45522
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/InputConnectionSplitter.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts.utils;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+/**
+ * A special version of {@link InputConnectionWrapper} so that you can forward method invocations
+ * to another {@link InputConnection}.
+ *
+ * <p>Useful for mocking/spying.</p>
+ */
+public final class InputConnectionSplitter extends InputConnectionWrapper {
+    private final InputConnection mForNotify;
+
+    public InputConnectionSplitter(InputConnection target, InputConnection forNotify) {
+        super(target, false);
+        mForNotify = forNotify;
+    }
+
+    @Override
+    public CharSequence getTextBeforeCursor(int n, int flags) {
+        mForNotify.getTextBeforeCursor(n, flags);
+        return super.getTextBeforeCursor(n, flags);
+    }
+
+    @Override
+    public CharSequence getTextAfterCursor(int n, int flags) {
+        mForNotify.getTextBeforeCursor(n, flags);
+        return super.getTextBeforeCursor(n, flags);
+    }
+
+    @Override
+    public CharSequence getSelectedText(int flags) {
+        mForNotify.getSelectedText(flags);
+        return super.getSelectedText(flags);
+    }
+
+    @Override
+    public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+        mForNotify.getSurroundingText(beforeLength, afterLength, flags);
+        return super.getSurroundingText(beforeLength, afterLength, flags);
+    }
+
+    @Override
+    public int getCursorCapsMode(int reqModes) {
+        mForNotify.getCursorCapsMode(reqModes);
+        return super.getCursorCapsMode(reqModes);
+    }
+
+    @Override
+    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+        mForNotify.getExtractedText(request, flags);
+        return super.getExtractedText(request, flags);
+    }
+
+    @Override
+    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+        mForNotify.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+        return super.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+    }
+
+    @Override
+    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+        mForNotify.deleteSurroundingText(beforeLength, afterLength);
+        return super.deleteSurroundingText(beforeLength, afterLength);
+    }
+
+    @Override
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        mForNotify.setComposingText(text, newCursorPosition);
+        return super.setComposingText(text, newCursorPosition);
+    }
+
+    @Override
+    public boolean setComposingText(CharSequence text,
+            int newCursorPosition, TextAttribute textAttribute) {
+        mForNotify.setComposingText(text, newCursorPosition, textAttribute);
+        return super.setComposingText(text, newCursorPosition, textAttribute);
+    }
+
+    @Override
+    public boolean setComposingRegion(int start, int end) {
+        mForNotify.setComposingRegion(start, end);
+        return super.setComposingRegion(start, end);
+    }
+
+    @Override
+    public boolean setComposingRegion(int start, int end, TextAttribute textAttribute) {
+        mForNotify.setComposingRegion(start, end, textAttribute);
+        return super.setComposingRegion(start, end, textAttribute);
+    }
+
+    @Override
+    public boolean finishComposingText() {
+        mForNotify.finishComposingText();
+        return super.finishComposingText();
+    }
+
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        mForNotify.commitText(text, newCursorPosition);
+        return super.commitText(text, newCursorPosition);
+    }
+
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition,
+            TextAttribute textAttribute) {
+        mForNotify.commitText(text, newCursorPosition, textAttribute);
+        return super.commitText(text, newCursorPosition, textAttribute);
+    }
+
+    @Override
+    public boolean commitCompletion(CompletionInfo text) {
+        mForNotify.commitCompletion(text);
+        return super.commitCompletion(text);
+    }
+
+    @Override
+    public boolean commitCorrection(CorrectionInfo correctionInfo) {
+        mForNotify.commitCorrection(correctionInfo);
+        return super.commitCorrection(correctionInfo);
+    }
+
+    @Override
+    public boolean setSelection(int start, int end) {
+        mForNotify.setSelection(start, end);
+        return super.setSelection(start, end);
+    }
+
+    @Override
+    public boolean performEditorAction(int editorAction) {
+        mForNotify.performEditorAction(editorAction);
+        return super.performEditorAction(editorAction);
+    }
+
+    @Override
+    public boolean performContextMenuAction(int id) {
+        mForNotify.performContextMenuAction(id);
+        return super.performContextMenuAction(id);
+    }
+
+    @Override
+    public boolean beginBatchEdit() {
+        mForNotify.beginBatchEdit();
+        return super.beginBatchEdit();
+    }
+
+    @Override
+    public boolean endBatchEdit() {
+        mForNotify.endBatchEdit();
+        return super.endBatchEdit();
+    }
+
+    @Override
+    public boolean sendKeyEvent(KeyEvent event) {
+        mForNotify.sendKeyEvent(event);
+        return super.sendKeyEvent(event);
+
+    }
+
+    @Override
+    public boolean clearMetaKeyStates(int states) {
+        mForNotify.clearMetaKeyStates(states);
+        return super.clearMetaKeyStates(states);
+    }
+
+    @Override
+    public boolean reportFullscreenMode(boolean enabled) {
+        mForNotify.reportFullscreenMode(enabled);
+        return super.reportFullscreenMode(enabled);
+    }
+
+    @Override
+    public boolean performSpellCheck() {
+        mForNotify.performSpellCheck();
+        return super.performSpellCheck();
+    }
+
+    @Override
+    public boolean performPrivateCommand(String action, Bundle data) {
+        mForNotify.performPrivateCommand(action, data);
+        return super.performPrivateCommand(action, data);
+    }
+
+    @Override
+    public boolean requestCursorUpdates(int cursorUpdateMode) {
+        mForNotify.requestCursorUpdates(cursorUpdateMode);
+        return super.requestCursorUpdates(cursorUpdateMode);
+    }
+
+    @Override
+    public Handler getHandler() {
+        mForNotify.getHandler();
+        return super.getHandler();
+    }
+
+    @Override
+    public void closeConnection() {
+        mForNotify.closeConnection();
+        super.closeConnection();
+    }
+
+    @Override
+    public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+        mForNotify.commitContent(inputContentInfo, flags, opts);
+        return super.commitContent(inputContentInfo, flags, opts);
+    }
+
+    @Override
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
+        mForNotify.setImeConsumesInput(imeConsumesInput);
+        return super.setImeConsumesInput(imeConsumesInput);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/NoOpInputConnection.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/NoOpInputConnection.java
new file mode 100644
index 0000000..c79827c
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/NoOpInputConnection.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts.utils;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+
+/**
+ * A no-op implementation of {@link InputConnection}.
+ *
+ * <p>Useful for mocking/spying.</p>
+ */
+public class NoOpInputConnection implements InputConnection {
+
+    @Override
+    public CharSequence getTextBeforeCursor(int n, int flags) {
+        return null;
+    }
+
+    @Override
+    public CharSequence getTextAfterCursor(int n, int flags) {
+        return null;
+    }
+
+    @Override
+    public CharSequence getSelectedText(int flags) {
+        return null;
+    }
+
+    @Override
+    public int getCursorCapsMode(int reqModes) {
+        return 0;
+    }
+
+    @Override
+    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+        return null;
+    }
+
+    @Override
+    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+        return false;
+    }
+
+    @Override
+    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+        return false;
+    }
+
+    @Override
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        return false;
+    }
+
+    @Override
+    public boolean setComposingRegion(int start, int end) {
+        return false;
+    }
+
+    @Override
+    public boolean finishComposingText() {
+        return false;
+    }
+
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        return false;
+    }
+
+    @Override
+    public boolean commitCompletion(CompletionInfo text) {
+        return false;
+    }
+
+    @Override
+    public boolean commitCorrection(CorrectionInfo correctionInfo) {
+        return false;
+    }
+
+    @Override
+    public boolean setSelection(int start, int end) {
+        return false;
+    }
+
+    @Override
+    public boolean performEditorAction(int editorAction) {
+        return false;
+    }
+
+    @Override
+    public boolean performContextMenuAction(int id) {
+        return false;
+    }
+
+    @Override
+    public boolean beginBatchEdit() {
+        return false;
+    }
+
+    @Override
+    public boolean endBatchEdit() {
+        return false;
+    }
+
+    @Override
+    public boolean sendKeyEvent(KeyEvent event) {
+        return false;
+    }
+
+    @Override
+    public boolean clearMetaKeyStates(int states) {
+        return false;
+    }
+
+    @Override
+    public boolean reportFullscreenMode(boolean enabled) {
+        return false;
+    }
+
+    @Override
+    public boolean performPrivateCommand(String action, Bundle data) {
+        return false;
+    }
+
+    @Override
+    public boolean requestCursorUpdates(int cursorUpdateMode) {
+        return false;
+    }
+
+    @Override
+    public boolean requestCursorUpdates(int cursorUpdateMode, int cursorUpdateFilter) {
+        return false;
+    }
+
+    @Override
+    public Handler getHandler() {
+        return null;
+    }
+
+    @Override
+    public void closeConnection() {
+    }
+
+    @Override
+    public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+        return false;
+    }
+}
diff --git a/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl b/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl
index 25a202e..fdbc84f 100644
--- a/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl
+++ b/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl
@@ -24,6 +24,8 @@
     List<String> globalGet(in String packageName, in String databaseName, in String namespace,
         in String id);
 
+    List<String> globalGetSchema(String packageName, String databaseName);
+
     boolean indexGloballySearchableDocument(in String databaseName, in String namespace,
         in String id, in List<Bundle> permissionBundles);
 
diff --git a/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java b/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java
index 38a52c4..4bc004d 100644
--- a/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java
+++ b/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java
@@ -21,9 +21,11 @@
 import android.app.Service;
 import android.app.appsearch.AppSearchBatchResult;
 import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.AppSearchSessionShim;
 import android.app.appsearch.GenericDocument;
 import android.app.appsearch.GetByDocumentIdRequest;
+import android.app.appsearch.GetSchemaResponse;
 import android.app.appsearch.GlobalSearchSessionShim;
 import android.app.appsearch.PutDocumentsRequest;
 import android.app.appsearch.SearchResultsShim;
@@ -119,6 +121,24 @@
             }
         }
 
+        public List<String> globalGetSchema(String packageName, String databaseName) {
+            try {
+                GetSchemaResponse response =
+                        mGlobalSearchSessionShim.getSchema(packageName, databaseName).get();
+                if (response == null || response.getSchemas().isEmpty()) {
+                    return null;
+                }
+                List<String> schemas = new ArrayList(response.getSchemas().size());
+                for (AppSearchSchema schema : response.getSchemas()) {
+                    schemas.add(schema.toString());
+                }
+                return schemas;
+            } catch (Exception e) {
+                Log.e(TAG, "Error retrieving global schema.", e);
+                return null;
+            }
+        }
+
         @Override
         public boolean indexGloballySearchableDocument(
                 String databaseName, String namespace, String id, List<Bundle> permissionBundles) {
diff --git a/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
index 4ff0bcc..d558ae0 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
@@ -29,6 +29,7 @@
 import android.app.appsearch.AppSearchSessionShim;
 import android.app.appsearch.GenericDocument;
 import android.app.appsearch.GetByDocumentIdRequest;
+import android.app.appsearch.GetSchemaResponse;
 import android.app.appsearch.GlobalSearchSessionShim;
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.PutDocumentsRequest;
@@ -684,6 +685,147 @@
     }
 
     @Test
+    public void testGlobalGetSchema_packageAccess_defaultAccess() throws Exception {
+        // 1. Create a schema in the test with default (no) access.
+        mDb.setSchema(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .build())
+                .get();
+
+        // 2. Neither PKG_A nor PKG_B should be able to retrieve the schema.
+        List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+        assertThat(schemaStrings).isNull();
+
+        schemaStrings = getSchemaAsPackage(PKG_B);
+        assertThat(schemaStrings).isNull();
+    }
+
+    @Test
+    public void testGlobalGetSchema_packageAccess_singleAccess() throws Exception {
+        // 1. Create a schema in the test with access granted to PKG_A, but not PKG_B.
+        mDb.setSchema(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .setSchemaTypeVisibilityForPackage(
+                                        AppSearchEmail.SCHEMA_TYPE,
+                                        /*visible=*/ true,
+                                        new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+                                .build())
+                .get();
+
+        // 2. Only PKG_A should be able to retrieve the schema.
+        List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+        assertThat(schemaStrings).containsExactly(AppSearchEmail.SCHEMA.toString());
+
+        schemaStrings = getSchemaAsPackage(PKG_B);
+        assertThat(schemaStrings).isNull();
+    }
+
+    @Test
+    public void testGlobalGetSchema_packageAccess_multiAccess() throws Exception {
+        // 1. Create a schema in the test with access granted to PKG_A and PKG_B.
+        mDb.setSchema(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .setSchemaTypeVisibilityForPackage(
+                                        AppSearchEmail.SCHEMA_TYPE,
+                                        /*visible=*/ true,
+                                        new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+                                .setSchemaTypeVisibilityForPackage(
+                                        AppSearchEmail.SCHEMA_TYPE,
+                                        /*visible=*/ true,
+                                        new PackageIdentifier(PKG_B, PKG_B_CERT_SHA256))
+                                .build())
+                .get();
+
+        // 2. Both packages should be able to retrieve the schema.
+        List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+        assertThat(schemaStrings).containsExactly(AppSearchEmail.SCHEMA.toString());
+
+        schemaStrings = getSchemaAsPackage(PKG_B);
+        assertThat(schemaStrings).containsExactly(AppSearchEmail.SCHEMA.toString());
+    }
+
+    @Test
+    public void testGlobalGetSchema_packageAccess_revokeAccess() throws Exception {
+        // 1. Create a schema in the test with access granted to PKG_A.
+        mDb.setSchema(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .setSchemaTypeVisibilityForPackage(
+                                        AppSearchEmail.SCHEMA_TYPE,
+                                        /*visible=*/ true,
+                                        new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+                                .build())
+                .get();
+
+        // 2. Now revoke that access.
+        mDb.setSchema(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .setSchemaTypeVisibilityForPackage(
+                                        AppSearchEmail.SCHEMA_TYPE,
+                                        /*visible=*/ false,
+                                        new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+                                .build())
+                .get();
+
+        // 3. PKG_A should NOT be able to retrieve the schema.
+        List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+        assertThat(schemaStrings).isNull();
+    }
+
+    @Test
+    public void testGlobalGetSchema_globalAccess_singleAccess() throws Exception {
+        // 1. Index documents for PKG_A and PKG_B. This will set the schema for each with the
+        // corresponding access set.
+        indexGloballySearchableDocument(PKG_A, DB_NAME, NAMESPACE_NAME, "id1");
+        indexNotGloballySearchableDocument(PKG_B, DB_NAME, NAMESPACE_NAME, "id1");
+
+        SystemUtil.runWithShellPermissionIdentity(
+                () -> {
+                    mGlobalSearchSession =
+                            GlobalSearchSessionShimImpl.createGlobalSearchSessionAsync(
+                                mContext).get();
+
+                    // 2. The schema for PKG_A should be retrievable, but PKG_B should not be.
+                    GetSchemaResponse response =
+                            mGlobalSearchSession.getSchema(PKG_A, DB_NAME).get();
+                    assertThat(response.getSchemas()).hasSize(1);
+
+                    response = mGlobalSearchSession.getSchema(PKG_B, DB_NAME).get();
+                    assertThat(response.getSchemas()).isEmpty();
+                },
+                READ_GLOBAL_APP_SEARCH_DATA);
+    }
+
+    @Test
+    public void testGlobalGetSchema_globalAccess_multiAccess() throws Exception {
+        // 1. Index documents for PKG_A and PKG_B. This will set the schema for each with the
+        // corresponding access set.
+        indexGloballySearchableDocument(PKG_A, DB_NAME, NAMESPACE_NAME, "id1");
+        indexGloballySearchableDocument(PKG_B, DB_NAME, NAMESPACE_NAME, "id1");
+
+        SystemUtil.runWithShellPermissionIdentity(
+                () -> {
+                    mGlobalSearchSession =
+                            GlobalSearchSessionShimImpl.createGlobalSearchSessionAsync(
+                                mContext).get();
+
+                    // 2. The schema for both PKG_A and PKG_B should be retrievable.
+                    GetSchemaResponse response =
+                            mGlobalSearchSession.getSchema(PKG_A, DB_NAME).get();
+                    assertThat(response.getSchemas()).hasSize(1);
+
+                    response = mGlobalSearchSession.getSchema(PKG_B, DB_NAME).get();
+                    assertThat(response.getSchemas()).hasSize(1);
+                },
+                READ_GLOBAL_APP_SEARCH_DATA);
+    }
+
+
+    @Test
     public void testReportSystemUsage() throws Exception {
         // Insert schema
         mDb.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
@@ -882,6 +1024,17 @@
         assertThat(observer.getDocumentChanges()).isEmpty();
     }
 
+    private List<String> getSchemaAsPackage(String pkg) throws Exception {
+        GlobalSearchSessionPlatformCtsTest.TestServiceConnection serviceConnection =
+                bindToHelperService(pkg);
+        try {
+            ICommandReceiver commandReceiver = serviceConnection.getCommandReceiver();
+            return commandReceiver.globalGetSchema(mContext.getPackageName(), DB_NAME);
+        } finally {
+            serviceConnection.unbind();
+        }
+    }
+
     private void assertPackageCannotAccess(String pkg) throws Exception {
         GlobalSearchSessionPlatformCtsTest.TestServiceConnection serviceConnection =
                 bindToHelperService(pkg);
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
index 209385c..1707e39 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
@@ -17,6 +17,7 @@
 package android.devicepolicy.cts;
 
 import static android.Manifest.permission.CAMERA;
+import static android.content.pm.PackageManager.FEATURE_CAMERA;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -35,6 +36,7 @@
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.EnsureHasPermission;
 import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.annotations.RequireFeature;
 import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
@@ -52,6 +54,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+@RequireFeature(FEATURE_CAMERA)
 @RunWith(BedsteadJUnit4.class)
 public final class CameraPolicyTest {
     @ClassRule
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
index bfb00d1..f1694b8 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
@@ -28,8 +28,6 @@
 import static com.android.queryable.queries.ActivityQuery.activity;
 import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.ProvisioningException;
@@ -50,6 +48,7 @@
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.packages.Package;
 import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.nene.utils.Poll;
 import com.android.bedstead.remotedpc.RemoteDpc;
 import com.android.bedstead.testapp.TestApp;
 import com.android.bedstead.testapp.TestAppInstance;
@@ -116,8 +115,11 @@
             profile = sDevicePolicyManager.createAndProvisionManagedProfile(
                     MANAGED_PROFILE_PROVISIONING_PARAMS);
 
-            assertThat(TestApis.packages().installedForUser(UserReference.of(profile)))
-                    .contains(Package.of(roleHolderApp.packageName()));
+            UserReference userReference = UserReference.of(profile);
+            Poll.forValue(() -> TestApis.packages().installedForUser(userReference))
+                    .toMeet(packages -> packages.contains(Package.of(roleHolderApp.packageName())))
+                    .errorOnFail("Role holder package not installed on the managed profile.")
+                    .await();
         } finally {
             if (profile != null) {
                 TestApis.users().find(profile).remove();
@@ -149,8 +151,11 @@
                     /* adminExtras= */ null,
                     /* flags= */ 0);
 
-            assertThat(TestApis.packages().installedForUser(UserReference.of(managedUser)))
-                    .contains(Package.of(roleHolderApp.packageName()));
+            UserReference userReference = UserReference.of(managedUser);
+            Poll.forValue(() -> TestApis.packages().installedForUser(userReference))
+                    .toMeet(packages -> packages.contains(Package.of(roleHolderApp.packageName())))
+                    .errorOnFail("Role holder package not installed on the managed user.")
+                    .await();
         } finally {
             if (managedUser != null) {
                 TestApis.users().find(managedUser).remove();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java
index 591c078..87b0efc4 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java
@@ -25,7 +25,6 @@
 
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.Postsubmit;
 import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
 import com.android.bedstead.harrier.policies.NearbyAppStreamingPolicy;
@@ -50,14 +49,12 @@
     }
 
     @PolicyAppliesTest(policy = NearbyAppStreamingPolicy.class)
-    @Postsubmit(reason = "new test")
     public void getNearbyAppStreamingPolicy_defaultToSameManagedAccountOnly() {
         assertThat(mDevicePolicyManager.getNearbyAppStreamingPolicy())
                 .isEqualTo(DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY);
     }
 
     @PolicyAppliesTest(policy = NearbyAppStreamingPolicy.class)
-    @Postsubmit(reason = "new test")
     public void setNearbyAppStreamingPolicy_policyApplied_works() {
         int originalPolicy = mDevicePolicyManager.getNearbyAppStreamingPolicy();
 
@@ -73,7 +70,6 @@
     }
 
     @CannotSetPolicyTest(policy = NearbyAppStreamingPolicy.class)
-    @Postsubmit(reason = "new test")
     public void setNearbyAppStreamingPolicy_policyIsNotAllowedToBeSet_throwsException() {
         assertThrows(SecurityException.class, () ->
                 mDevicePolicyManager.setNearbyAppStreamingPolicy(
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java
index f50d985..c5d9ca1 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java
@@ -25,7 +25,6 @@
 
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.Postsubmit;
 import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
 import com.android.bedstead.harrier.policies.NearbyNotificationStreamingPolicy;
@@ -50,14 +49,12 @@
     }
 
     @PolicyAppliesTest(policy = NearbyNotificationStreamingPolicy.class)
-    @Postsubmit(reason = "new test")
     public void getNearbyNotificationStreamingPolicy_defaultToSameManagedAccountOnly() {
         assertThat(mDevicePolicyManager.getNearbyNotificationStreamingPolicy())
                 .isEqualTo(DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY);
     }
 
     @PolicyAppliesTest(policy = NearbyNotificationStreamingPolicy.class)
-    @Postsubmit(reason = "new test")
     public void setNearbyNotificationStreamingPolicy_policyApplied_works() {
         int originalPolicy = mDevicePolicyManager.getNearbyNotificationStreamingPolicy();
 
@@ -73,7 +70,6 @@
     }
 
     @CannotSetPolicyTest(policy = NearbyNotificationStreamingPolicy.class)
-    @Postsubmit(reason = "new test")
     public void setNearbyAppStreamingPolicy_policyIsNotAllowedToBeSet_throwsException() {
         assertThrows(SecurityException.class, () ->
                 mDevicePolicyManager.setNearbyNotificationStreamingPolicy(
diff --git a/tests/framework/base/locale/OWNERS b/tests/framework/base/locale/OWNERS
index 694a4ac..0e13191 100644
--- a/tests/framework/base/locale/OWNERS
+++ b/tests/framework/base/locale/OWNERS
@@ -1,3 +1,4 @@
 # Bug template url: https://b.corp.google.com/issues/new?component=1082762&template=1601534
 pratyushmore@google.com
-goldmanj@google.com
\ No newline at end of file
+goldmanj@google.com
+ankitavyas@google.com
diff --git a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
index 23143fc..9fdf472 100644
--- a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
+++ b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
@@ -26,6 +26,7 @@
 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_CREATION_INFO_PROVIDER_ACTION;
 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_MAIN_ACTIVITY;
 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_PACKAGE;
+import static android.localemanager.cts.util.LocaleConstants.NON_EXISTENT_PACKAGE;
 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_BROADCAST_INFO_PROVIDER_ACTION;
 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_BROADCAST_RECEIVER;
 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_CONFIG_CHANGED_INFO_PROVIDER_ACTION;
@@ -472,6 +473,36 @@
         assertEquals(LocaleList.getEmptyLocaleList(), sLocaleManager.getApplicationLocales());
     }
 
+    @Test(expected = NullPointerException.class)
+    public void testGetApplicationLocales_nullPackageName_throwsNPE() {
+        sLocaleManager.getApplicationLocales(/* appPackageName= */ null);
+        fail("Expected NullPointerException due to null package name argument.");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetApplicationLocales_invalidPackageName_throwsIllegalArgumentException() {
+        sLocaleManager.getApplicationLocales(NON_EXISTENT_PACKAGE);
+        fail("Expected IllegalArgumentException due to invalid package.");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testSetApplicationLocales_nullPackageName_throwsNPE() {
+        sLocaleManager.setApplicationLocales(/* appPackageName= */ null, DEFAULT_APP_LOCALES);
+        fail("Expected NullPointerException due to null package name argument.");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testSetApplicationLocales_nullLocales_throwsNPE() {
+        sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, /* locales= */ null);
+        fail("Expected NullPointerException due to null locales argument.");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetApplicationLocales_invalidPackageName_throwsIllegalArgumentException() {
+        sLocaleManager.setApplicationLocales(NON_EXISTENT_PACKAGE, DEFAULT_APP_LOCALES);
+        fail("Expected IllegalArgumentException due to invalid package.");
+    }
+
     /**
      * Verifies that the locales are correctly set for calling(instrumentation) app
      * by fetching locales of the app with a binder call.
diff --git a/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java b/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java
index fd94bba..dcbe09d 100644
--- a/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java
+++ b/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java
@@ -38,6 +38,8 @@
 
     public static final String INSTALLER_PACKAGE = "android.localemanager.cts.installer";
 
+    public static final String NON_EXISTENT_PACKAGE = "android.localemanager.nonexistentapp";
+
     public static final ComponentName TEST_APP_MAIN_ACTIVITY = new ComponentName(TEST_APP_PACKAGE,
             TEST_APP_PACKAGE + ".MainActivity");
 
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
index 3e49a2d..0377c7a 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
@@ -20,8 +20,8 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.UNEVEN_CONTAINERS_DEFAULT_SPLIT_RATIO;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assertValidSplit;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertNotVisible;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForFillsTask;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForVisible;
 
 import static org.junit.Assert.assertTrue;
 
@@ -91,7 +91,7 @@
             mReportedDisplayMetrics.setSize(new Size((int) (originalDisplaySize.getWidth() * 0.9),
                     originalDisplaySize.getHeight()));
             waitForFillsTask(secondaryActivity);
-            assertTrue(waitForVisible(primaryActivity, false));
+            waitAndAssertNotVisible(primaryActivity);
 
             // Return the display to its original size and verify that the activities are split
             secondaryActivity.resetBoundsChangeCounter();
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
index 2adc1b7..ff16142 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
@@ -24,7 +24,7 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.EMBEDDED_ACTIVITY_ID;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityCrossUidInSplit;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityCrossUidInSplit_expectFail;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -122,7 +122,7 @@
         // Start an activity that will attempt to embed TestActivityKnownEmbeddingCerts
         startActivityNewTask(SIGNED_EMBEDDING_ACTIVITY);
 
-        assertTrue(waitForResumed(EMBEDDED_ACTIVITY_ID));
+        waitAndAssertResumed(EMBEDDED_ACTIVITY_ID);
         TestActivityWithId embeddedActivity = getResumedActivityById(EMBEDDED_ACTIVITY_ID);
         assertNotNull(embeddedActivity);
         assertTrue(mActivityEmbeddingComponent.isActivityEmbedded(embeddedActivity));
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
index 69ac897..c1fa777 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
@@ -22,8 +22,8 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifyNotSplit;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.verifyFillsTask;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForFinishing;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertFinishing;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
 
 import static androidx.window.extensions.embedding.SplitRule.FINISH_ADJACENT;
 import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
@@ -129,7 +129,7 @@
                 .preventSplitActivities().setFinishPrimaryWithSecondary(FINISH_NEVER).start();
         // Verify the paired finish behavior
         activityPair.second.finish();
-        assertTrue(waitForResumed(activityPair.first));
+        waitAndAssertResumed(activityPair.first);
     }
 
     /**
@@ -145,7 +145,7 @@
                 .preventSplitActivities().setFinishPrimaryWithSecondary(FINISH_ADJACENT).start();
         // Verify the paired finish behavior
         activityPair.second.finish();
-        assertTrue(waitForResumed(activityPair.first));
+        waitAndAssertResumed(activityPair.first);
     }
 
     /**
@@ -161,7 +161,7 @@
                 .preventSplitActivities().setFinishPrimaryWithSecondary(FINISH_ALWAYS).start();
         // Verify the paired finish behavior
         activityPair.second.finish();
-        assertTrue(waitForFinishing(activityPair.first));
+        waitAndAssertFinishing(activityPair.first);
     }
 
     /**
@@ -194,7 +194,7 @@
                 .setFinishPrimaryWithSecondary(FINISH_ADJACENT).start();
         // Verify the paired finish behavior
         activityPair.second.finish();
-        assertTrue(waitForFinishing(activityPair.first));
+        waitAndAssertFinishing(activityPair.first);
     }
 
     /**
@@ -209,7 +209,7 @@
                 .setFinishPrimaryWithSecondary(FINISH_ALWAYS).start();
         // Verify the paired finish behavior
         activityPair.second.finish();
-        assertTrue(waitForFinishing(activityPair.first));
+        waitAndAssertFinishing(activityPair.first);
     }
 
     /**
@@ -224,7 +224,7 @@
                 .preventSplitActivities().setFinishSecondaryWithPrimary(FINISH_NEVER).start();
         // Verify the paired finish behavior
         activityPair.first.finish();
-        assertTrue(waitForResumed(activityPair.second));
+        waitAndAssertResumed(activityPair.second);
     }
 
     /**
@@ -240,7 +240,7 @@
                 .preventSplitActivities().setFinishSecondaryWithPrimary(FINISH_ADJACENT).start();
         // Verify the paired finish behavior
         activityPair.first.finish();
-        assertTrue(waitForResumed(activityPair.second));
+        waitAndAssertResumed(activityPair.second);
     }
 
     /**
@@ -256,7 +256,7 @@
                 .preventSplitActivities().setFinishSecondaryWithPrimary(FINISH_ALWAYS).start();
         // Verify the paired finish behavior
         activityPair.first.finish();
-        assertTrue(waitForFinishing(activityPair.second));
+        waitAndAssertFinishing(activityPair.second);
     }
 
     /**
@@ -289,7 +289,7 @@
                 .setFinishSecondaryWithPrimary(FINISH_ADJACENT).start();
         // Verify the paired finish behavior
         activityPair.first.finish();
-        assertTrue(waitForFinishing(activityPair.second));
+        waitAndAssertFinishing(activityPair.second);
     }
 
     /**
@@ -304,7 +304,7 @@
                 .setFinishSecondaryWithPrimary(FINISH_ALWAYS).start();
         // Verify the paired finish behavior
         activityPair.first.finish();
-        assertTrue(waitForFinishing(activityPair.second));
+        waitAndAssertFinishing(activityPair.second);
     }
 
     /**
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
index 5c2d696..38c34f1 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -22,7 +22,7 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.getPrimaryStackTopActivity;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.getSecondaryStackTopActivity;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -255,14 +255,14 @@
                 alwaysExpandedActivityId);
 
         // Verify that the always expanded activity is resumed and fills its parent
-        waitForResumed(alwaysExpandedActivityId);
+        waitAndAssertResumed(alwaysExpandedActivityId);
         Activity alwaysExpandedActivity = getResumedActivityById(alwaysExpandedActivityId);
         assertEquals(getMaximumActivityBounds(alwaysExpandedActivity),
                 getActivityBounds(alwaysExpandedActivity));
 
         // Finish the always expanded activity and verify that the split is resumed
         alwaysExpandedActivity.finish();
-        waitForResumed(Arrays.asList(primaryActivity, secondaryActivity));
+        waitAndAssertResumed(Arrays.asList(primaryActivity, secondaryActivity));
         assertValidSplit(primaryActivity, secondaryActivity, splitPairRule);
     }
 
@@ -296,14 +296,14 @@
                 alwaysExpandedActivityId);
 
         // Verify that the always expanded activity is resumed and fills its parent
-        waitForResumed(alwaysExpandedActivityId);
+        waitAndAssertResumed(alwaysExpandedActivityId);
         Activity alwaysExpandedActivity = getResumedActivityById(alwaysExpandedActivityId);
         assertEquals(getMaximumActivityBounds(alwaysExpandedActivity),
                 getActivityBounds(alwaysExpandedActivity));
 
         // Finish the always expanded activity and verify that the split is resumed
         alwaysExpandedActivity.finish();
-        waitForResumed(Arrays.asList(primaryActivity, secondaryActivity));
+        waitAndAssertResumed(Arrays.asList(primaryActivity, secondaryActivity));
         assertValidSplit(primaryActivity, secondaryActivity, splitPairRule);
     }
 
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
index 693d854..bc00111 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
@@ -19,12 +19,12 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.DEFAULT_SPLIT_RATIO;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assertValidSplit;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.verifyFillsTask;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForFinishing;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertFinishing;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertNotResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
 
 import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
@@ -83,7 +83,7 @@
 
         // Finishing the primary activity and verify that the placeholder activity is also finishing
         primaryActivity.finish();
-        assertTrue(waitForFinishing(placeholderActivity));
+        waitAndAssertFinishing(placeholderActivity);
     }
 
     /**
@@ -104,7 +104,7 @@
         // the primary activity fills the task.
         Activity primaryActivity = startActivityNewTask(TestActivityWithId.class,
                 PRIMARY_ACTIVITY_ID);
-        assertFalse(waitForResumed(PLACEHOLDER_ACTIVITY_ID));
+        waitAndAssertNotResumed(PLACEHOLDER_ACTIVITY_ID);
         verifyFillsTask(primaryActivity);
     }
 
@@ -129,7 +129,7 @@
 
         // Finish the placeholder activity and verify that the primary activity is also finishing
         placeholderActivity.finish();
-        assertTrue(waitForFinishing(primaryActivity));
+        waitAndAssertFinishing(primaryActivity);
     }
 
     /**
@@ -193,7 +193,7 @@
 
         // Verify that the placeholder activity was finished and that the primary activity now
         // fills the task.
-        assertTrue(waitForFinishing(placeholderActivity));
+        waitAndAssertFinishing(placeholderActivity);
         assertTrue(primaryActivity.waitForBoundsChange());
         verifyFillsTask(primaryActivity);
     }
@@ -222,7 +222,7 @@
         Activity primaryActivity = startActivityNewTask(TestActivityWithId.class,
                 PRIMARY_ACTIVITY_ID);
         verifyFillsTask(primaryActivity);
-        assertFalse(waitForResumed(PLACEHOLDER_ACTIVITY_ID));
+        waitAndAssertNotResumed(PLACEHOLDER_ACTIVITY_ID);
 
         // Increase display width by 10% so that the primary and placeholder activities are stacked
         final Size currentSize = mReportedDisplayMetrics.getSize();
@@ -230,7 +230,7 @@
                 currentSize.getHeight()));
 
         // Verify that the placeholder activity is launched into a split with the primary activity
-        assertTrue(waitForResumed(PLACEHOLDER_ACTIVITY_ID));
+        waitAndAssertResumed(PLACEHOLDER_ACTIVITY_ID);
         Activity placeholderActivity = getResumedActivityById(PLACEHOLDER_ACTIVITY_ID);
         assertValidSplit(primaryActivity, placeholderActivity, splitPlaceholderRule);
     }
@@ -268,7 +268,7 @@
         // Verify that the placeholder was not finished and fills the task
         assertTrue(placeholderActivity.waitForBoundsChange());
         verifyFillsTask(placeholderActivity);
-        assertTrue(waitForResumed(Arrays.asList(placeholderActivity)));
+        waitAndAssertResumed(Arrays.asList(placeholderActivity));
     }
 
     /**
@@ -350,10 +350,10 @@
         // Launch the primary activity
         startActivityNewTask(TestActivityWithId.class, primaryActivityId);
         // Get primary activity
-        assertTrue(waitForResumed(primaryActivityId));
+        waitAndAssertResumed(primaryActivityId);
         Activity primaryActivity = getResumedActivityById(primaryActivityId);
         // Get placeholder activity
-        assertTrue(waitForResumed(placeholderActivityId));
+        waitAndAssertResumed(placeholderActivityId);
         Activity placeholderActivity = getResumedActivityById(placeholderActivityId);
         // Verify they are correctly split
         assertValidSplit(primaryActivity, placeholderActivity, splitPlaceholderRule);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
index ff60f60..0e415aa 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
@@ -91,7 +91,7 @@
         startActivityFromActivity(activityLaunchingFrom, TestActivityWithId.class,
                 secondActivityId);
         // Verify both activities are in the correct lifecycle state
-        waitForResumed(secondActivityId);
+        waitAndAssertResumed(secondActivityId);
         assertFalse(isActivityResumed(activityLaunchingFrom));
         TestActivity secondActivity = getResumedActivityById(secondActivityId);
         // Verify the second activity is not split with the first
@@ -101,16 +101,17 @@
 
     public static Activity startActivityAndVerifySplit(@NonNull Activity activityLaunchingFrom,
             @NonNull Activity expectedPrimaryActivity, @NonNull Class secondActivityClass,
-            @NonNull SplitPairRule splitPairRule, @NonNull String secondActivityId,
+            @NonNull SplitPairRule splitPairRule, @NonNull String secondaryActivityId,
             int expectedCallbackCount,
             @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer) {
         // Set the expected callback count
         splitInfoConsumer.setCount(expectedCallbackCount);
 
         // Start second activity
-        startActivityFromActivity(activityLaunchingFrom, secondActivityClass, secondActivityId);
+        startActivityFromActivity(activityLaunchingFrom, secondActivityClass, secondaryActivityId);
 
-        // Get updated split info
+        // A split info callback should occur after the new activity is launched because the split
+        // states have changed.
         List<SplitInfo> activeSplitStates = null;
         try {
             activeSplitStates = splitInfoConsumer.waitAndGet();
@@ -118,15 +119,17 @@
             fail("startActivityAndVerifySplit() InterruptedException");
         }
 
-        // Get second activity from split info
-        Activity secondActivity = getSecondActivity(activeSplitStates, expectedPrimaryActivity,
-                secondActivityId);
-        assertNotNull(secondActivity);
+        // Wait for secondary activity to be resumed and verify that the newly sent split info
+        // contains the secondary activity.
+        waitAndAssertResumed(secondaryActivityId);
+        final Activity secondaryActivity = getResumedActivityById(secondaryActivityId);
+        assertTrue(splitInfoTopSplitIsCorrect(activeSplitStates, expectedPrimaryActivity,
+                secondaryActivity));
 
-        assertValidSplit(expectedPrimaryActivity, secondActivity, splitPairRule);
+        assertValidSplit(expectedPrimaryActivity, secondaryActivity, splitPairRule);
 
         // Return second activity for easy access in calling method
-        return secondActivity;
+        return secondaryActivity;
     }
 
     public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
@@ -234,7 +237,7 @@
      */
     public static void assertValidSplit(@NonNull Activity primaryActivity,
             @Nullable Activity secondaryActivity, SplitRule splitRule) {
-        waitForResumed(secondaryActivity != null
+        waitAndAssertResumed(secondaryActivity != null
                 ? Arrays.asList(primaryActivity, secondaryActivity)
                 : Collections.singletonList(primaryActivity));
 
@@ -273,7 +276,7 @@
                 .equals(getMaximumActivityBounds(activity)));
     }
 
-    public static boolean waitForResumed(
+    private static boolean waitForResumed(
             @NonNull List<Activity> activityList) {
         final long startTime = System.currentTimeMillis();
         while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
@@ -291,7 +294,7 @@
         return false;
     }
 
-    public static boolean waitForResumed(@NonNull String activityId) {
+    private static boolean waitForResumed(@NonNull String activityId) {
         final long startTime = System.currentTimeMillis();
         while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
             if (getResumedActivityById(activityId) != null) {
@@ -301,7 +304,30 @@
         return false;
     }
 
-    public static boolean waitForVisible(@NonNull Activity activity, boolean visible) {
+    private static boolean waitForResumed(@NonNull Activity activity) {
+        return waitForResumed(Arrays.asList(activity));
+    }
+
+    public static void waitAndAssertResumed(@NonNull String activityId) {
+        assertTrue("Activity with id=" + activityId + " should be resumed",
+                waitForResumed(activityId));
+    }
+
+    public static void waitAndAssertResumed(@NonNull Activity activity) {
+        assertTrue(activity + " should be resumed", waitForResumed(activity));
+    }
+
+    public static void waitAndAssertResumed(@NonNull List<Activity> activityList) {
+        assertTrue("All activities in this list should be resumed:" + activityList,
+                waitForResumed(activityList));
+    }
+
+    public static void waitAndAssertNotResumed(@NonNull String activityId) {
+        assertFalse("Activity with id=" + activityId + " should not be resumed",
+                waitForResumed(activityId));
+    }
+
+    private static boolean waitForVisible(@NonNull Activity activity, boolean visible) {
         final long startTime = System.currentTimeMillis();
         while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
             if (WindowManagerJetpackTestBase.isActivityVisible(activity) == visible) {
@@ -311,11 +337,12 @@
         return false;
     }
 
-    public static boolean waitForResumed(@NonNull Activity activity) {
-        return waitForResumed(Arrays.asList(activity));
+    public static void waitAndAssertNotVisible(@NonNull Activity activity) {
+        assertTrue(activity + " should not be visible",
+                waitForVisible(activity, false /* visible */));
     }
 
-    public static boolean waitForFinishing(@NonNull Activity activity) {
+    private static boolean waitForFinishing(@NonNull Activity activity) {
         final long startTime = System.currentTimeMillis();
         while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
             if (activity.isFinishing()) {
@@ -325,6 +352,10 @@
         return activity.isFinishing();
     }
 
+    public static void waitAndAssertFinishing(@NonNull Activity activity) {
+        assertTrue(activity + " should be finishing", waitForFinishing(activity));
+    }
+
     @Nullable
     public static Activity getPrimaryStackTopActivity(SplitInfo splitInfo) {
         List<Activity> primaryActivityStack = splitInfo.getPrimaryActivityStack().getActivities();
@@ -378,4 +409,12 @@
             outSecondaryActivityBounds.set(leftContainerBounds);
         }
     }
+
+    private static boolean splitInfoTopSplitIsCorrect(@NonNull List<SplitInfo> splitInfoList,
+            @NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
+        assertFalse("Split info callback should not be empty", splitInfoList.isEmpty());
+        final SplitInfo topSplit = splitInfoList.get(splitInfoList.size() - 1);
+        return primaryActivity.equals(getPrimaryStackTopActivity(topSplit))
+                && secondaryActivity.equals(getSecondaryStackTopActivity(topSplit));
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java
index cb91494..b654c73 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java
@@ -50,6 +50,8 @@
 import org.junit.Test;
 import org.junit.rules.TestName;
 
+import java.util.concurrent.CountDownLatch;
+
 public class SurfacePackageFlickerTest {
     private static final int DEFAULT_LAYOUT_WIDTH = 100;
     private static final int DEFAULT_LAYOUT_HEIGHT = 100;
@@ -86,6 +88,8 @@
         private SurfaceView mSurfaceView;
         private SurfaceControlViewHost mSurfaceControlViewHost;
         private FrameLayout mParent;
+        private final CountDownLatch mFirstDrawLatch = new CountDownLatch(1);
+
 
         private final Runnable mRecreateSurfaceViewCallback = new Runnable() {
                 public void run() {
@@ -125,9 +129,18 @@
 
             v.getViewTreeObserver().registerFrameCommitCallback(() -> {
                 parent.post(mRecreateSurfaceViewCallback);
+                mFirstDrawLatch.countDown();
             });
         }
 
+        public void waitForReady() {
+            try {
+                mFirstDrawLatch.await();
+            } catch (Exception e) {
+                // Oh well
+            }
+        }
+
         @Override
         public void end() {
             mSurfaceControlViewHost.release();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
index 322355f..2e29a74 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -73,12 +73,11 @@
 import java.util.ArrayList;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Ensure moving windows and tapping is done synchronously.
@@ -728,9 +727,10 @@
         eventHover.setSource(InputDevice.SOURCE_MOUSE);
         try {
             mInstrumentation.sendPointerSync(eventHover);
-            fail("Not allowed to inject event to the window from another process.");
-        } catch (SecurityException e) {
-            // Should not be allowed to inject event to the window from another process.
+            fail("Not allowed to inject to windows owned by another uid from Instrumentation.");
+        } catch (RuntimeException e) {
+            // Should not be allowed to inject event to a window owned by another uid from the
+            // Instrumentation class.
         }
     }
 
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 3f8e8b1..81fdb5a 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -17,6 +17,7 @@
 package android.location.cts.fine;
 
 import static android.Manifest.permission.LOCATION_BYPASS;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static android.app.AppOpsManager.OPSTR_MONITOR_HIGH_POWER_LOCATION;
 import static android.app.AppOpsManager.OPSTR_MONITOR_LOCATION;
 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
@@ -59,6 +60,7 @@
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
+import android.location.LastLocationRequest;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -125,6 +127,7 @@
             "invalid_location_attribution_tag";
 
     private static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist";
+    private static final String ADAS_SETTINGS_ALLOWLIST = "adas_settings_allowlist";
 
     private Random mRandom;
     private Context mContext;
@@ -234,6 +237,110 @@
     }
 
     @Test
+    public void testGetLastKnownLocation_AdasLocationSettings_ReturnsLocation() throws Exception {
+        assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE));
+
+        try (LocationListenerCapture capture = new LocationListenerCapture(mContext);
+             DeviceConfigStateHelper locationDeviceConfigStateHelper =
+                     new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_LOCATION)) {
+
+            locationDeviceConfigStateHelper.set(ADAS_SETTINGS_ALLOWLIST,
+                    mContext.getPackageName());
+
+            mManager.addTestProvider(
+                    GPS_PROVIDER,
+                    false,
+                    true,
+                    false,
+                    false,
+                    true,
+                    true,
+                    true,
+                    Criteria.POWER_HIGH,
+                    Criteria.ACCURACY_FINE);
+
+            Location loc = createLocation(GPS_PROVIDER, mRandom);
+            mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+
+            mManager.setTestProviderEnabled(GPS_PROVIDER, true);
+
+            getInstrumentation()
+                    .getUiAutomation()
+                    .adoptShellPermissionIdentity(LOCATION_BYPASS, WRITE_SECURE_SETTINGS);
+
+            try {
+                // Returns loc when ADAS toggle is on.
+                mManager.setLocationEnabledForUser(false, mContext.getUser());
+                mManager.setAdasGnssLocationEnabled(true);
+                assertThat(
+                        mManager.getLastKnownLocation(
+                                GPS_PROVIDER,
+                                new LastLocationRequest.Builder()
+                                        .setAdasGnssBypass(true)
+                                        .build()))
+                        .isEqualTo(loc);
+
+            } finally {
+                mManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
+                getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+            }
+        }
+    }
+
+    @Test
+    public void testGetLastKnownLocation_AdasLocationSettings_ReturnsNull() throws Exception {
+        assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE));
+
+        try (LocationListenerCapture capture = new LocationListenerCapture(mContext);
+             DeviceConfigStateHelper locationDeviceConfigStateHelper =
+                     new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_LOCATION)) {
+
+            locationDeviceConfigStateHelper.set(ADAS_SETTINGS_ALLOWLIST,
+                    mContext.getPackageName());
+
+            mManager.addTestProvider(
+                    GPS_PROVIDER,
+                    false,
+                    true,
+                    false,
+                    false,
+                    true,
+                    true,
+                    true,
+                    Criteria.POWER_HIGH,
+                    Criteria.ACCURACY_FINE);
+
+            Location loc = createLocation(GPS_PROVIDER, mRandom);
+            mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+
+            mManager.setTestProviderEnabled(GPS_PROVIDER, true);
+
+            getInstrumentation()
+                    .getUiAutomation()
+                    .adoptShellPermissionIdentity(LOCATION_BYPASS, WRITE_SECURE_SETTINGS);
+
+            try {
+                // Returns null when ADAS toggle is off
+                mManager.setAdasGnssLocationEnabled(false);
+                mManager.setLocationEnabledForUser(false, mContext.getUser());
+
+                assertThat(
+                        mManager.getLastKnownLocation(
+                                GPS_PROVIDER,
+                                new LastLocationRequest.Builder()
+                                        .setAdasGnssBypass(true)
+                                        .build()))
+                        .isNull();
+
+            } finally {
+                mManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
+                mManager.setAdasGnssLocationEnabled(true);
+                getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+            }
+        }
+    }
+
+    @Test
     public void testGetLastKnownLocation_RemoveProvider() {
         Location loc1 = createLocation(TEST_PROVIDER, mRandom);
 
@@ -843,6 +950,56 @@
     }
 
     @Test
+    public void testRequestLocationUpdates_AdasGnssBypass() throws Exception {
+        assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE));
+
+        try (LocationListenerCapture capture = new LocationListenerCapture(mContext);
+             DeviceConfigStateHelper locationDeviceConfigStateHelper =
+                     new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_LOCATION)) {
+
+            locationDeviceConfigStateHelper.set(ADAS_SETTINGS_ALLOWLIST,
+                    mContext.getPackageName());
+
+            mManager.addTestProvider(
+                    GPS_PROVIDER,
+                    false,
+                    true,
+                    false,
+                    false,
+                    true,
+                    true,
+                    true,
+                    Criteria.POWER_HIGH,
+                    Criteria.ACCURACY_FINE);
+
+            getInstrumentation().getUiAutomation()
+                    .adoptShellPermissionIdentity(LOCATION_BYPASS);
+            try {
+                mManager.requestLocationUpdates(
+                        GPS_PROVIDER,
+                        new LocationRequest.Builder(0)
+                                .setAdasGnssBypass(true)
+                                .build(),
+                        Executors.newSingleThreadExecutor(),
+                        capture);
+            } finally {
+                getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+            }
+
+            // turn off provider
+            mManager.setTestProviderEnabled(GPS_PROVIDER, false);
+
+            // test that all restrictions are bypassed
+            Location loc = createLocation(GPS_PROVIDER, mRandom);
+            mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+            assertThat(capture.getNextLocation(FAILURE_TIMEOUT_MS)).isEqualTo(loc);
+            loc = createLocation(GPS_PROVIDER, mRandom);
+            mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+            assertThat(capture.getNextLocation(FAILURE_TIMEOUT_MS)).isEqualTo(loc);
+        }
+    }
+
+    @Test
     public void testMonitoring() throws Exception {
         AppOpsManager appOps = Objects.requireNonNull(
                 mContext.getSystemService(AppOpsManager.class));
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java
index 4ba143b..24691b4 100644
--- a/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java
@@ -61,6 +61,23 @@
     }
 
     @Test
+    public void testWriteToParcelWithoutSomeOptionalFields() {
+        GnssSingleSatCorrection object = new GnssSingleSatCorrection.Builder()
+                .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
+                .setSatelliteId(12)
+                .setCarrierFrequencyHz(1575420000f)
+                .setProbabilityLineOfSight(0.1f)
+                .build();
+        Parcel parcel = Parcel.obtain();
+        object.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        GnssSingleSatCorrection fromParcel =
+                GnssSingleSatCorrection.CREATOR.createFromParcel(parcel);
+        assertEquals(object, fromParcel);
+        parcel.recycle();
+    }
+
+    @Test
     public void testClear() {
         GnssSingleSatCorrection.Builder builder = createTestSingleSatCorrectionBuilder();
         builder.clearProbabilityLineOfSight();
diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
index 83a7da7..cfe7835 100644
--- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
@@ -29,6 +29,7 @@
 
 import org.junit.Assert;
 import org.junit.Assume;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -92,6 +93,7 @@
      * it should be capable of displaying the same
      */
     @Test
+    @Ignore("TODO(b/228237404) Enable once display capabilities can be queried at codec2 level")
     public void testHDRDisplayCapabilities() {
         Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T);
         Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/"));
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
index 10ec1e6..9f0bc44 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
@@ -96,7 +96,19 @@
                 reportPerfClass + " for requirement " + this.id + " performance class should at " +
                 "least be: " + expectedPerfClass);
             for (RequiredMeasurement<?> rm: this.mRequiredMeasurements.values()) {
-                Log.w(Requirement.TAG, rm.toString());
+                Map<Integer, RequirementConstants.Result> perfClasses = rm.getPerformanceClass();
+                int maxMetPerformanceClass = 0;
+                for (int pc: perfClasses.keySet()) {
+                    if (perfClasses.get(pc) == RequirementConstants.Result.MET) {
+                        maxMetPerformanceClass = Math.max(maxMetPerformanceClass, pc);
+                    }
+                }
+
+                if (maxMetPerformanceClass < expectedPerfClass) {
+                    Log.w(Requirement.TAG, rm.toString());
+                } else {
+                    Log.i(Requirement.TAG, rm.toString());
+                }
             }
             return false;
         } else {
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
index e48ba00..bb4e6e3 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
@@ -596,7 +596,10 @@
 
     @Test
     fun ensurePhoneCallOpsRestricted() {
-        assumeTrue(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
+        val pm = mContext.packageManager
+        assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) ||
+                pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
+                pm.hasSystemFeature(PackageManager.FEATURE_TELECOM))
         val micReturn = mAppOps.noteOp(OPSTR_PHONE_CALL_MICROPHONE, Process.myUid(), mOpPackageName,
                 null, null)
         assertEquals(MODE_IGNORED, micReturn)
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
index f3918c7..dde5621 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
@@ -26,8 +26,6 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -42,7 +40,6 @@
     private static final String TAG = BluetoothA2dpSinkTest.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_A2DP_SINK = "profile_supported_a2dp_sink";
 
     private boolean mHasBluetooth;
     private BluetoothAdapter mAdapter;
@@ -57,17 +54,11 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_BLUETOOTH);
 
+        mHasBluetooth = TestUtils.hasBluetooth();
         if (!mHasBluetooth) return;
 
-        Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
-                "com.android.bluetooth.services");
-        int a2dpSinkSupportId = bluetoothResources.getIdentifier(
-                PROFILE_SUPPORTED_A2DP_SINK, "bool", "com.android.bluetooth.services");
-        assertTrue("resource profile_supported_a2dp not found", a2dpSinkSupportId != 0);
-        mIsA2dpSinkSupported = bluetoothResources.getBoolean(a2dpSinkSupportId);
+        mIsA2dpSinkSupported = TestUtils.isProfileEnabled(BluetoothProfile.A2DP_SINK);
         if (!mIsA2dpSinkSupported) return;
 
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -89,15 +80,18 @@
     @Override
     public void tearDown() throws Exception {
         super.tearDown();
-        if (!(mHasBluetooth && mIsA2dpSinkSupported)) return;
-
+        if (!(mHasBluetooth && mIsA2dpSinkSupported)) {
+            return;
+        }
         if (mAdapter != null && mBluetoothA2dpSink != null) {
             mAdapter.closeProfileProxy(BluetoothProfile.A2DP_SINK, mBluetoothA2dpSink);
             mBluetoothA2dpSink = null;
             mIsProfileReady = false;
         }
         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
-        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
         mUiAutomation.dropShellPermissionIdentity();
         mAdapter = null;
     }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
index 191c4a2..912bf70 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
@@ -21,12 +21,9 @@
 import android.app.UiAutomation;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -41,7 +38,6 @@
     private static final String TAG = BluetoothA2dpTest.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_A2DP = "profile_supported_a2dp";
 
     private boolean mHasBluetooth;
     private BluetoothAdapter mAdapter;
@@ -56,10 +52,13 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_BLUETOOTH);
 
+        mHasBluetooth = TestUtils.hasBluetooth();
         if (!mHasBluetooth) return;
+
+        mIsA2dpSupported = TestUtils.isProfileEnabled(BluetoothProfile.A2DP_SINK);
+        if (!mIsA2dpSupported) return;
+
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
 
@@ -72,14 +71,6 @@
         mIsProfileReady = false;
         mBluetoothA2dp = null;
 
-        Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
-                "com.android.bluetooth.services");
-        int a2dpSupportId = bluetoothResources.getIdentifier(
-                PROFILE_SUPPORTED_A2DP, "bool", "com.android.bluetooth.services");
-        assertTrue("resource profile_supported_a2dp not found", a2dpSupportId != 0);
-        mIsA2dpSupported = bluetoothResources.getBoolean(a2dpSupportId);
-        if (!mIsA2dpSupported) return;
-
         mAdapter.getProfileProxy(getContext(), new BluetoothA2dpServiceListener(),
                 BluetoothProfile.A2DP);
     }
@@ -87,16 +78,19 @@
     @Override
     public void tearDown() throws Exception {
         super.tearDown();
-        if (mHasBluetooth) {
-            if (mAdapter != null && mBluetoothA2dp != null) {
-                mAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
-                mBluetoothA2dp = null;
-                mIsProfileReady = false;
-            }
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
-            mAdapter = null;
-            mUiAutomation.dropShellPermissionIdentity();
+        if (!(mHasBluetooth && mIsA2dpSupported)) {
+            return;
         }
+        if (mAdapter != null && mBluetoothA2dp != null) {
+            mAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
+            mBluetoothA2dp = null;
+            mIsProfileReady = false;
+        }
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
+        mAdapter = null;
+        mUiAutomation.dropShellPermissionIdentity();
     }
 
     public void test_getConnectedDevices() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
index 54766fe..9e9459c 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
@@ -35,12 +35,15 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.SystemProperties;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.Set;
@@ -576,8 +579,19 @@
                 BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO));
         assertEquals("HAP_CLIENT",
                 BluetoothProfile.getProfileName(BluetoothProfile.HAP_CLIENT));
-        assertEquals("UNKNOWN_PROFILE",
-                BluetoothProfile.getProfileName(BluetoothProfile.HAP_CLIENT + 1));
+
+        if (!ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+            return;
+        }
+
+        assertEquals("VOLUME_CONTROL",
+                BluetoothProfile.getProfileName(BluetoothProfile.VOLUME_CONTROL));
+        assertEquals("CSIP_SET_COORDINATOR",
+                BluetoothProfile.getProfileName(BluetoothProfile.CSIP_SET_COORDINATOR));
+        assertEquals("LE_AUDIO_BROADCAST",
+                BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO_BROADCAST));
+        assertEquals("LE_AUDIO_BROADCAST_ASSISTANT",
+                BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT));
     }
 
     private static void sleep(long t) {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
new file mode 100644
index 0000000..fa96ddf
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2022 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.bluetooth.cts;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.ParcelUuid;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BluetoothCsipSetCoordinatorTest extends AndroidTestCase {
+    private static final String TAG = BluetoothCsipSetCoordinatorTest.class.getSimpleName();
+
+    private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
+
+    private boolean mHasBluetooth;
+    private BluetoothAdapter mAdapter;
+
+    private BluetoothCsipSetCoordinator mBluetoothCsipSetCoordinator;
+    private boolean mIsCsipSetCoordinatorSupported;
+    private boolean mIsProfileReady;
+    private Condition mConditionProfileIsConnected;
+    private ReentrantLock mProfileConnectedlock;
+    private boolean mGroupLockCallbackCalled;
+    private TestCallback mTestCallback;
+    private Executor mTestExecutor;
+    private BluetoothDevice mTestDevice;
+    private boolean mIsLocked;
+    private int mTestOperationStatus;
+    private int mTestGroupId;
+
+    class TestCallback implements BluetoothCsipSetCoordinator.ClientLockCallback {
+        @Override
+        public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) {
+            mGroupLockCallbackCalled = true;
+            assertTrue(groupId == mTestGroupId);
+            assertTrue(opStatus == mTestOperationStatus);
+            assertTrue(isLocked == mIsLocked);
+        }
+    };
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+            mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_BLUETOOTH);
+
+            if (!mHasBluetooth) return;
+
+            TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+            BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
+            mAdapter = manager.getAdapter();
+            assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
+
+            mProfileConnectedlock = new ReentrantLock();
+            mConditionProfileIsConnected  = mProfileConnectedlock.newCondition();
+            mIsProfileReady = false;
+            mBluetoothCsipSetCoordinator = null;
+
+            boolean isLeAudioSupportedInConfig =
+                     TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO);
+            boolean isCsipConfigEnabled =
+                     TestUtils.getProfileConfigValueOrDie(BluetoothProfile.CSIP_SET_COORDINATOR);
+            if (isLeAudioSupportedInConfig) {
+                /* If Le Audio is supported then CSIP shall be supported */
+                assertTrue("Config must be true when profile is supported", isCsipConfigEnabled);
+            }
+
+            if (isCsipConfigEnabled) {
+                mIsCsipSetCoordinatorSupported = mAdapter.getProfileProxy(getContext(),
+                        new BluetoothCsipServiceListener(),
+                        BluetoothProfile.CSIP_SET_COORDINATOR);
+                assertTrue("Service shall be supported ", mIsCsipSetCoordinatorSupported);
+
+                mTestCallback = new TestCallback();
+                mTestExecutor = mContext.getMainExecutor();
+            }
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mHasBluetooth) {
+            if (mBluetoothCsipSetCoordinator != null) {
+                mBluetoothCsipSetCoordinator.close();
+                mBluetoothCsipSetCoordinator = null;
+                mIsProfileReady = false;
+                mTestDevice = null;
+                mIsLocked = false;
+                mTestCallback = null;
+                mTestExecutor = null;
+            }
+            if (mAdapter != null ) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+                mAdapter = null;
+            }
+            TestUtils.dropPermissionAsShellUid();
+        }
+    }
+
+    public void testGetConnectedDevices() {
+        if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothCsipSetCoordinator);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        // Verify returns empty list if bluetooth is not enabled
+        List<BluetoothDevice> connectedDevices = mBluetoothCsipSetCoordinator.getConnectedDevices();
+        assertTrue(connectedDevices.isEmpty());
+    }
+
+    public void testGetDevicesMatchingConnectionStates() {
+        if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothCsipSetCoordinator);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        // Verify returns empty list if bluetooth is not enabled
+        List<BluetoothDevice> connectedDevices =
+                mBluetoothCsipSetCoordinator.getDevicesMatchingConnectionStates(null);
+        assertTrue(connectedDevices.isEmpty());
+    }
+
+    public void testGetGroupUuidMapByDevice() {
+        if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothCsipSetCoordinator);
+
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+
+        TestUtils.dropPermissionAsShellUid();
+        // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+        assertThrows(SecurityException.class, () ->
+                mBluetoothCsipSetCoordinator.getGroupUuidMapByDevice(mTestDevice));
+
+        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        Map<Integer, ParcelUuid> result = mBluetoothCsipSetCoordinator
+                .getGroupUuidMapByDevice(mTestDevice);
+        assertTrue(result.isEmpty());
+    }
+
+    public void testLockUnlockGroup() {
+        if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothCsipSetCoordinator);
+
+        mTestGroupId = 1;
+         // Verify parameter
+        assertThrows(NullPointerException.class, () ->
+                mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId, null, mTestCallback));
+        assertThrows(NullPointerException.class, () ->
+                mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId, mTestExecutor, null));
+
+        TestUtils.dropPermissionAsShellUid();
+        // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+        assertThrows(SecurityException.class,
+                () -> mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId, mTestExecutor,
+                                                            mTestCallback));
+
+        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+        // Lock group
+        try {
+            UUID uuid = mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId,
+                    mTestExecutor, mTestCallback);
+            mBluetoothCsipSetCoordinator.unlockGroup(uuid);
+        } catch (Exception e) {
+            fail("Exception caught from register(): " + e.toString());
+        }
+    }
+
+    public void testTestLockCallback() {
+        if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothCsipSetCoordinator);
+
+        /* Note. This is just for api coverage until proper testing tools are set up */
+        mTestGroupId = 1;
+        mTestOperationStatus = 1;
+        mIsLocked = true;
+
+        mTestCallback.onGroupLockSet(mTestGroupId, mTestOperationStatus, mIsLocked);
+        assertTrue(mGroupLockCallbackCalled);
+    }
+
+    public void testGetAllGroupIds() {
+        if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothCsipSetCoordinator);
+
+        TestUtils.dropPermissionAsShellUid();
+        assertThrows(SecurityException.class, () ->
+                mBluetoothCsipSetCoordinator.getAllGroupIds(BluetoothUuid.CAP));
+
+        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        List<Integer> result = mBluetoothCsipSetCoordinator.getAllGroupIds(null);
+        assertTrue(result.isEmpty());
+    }
+
+    private boolean waitForProfileConnect() {
+        mProfileConnectedlock.lock();
+        try {
+            // Wait for the Adapter to be disabled
+            while (!mIsProfileReady) {
+                if (!mConditionProfileIsConnected.await(
+                        PROXY_CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    // Timeout
+                    Log.e(TAG, "Timeout while waiting for Profile Connect");
+                    break;
+                } // else spurious wakeups
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "waitForProfileConnect: interrrupted");
+        } finally {
+            mProfileConnectedlock.unlock();
+        }
+        return mIsProfileReady;
+    }
+
+    private final class BluetoothCsipServiceListener implements
+            BluetoothProfile.ServiceListener {
+
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mProfileConnectedlock.lock();
+            mBluetoothCsipSetCoordinator = (BluetoothCsipSetCoordinator) proxy;
+            mIsProfileReady = true;
+            try {
+                mConditionProfileIsConnected.signal();
+            } finally {
+                mProfileConnectedlock.unlock();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+        }
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
index 6c7b933..c83f014 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
@@ -54,7 +54,6 @@
     private static final String TAG = BluetoothHapClientTest.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_HAP_CLIENT = "profile_supported_hap_client";
 
     private Context mContext;
     private boolean mHasBluetooth;
@@ -76,37 +75,40 @@
         if (!mHasBluetooth) {
             return;
         }
+
+        mIsHapClientSupported = TestUtils.isProfileEnabled(BluetoothProfile.HAP_CLIENT);
+        if (!mIsHapClientSupported) {
+            return;
+        }
+
         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
         mAdapter = TestUtils.getBluetoothAdapterOrDie();
         assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
 
-
         mProfileConnectedlock = new ReentrantLock();
         mConditionProfileIsConnected  = mProfileConnectedlock.newCondition();
         mIsProfileReady = false;
         mBluetoothHapClient = null;
 
-        mIsHapClientSupported = TestUtils.getProfileConfigValueOrDie(BluetoothProfile.HAP_CLIENT);
-        if (!mIsHapClientSupported) {
-            return;
-        }
-
         mAdapter.getProfileProxy(mContext, new BluetoothHapClientServiceListener(),
                 BluetoothProfile.HAP_CLIENT);
     }
 
     @After
     public void tearDown() throws Exception {
-        if (mHasBluetooth) {
-            if (mAdapter != null && mBluetoothHapClient != null) {
-                mBluetoothHapClient.close();
-                mBluetoothHapClient = null;
-                mIsProfileReady = false;
-            }
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
-            mAdapter = null;
-            TestUtils.dropPermissionAsShellUid();
+        if (!(mHasBluetooth && mIsHapClientSupported)) {
+            return;
         }
+        if (mAdapter != null && mBluetoothHapClient != null) {
+            mBluetoothHapClient.close();
+            mBluetoothHapClient = null;
+            mIsProfileReady = false;
+        }
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
+        mAdapter = null;
+        TestUtils.dropPermissionAsShellUid();
     }
 
     @Test
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
index afafa39..df8ecf1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
@@ -61,20 +61,27 @@
         if (!mHasBluetooth) {
             return;
         }
+
+        mIsHapSupported = TestUtils.isProfileEnabled(BluetoothProfile.HAP_CLIENT);
+        if (!mIsHapSupported) {
+            return;
+        }
+
         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
         mAdapter = TestUtils.getBluetoothAdapterOrDie();
         assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
-
-        mIsHapSupported = TestUtils.getProfileConfigValueOrDie(BluetoothProfile.HAP_CLIENT);
     }
 
     @After
     public void tearDown() {
-        if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
-            mAdapter = null;
-            TestUtils.dropPermissionAsShellUid();
+        if (!(mHasBluetooth && mIsHapSupported)) {
+            return;
         }
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
+        mAdapter = null;
+        TestUtils.dropPermissionAsShellUid();
     }
 
     @Test
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
index cf8f1a2..6925d9e 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
@@ -19,13 +19,12 @@
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 
 import android.app.UiAutomation;
-import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -40,7 +39,6 @@
     private static final String TAG = BluetoothHeadsetTest.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_HEADSET = "profile_supported_hs_hfp";
 
     private boolean mHasBluetooth;
     private BluetoothAdapter mAdapter;
@@ -57,8 +55,11 @@
         super.setUp();
         mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_BLUETOOTH);
-
         if (!mHasBluetooth) return;
+
+        mIsHeadsetSupported = TestUtils.isProfileEnabled(BluetoothProfile.HEADSET);
+        if (!mIsHeadsetSupported) return;
+
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
 
@@ -71,14 +72,6 @@
         mIsProfileReady = false;
         mBluetoothHeadset = null;
 
-        Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
-                "com.android.bluetooth.services");
-        int headsetSupportId = bluetoothResources.getIdentifier(
-                PROFILE_SUPPORTED_HEADSET, "bool", "com.android.bluetooth.services");
-        assertTrue("resource profile_supported_hs_hfp not found", headsetSupportId != 0);
-        mIsHeadsetSupported = bluetoothResources.getBoolean(headsetSupportId);
-        if (!mIsHeadsetSupported) return;
-
         mAdapter.getProfileProxy(getContext(), new BluetoothHeadsetServiceListener(),
                 BluetoothProfile.HEADSET);
     }
@@ -86,16 +79,19 @@
     @Override
     public void tearDown() throws Exception {
         super.tearDown();
-        if (mHasBluetooth) {
-            if (mAdapter != null && mBluetoothHeadset != null) {
-                mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
-                mBluetoothHeadset = null;
-                mIsProfileReady = false;
-            }
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
-            mAdapter = null;
-            mUiAutomation.dropShellPermissionIdentity();
+        if (!(mHasBluetooth && mIsHeadsetSupported)) {
+            return;
         }
+        if (mAdapter != null && mBluetoothHeadset != null) {
+            mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
+            mBluetoothHeadset = null;
+            mIsProfileReady = false;
+        }
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
+        mAdapter = null;
+        mUiAutomation.dropShellPermissionIdentity();
     }
 
     public void test_getConnectedDevices() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
index 857e12c..1a258bc 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
@@ -39,7 +39,6 @@
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -56,7 +55,6 @@
     private static final String TAG = BluetoothHidDevice.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_HID_DEVICE = "profile_supported_hid_device";
 
     private boolean mHasBluetooth;
     private boolean mIsHidSupported;
@@ -76,6 +74,10 @@
         mHasBluetooth =
                 getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
         if (!mHasBluetooth) return;
+
+        mIsHidSupported = TestUtils.isProfileEnabled(BluetoothProfile.HID_DEVICE);
+        if (!mIsHidSupported) return;
+
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
 
@@ -89,13 +91,6 @@
         mBluetoothHidDevice = null;
         mExecutor = Executors.newSingleThreadExecutor();
 
-        Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
-                "com.android.bluetooth.services");
-        int hidSupportId = bluetoothResources.getIdentifier(PROFILE_SUPPORTED_HID_DEVICE, "bool",
-                "com.android.bluetooth.services");
-        mIsHidSupported = bluetoothResources.getBoolean(hidSupportId);
-        if (!mIsHidSupported) return;
-
         mAdapter.getProfileProxy(getContext(), new BluetoothHidServiceListener(),
                 BluetoothProfile.HID_DEVICE);
     }
@@ -103,17 +98,20 @@
     @Override
     public void tearDown() throws Exception {
         super.tearDown();
-        if (mHasBluetooth) {
-            if (mAdapter != null && mBluetoothHidDevice != null) {
-                mAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, mBluetoothHidDevice);
-                mBluetoothHidDevice = null;
-                mIsProfileReady = false;
-            }
-            mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
-            mAdapter = null;
-            mUiAutomation.dropShellPermissionIdentity();
+        if (!(mHasBluetooth && mIsHidSupported)) {
+            return;
         }
+        if (mAdapter != null && mBluetoothHidDevice != null) {
+            mAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, mBluetoothHidDevice);
+            mBluetoothHidDevice = null;
+            mIsProfileReady = false;
+        }
+        mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
+        mAdapter = null;
+        mUiAutomation.dropShellPermissionIdentity();
     }
 
     public void test_getDevicesMatchingConnectionStates() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
index 0100442..0446adb 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
@@ -75,8 +75,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -85,8 +84,7 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (!mIsBroadcastSourceSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -95,7 +93,9 @@
     @After
     public void tearDown() {
         if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
index 08deed4..c22e77d5 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
@@ -81,8 +81,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -91,8 +90,7 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (!mIsBroadcastSourceSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -101,7 +99,9 @@
     @After
     public void tearDown() {
         if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
index 0db7a05..2a1f31d 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
@@ -16,25 +16,26 @@
 
 package android.bluetooth.cts;
 
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
 import static org.junit.Assert.assertThrows;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeAudio;
-import android.bluetooth.BluetoothLeAudioCodecStatus;
 import android.bluetooth.BluetoothLeAudioCodecConfig;
+import android.bluetooth.BluetoothLeAudioCodecStatus;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.os.Build;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-import androidx.test.InstrumentationRegistry;
-
 import com.android.compatibility.common.util.ApiLevelUtil;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -45,8 +46,6 @@
     private static final String TAG = BluetoothLeAudioTest.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_LE_AUDIO = "profile_supported_leaudio";
-    private static final int LE_AUDIO_PROFILE_CONSTANT = 22;
 
     private boolean mHasBluetooth;
     private BluetoothAdapter mAdapter;
@@ -56,17 +55,72 @@
     private boolean mIsProfileReady;
     private Condition mConditionProfileIsConnected;
     private ReentrantLock mProfileConnectedlock;
+    private Executor mTestExecutor;
+    private TestCallback mTestCallback;
+    private boolean mCodecConfigChangedCalled;
+    private boolean mGroupNodeAddedCalled;
+    private boolean mGroupNodeRemovedCalled;
+    private boolean mGroupStatusChangedCalled;
+    private BluetoothDevice mTestDevice;
+    private int mTestGroupId;
+    private int mTestGroupStatus;
+
+    private static final BluetoothLeAudioCodecConfig LC3_16KHZ_CONFIG =
+            new BluetoothLeAudioCodecConfig.Builder()
+                    .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                    .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
+                    .build();
+
+    private static final List<BluetoothLeAudioCodecConfig> TEST_CODEC_CAPA_CONFIG =
+            new ArrayList() {{
+                    add(LC3_16KHZ_CONFIG);
+            }};
+
+    private static final BluetoothLeAudioCodecStatus TEST_CODEC_STATUS =
+            new BluetoothLeAudioCodecStatus(LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG,
+                    TEST_CODEC_CAPA_CONFIG, TEST_CODEC_CAPA_CONFIG,
+                    TEST_CODEC_CAPA_CONFIG, TEST_CODEC_CAPA_CONFIG);
+
+    class TestCallback implements BluetoothLeAudio.Callback {
+        @Override
+        public void onCodecConfigChanged(int groupId,
+                                         BluetoothLeAudioCodecStatus status) {
+            mCodecConfigChangedCalled = true;
+            assertTrue(groupId == mTestGroupId);
+            assertTrue(status == TEST_CODEC_STATUS);
+        }
+        @Override
+        public void onGroupNodeAdded(BluetoothDevice device, int groupId) {
+            mGroupNodeAddedCalled = true;
+            assertTrue(groupId == mTestGroupId);
+            assertTrue(device == mTestDevice);
+        }
+        @Override
+        public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
+            mGroupNodeRemovedCalled = true;
+            assertTrue(groupId == mTestGroupId);
+            assertTrue(device == mTestDevice);
+        }
+        @Override
+        public void onGroupStatusChanged(int groupId, int groupStatus) {
+            mGroupStatusChangedCalled = true;
+            assertTrue(groupId == mTestGroupId);
+            assertTrue(groupStatus == mTestGroupStatus);
+        }
+    };
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)) {
+        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
             mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_BLUETOOTH);
-
             if (!mHasBluetooth) return;
-            InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .adoptShellPermissionIdentity(android.Manifest.permission.BLUETOOTH_CONNECT);
+
+            mIsLeAudioSupported = TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO);
+            if (!mIsLeAudioSupported) return;
+
+            TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
 
             BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
             mAdapter = manager.getAdapter();
@@ -77,33 +131,30 @@
             mIsProfileReady = false;
             mBluetoothLeAudio = null;
 
-            Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
-                    "com.android.bluetooth.services");
-            int leAudioSupportId = bluetoothResources.getIdentifier(
-                    PROFILE_SUPPORTED_LE_AUDIO, "bool", "com.android.bluetooth.services");
-            if (leAudioSupportId == 0) return;
-            mIsLeAudioSupported = bluetoothResources.getBoolean(leAudioSupportId);
-            if (!mIsLeAudioSupported) return;
-
             mAdapter.getProfileProxy(getContext(), new BluetoothLeAudioServiceListener(),
-                    LE_AUDIO_PROFILE_CONSTANT);
+                    BluetoothProfile.LE_AUDIO);
+
+            mTestExecutor = mContext.getMainExecutor();
+            mTestCallback = new TestCallback();
         }
     }
 
     @Override
     public void tearDown() throws Exception {
         super.tearDown();
-        if (mHasBluetooth) {
-            if (mAdapter != null && mBluetoothLeAudio != null) {
-                mBluetoothLeAudio.close();
-                mBluetoothLeAudio = null;
-                mIsProfileReady = false;
-            }
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
-            InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .dropShellPermissionIdentity();
-            mAdapter = null;
+        if (!(mHasBluetooth && mIsLeAudioSupported)) {
+            return;
         }
+        if (mBluetoothLeAudio != null) {
+            mBluetoothLeAudio.close();
+            mBluetoothLeAudio = null;
+            mIsProfileReady = false;
+        }
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
+        TestUtils.dropPermissionAsShellUid();
+        mAdapter = null;
     }
 
     public void testGetConnectedDevices() {
@@ -139,7 +190,7 @@
         assertTrue(waitForProfileConnect());
         assertNotNull(mBluetoothLeAudio);
 
-        BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
 
         // Verify returns false when invalid input is given
         assertEquals(BluetoothProfile.STATE_DISCONNECTED,
@@ -149,7 +200,7 @@
 
         // Verify returns false if bluetooth is not enabled
         assertEquals(BluetoothProfile.STATE_DISCONNECTED,
-                mBluetoothLeAudio.getConnectionState(testDevice));
+                mBluetoothLeAudio.getConnectionState(mTestDevice));
     }
 
     public void testGetAudioLocation() {
@@ -158,13 +209,13 @@
         assertTrue(waitForProfileConnect());
         assertNotNull(mBluetoothLeAudio);
 
-        BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
 
         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
 
         // Verify returns false if bluetooth is not enabled
         assertEquals(BluetoothLeAudio.AUDIO_LOCATION_INVALID,
-                mBluetoothLeAudio.getAudioLocation(testDevice));
+                mBluetoothLeAudio.getAudioLocation(mTestDevice));
     }
 
     public void test_setgetConnectionPolicy() {
@@ -178,33 +229,86 @@
                 mBluetoothLeAudio.getConnectionPolicy(null));
     }
 
-    public void testRegisterCallback() {
+    public void testRegisterCallbackNoPermission() {
+        if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+        TestUtils.dropPermissionAsShellUid();
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothLeAudio);
+
+        // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+        assertThrows(SecurityException.class,
+                () -> mBluetoothLeAudio.registerCallback(mTestExecutor, mTestCallback));
+
+        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+    }
+
+    public void testRegisterUnregisterCallback() {
         if (!(mHasBluetooth && mIsLeAudioSupported)) return;
 
         assertTrue(waitForProfileConnect());
         assertNotNull(mBluetoothLeAudio);
 
-        Executor executor = mContext.getMainExecutor();
-        BluetoothLeAudio.Callback callback =
-                new BluetoothLeAudio.Callback() {
-                    @Override
-                    public void onCodecConfigChanged(int groupId,
-                                                     BluetoothLeAudioCodecStatus status) {}
-                    @Override
-                    public void onGroupNodeAdded(BluetoothDevice device, int groupId) {}
-                    @Override
-                    public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {}
-                    @Override
-                    public void onGroupStatusChanged(int groupId, int groupStatus) {}
-                };
-
         // Verify parameter
         assertThrows(NullPointerException.class, () ->
-                mBluetoothLeAudio.registerCallback(null, callback));
+                mBluetoothLeAudio.registerCallback(null, mTestCallback));
         assertThrows(NullPointerException.class, () ->
-                mBluetoothLeAudio.registerCallback(executor, null));
+                mBluetoothLeAudio.registerCallback(mTestExecutor, null));
         assertThrows(NullPointerException.class, () ->
                 mBluetoothLeAudio.unregisterCallback(null));
+
+        // Test success register unregister
+        try {
+            mBluetoothLeAudio.registerCallback(mTestExecutor, mTestCallback);
+        } catch (Exception e) {
+            fail("Exception caught from register(): " + e.toString());
+        }
+
+        try {
+            mBluetoothLeAudio.unregisterCallback(mTestCallback);
+        } catch (Exception e) {
+            fail("Exception caught from unregister(): " + e.toString());
+        }
+    }
+
+    public void testCallback() {
+        if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothLeAudio);
+
+        mTestGroupId = 1;
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        mTestGroupStatus = 0;
+
+        mCodecConfigChangedCalled = false;
+        mGroupNodeAddedCalled = false;
+        mGroupStatusChangedCalled = false;
+        mGroupNodeRemovedCalled = false;
+
+        mTestCallback.onCodecConfigChanged(mTestGroupId, TEST_CODEC_STATUS);
+        mTestCallback.onGroupNodeAdded(mTestDevice, mTestGroupId);
+        mTestCallback.onGroupNodeRemoved(mTestDevice, mTestGroupId);
+        mTestCallback.onGroupStatusChanged(mTestGroupId, mTestGroupStatus);
+
+        assertTrue(mCodecConfigChangedCalled);
+        assertTrue(mGroupNodeAddedCalled);
+        assertTrue(mGroupNodeRemovedCalled);
+        assertTrue(mGroupStatusChangedCalled);
+    }
+
+    public void testGetConnectedGroupLeadDevice() {
+        if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothLeAudio);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        int groupId = 1;
+
+        // Verify returns null for unknown group id
+        assertEquals(null, mBluetoothLeAudio.getConnectedGroupLeadDevice(groupId));
     }
 
     private boolean waitForProfileConnect() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
index 667aa52..1d800bd 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
@@ -95,8 +95,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -114,7 +113,9 @@
                 mBluetoothLeBroadcastAssistant = null;
                 mIsProfileReady = false;
             }
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
index 7390b82..b72a04c 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
@@ -69,8 +69,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -79,8 +78,8 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (!mIsBroadcastSourceSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(
+                            BluetoothProfile.LE_AUDIO_BROADCAST);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -89,7 +88,9 @@
     @After
     public void tearDown() {
         if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
index ad96fe0..bd1d9b9 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
@@ -92,8 +92,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -102,8 +101,7 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (!mIsBroadcastSourceSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -112,7 +110,9 @@
     @After
     public void tearDown() {
         if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
index cc28740..edb8b06 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
@@ -87,8 +87,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -97,8 +96,7 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (!mIsBroadcastSourceSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -107,7 +105,9 @@
     @After
     public void tearDown() {
         if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
index 910e55d..4f36364 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
@@ -83,8 +83,7 @@
                 mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
         if (mIsBroadcastAssistantSupported) {
             boolean isBroadcastAssistantEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastAssistantEnabledInConfig);
         }
@@ -93,8 +92,7 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (!mIsBroadcastSourceSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(
-                            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -103,7 +101,9 @@
     @After
     public void tearDown() {
         if (mHasBluetooth) {
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
index d73f721..a58ae03 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
@@ -55,7 +55,6 @@
     private static final String TAG = BluetoothLeBroadcastTest.class.getSimpleName();
 
     private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
-    private static final String PROFILE_SUPPORTED_LE_BROADCAST = "profile_supported_le_broadcast";
 
     private Context mContext;
     private boolean mHasBluetooth;
@@ -90,7 +89,7 @@
                 mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
         if (mIsLeBroadcastSupported) {
             boolean isBroadcastSourceEnabledInConfig =
-                    TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO_BROADCAST);
+                    TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
             assertTrue("Config must be true when profile is supported",
                     isBroadcastSourceEnabledInConfig);
         }
@@ -111,7 +110,9 @@
                 mBluetoothLeBroadcast = null;
                 mIsProfileReady = false;
             }
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mAdapter = null;
             TestUtils.dropPermissionAsShellUid();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
index 29d9bd5..1f220cd 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
@@ -27,7 +27,6 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothSap;
 import android.content.pm.PackageManager;
-import android.sysprop.BluetoothProperties;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
@@ -62,7 +61,7 @@
 
         if (!mHasBluetooth) return;
 
-        mIsSapSupported = BluetoothProperties.isProfileSapServerEnabled().orElse(false);
+        mIsSapSupported = TestUtils.isProfileEnabled(BluetoothProfile.SAP);
         if (!mIsSapSupported) return;
 
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -90,7 +89,9 @@
                 mIsProfileReady = false;
             }
             mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
-            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            if (mAdapter != null) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+            }
             mUiAutomation.dropShellPermissionIdentity();
             mAdapter = null;
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
new file mode 100644
index 0000000..b6d20ec
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2022 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.bluetooth.cts;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothVolumeControl;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BluetoothVolumeControlTest extends AndroidTestCase {
+    private static final String TAG = BluetoothVolumeControlTest.class.getSimpleName();
+
+    private static final int PROXY_CONNECTION_TIMEOUT_MS = 500;  // ms timeout for Proxy Connect
+
+    private boolean mHasBluetooth;
+    private BluetoothAdapter mAdapter;
+
+    private BluetoothVolumeControl mBluetoothVolumeControl;
+    private boolean mIsVolumeControlSupported;
+    private boolean mIsProfileReady;
+    private Condition mConditionProfileIsConnected;
+    private ReentrantLock mProfileConnectedlock;
+    private boolean mVolumeOffsetChangedCallbackCalled;
+    private TestCallback mTestCallback;
+    private Executor mTestExecutor;
+    private BluetoothDevice mTestDevice;
+    private int mTestVolumeOffset;
+
+    class TestCallback implements BluetoothVolumeControl.Callback {
+        @Override
+        public void onVolumeOffsetChanged(BluetoothDevice device, int volumeOffset) {
+            mVolumeOffsetChangedCallbackCalled = true;
+            assertTrue(device == mTestDevice);
+            assertTrue(volumeOffset == mTestVolumeOffset);
+        }
+    };
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+            mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_BLUETOOTH);
+
+            if (!mHasBluetooth) return;
+
+            TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+            BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
+            mAdapter = manager.getAdapter();
+            assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
+
+            mProfileConnectedlock = new ReentrantLock();
+            mConditionProfileIsConnected  = mProfileConnectedlock.newCondition();
+            mIsProfileReady = false;
+            mBluetoothVolumeControl = null;
+
+            boolean isLeAudioSupportedInConfig =
+                     TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO);
+            boolean isVolumeControlEnabledInConfig =
+                     TestUtils.getProfileConfigValueOrDie(BluetoothProfile.VOLUME_CONTROL);
+            if (isLeAudioSupportedInConfig) {
+                /* If Le Audio is supported then Volume Control shall be supported */
+                assertTrue("Config must be true when profile is supported",
+                        isVolumeControlEnabledInConfig);
+            }
+
+            if (isVolumeControlEnabledInConfig) {
+                mIsVolumeControlSupported = mAdapter.getProfileProxy(getContext(),
+                        new BluetoothVolumeControlServiceListener(),
+                        BluetoothProfile.VOLUME_CONTROL);
+                assertTrue("Service shall be supported ", mIsVolumeControlSupported);
+
+                mTestCallback = new TestCallback();
+                mTestExecutor = mContext.getMainExecutor();
+            }
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (mHasBluetooth) {
+            if (mBluetoothVolumeControl != null) {
+                mBluetoothVolumeControl.close();
+                mBluetoothVolumeControl = null;
+                mIsProfileReady = false;
+                mTestDevice = null;
+                mTestVolumeOffset = 0;
+                mTestCallback = null;
+                mTestExecutor = null;
+            }
+            if (mAdapter != null ) {
+                assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+                mAdapter = null;
+            }
+            TestUtils.dropPermissionAsShellUid();
+        }
+    }
+
+    public void testGetConnectedDevices() {
+        if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothVolumeControl);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        // Verify returns empty list if bluetooth is not enabled
+        List<BluetoothDevice> connectedDevices = mBluetoothVolumeControl.getConnectedDevices();
+        assertTrue(connectedDevices.isEmpty());
+    }
+
+    public void testGetDevicesMatchingConnectionStates() {
+        if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothVolumeControl);
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        // Verify returns empty list if bluetooth is not enabled
+        List<BluetoothDevice> connectedDevices =
+                mBluetoothVolumeControl.getDevicesMatchingConnectionStates(null);
+        assertTrue(connectedDevices.isEmpty());
+    }
+
+    public void  testRegisterUnregisterCallback() {
+        if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothVolumeControl);
+
+        // Verify parameter
+        assertThrows(NullPointerException.class, () ->
+                mBluetoothVolumeControl.registerCallback(null, mTestCallback));
+        assertThrows(NullPointerException.class, () ->
+                mBluetoothVolumeControl.registerCallback(mTestExecutor, null));
+        assertThrows(NullPointerException.class, () ->
+                mBluetoothVolumeControl.unregisterCallback(null));
+
+        // Test success register unregister
+        try {
+            mBluetoothVolumeControl.registerCallback(mTestExecutor, mTestCallback);
+        } catch (Exception e) {
+            fail("Exception caught from register(): " + e.toString());
+        }
+
+        try {
+            mBluetoothVolumeControl.unregisterCallback(mTestCallback);
+        } catch (Exception e) {
+            fail("Exception caught from unregister(): " + e.toString());
+        }
+
+        TestUtils.dropPermissionAsShellUid();
+        // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+        assertThrows(SecurityException.class,
+                () -> mBluetoothVolumeControl.registerCallback(mTestExecutor, mTestCallback));
+
+        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+    }
+
+    public void testSetVolumeOffset() {
+        if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothVolumeControl);
+
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+
+        try {
+            mBluetoothVolumeControl.setVolumeOffset(mTestDevice, 0);
+        } catch (Exception e) {
+            fail("Exception caught from connect(): " + e.toString());
+        }
+    }
+
+    public void testIsVolumeOffsetAvailable() {
+        if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothVolumeControl);
+
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        // Verify returns false if bluetooth is not enabled
+        assertTrue(!mBluetoothVolumeControl.isVolumeOffsetAvailable(mTestDevice));
+    }
+
+    public void testVolumeOffsetCallback() {
+        if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothVolumeControl);
+
+        mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        mVolumeOffsetChangedCallbackCalled = false;
+
+        /* Note. This is just for api coverage until proper testing tools are set up */
+        mTestVolumeOffset = 1;
+        mTestCallback.onVolumeOffsetChanged(mTestDevice, mTestVolumeOffset);
+        assertTrue(mVolumeOffsetChangedCallbackCalled);
+    }
+
+    private boolean waitForProfileConnect() {
+        mProfileConnectedlock.lock();
+        try {
+            // Wait for the Adapter to be disabled
+            while (!mIsProfileReady) {
+                if (!mConditionProfileIsConnected.await(
+                        PROXY_CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    // Timeout
+                    Log.e(TAG, "Timeout while waiting for Profile Connect");
+                    break;
+                } // else spurious wakeups
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "waitForProfileConnect: interrrupted");
+        } finally {
+            mProfileConnectedlock.unlock();
+        }
+        return mIsProfileReady;
+    }
+
+    private final class BluetoothVolumeControlServiceListener implements
+            BluetoothProfile.ServiceListener {
+
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mProfileConnectedlock.lock();
+            mBluetoothVolumeControl = (BluetoothVolumeControl) proxy;
+            mIsProfileReady = true;
+            try {
+                mConditionProfileIsConnected.signal();
+            } finally {
+                mProfileConnectedlock.unlock();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+        }
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
index 7a227fc..2ed393f 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
@@ -83,6 +83,9 @@
         if (!isBleSupported()) return;
         mIsBleSupported = true;
 
+        mIsHearingAidSupported = TestUtils.isProfileEnabled(BluetoothProfile.HEARING_AID);
+        if (!mIsHearingAidSupported) return;
+
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
 
@@ -95,17 +98,18 @@
         mConditionProfileIsConnected  = mProfileConnectedlock.newCondition();
         mIsProfileReady = false;
         mService = null;
-        mIsHearingAidSupported = mBluetoothAdapter.getProfileProxy(getContext(),
-                                                  new HearingAidsServiceListener(),
-                                                  BluetoothProfile.HEARING_AID);
-        if (!mIsHearingAidSupported) return;
+        mBluetoothAdapter.getProfileProxy(getContext(), new HearingAidsServiceListener(),
+                BluetoothProfile.HEARING_AID);
     }
 
     @Override
     public void tearDown() {
-        if (!mIsBleSupported) return;
-
-        assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+        if (!(mIsBleSupported && mIsHearingAidSupported)) {
+            return;
+        }
+        if (mBluetoothAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+        }
         mUiAutomation.dropShellPermissionIdentity();
     }
 
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
index d152d84..62d1c6a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
@@ -51,7 +51,9 @@
         if (!TestUtils.isBleSupported(getContext())) {
             return;
         }
-        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        if (mAdapter != null) {
+            assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+        }
         mAdapter = null;
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
             .dropShellPermissionIdentity();
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
index 40d89fd..34bc0a3 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
@@ -16,9 +16,7 @@
 
 package android.bluetooth.cts;
 
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,8 +26,8 @@
 import android.bluetooth.le.ScanRecord;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.provider.Settings;
+import android.sysprop.BluetoothProperties;
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -50,93 +48,6 @@
     static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth.services";
 
     /**
-     * Get the Config.xml name tag for a particular Bluetooth profile
-     * @param profile profile id from {@link BluetoothProfile}
-     * @return config name tag, or null if the tag name is not available
-     */
-    @Nullable static String profileIdToConfigTag(int profile) {
-        switch (profile) {
-            case BluetoothProfile.A2DP:
-                return "profile_supported_a2dp";
-            case BluetoothProfile.A2DP_SINK:
-                return "profile_supported_a2dp_sink";
-            case BluetoothProfile.HEADSET:
-                return "profile_supported_hs_hfp";
-            case BluetoothProfile.HEADSET_CLIENT:
-                return "profile_supported_hfpclient";
-            case BluetoothProfile.HID_HOST:
-                return "profile_supported_hid_host";
-            case BluetoothProfile.OPP:
-                return "profile_supported_opp";
-            case BluetoothProfile.PAN:
-                return "profile_supported_pan";
-            case BluetoothProfile.PBAP:
-                return "profile_supported_pbap";
-            case BluetoothProfile.GATT:
-                return "profile_supported_gatt";
-            case BluetoothProfile.MAP:
-                return "profile_supported_map";
-            // Hidden profile
-            // case BluetoothProfile.AVRCP:
-            //    return "profile_supported_avrcp_target";
-            case BluetoothProfile.AVRCP_CONTROLLER:
-                return "profile_supported_avrcp_controller";
-            case BluetoothProfile.SAP:
-                return "profile_supported_sap";
-            case BluetoothProfile.PBAP_CLIENT:
-                return "profile_supported_pbapclient";
-            case BluetoothProfile.MAP_CLIENT:
-                return "profile_supported_mapmce";
-            case BluetoothProfile.HID_DEVICE:
-                return "profile_supported_hid_device";
-            case BluetoothProfile.LE_AUDIO:
-                return "profile_supported_le_audio";
-            case BluetoothProfile.LE_AUDIO_BROADCAST:
-                return "profile_supported_le_audio_broadcast";
-            case BluetoothProfile.VOLUME_CONTROL:
-                return "profile_supported_vc";
-            // Hidden profile
-            // case BluetoothProfile.MCP_SERVER:
-            //    return "profile_supported_mcp_server";
-            case BluetoothProfile.CSIP_SET_COORDINATOR:
-                return "profile_supported_csip_set_coordinator";
-            // Hidden profile
-            // case BluetoothProfile.LE_CALL_CONTROL:
-            //    return "profile_supported_le_call_control";
-            case BluetoothProfile.HAP_CLIENT:
-                return "profile_supported_hap_client";
-            case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
-                return "profile_supported_bass_client";
-            default:
-                return null;
-        }
-    }
-
-    /**
-     * Checks if a particular Bluetooth profile is configured for this device
-     * Fail the test if profile config status cannot be obtained
-     */
-    static boolean getProfileConfigValueOrDie(int profile) {
-        String profileConfigValueTag = profileIdToConfigTag(profile);
-        assertNotNull(profileConfigValueTag);
-        assertNotEquals("profile tag cannot be empty", 0, profileConfigValueTag.length());
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        Resources bluetoothResources = null;
-        try {
-            bluetoothResources = context.getPackageManager().getResourcesForApplication(
-                    BLUETOOTH_PACKAGE_NAME);
-        } catch (PackageManager.NameNotFoundException e) {
-            fail("Cannot get Bluetooth package resource");
-        }
-        int resourceId = bluetoothResources.getIdentifier(
-                profileConfigValueTag, "bool", BLUETOOTH_PACKAGE_NAME);
-        if (resourceId == 0) {
-            return false;
-        }
-        return bluetoothResources.getBoolean(resourceId);
-    }
-
-    /**
      * Checks whether this device has Bluetooth feature
      * @return true if this device has Bluetooth feature
      */
@@ -147,10 +58,74 @@
     }
 
     /**
+     * Get the current enabled status of a given profile
+     */
+    static boolean isProfileEnabled(int profile) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);
+            case BluetoothProfile.A2DP_SINK:
+                return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
+            // Hidden profile
+            // case BluetoothProfile.AVRCP:
+            //     return BluetoothProperties.isProfileAvrcpTargetEnabled().orElse(false);
+            case BluetoothProfile.AVRCP_CONTROLLER:
+                return BluetoothProperties.isProfileAvrcpControllerEnabled().orElse(false);
+            case BluetoothProfile.CSIP_SET_COORDINATOR:
+                return BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false);
+            case BluetoothProfile.GATT:
+                return BluetoothProperties.isProfileGattEnabled().orElse(false);
+            case BluetoothProfile.HAP_CLIENT:
+                return BluetoothProperties.isProfileHapClientEnabled().orElse(false);
+            case BluetoothProfile.HEADSET:
+                return BluetoothProperties.isProfileHfpAgEnabled().orElse(false);
+            case BluetoothProfile.HEADSET_CLIENT:
+                return BluetoothProperties.isProfileHfpHfEnabled().orElse(false);
+            case BluetoothProfile.HEARING_AID:
+                return BluetoothProperties.isProfileAshaCentralEnabled().orElse(false);
+            case BluetoothProfile.HID_DEVICE:
+                return BluetoothProperties.isProfileHidDeviceEnabled().orElse(false);
+            case BluetoothProfile.HID_HOST:
+                return BluetoothProperties.isProfileHidHostEnabled().orElse(false);
+            case BluetoothProfile.LE_AUDIO:
+                return BluetoothProperties.isProfileBapUnicastServerEnabled().orElse(false);
+            case BluetoothProfile.LE_AUDIO_BROADCAST:
+                return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false);
+            case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
+                return BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
+            // Hidden profile
+            // case BluetoothProfile.LE_CALL_CONTROL:
+            //     return BluetoothProperties.isProfileTbsServerEnabled().orElse(false);
+            case BluetoothProfile.MAP:
+                return BluetoothProperties.isProfileMapServerEnabled().orElse(false);
+            case BluetoothProfile.MAP_CLIENT:
+                return BluetoothProperties.isProfileMapClientEnabled().orElse(false);
+            // Hidden profile
+            // case BluetoothProfile.MCP_SERVER:
+            //     return BluetoothProperties.isProfileMcpServerEnabled().orElse(false);
+            case BluetoothProfile.OPP:
+                return BluetoothProperties.isProfileOppEnabled().orElse(false);
+            case BluetoothProfile.PAN:
+                return BluetoothProperties.isProfilePanNapEnabled().orElse(false)
+                        || BluetoothProperties.isProfilePanPanuEnabled().orElse(false);
+            case BluetoothProfile.PBAP:
+                return BluetoothProperties.isProfilePbapServerEnabled().orElse(false);
+            case BluetoothProfile.PBAP_CLIENT:
+                return BluetoothProperties.isProfilePbapClientEnabled().orElse(false);
+            case BluetoothProfile.SAP:
+                return BluetoothProperties.isProfileSapServerEnabled().orElse(false);
+            case BluetoothProfile.VOLUME_CONTROL:
+                return BluetoothProperties.isProfileVcServerEnabled().orElse(false);
+            default:
+                return false;
+        }
+    }
+
+    /**
      * Adopt shell UID's permission via {@link android.app.UiAutomation}
      * @param permission permission to adopt
      */
-    static void adoptPermissionAsShellUid(@NonNull String permission) {
+    static void adoptPermissionAsShellUid(@Nullable String... permission) {
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity(permission);
     }
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java
index aab86c7..218486b 100644
--- a/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java
@@ -42,7 +42,6 @@
                 assertTrue(BuildHelper.isUserBuild());
                 assertFalse(BuildHelper.isUserDebugBuild());
                 assertFalse(BuildHelper.isEngBuild());
-                assertFalse(BuildHelper.isDebuggableBuild());
                 break;
             case BUILD_TYPE_USER_DEBUG:
                 assertFalse(BuildHelper.isUserBuild());
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/provider/SettingsHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/provider/SettingsHelperTest.java
new file mode 100644
index 0000000..e6c21f2
--- /dev/null
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/provider/SettingsHelperTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cts.builtin.provider;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.car.builtin.provider.SettingsHelper;
+
+import org.junit.Test;
+
+public final class SettingsHelperTest {
+
+    @Test
+    public void testConstants() {
+        assertWithMessage("SYSTEM_LOCALES").that(SettingsHelper.SYSTEM_LOCALES)
+                .isEqualTo("system_locales");
+    }
+}
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java
index ecd867b..eb5b0e9 100644
--- a/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java
@@ -16,7 +16,7 @@
 
 package android.car.cts.builtin.util;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.app.Instrumentation;
 import android.car.builtin.util.AssistUtilsHelper;
@@ -26,10 +26,13 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
@@ -42,23 +45,27 @@
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private final Context mContext = mInstrumentation.getContext();
 
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
+                PERMISSION_ACCESS_VOICE_INTERACTION_SERVICE);
+    }
+
+    @After
+    public void cleanUp() {
+        mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
+    }
+
     @Test
     public void testOnShownCallback() throws Exception {
-        try {
-            // setup
-            mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
-                    PERMISSION_ACCESS_VOICE_INTERACTION_SERVICE);
+        SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+        AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+        callbackHelperImpl.waitForCallback();
 
-            // execution
-            SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
-            AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
-            callbackHelperImpl.waitForCallbackLatch();
+        assertWithMessage("Voice session shown")
+                .that(callbackHelperImpl.isSessionOnShown()).isTrue();
 
-            // assertion
-            assertTrue(callbackHelperImpl.isSessionOnShown());
-        } finally {
-            mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
-        }
+        hideSessionAndWait();
     }
 
     @Test
@@ -67,7 +74,98 @@
         // call onFailed API
     }
 
-    private static class SessionShowCallbackHelperImpl implements
+    @Test
+    public void isSessionRunning_whenSessionIsShown_succeeds() throws Exception {
+        SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+        AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+        callbackHelperImpl.waitForCallback();
+
+        assertWithMessage("Voice interaction session running")
+                .that(AssistUtilsHelper.isSessionRunning(mContext)).isTrue();
+
+        hideSessionAndWait();
+    }
+
+    @Test
+    public void registerVoiceInteractionSessionListenerHelper_onShowSession() throws Exception {
+        VoiceInteractionSessionListener listener = new VoiceInteractionSessionListener();
+        AssistUtilsHelper.registerVoiceInteractionSessionListenerHelper(mContext, listener);
+
+        SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+        AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+        callbackHelperImpl.waitForCallback();
+
+        listener.waitForSessionChange();
+
+        assertWithMessage("Voice interaction session shown")
+                .that(listener.mIsSessionShown).isTrue();
+
+        hideSessionAndWait();
+    }
+
+    @Test
+    public void registerVoiceInteractionSessionListenerHelper_hideCurrentSession()
+            throws Exception {
+        VoiceInteractionSessionListener listener = new VoiceInteractionSessionListener();
+        AssistUtilsHelper.registerVoiceInteractionSessionListenerHelper(mContext, listener);
+
+        SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+        AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+        callbackHelperImpl.waitForCallback();
+
+        listener.waitForSessionChange();
+        listener.reset();
+
+        AssistUtilsHelper.hideCurrentSession(mContext);
+        listener.waitForSessionChange();
+
+        assertWithMessage("Voice interaction session shown")
+                .that(listener.mIsSessionShown).isFalse();
+    }
+
+    private void hideSessionAndWait() throws Exception {
+        if (!AssistUtilsHelper.isSessionRunning(mContext)) {
+            return;
+        }
+        VoiceInteractionSessionListener listener = new VoiceInteractionSessionListener();
+        AssistUtilsHelper.registerVoiceInteractionSessionListenerHelper(mContext, listener);
+
+        listener.reset();
+        listener.waitForSessionChange();
+    }
+
+    private static final class VoiceInteractionSessionListener implements
+            AssistUtilsHelper.VoiceInteractionSessionListenerHelper {
+
+        private final Semaphore mChangeWait = new Semaphore(0);
+        private boolean mIsSessionShown;
+
+        @Override
+        public void onVoiceSessionShown() {
+            mIsSessionShown = true;
+            Log.d(TAG, "onVoiceSessionShown is called");
+            mChangeWait.release();
+        }
+
+        @Override
+        public void onVoiceSessionHidden() {
+            mIsSessionShown = false;
+            Log.d(TAG, "onVoiceSessionHidden is called");
+            mChangeWait.release();
+        }
+
+        private void waitForSessionChange() throws Exception {
+            if (!mChangeWait.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                throw new IllegalStateException("Timed out waiting for session change");
+            }
+        }
+
+        private void reset() {
+            mChangeWait.drainPermits();
+        }
+    }
+
+    private static final class SessionShowCallbackHelperImpl implements
             AssistUtilsHelper.VoiceInteractionSessionShowCallbackHelper {
 
         private final CountDownLatch mCallbackLatch = new CountDownLatch(1);
@@ -87,7 +185,7 @@
             return mIsSessionOnShown;
         }
 
-        private void waitForCallbackLatch() throws Exception {
+        private void waitForCallback() throws Exception {
             mCallbackLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
         }
     }
diff --git a/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java b/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
index 2001aa3..a907340 100644
--- a/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
+++ b/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
@@ -48,7 +48,7 @@
  */
 public final class GameServiceTestService extends Service {
 
-    private static final long SCREENSHOT_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
+    private static final long SCREENSHOT_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(15);
 
     @Nullable
     private ActivityResult mLastActivityResult;
diff --git a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
index bf17876..fb3d7ed 100644
--- a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
@@ -50,7 +50,7 @@
 @RunWith(AndroidJUnit4.class)
 public class SystemPaletteTest {
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
     private static final String TAG = "SystemPaletteTest";
 
     @Test
@@ -87,7 +87,7 @@
             });
 
             final int[] allColors = new int[65];
-            new PollingCheck(5_000L, "Invalid tonal palettes for " + color + " " + style) {
+            new PollingCheck(15_000L, "Invalid tonal palettes for " + color + " " + style) {
                 @Override
                 protected boolean check() {
 
@@ -98,8 +98,12 @@
                     System.arraycopy(getAllNeutral2Colors(context), 0, allColors, 52, 13);
 
                     if (DEBUG) {
+                        final String setting = Settings.Secure
+                                .getString(context.getContentResolver(),
+                                        Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES);
                         Log.d(TAG, "Expected:\n" + Arrays.toString(expectedPalette)
-                                        + "\nActual:\n" + Arrays.toString(allColors));
+                                        + "\nActual:\n" + Arrays.toString(allColors)
+                                        + "\nSetting:\n" + setting);
                     }
 
                     return Arrays.equals(allColors, expectedPalette);
diff --git a/tests/tests/keystore/Android.bp b/tests/tests/keystore/Android.bp
index a7e67e3..076ce21 100644
--- a/tests/tests/keystore/Android.bp
+++ b/tests/tests/keystore/Android.bp
@@ -62,6 +62,7 @@
         "android.test.base",
     ],
     static_libs: [
+        "Nene",
         "androidx.test.rules",
         "compatibility-device-util-axt",
         "core-tests-support",
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index bd9a64a..3e3a3c0 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -64,6 +64,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.keystore.cts.Attestation;
 import android.keystore.cts.util.TestUtils;
 import android.os.Build;
 import android.os.SystemProperties;
@@ -80,6 +81,8 @@
 import androidx.test.filters.RequiresDevice;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.permissions.PermissionContext;
 import com.android.compatibility.common.util.CddTest;
 
 import com.google.common.collect.ImmutableSet;
@@ -490,6 +493,54 @@
         }
     }
 
+    @Test
+    public void testEcAttestation_UniqueIdWorksWithCorrectPermission() throws Exception {
+        String keystoreAlias = "test_key";
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+                .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+                .setAttestationChallenge(new byte[128])
+                .setUniqueIdIncluded(true)
+                .build();
+
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+
+        try (PermissionContext c = TestApis.permissions().withPermission(
+                  "android.permission.REQUEST_UNIQUE_ID_ATTESTATION")) {
+            generateKeyPair(KEY_ALGORITHM_EC, spec);
+            Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+            Attestation attestation = Attestation.loadFromCertificate((X509Certificate) certificates[0]);
+            byte[] firstUniqueId = attestation.getUniqueId();
+            assertTrue("UniqueId must not be empty", firstUniqueId.length > 0);
+
+            // The unique id rotates (30 days in the default implementation), and it's possible to
+            // get a spurious failure if the test runs exactly when the rotation occurs. Allow a
+            // single retry, just in case.
+            byte[] secondUniqueId = null;
+            for (int i = 0; i < 2; ++i) {
+                keyStore.deleteEntry(keystoreAlias);
+
+                generateKeyPair(KEY_ALGORITHM_EC, spec);
+                certificates = keyStore.getCertificateChain(keystoreAlias);
+                attestation = Attestation.loadFromCertificate((X509Certificate) certificates[0]);
+                secondUniqueId = attestation.getUniqueId();
+
+                if (Arrays.equals(firstUniqueId, secondUniqueId)) {
+                    break;
+                } else {
+                    firstUniqueId = secondUniqueId;
+                    secondUniqueId = null;
+                }
+            }
+            assertTrue("UniqueIds must be consistent",
+                    Arrays.equals(firstUniqueId, secondUniqueId));
+
+        } finally {
+            keyStore.deleteEntry(keystoreAlias);
+        }
+    }
+
     @RequiresDevice
     @Test
     public void testRsaAttestation() throws Exception {
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
index ff1944e..7412d16 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
@@ -23,6 +23,7 @@
 import android.media.MediaFormat;
 import android.media.cts.InputSurface;
 import android.media.cts.OutputSurface;
+import android.media.cts.TestArgs;
 import android.opengl.GLES20;
 import android.util.Log;
 
@@ -126,15 +127,35 @@
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] encoderNamesForMime = MediaUtils.getEncoderNamesForMime((String)arg[0]);
-            String[] decoderNamesForMime = MediaUtils.getDecoderNamesForMime((String)arg[0]);
-            Object[] testArgs = new Object[argLength + 2];
-            // Add encoder name and decoder name as first two arguments and then
-            // copy arguments passed
-            testArgs[0] = encoderNamesForMime[0];
-            testArgs[1] = decoderNamesForMime[0];
-            System.arraycopy(arg, 0, testArgs, 2, argLength);
-            argsList.add(testArgs);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] encoderNames = MediaUtils.getEncoderNamesForMime(mediaType);
+            String[] decoderNames = MediaUtils.getDecoderNamesForMime(mediaType);
+            // First pair of decoder and encoder that supports given mediaType is chosen
+            outerLoop:
+            for (String decoder : decoderNames) {
+                if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
+                    continue;
+                }
+                for (String encoder : encoderNames) {
+                    if (TestArgs.CODEC_PREFIX != null &&
+                            !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
+                        continue;
+                    }
+                    Object[] testArgs = new Object[argLength + 2];
+                    // Add encoder name and decoder name as first two arguments and then
+                    // copy arguments passed
+                    testArgs[0] = encoder;
+                    testArgs[1] = decoder;
+                    System.arraycopy(arg, 0, testArgs, 2, argLength);
+                    argsList.add(testArgs);
+                    // Only one combination of encoder and decoder is tested
+                    break outerLoop;
+                }
+            }
         }
         return argsList;
     }
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
index 4a023a0..be4c935 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
@@ -28,6 +28,7 @@
 import android.media.cts.NdkMediaCodec;
 import android.media.cts.OutputSurface;
 import android.media.cts.SdkMediaCodec;
+import android.media.cts.TestArgs;
 import android.opengl.GLES20;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresDevice;
@@ -128,17 +129,41 @@
         int argLength = exhaustiveArgsList.get(0).length;
         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         for (Object[] arg : exhaustiveArgsList) {
-            MediaFormat format = MediaFormat.createVideoFormat((String)arg[0], (Integer)arg[1],
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+
+            MediaFormat format = MediaFormat.createVideoFormat(mediaType, (Integer)arg[1],
                     (Integer)arg[2]);
 
-            String eName = mcl.findEncoderForFormat(format);
-            String dName = mcl.findDecoderForFormat(format);
-
-            Object[] testArgs = new Object[argLength + 2];
-            testArgs[0] = eName;
-            testArgs[1] = dName;
-            System.arraycopy(arg, 0, testArgs, 2, argLength);
-            argsList.add(testArgs);
+            String[] encoderNames = MediaUtils.getEncoderNamesForMime(mediaType);
+            String[] decoderNames = MediaUtils.getDecoderNamesForMime(mediaType);
+            // First pair of decoder and encoder that supports given format is chosen
+            outerLoop:
+            for (String decoder : decoderNames) {
+                if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
+                    continue;
+                }
+                for (String encoder : encoderNames) {
+                    if (TestArgs.CODEC_PREFIX != null &&
+                            !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
+                        continue;
+                    }
+                    if (MediaUtils.supports(encoder, format) &&
+                            MediaUtils.supports(decoder, format)) {
+                        Object[] testArgs = new Object[argLength + 2];
+                        testArgs[0] = encoder;
+                        testArgs[1] = decoder;
+                        System.arraycopy(arg, 0, testArgs, 2, argLength);
+                        argsList.add(testArgs);
+                        // Test only the first codecs that support given format.
+                        // Remove the following break statement to test all codecs on the device.
+                        break outerLoop;
+                    }
+                }
+            }
         }
         return argsList;
     }
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
index 5ef9e7f..ecdb084 100755
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
@@ -30,6 +30,7 @@
 import android.media.cts.InputSurface;
 import android.media.cts.NonMediaMainlineTest;
 import android.media.cts.OutputSurface;
+import android.media.cts.TestArgs;
 import android.opengl.GLES20;
 import android.os.Build;
 import android.os.Bundle;
@@ -95,8 +96,6 @@
     private static final boolean VERBOSE = false;           // lots of logging
     private static final boolean DEBUG_SAVE_FILE = false;   // save copy of encoded movie
     private static final String DEBUG_FILE_NAME_BASE = "/sdcard/test.";
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static String mCodecPrefix;
 
     // Virtual display characteristics.  Scaled down from full display size because not all
     // devices can encode at the resolution of their own display.
@@ -161,9 +160,14 @@
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] componentNames = MediaUtils.getEncoderNamesForMime((String)arg[0]);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] componentNames = MediaUtils.getEncoderNamesForMime(mediaType);
             for (String name : componentNames) {
-                if (mCodecPrefix != null && !name.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 Object[] testArgs = new Object[argLength + 1];
@@ -176,11 +180,6 @@
         return argsList;
     }
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     @Parameterized.Parameters(name = "{index}({0}:{6})")
     public static Collection<Object[]> input() {
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
index 1dd7651..61e8e33 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
@@ -21,6 +21,7 @@
 import android.media.MediaFormat;
 import android.media.cts.MediaCodecWrapper;
 import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.TestArgs;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
@@ -96,9 +97,6 @@
     // Maximum allowed key frame interval variation from the target value.
     private static final int MAX_KEYFRAME_INTERVAL_VARIATION = 3;
 
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static final String mCodecPrefix;
-
     @Parameterized.Parameter(0)
     public String mCodecName;
 
@@ -108,18 +106,18 @@
     @Parameterized.Parameter(2)
     public int mBitRateMode;
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) {
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType);
             for (String encoder : encodersForMime) {
-                if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null && !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 Object[] testArgs = new Object[argLength + 1];
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
index 2cd4632..ab46ff1 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
@@ -20,6 +20,7 @@
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
 import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.TestArgs;
 import android.platform.test.annotations.RequiresDevice;
 import android.util.Log;
 import android.util.Size;
@@ -73,14 +74,24 @@
             if (info.isAlias() || info.isEncoder()) {
                 continue;
             }
+            String name = info.getName();
+            if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
+                continue;
+            }
+
             for (String type : info.getSupportedTypes()) {
                 if (!SUPPORTED_TYPES.contains(type)) {
                     continue;
                 }
-                testParams.add(new Object[] { info.getName(), type, Integer.valueOf(90) });
-                testParams.add(new Object[] { info.getName(), type, Integer.valueOf(180) });
-                testParams.add(new Object[] { info.getName(), type, Integer.valueOf(270) });
-                testParams.add(new Object[] { info.getName(), type, Integer.valueOf(360) });
+                if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                        !type.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                    continue;
+                }
+
+                testParams.add(new Object[] { name, type, Integer.valueOf(90) });
+                testParams.add(new Object[] { name, type, Integer.valueOf(180) });
+                testParams.add(new Object[] { name, type, Integer.valueOf(270) });
+                testParams.add(new Object[] { name, type, Integer.valueOf(360) });
             }
         }
         return testParams;
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java
index a6e587a..eb3bcf3 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java
@@ -23,6 +23,7 @@
 import android.media.MediaFormat;
 import android.media.cts.MediaCodecWrapper;
 import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.TestArgs;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
@@ -69,9 +70,6 @@
     // List of bitrates used in quality and basic bitrate tests.
     private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 };
 
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static final String mCodecPrefix;
-
     @Parameterized.Parameter(0)
     public String mCodecName;
 
@@ -81,18 +79,18 @@
     @Parameterized.Parameter(2)
     public int mBitRateMode;
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) {
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType);
             for (String encoder : encodersForMime) {
-                if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null && !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 Object[] testArgs = new Object[argLength + 1];
diff --git a/tests/tests/media/common/src/android/media/cts/TestArgs.java b/tests/tests/media/common/src/android/media/cts/TestArgs.java
new file mode 100644
index 0000000..a470d41
--- /dev/null
+++ b/tests/tests/media/common/src/android/media/cts/TestArgs.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 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 androidx.test.platform.app.InstrumentationRegistry;
+
+/**
+ * Contains arguments passed to the tests.
+ */
+public final class TestArgs {
+    private static final String CODEC_PREFIX_KEY = "codec-prefix";
+    public static final String CODEC_PREFIX;
+    private static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix";
+    public static final String MEDIA_TYPE_PREFIX;
+
+    static {
+        android.os.Bundle args = InstrumentationRegistry.getArguments();
+        CODEC_PREFIX = args.getString(CODEC_PREFIX_KEY);
+        MEDIA_TYPE_PREFIX = args.getString(MEDIA_TYPE_PREFIX_KEY);
+    }
+}
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
index e9b1093..32d2762 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
@@ -30,6 +30,7 @@
 import android.media.cts.MediaTestBase;
 import android.media.cts.OutputSurface;
 import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
 import android.os.Build;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -90,20 +91,12 @@
         super.tearDown();
     }
 
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static final String mCodecPrefix;
-
     @Parameterized.Parameter(0)
     public String mCodecName;
 
     @Parameterized.Parameter(1)
     public CodecList mCodecs;
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     public static Iterable<Codec> H264(CodecFactory factory) {
         return factory.createCodecList(
                 MediaFormat.MIMETYPE_VIDEO_AVC,
@@ -277,7 +270,8 @@
             if (arg instanceof CodecList) {
                 CodecList codecList = (CodecList)arg;
                 for (Codec codec : codecList) {
-                    if (mCodecPrefix != null && !codec.name.startsWith(mCodecPrefix)) {
+                    if (TestArgs.CODEC_PREFIX != null &&
+                            !codec.name.startsWith(TestArgs.CODEC_PREFIX)) {
                         continue;
                     }
                     Object[] testArgs = new Object[2];
@@ -1542,6 +1536,11 @@
 
     public CodecFamily(String mime, final String ... resources) {
         try {
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mime.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                return;
+            }
+
             /* read all media */
             Media[] mediaList = new Media[resources.length];
             for (int i = 0; i < resources.length; i++) {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
index 0f3281c..6a2a0c3 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
@@ -27,6 +27,7 @@
 import android.media.MediaFormat;
 import android.media.cts.MediaCodecTunneledPlayer;
 import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.TestArgs;
 import android.os.Environment;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -69,7 +70,6 @@
     private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90;
     private static final int OFFSET = 10;
     private static final long PER_TEST_TIMEOUT_MS = 60000;
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
     private static final String[] VIDEO_FILES = {
         // 144p
         "video_decode_accuracy_and_capability-h264_256x108_30fps.mp4",
@@ -150,7 +150,6 @@
 
     private static final String INP_PREFIX = WorkDir.getMediaDirString() +
             "assets/decode_accuracy/";
-    private static String mCodecPrefix;
 
     private View videoView;
     private VideoViewFactory videoViewFactory;
@@ -160,11 +159,6 @@
     private String methodName;
     private SimplePlayer player;
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     public DecodeAccuracyTest(String decoderName, String fileName, String testName) {
         this.testName = testName;
         this.fileName = fileName;
@@ -199,9 +193,14 @@
             MediaFormat mediaFormat =
                     MediaUtils.getTrackFormatForResource(INP_PREFIX + file, "video");
             String mediaType = mediaFormat.getString(MediaFormat.KEY_MIME);
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
             String[] componentNames = MediaUtils.getDecoderNamesForMime(mediaType);
             for (String componentName : componentNames) {
-                if (mCodecPrefix != null && !componentName.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null &&
+                        !componentName.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 if (MediaUtils.supports(componentName, mediaFormat)) {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
index 0c79713..5cdafaf 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
@@ -24,6 +24,7 @@
 import android.media.MediaFormat;
 import android.media.cts.MediaTestBase;
 import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -77,12 +78,10 @@
     private static final String REPORT_LOG_NAME = "CtsMediaDecoderTestCases";
     private static final String TAG = "DecoderConformanceTest";
     private static final String CONFORMANCE_SUBDIR = "conformance_vectors/";
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
     private static final String mInpPrefix = WorkDir.getMediaDirString() + CONFORMANCE_SUBDIR;
     private static final Map<String, String> MIMETYPE_TO_TAG = new HashMap<String, String>() {{
         put(MediaFormat.MIMETYPE_VIDEO_VP9, "vp9");
     }};
-    private static String mCodecPrefix;
 
     private final String mDecoderName;
     private final String mMediaType;
@@ -93,21 +92,20 @@
 
     private DeviceReportLog mReportLog;
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     @Parameterized.Parameters(name = "{index}({0})")
     public static Collection<Object[]> input() throws Exception {
         final String[] mediaTypeList = new String[] {MediaFormat.MIMETYPE_VIDEO_VP9};
         final List<Object[]> argsList = new ArrayList<>();
         for (String mediaType : mediaTypeList) {
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
             String[] componentNames = MediaUtils.getDecoderNamesForMime(mediaType);
             List<String> testVectors = readCodecTestVectors(mediaType);
             for (String testVector : testVectors) {
                 for (String name : componentNames) {
-                    if (mCodecPrefix != null && !name.startsWith(mCodecPrefix)) {
+                    if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
                         continue;
                     }
                     argsList.add(new Object[] {name, mediaType, testVector});
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
index e2f5b25..d31e8b4 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
@@ -32,6 +32,7 @@
 import android.media.MediaFormat;
 import android.media.cts.CodecUtils;
 import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.platform.test.annotations.AppModeFull;
@@ -129,8 +130,15 @@
         final List<Object[]> argsList = new ArrayList<>();
         for (MediaAssets assets : ASSETS) {
             String mime = assets.getMime();
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mime.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
             String[] decoders = MediaUtils.getDecoderNamesForMime(mime);
             for (String decoder: decoders) {
+                if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
+                    continue;
+                }
                 for (MediaAsset asset : assets.getAssets()) {
                     String id = asset.getWidth() + "x" + asset.getHeight();
                     id += "_" + asset.getBitDepth() + "bit";
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
index 61f6cdd..35d377e 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
@@ -28,6 +28,7 @@
 import android.media.cts.MediaHeavyPresubmitTest;
 import android.media.cts.MediaTestBase;
 import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
 import android.media.cts.TestUtils;
 import android.os.Bundle;
 import android.platform.test.annotations.AppModeFull;
@@ -85,9 +86,6 @@
 
     private static final int MAX_SIZE_SAMPLES_IN_MEMORY_BYTES = 12 << 20;  // 12MB
 
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static final String mCodecPrefix;
-
     private final String mDecoderName;
     private final String mMediaType;
     private final String[] mResources;
@@ -106,9 +104,14 @@
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] decoders = MediaUtils.getDecoderNamesForMime((String) arg[0]);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] decoders = MediaUtils.getDecoderNamesForMime(mediaType);
             for (String decoder : decoders) {
-                if (mCodecPrefix != null && !decoder.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 Object[] testArgs = new Object[argLength + 1];
@@ -121,11 +124,6 @@
         return argsList;
     }
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     @Parameterized.Parameters(name = "{index}({0}:{3})")
     public static Collection<Object[]> input() {
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
index c95ed69..226bf85 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
@@ -21,6 +21,7 @@
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
 import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.RequiresDevice;
 import android.util.Log;
@@ -84,9 +85,6 @@
     private static boolean sSaveResults = false;
     static final Map<String, String> mDefaultEncoders = new HashMap<>();
 
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static String mCodecPrefix;
-
     private final String mEncoderName;
     private final String mMime;
     private final int[] mProfiles;
@@ -111,9 +109,14 @@
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] componentNames = MediaUtils.getEncoderNamesForMime((String)arg[0]);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] componentNames = MediaUtils.getEncoderNamesForMime(mediaType);
             for (String name : componentNames) {
-                if (mCodecPrefix != null && !name.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 Object[] testArgs = new Object[argLength + 1];
@@ -125,11 +128,6 @@
         return argsList;
     }
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
-
     @Parameterized.Parameters(name = "{index}({0}_{1})")
     public static Collection<Object[]> input() {
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
index b92f2ca..7b55e80 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
@@ -42,6 +42,7 @@
 import android.media.cts.NonMediaMainlineTest;
 import android.media.cts.OutputSurface;
 import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -100,9 +101,6 @@
     private static final String SOURCE_URL =
             mInpPrefix + "video_480x360_mp4_h264_871kbps_30fps.mp4";
 
-    private static final String CODEC_PREFIX_KEY = "codec-prefix";
-    private static final String mCodecPrefix;
-
     private final Encoder mEncHandle;
 
     private final boolean DEBUG = false;
@@ -119,10 +117,6 @@
         super.tearDown();
     }
 
-    static {
-        android.os.Bundle args = InstrumentationRegistry.getArguments();
-        mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
-    }
 
     class VideoStorage {
         private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
@@ -1271,9 +1265,14 @@
         final List<Object[]> argsList = new ArrayList<>();
         int argLength = exhaustiveArgsList.get(0).length;
         for (Object[] arg : exhaustiveArgsList) {
-            String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
+            String mediaType = (String)arg[0];
+            if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+                    !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+                continue;
+            }
+            String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType);
             for (String encoder : encodersForMime) {
-                if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
+                if (TestArgs.CODEC_PREFIX != null && !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
                     continue;
                 }
                 Object[] testArgs = new Object[argLength + 1];
diff --git a/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java b/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
index 6061f4a..3345f56 100644
--- a/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
+++ b/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
@@ -179,7 +179,7 @@
     }
 
     @Test
-    @SystemUserOnly(reason = "Profiles are only supported on system user.")
+    @RequireFeature(FEATURE_MANAGED_USERS)
     @EnsureHasPermission({CREATE_USERS, QUERY_USERS})
     public void testManagedProfile() throws Exception {
         UserHandle userHandle = null;
@@ -187,7 +187,9 @@
         try {
             userHandle = mUserManager.createProfile(
                     "Managed profile", UserManager.USER_TYPE_PROFILE_MANAGED, new HashSet<>());
-            assertThat(userHandle).isNotNull();
+
+            // Not all devices and user types support managed profiles; skip if this one doesn't.
+            assumeTrue("Couldn't create a managed profile", userHandle != null);
 
             final UserManager umOfProfile = sContext
                     .createPackageContextAsUser("android", 0, userHandle)
@@ -208,21 +210,24 @@
     @Test
     @EnsureHasPermission({QUERY_USERS})
     public void testSystemUser() throws Exception {
-      final UserManager umOfSys = sContext
-        .createPackageContextAsUser("android", 0, UserHandle.SYSTEM)
-        .getSystemService(UserManager.class);
+        final UserManager umOfSys = sContext
+                .createPackageContextAsUser("android", 0, UserHandle.SYSTEM)
+                .getSystemService(UserManager.class);
 
-      assertThat(umOfSys.isSystemUser()).isTrue();
+        // TODO(b/222584163): Remove the if{} clause after v33 Sdk bump.
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
+            assertThat(umOfSys.isSystemUser()).isTrue();
+        }
 
-      // We cannot demand what type of user SYSTEM is, but we can say some things it isn't.
-      assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)).isFalse();
-      assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_MANAGED)).isFalse();
-      assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_FULL_GUEST)).isFalse();
+        // We cannot demand what type of user SYSTEM is, but we can say some things it isn't.
+        assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)).isFalse();
+        assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_MANAGED)).isFalse();
+        assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_FULL_GUEST)).isFalse();
 
-      assertThat(umOfSys.isProfile()).isFalse();
-      assertThat(umOfSys.isManagedProfile()).isFalse();
-      assertThat(umOfSys.isManagedProfile(UserHandle.USER_SYSTEM)).isFalse();
-      assertThat(umOfSys.isCloneProfile()).isFalse();
+        assertThat(umOfSys.isProfile()).isFalse();
+        assertThat(umOfSys.isManagedProfile()).isFalse();
+        assertThat(umOfSys.isManagedProfile(UserHandle.USER_SYSTEM)).isFalse();
+        assertThat(umOfSys.isCloneProfile()).isFalse();
     }
 
     @Test
diff --git a/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
index 175a07c..3c99b34 100644
--- a/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
@@ -17,7 +17,6 @@
 package android.permission.cts;
 
 import static org.junit.Assert.assertThrows;
-import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeNotNull;
 import static org.junit.Assume.assumeTrue;
 
@@ -83,22 +82,6 @@
     }
 
     /**
-     * Verify that calling {@link EthernetManager#updateConfiguration(
-     *String, EthernetNetworkUpdateRequest, Executor, BiConsumer)} requires automotive feature.
-     * <p>Tests Feature:
-     * {@link PackageManager#FEATURE_AUTOMOTIVE}.
-     */
-    @Test
-    public void testUpdateConfigurationWithCapabilitiesRequiresAutomotiveFeature() {
-        assumeFalse(mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE));
-        assertThrows("Should not be able to update NetworkCapabilities without automotive feature",
-                UnsupportedOperationException.class,
-                () -> mEthernetManager.updateConfiguration(TEST_IFACE,
-                        buildUpdateRequest(), null, null));
-    }
-
-    /**
      * Verify that calling {@link EthernetManager#enableInterface}
      * requires permissions.
      * <p>Tests Permission:
diff --git a/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java b/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
index 1d678ed..700ca09 100644
--- a/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
@@ -280,12 +280,12 @@
 
     private void enableTestMode() {
         runShellCommandOrThrow("dumpsys activity service"
-                + " com.android.bluetooth/.btservice.AdapterService set-test-mode enabled");
+                + " com.android.bluetooth.btservice.AdapterService set-test-mode enabled");
     }
 
     private void disableTestMode() {
         runShellCommandOrThrow("dumpsys activity service"
-                + " com.android.bluetooth/.btservice.AdapterService set-test-mode disabled");
+                + " com.android.bluetooth.btservice.AdapterService set-test-mode disabled");
     }
 
 }
diff --git a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
index bd0c5dc..a117799 100755
--- a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
@@ -44,8 +44,6 @@
 import com.android.compatibility.common.util.PropertyUtil;
 import com.android.compatibility.common.util.SystemUtil;
 
-import com.google.common.collect.ImmutableSet;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -72,27 +70,6 @@
 @AppModeFull(reason = "This test test platform properties, not capabilities of an apps")
 @RunWith(AndroidJUnit4.class)
 public class PrivappPermissionsTest {
-
-    // TODO(b/191740932): Migrate APK-in-APEX priv-app allowlists inside their respective modules,
-    // so that {@code adb shell cmd package get-privapp-permissions <packageName>} lists them.
-    private static final Set<String> APK_IN_APEX_ALLOWLIST_MIGRATION_BURNDOWN_LIST =
-            ImmutableSet.of(
-                // Updatable APEX packages
-                "com.google.android.networkstack.tethering",
-                "com.google.android.ext.services",
-                "com.google.android.providers.media.module",
-                "com.google.android.permissioncontroller",
-                "com.google.android.cellbroadcastreceiver",
-                "com.google.android.cellbroadcastservice",
-                // AOSP APEX packages
-                "com.android.networkstack.tethering",
-                "com.android.ext.services",
-                "com.android.providers.media.module",
-                "com.android.permissioncontroller",
-                "com.android.cellbroadcastreceiver.module",
-                "com.android.cellbroadcastservice"
-            );
-
     private static final String TAG = "PrivappPermissionsTest";
 
     private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -130,12 +107,6 @@
                 continue;
             }
 
-            // Exempt apk-in-apexes that have not had their priv-app permission allowlist .xml
-            // migrated inside their respective APEXes.
-            if (APK_IN_APEX_ALLOWLIST_MIGRATION_BURNDOWN_LIST.contains(packageName)) {
-                continue;
-            }
-
             PackageInfo factoryPkg = pm
                     .getPackageInfo(packageName, MATCH_FACTORY_ONLY | GET_PERMISSIONS
                         | MATCH_UNINSTALLED_PACKAGES);
diff --git a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
index 5310f49..4a63df5 100644
--- a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
@@ -289,7 +289,9 @@
         launchApp()
         clickPermissionRequestDenyButton()
         waitForIdle()
-        assertNotificationReviewRequiredState(shouldBeSet = false)
+        SystemUtil.eventually {
+            assertNotificationReviewRequiredState(shouldBeSet = false)
+        }
     }
 
     @Test
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
index 6e0f72c..f5d7fc1 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
@@ -87,15 +87,15 @@
     fun testDenyGrantDenyCalendarDuringReview() {
         startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
             // Deny
-            click(By.text("Calendar"))
+            clickPermissionControllerUi(By.text("Calendar"))
             // Confirm deny
             click(By.res("android:id/button1"))
 
             // Grant
-            click(By.text("Calendar"))
+            clickPermissionControllerUi(By.text("Calendar"))
 
             // Deny
-            click(By.text("Calendar"))
+            clickPermissionControllerUi(By.text("Calendar"))
 
             clickPermissionReviewContinue()
         }
diff --git a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
index d39fb9c..0b88e01 100644
--- a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
@@ -30,6 +30,7 @@
 import androidx.test.filters.SdkSuppress
 import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import java.util.regex.Pattern
 import org.junit.After
 import org.junit.Assume
 import org.junit.Before
@@ -43,6 +44,7 @@
     companion object {
         const val LOCATION = -1
         const val WARNING_BANNER_ENABLED = "warning_banner_enabled"
+        const val DELAY_MILLIS = 3000L
     }
 
     val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
@@ -129,7 +131,8 @@
                     android.os.Process.myUserHandle())
                 if (enable) {
                     try {
-                        waitFindObjectOrNull(By.text("CLOSE"))?.click()
+                        val closePattern = Pattern.compile("close", Pattern.CASE_INSENSITIVE)
+                        waitFindObjectOrNull(By.text(closePattern), DELAY_MILLIS)?.click()
                     } catch (e: Exception) {
                         // Do nothing, warning didn't show up so test can proceed
                     }
diff --git a/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java b/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java
index 51664ea..c47ba7a 100644
--- a/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java
+++ b/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java
@@ -17,6 +17,7 @@
 package android.settings.cts;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -28,6 +29,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,6 +39,13 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class TetherProvisioningCarrierDialogActivityTest {
+    @Before
+    public void setUp() throws Exception {
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+        assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+    }
+
     @Test
     public void testTetheringProvisioningCarrierUiExisted() throws RemoteException {
         final Intent intent = new Intent(Settings.ACTION_TETHER_UNSUPPORTED_CARRIER_UI);
diff --git a/tests/tests/taskfpscallback/Android.bp b/tests/tests/taskfpscallback/Android.bp
new file mode 100644
index 0000000..8a89132
--- /dev/null
+++ b/tests/tests/taskfpscallback/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsTaskFpsCallbackTestCases",
+    defaults: ["cts_defaults"],
+
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    static_libs: [
+        "androidx.test.core",
+        "compatibility-device-util-axt",
+        "ctsdeviceutillegacy-axt",
+        "ctstestrunner-axt",
+        "ctstestserver",
+        "junit",
+        "truth-prebuilt",
+    ],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "test_current",
+}
diff --git a/tests/tests/taskfpscallback/AndroidManifest.xml b/tests/tests/taskfpscallback/AndroidManifest.xml
new file mode 100644
index 0000000..f7bd875
--- /dev/null
+++ b/tests/tests/taskfpscallback/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.taskfpscallback.cts">
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".TaskFpsCallbackCtsActivity"
+                  android:label="TaskFpsCallbackCtsActivity"
+                  android:visibleToInstantApps="true"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.taskfpscallback.cts"
+         android:label="CTS tests of android TaskFpsCallback service">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/taskfpscallback/AndroidTest.xml b/tests/tests/taskfpscallback/AndroidTest.xml
new file mode 100644
index 0000000..5a41eb4
--- /dev/null
+++ b/tests/tests/taskfpscallback/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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 TaskFpsCallback test cases">
+    <option name="test-suite-tag" value="cts" />
+
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsTaskFpsCallbackTestCases.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.taskfpscallback.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/taskfpscallback/OWNERS b/tests/tests/taskfpscallback/OWNERS
new file mode 100644
index 0000000..d0186b0
--- /dev/null
+++ b/tests/tests/taskfpscallback/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 878256
+lpy@google.com
+xwxw@google.com
diff --git a/tests/tests/taskfpscallback/TEST_MAPPING b/tests/tests/taskfpscallback/TEST_MAPPING
new file mode 100644
index 0000000..4db2309
--- /dev/null
+++ b/tests/tests/taskfpscallback/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsTaskFpsCallbackTestCases"
+    }
+  ]
+}
+
diff --git a/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsActivity.java b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsActivity.java
new file mode 100644
index 0000000..284de4e
--- /dev/null
+++ b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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.taskfpscallback.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class TaskFpsCallbackCtsActivity extends Activity {
+
+    private static final String TAG = "TaskFpsCallbackCtsActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsTest.java b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsTest.java
new file mode 100644
index 0000000..44cf60d
--- /dev/null
+++ b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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.taskfpscallback.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.view.WindowManager;
+import android.window.TaskFpsCallback;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TaskFpsCallbackCtsTest {
+    private static final String TAG = "TaskFpsCallbackCtsTest";
+
+    private TaskFpsCallbackCtsActivity mActivity;
+    private Context mContext;
+    private WindowManager mWindowManager;
+
+    @Rule
+    public ActivityScenarioRule<TaskFpsCallbackCtsActivity> mActivityRule =
+            new ActivityScenarioRule<>(TaskFpsCallbackCtsActivity.class);
+
+    @Before
+    public void setUp() {
+        mActivityRule.getScenario().onActivity(activity -> {
+            mActivity = activity;
+        });
+
+        final Instrumentation instrumentation = getInstrumentation();
+        mContext = instrumentation.getContext();
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+    }
+
+    @Test
+    public void testRegister() throws Exception {
+        final TaskFpsCallback callback = new TaskFpsCallback() {
+            @Override
+            public void onFpsReported(float fps) {
+                // Ignore
+            }
+        };
+
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mWindowManager,
+                (windowManager) -> windowManager.registerTaskFpsCallback(
+                        mActivity.getTaskId(), Runnable::run, callback),
+                "android.permission.ACCESS_FPS_COUNTER");
+
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mWindowManager,
+                (windowManager) -> windowManager.unregisterTaskFpsCallback(callback),
+                "android.permission.ACCESS_FPS_COUNTER");
+    }
+
+    @Test
+    public void testRegisterWithoutPermission() {
+        final TaskFpsCallback callback = new TaskFpsCallback() {
+            @Override
+            public void onFpsReported(float fps) {
+                // Ignore
+            }
+        };
+        assertThrows(SecurityException.class, () -> mWindowManager.registerTaskFpsCallback(
+                mActivity.getTaskId(), Runnable::run, callback));
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
index 57af2d1..f107d4b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
@@ -577,19 +577,25 @@
             return;
         }
 
-        mContext.getPackageManager().setApplicationEnabledSetting(CARMODE_APP1_PACKAGE,
-                COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
-        mInCallCallbacks.resetLatch();
+        try {
+            mContext.getPackageManager().setApplicationEnabledSetting(CARMODE_APP1_PACKAGE,
+                    COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+            mInCallCallbacks.resetLatch();
 
-        // Place a call and verify it went to the default dialer
-        placeAndVerifyCall();
-        verifyConnectionForOutgoingCall();
+            // Place a call and verify it went to the default dialer
+            placeAndVerifyCall();
+            verifyConnectionForOutgoingCall();
 
-        // Now, request automotive projection; shouldn't unbind from default dialer.
-        requestAndVerifyAutomotiveProjection(mCarModeIncallServiceControlOne, true);
-        assertFalse(mInCallCallbacks.waitForUnbind());
+            // Now, request automotive projection; shouldn't unbind from default dialer.
+            requestAndVerifyAutomotiveProjection(mCarModeIncallServiceControlOne, true);
+            assertFalse(mInCallCallbacks.waitForUnbind());
 
-        releaseAutomotiveProjection(mCarModeIncallServiceControlOne);
+            releaseAutomotiveProjection(mCarModeIncallServiceControlOne);
+            mInCallCallbacks.getService().disconnectAllCalls();
+        } finally {
+            mContext.getPackageManager().setApplicationEnabledSetting(CARMODE_APP1_PACKAGE,
+                    COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+        }
     }
 
 
diff --git a/tests/tests/telecom/src/android/telecom/cts/InCallServiceFlagChecker.java b/tests/tests/telecom/src/android/telecom/cts/InCallServiceFlagChecker.java
new file mode 100644
index 0000000..33e611c
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/InCallServiceFlagChecker.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom.cts;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * The CTS test class InCallServiceFlagChecker is meant to check flags passed into InCallController.
+ * Flags passed in InCallController can cause unwanted behavior.
+ */
+public class InCallServiceFlagChecker extends BaseTelecomTestWithMockServices {
+
+    public static final String LOG_TAG = InCallServiceFlagChecker.class.getName();
+    public static final String TARGET_SERVICE = ".MockInCallService:system";
+    public static final String DUMPSYS_COMMAND = "dumpsys activity services android.telecom.cts";
+    public static final String FLAGS_TEXT_MATCHER = "flags=0x";
+    public static final int FLAGS_OFFSET = FLAGS_TEXT_MATCHER.length();
+    public static final char SPACE_CHAR = ' ';
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        NewOutgoingCallBroadcastReceiver.reset();
+        if (mShouldTestTelecom) {
+            setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        TestUtils.clearSystemDialerOverride(getInstrumentation());
+        TestUtils.removeTestEmergencyNumber(getInstrumentation(), TEST_EMERGENCY_NUMBER);
+    }
+
+    /**
+     * CTS test to ensure InCallService bindings DO NOT have BIND_ABOVE_CLIENT flag set on binding.
+     */
+    public void testIsBindAboveClientFlagSet() throws Exception {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        // Trigger InCallService so flags can be examined. Otherwise, the service will not show
+        // up when the dumpsys command is executed.
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
+        // Dump the testing package, android.telecom.cts, service information. Doing so will expose
+        // if the BIND_ABOVE_CLIENT flag is set or not on our target InCallService.
+        String dumpOutput = TestUtils.executeShellCommand(getInstrumentation(), DUMPSYS_COMMAND);
+        assertNotNull(dumpOutput);
+        assertTrue(dumpOutput.length() > 0);
+        Log.i(LOG_TAG, dumpOutput);
+
+        // Take off chunk of unwanted pretext.
+        String targetSection = dumpOutput.substring(dumpOutput.indexOf(TARGET_SERVICE));
+        assertNotNull(targetSection);
+        assertTrue(targetSection.length() > 0);
+        Log.i(LOG_TAG, targetSection);
+
+        // extract the TARGET_SERVICE flags
+        int flagsValue = extractFlagsValue(targetSection);
+
+        // assert the Context.BIND_ABOVE_CLIENT flag is not set!
+        assertTrue((flagsValue & Context.BIND_ABOVE_CLIENT) != Context.BIND_ABOVE_CLIENT);
+    }
+
+
+    /**
+     * Find the first flag section of the given string which contains ConnectionRecordObject's
+     *
+     * @param s ConnectionRecordObject in string format.
+     * @return flags decimal value in the ConnectionRecordObject.
+     */
+    public int extractFlagsValue(String s) {
+        // builder to pull flag digits from dumped text
+        StringBuilder sb = new StringBuilder();
+
+        // find the flags=0x text to begin pulling flag digits
+        int i = s.indexOf(FLAGS_TEXT_MATCHER) + FLAGS_OFFSET;
+        int n = s.length();
+
+        // If (i == -1) then this means the flag information was never found.
+        assertTrue(i != -1); // assert we found flag info
+
+        // stop when either there is no more text or a space is encountered
+        while (i < n && s.charAt(i) != SPACE_CHAR) {
+            sb.append(s.charAt(i));
+            i++;
+        }
+
+        // convert string builder into string
+        String extractedFlagAsString = sb.toString();
+        assertNotNull(extractedFlagAsString);
+        assertTrue(extractedFlagAsString.length() > 0);
+        Log.i(LOG_TAG, extractedFlagAsString);
+
+        // convert the string into and integer and return value
+        return Integer.parseInt(extractedFlagAsString);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
index d63d90d..bd060b9 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
@@ -162,11 +162,13 @@
         assertRemaining(sms.getMessageBody().length(), result[2], SmsMessage.MAX_USER_DATA_SEPTETS);
         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
 
-        // Test createFromPdu to test ENCODING_KSC5601
+        // Test createFromPdu to test ENCODING_KSC5601 (0x84 as DCS in pdu)
+        // The only way of retrieving the Data Coding Scheme is via SmsMessage#calculateLength,
+        // but it never returns ENCODING_KSC5601, so we're testing for ENCODING_16BIT instead.
         pdu = "07916164260220F0040B914151245584F600846060605130308A04D4F29C0E";
         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
-        result = SmsMessage.calculateLength(sms.getMessageBody(), true);
-        assertEquals(SmsMessage.ENCODING_KSC5601, result[3]);
+        result = SmsMessage.calculateLength(sms.getMessageBody(), false);
+        assertEquals(SmsMessage.ENCODING_16BIT, result[3]);
     }
 
     private void assertRemaining(int messageLength, int remaining, int maxChars) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt
index a190501..910f9d6 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt
@@ -23,7 +23,7 @@
 import android.graphics.Rect
 import android.graphics.NinePatch
 import android.uirendering.cts.R
-import android.uirendering.cts.bitmapcomparers.ExactComparer
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer
 import android.uirendering.cts.testinfrastructure.ActivityTestBase
 import android.uirendering.cts.testinfrastructure.CanvasClient
 import androidx.test.runner.AndroidJUnit4
@@ -58,7 +58,7 @@
             for (paint in arrayOf(null, Paint(), Paint().apply { isFilterBitmap = false })) {
                 addCanvasClientWithoutUsingPicture(NinePatchCanvasClient(np, paint), hw)
             }
-            runWithComparer(ExactComparer())
+            runWithComparer(MSSIMComparer(0.99))
         }
 
         np.bitmap.recycle()
diff --git a/tests/tests/view/Android.bp b/tests/tests/view/Android.bp
index f49b64c..a2d863b 100644
--- a/tests/tests/view/Android.bp
+++ b/tests/tests/view/Android.bp
@@ -41,6 +41,7 @@
         "ctstestrunner-axt",
         "cts-input-lib",
         "cts-hardware-lib",
+        "junit-params",
         "mockito-target-minus-junit4",
         "platform-test-annotations",
         "ub-uiautomator",
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 1f6befb..514c6fa 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -148,6 +148,17 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.view.cts.SDRTestActivity"
+            android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
+            android:screenOrientation="locked"
+            android:label="SDRTestActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.cts.TextureViewCameraActivity"
              android:screenOrientation="locked"
              android:exported="true">
diff --git a/tests/tests/view/res/layout-watch/gesture_exclusion_basic.xml b/tests/tests/view/res/layout-watch/gesture_exclusion_basic.xml
new file mode 100644
index 0000000..2b15fc5
--- /dev/null
+++ b/tests/tests/view/res/layout-watch/gesture_exclusion_basic.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/abslistview_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/animating_view"
+        android:layout_width="5px"
+        android:layout_height="5px"
+        android:layout_gravity="center|left"
+        android:background="#ff00ff00" />
+</FrameLayout>
diff --git a/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
index 962d75a..ba711c4 100644
--- a/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
+++ b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
@@ -93,7 +93,9 @@
 
     @After
     public void teardown() {
-        mOrientationSession.close();
+        if (mOrientationSession != null) {
+            mOrientationSession.close();
+        }
     }
 
     @Test
diff --git a/tests/tests/view/src/android/view/cts/SDRTestActivity.java b/tests/tests/view/src/android/view/cts/SDRTestActivity.java
new file mode 100644
index 0000000..c862a31
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/SDRTestActivity.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.cts;
+
+import android.app.Activity;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+
+public class SDRTestActivity extends Activity
+        implements SurfaceHolder.Callback, SurfaceTextureListener {
+    private SurfaceView mSurfaceView;
+    private TextureView mTextureView;
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {}
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {}
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mTextureView = new TextureView(this);
+        mSurfaceView = new SurfaceView(this);
+        mTextureView.setSurfaceTextureListener(this);
+
+        FrameLayout content = new FrameLayout(this);
+        content.addView(mSurfaceView,
+                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+        content.addView(mTextureView,
+                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+        mSurfaceView.getHolder().addCallback(this);
+        setContentView(content);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+    }
+
+    public TextureView getTextureView() {
+        return mTextureView;
+    }
+
+    public SurfaceView getSurfaceView() {
+        return mSurfaceView;
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {}
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        return true;
+    }
+
+    @Override
+    public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
+}
+
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index d5972a4..3140c2a 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -24,37 +24,46 @@
 import static android.opengl.GLES20.glScissor;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.app.Instrumentation;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorSpace;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
+import android.hardware.DataSpace;
 import android.media.Image;
 import android.media.ImageWriter;
 import android.util.Half;
 import android.view.PixelCopy;
 import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
 import android.view.cts.util.BitmapDumper;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.SynchronousPixelCopy;
 import com.android.compatibility.common.util.WidgetTestUtils;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
@@ -66,8 +75,11 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 @MediumTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
 public class TextureViewTest {
 
     static final int EGL_GL_COLORSPACE_SRGB_KHR = 0x3089;
@@ -78,11 +90,23 @@
     static final int EGL_GL_COLORSPACE_SCRGB_EXT = 0x3351;
     static final int EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT = 0x3350;
 
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        assertNotNull(mInstrumentation);
+    }
+
     @Rule
     public ActivityTestRule<TextureViewCtsActivity> mActivityRule =
             new ActivityTestRule<>(TextureViewCtsActivity.class, false, false);
 
     @Rule
+    public ActivityTestRule<SDRTestActivity> mSDRActivityRule =
+            new ActivityTestRule<>(SDRTestActivity.class, false, false);
+
+    @Rule
     public TestName mTestName = new TestName();
 
     @Test
@@ -235,6 +259,137 @@
                 screenshot.getPixel(texturePos.right - 10, texturePos.bottom - 10));
     }
 
+    private static Object[] testDataSpaces() {
+        return new Integer[] {
+            DataSpace.DATASPACE_SCRGB_LINEAR,
+            DataSpace.DATASPACE_SRGB,
+            DataSpace.DATASPACE_SCRGB,
+            DataSpace.DATASPACE_DISPLAY_P3,
+            DataSpace.DATASPACE_ADOBE_RGB,
+            DataSpace.DATASPACE_BT2020,
+            DataSpace.DATASPACE_BT709,
+            DataSpace.DATASPACE_DCI_P3,
+            DataSpace.DATASPACE_SRGB_LINEAR
+        };
+    }
+
+    @Test
+    @Parameters(method = "testDataSpaces")
+    public void testSDRFromSurfaceViewAndTextureView(int dataSpace) throws Throwable {
+        final int tiffanyBlue = 0xFF0ABAB5;
+        Color color = Color.valueOf(tiffanyBlue).convert(ColorSpace.getFromDataSpace(dataSpace));
+        long converted = color.pack();
+        assertNotEquals(Color.valueOf(tiffanyBlue).toArgb(), color);
+
+        final SDRTestActivity activity =
+                mSDRActivityRule.launchActivity(/*startIntent*/ null);
+
+        TextureView textureView = activity.getTextureView();
+        int width = textureView.getWidth();
+        int height = textureView.getHeight();
+
+        // through textureView, paint left part
+        SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
+        Surface surface = new Surface(surfaceTexture);
+        assertTrue(surface.isValid());
+
+        ImageWriter writer = new ImageWriter
+                .Builder(surface)
+                .setHardwareBufferFormat(PixelFormat.RGBA_8888)
+                .setDataSpace(dataSpace)
+                .build();
+        Image image = writer.dequeueInputImage();
+        assertEquals(dataSpace, image.getDataSpace());
+        Image.Plane plane = image.getPlanes()[0];
+        Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, image.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        Paint paint = new Paint();
+        paint.setAntiAlias(false);
+        paint.setColor(converted);
+        canvas.drawRect(width / 2, 0f, width, height, paint);
+        bitmap.copyPixelsToBuffer(plane.getBuffer());
+        writer.queueInputImage(image);
+
+        final Rect textureViewPos = new Rect();
+        WidgetTestUtils.runOnMainAndDrawSync(mSDRActivityRule,
+                activity.findViewById(android.R.id.content), () -> {
+                int[] outLocation = new int[2];
+                textureView.getLocationInSurface(outLocation);
+                textureViewPos.left = outLocation[0] + width / 2;
+                textureViewPos.top = outLocation[1];
+                textureViewPos.right = textureViewPos.left + width / 2;
+                textureViewPos.bottom = textureViewPos.top + height;
+            });
+
+        Bitmap textureViewScreenshot = Bitmap.createBitmap(
+                textureViewPos.width(), textureViewPos.height(), Bitmap.Config.ARGB_8888);
+        int textureViewResult =
+                new SynchronousPixelCopy().request(surface, textureViewPos, textureViewScreenshot);
+        assertEquals("Copy request failed", PixelCopy.SUCCESS, textureViewResult);
+
+        // through surfaceView, paint right part
+        SurfaceView surfaceView = activity.getSurfaceView();
+        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+                ImageWriter writer = new ImageWriter
+                        .Builder(holder.getSurface())
+                        .setHardwareBufferFormat(PixelFormat.RGBA_8888)
+                        .setDataSpace(dataSpace)
+                        .build();
+                Image image = writer.dequeueInputImage();
+                assertEquals(dataSpace, image.getDataSpace());
+                Image.Plane plane = image.getPlanes()[0];
+                Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, image.getHeight(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas canvas = new Canvas(bitmap);
+                Paint paint = new Paint();
+                paint.setAntiAlias(false);
+                paint.setColor(converted);
+                canvas.drawRect(0f, 0f, width / 2, height, paint);
+                bitmap.copyPixelsToBuffer(plane.getBuffer());
+                writer.queueInputImage(image);
+            }
+
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {}
+        });
+
+        // wait here to ensure SF has latched the buffer that has been queued in
+        // this is the easiest way to solve copy failure but sacrifice the performance.
+        Thread.sleep(100);
+        final Rect surfaceViewPos = new Rect();
+        WidgetTestUtils.runOnMainAndDrawSync(mSDRActivityRule, surfaceView, () -> {
+            ((ViewGroup) surfaceView.getParent()).removeView(surfaceView);
+            activity.setContentView(surfaceView);
+            int[] outLocation = new int[2];
+            surfaceView.getLocationInSurface(outLocation);
+            surfaceViewPos.left = outLocation[0];
+            surfaceViewPos.top = outLocation[1];
+            surfaceViewPos.right = surfaceViewPos.left + width / 2;
+            surfaceViewPos.bottom = surfaceViewPos.top + height;
+        });
+
+        Bitmap surfaceViewScreenshot = mInstrumentation
+                .getUiAutomation()
+                .takeScreenshot(activity.getWindow());
+        // resize screenshot to left part
+        surfaceViewScreenshot.setWidth(surfaceViewPos.width());
+        surfaceViewScreenshot.setHeight(surfaceViewPos.height());
+        assertEquals(ColorSpace.get(ColorSpace.Named.SRGB),
+                surfaceViewScreenshot.getColorSpace());
+
+        int surfaceViewResult = new SynchronousPixelCopy()
+                .request(surfaceView, surfaceViewPos, surfaceViewScreenshot);
+        assertEquals("Copy request failed", PixelCopy.SUCCESS, surfaceViewResult);
+
+        assertTrue(textureViewScreenshot.sameAs(surfaceViewScreenshot));
+    }
+
     @Test
     public void testCropRect() throws Throwable {
         final TextureViewCtsActivity activity = mActivityRule.launchActivity(/*startIntent*/ null);
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 4bdc106..e5a7ecf 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -261,6 +261,8 @@
                     (FrameLayout) findViewById(android.R.id.content));
         });
 
+        animationTestCase.waitForReady();
+
         mHandler.postDelayed(() -> {
             Log.d(TAG, "Starting capture");
 
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java
index 0de06d2..71810d9 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java
@@ -37,4 +37,8 @@
         boundsToCheck.offset(topLeft[0], topLeft[1]);
         return  boundsToCheck;
     }
+
+    default void waitForReady() {
+        return;
+    }
 }
diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
index df36111..8e3c098 100644
--- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
+++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
@@ -59,11 +59,11 @@
 import com.google.uwb.support.multichip.ChipInfoParams;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.List;
+import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
@@ -402,15 +402,41 @@
     }
 
     private class UwbVendorUciCallback implements UwbManager.UwbVendorUciCallback {
+        private final CountDownLatch mRspCountDownLatch;
+        private final CountDownLatch mNtfCountDownLatch;
+
+        public int gid;
+        public int oid;
+        public byte[] payload;
+
+        UwbVendorUciCallback(
+                @NonNull CountDownLatch rspCountDownLatch,
+                @NonNull CountDownLatch ntfCountDownLatch) {
+            mRspCountDownLatch = rspCountDownLatch;
+            mNtfCountDownLatch = ntfCountDownLatch;
+        }
+
         @Override
-        public void onVendorUciResponse(int gid, int oid, byte[] payload) { }
+        public void onVendorUciResponse(int gid, int oid, byte[] payload) {
+            this.gid = gid;
+            this.oid = oid;
+            this.payload = payload;
+            mRspCountDownLatch.countDown();
+        }
+
         @Override
-        public void onVendorUciNotification(int gid, int oid, byte[] payload) { }
+        public void onVendorUciNotification(int gid, int oid, byte[] payload) {
+            this.gid = gid;
+            this.oid = oid;
+            this.payload = payload;
+            mNtfCountDownLatch.countDown();
+        }
     }
 
     @Test
     public void testRegisterVendorUciCallbackWithoutUwbPrivileged() {
-        UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback();
+        UwbManager.UwbVendorUciCallback cb =
+                new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
         try {
             mUwbManager.registerUwbVendorUciCallback(
                     Executors.newSingleThreadExecutor(), cb);
@@ -423,14 +449,19 @@
     }
 
     @Test
-    @Ignore("Not implemented yet")
     public void testUnregisterVendorUciCallbackWithoutUwbPrivileged() {
-        UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback();
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        UwbManager.UwbVendorUciCallback cb =
+                new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
         try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
             mUwbManager.registerUwbVendorUciCallback(
                     Executors.newSingleThreadExecutor(), cb);
         } catch (SecurityException e) {
             /* pass */
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
         }
         try {
             mUwbManager.unregisterUwbVendorUciCallback(cb);
@@ -444,7 +475,8 @@
 
     @Test
     public void testInvalidCallbackUnregisterVendorUciCallback() {
-        UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback();
+        UwbManager.UwbVendorUciCallback cb =
+                new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
         try {
             mUwbManager.registerUwbVendorUciCallback(
                     Executors.newSingleThreadExecutor(), cb);
@@ -876,4 +908,70 @@
             uiAutomation.dropShellPermissionIdentity();
         }
     }
+
+    @Test
+    public void testSendVendorUciMessage() throws Exception {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+        CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+        UwbVendorUciCallback cb =
+                new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+        try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
+            mUwbManager.registerUwbVendorUciCallback(
+                    Executors.newSingleThreadExecutor(), cb);
+
+            // Send random payload with a vendor gid.
+            byte[] payload = new byte[100];
+            new Random().nextBytes(payload);
+            int gid = 9;
+            int oid = 1;
+            mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+            // Wait for response.
+            assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(cb.gid).isEqualTo(gid);
+            assertThat(cb.oid).isEqualTo(oid);
+            assertThat(cb.payload).isNotEmpty();
+        } catch (SecurityException e) {
+            /* pass */
+        } finally {
+            mUwbManager.unregisterUwbVendorUciCallback(cb);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testSendVendorUciMessageWithFragmentedPackets() throws Exception {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+        CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+        UwbVendorUciCallback cb =
+                new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+        try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
+            mUwbManager.registerUwbVendorUciCallback(
+                    Executors.newSingleThreadExecutor(), cb);
+
+            // Send random payload > 255 bytes with a vendor gid.
+            byte[] payload = new byte[400];
+            new Random().nextBytes(payload);
+            int gid = 9;
+            int oid = 1;
+            mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+            // Wait for response.
+            assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(cb.gid).isEqualTo(gid);
+            assertThat(cb.oid).isEqualTo(oid);
+            assertThat(cb.payload).isNotEmpty();
+        } catch (SecurityException e) {
+            /* pass */
+        } finally {
+            mUwbManager.unregisterUwbVendorUciCallback(cb);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
 }
diff --git a/tools/cts-tradefed/res/config/cts-developer-exclude.xml b/tools/cts-tradefed/res/config/cts-developer-exclude.xml
index c7bec16..21f0411 100644
--- a/tools/cts-tradefed/res/config/cts-developer-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-developer-exclude.xml
@@ -16,4 +16,6 @@
   -->
 <configuration description="Excluded CTS developer tests from main CTS runs">
     <!-- Exclude the set of tests from the cts-developer plan here -->
+    <option name="compatibility:exclude-filter" value="CtsServiceKillTestCases" />
+
 </configuration>
diff --git a/tools/cts-tradefed/res/config/cts-developer.xml b/tools/cts-tradefed/res/config/cts-developer.xml
index 62c39d8..601227e 100644
--- a/tools/cts-tradefed/res/config/cts-developer.xml
+++ b/tools/cts-tradefed/res/config/cts-developer.xml
@@ -21,5 +21,6 @@
     <option name="plan" value="cts-developer" />
 
     <!-- Include the set of tests from the cts-developer-exclude plan here -->
+    <option name="compatibility:include-filter" value="CtsServiceKillTestCases" />
 
 </configuration>