Merge "BT: Remove HAP CTS tests that use non system APIs"
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/OWNERS
new file mode 100644
index 0000000..2718191
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1151772
+mrcasey@google.com
+mkephart@google.com
+moellerj@google.com
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
index 58e767e..f4c89e8 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
@@ -16,7 +16,7 @@
 
 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;
 
@@ -47,6 +47,8 @@
 
     public static final String PROPERTY_LOCALE = "persist.sys.locale";
     private static final String POWER_CONTROL_MODE = "power_control_mode";
+    private static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+            "power_state_change_on_active_source_lost";
 
     /** Enum contains the list of possible address types. */
     private enum AddressType {
@@ -190,6 +192,36 @@
                 "Could not parse logicalAddress from dumpsys.");
     }
 
+    /**
+     * Gets the system audio mode status of the device by parsing the dumpsys hdmi_control. Returns
+     * true when system audio mode is on and false when system audio mode is off
+     */
+    public boolean isSystemAudioModeOn(ITestDevice device) throws DumpsysParseException {
+        List<LogicalAddress> logicalAddressList = new ArrayList<>();
+        String line;
+        String pattern =
+                "(.*?)"
+                        + "(mSystemAudioActivated: )"
+                        + "(?<"
+                        + "systemAudioModeStatus"
+                        + ">[true|false])"
+                        + "(.*?)";
+        Pattern p = Pattern.compile(pattern);
+        try {
+            String dumpsys = device.executeShellCommand("dumpsys hdmi_control");
+            BufferedReader reader = new BufferedReader(new StringReader(dumpsys));
+            while ((line = reader.readLine()) != null) {
+                Matcher m = p.matcher(line);
+                if (m.matches()) {
+                    return m.group("systemAudioModeStatus").equals("true");
+                }
+            }
+        } catch (IOException | DeviceNotAvailableException e) {
+            throw new DumpsysParseException("Could not parse system audio mode from dumpsys.", e);
+        }
+        throw new DumpsysParseException("Could not parse system audio mode from dumpsys.");
+    }
+
     /** Gets the DUT's logical address to which messages should be sent */
     public LogicalAddress getTargetLogicalAddress() throws DumpsysParseException {
         return getTargetLogicalAddress(getDevice(), mTestDeviceType);
@@ -366,16 +398,9 @@
                 "dumpsys hdmi_control | sed -n '/mDeviceInfos/,/mCecController/{//!p;}'");
     }
 
-    public void checkDeviceAsleep() throws Exception {
-        ITestDevice device = getDevice();
-        TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
-        String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
-        assertThat(wakeState.trim()).isEqualTo("mWakefulness=Asleep");
-    }
-
     public void sendDeviceToSleepAndValidate() throws Exception {
         sendDeviceToSleep();
-        checkDeviceAsleep();
+        assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
     }
 
     public void waitForTransitionTo(int finalState) throws Exception {
@@ -417,6 +442,7 @@
 
     public void sendDeviceToSleep() throws Exception {
         sendDeviceToSleepWithoutWait();
+        assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
         waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
     }
 
@@ -436,15 +462,38 @@
     public void wakeUpDevice() throws Exception {
         ITestDevice device = getDevice();
         device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_AWAKE);
         waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
         WakeLockHelper.releasePartialWakeLock(device);
     }
 
     public void checkStandbyAndWakeUp() throws Exception {
-        checkDeviceAsleep();
+        assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
         wakeUpDevice();
     }
 
+    public void assertDeviceWakefulness(String wakefulness) throws Exception {
+        ITestDevice device = getDevice();
+        String actualWakefulness;
+        int waitTimeSeconds = 0;
+
+        do {
+            TimeUnit.SECONDS.sleep(HdmiCecConstants.SLEEP_TIMESTEP_SECONDS);
+            waitTimeSeconds += HdmiCecConstants.SLEEP_TIMESTEP_SECONDS;
+            actualWakefulness =
+                    device.executeShellCommand("dumpsys power | grep mWakefulness=")
+                            .trim().replace("mWakefulness=", "");
+        } while (!actualWakefulness.equals(wakefulness)
+                && waitTimeSeconds <= HdmiCecConstants.MAX_SLEEP_TIME_SECONDS);
+        assertWithMessage(
+                "Device wakefulness is "
+                        + actualWakefulness
+                        + " but expected to be "
+                        + wakefulness)
+                .that(actualWakefulness)
+                .isEqualTo(wakefulness);
+    }
+
     public void sendOtp() throws Exception {
         ITestDevice device = getDevice();
         device.executeShellCommand("cmd hdmi_control onetouchplay");
@@ -456,6 +505,13 @@
         return val;
     }
 
+    public String setPowerStateChangeOnActiveSourceLost(String valToSet) throws Exception {
+        String previousPowerStateChange =
+                getSettingsValue(POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST);
+        setSettingsValue(POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, valToSet);
+        return previousPowerStateChange;
+    }
+
     public boolean isDeviceActiveSource(ITestDevice device) throws DumpsysParseException {
         final String activeSource = "activeSource";
         final String pattern =
@@ -481,4 +537,39 @@
         }
         throw new DumpsysParseException("Could not parse isActiveSource() from dumpsys.");
     }
+
+    /**
+     * For source devices, simulate that a sink is connected by responding to the
+     * {@code Give Power Status} message that is sent when re-enabling CEC.
+     * Validate that HdmiControlService#mIsCecAvailable is set to true as a result.
+     */
+    public void simulateCecSinkConnected(ITestDevice device, LogicalAddress source)
+            throws Exception {
+        hdmiCecClient.clearClientOutput();
+        device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
+        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
+        // only respond to the retry.
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
+        hdmiCecClient.clearClientOutput();
+        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);
+    }
+
+    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();
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index c313680..083cf2e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -130,6 +130,10 @@
     public static final int CEC_POWER_STATUS_IN_TRANSITION_TO_ON = 0x2;
     public static final int CEC_POWER_STATUS_IN_TRANSITION_TO_STANDBY = 0x3;
 
+    /** PowerManager wakefulness states */
+    public static final String WAKEFULNESS_AWAKE = "Awake";
+    public static final String WAKEFULNESS_ASLEEP = "Asleep";
+
     /** Poll Message Success */
     public static final String POLL_SUCCESS = "POLL message sent";
 
@@ -153,6 +157,10 @@
     public static final String POWER_CONTROL_MODE_NONE = "none";
     public static final String POWER_CONTROL_MODE_TV = "to_tv";
 
+    // Power State Change on Active Source Lost Settings values
+    public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
+    public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
+
     // 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;
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
index f95a98c..55606e4 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
@@ -17,7 +17,6 @@
 package android.hdmicec.cts.common;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
@@ -315,17 +314,9 @@
         for (Integer operand : powerControlOperands) {
             try {
                 sendDeviceToSleep();
-                String wakeStateBefore = device.executeShellCommand(
-                        "dumpsys power | grep mWakefulness=");
-                assertThat(wakeStateBefore.trim()).isEqualTo("mWakefulness=Asleep");
-
                 hdmiCecClient.sendUserControlPressAndRelease(source, operand, false);
-
                 TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
-                String wakeStateAfter = device.executeShellCommand(
-                        "dumpsys power | grep mWakefulness=");
-                assertWithMessage("Device should wake up on <User Control Pressed> %s", operand)
-                        .that(wakeStateAfter.trim()).isEqualTo("mWakefulness=Awake");
+                assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_AWAKE);
             } finally {
                 wakeUpDevice();
             }
@@ -352,19 +343,10 @@
         for (Integer operand : powerControlOperands) {
             try {
                 wakeUpDevice();
-                String wakeStateBefore = device.executeShellCommand(
-                        "dumpsys power | grep mWakefulness=");
-                assertThat(wakeStateBefore.trim()).isEqualTo("mWakefulness=Awake");
-
                 WakeLockHelper.acquirePartialWakeLock(device);
                 hdmiCecClient.sendUserControlPressAndRelease(source, operand, false);
-
                 TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
-                String wakeStateAfter = device.executeShellCommand(
-                        "dumpsys power | grep mWakefulness=");
-                assertWithMessage("Device should go to standby on <User Control Pressed> %s",
-                        operand)
-                        .that(wakeStateAfter.trim()).isEqualTo("mWakefulness=Asleep");
+                assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
             } finally {
                 wakeUpDevice();
             }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceSelectForPlaybackTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceSelectForPlaybackTest.java
index d46150b..279bc2b 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceSelectForPlaybackTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceSelectForPlaybackTest.java
@@ -52,15 +52,6 @@
                                     this, HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE))
                     .around(hdmiCecClient);
 
-    private String setPowerStateChangeOnActiveSourceLost(String valToSet) throws Exception {
-        ITestDevice device = getDevice();
-        String previousPowerStateChange = device.executeShellCommand("cmd hdmi_control cec_setting "
-                + "get power_state_change_on_active_source_lost").split(" = ")[1].trim();
-        device.executeShellCommand("cmd hdmi_control cec_setting "
-                + "set power_state_change_on_active_source_lost " + valToSet);
-        return previousPowerStateChange;
-    }
-
     private int getUnusedPhysicalAddress(int initialValue, int usedValue) {
         if (initialValue == usedValue)
             return 0x2000;
@@ -89,7 +80,8 @@
         // Store previous power state change on active source lost.
         // Set the power state change to none, such that the device won't go to sleep when the
         // active source is changed.
-        String previousPowerStateChange = setPowerStateChangeOnActiveSourceLost("none");
+        String previousPowerStateChange = setPowerStateChangeOnActiveSourceLost(
+                HdmiCecConstants.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
         try {
             int dumpsysPhysicalAddress = getDumpsysPhysicalAddress();
             // Add Playback 2 in the network.
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
index 408d719..5b361be 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
@@ -16,9 +16,6 @@
 
 package android.hdmicec.cts.playback;
 
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.CecOperand;
@@ -28,16 +25,16 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
 /**
  * HDMI CEC tests verifying power status related messages of the device (CEC 2.0 CTS Section 7.6)
  */
@@ -113,29 +110,15 @@
      */
     @Test
     public void cect_hf4_6_7_setStreamPath_powerOn() throws Exception {
-        ITestDevice device = getDevice();
-
         try {
             sendDeviceToSleep();
-
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.MAX_SLEEP_TIME_SECONDS);
-
-            String wakeStateBefore = device.executeShellCommand(
-                    "dumpsys power | grep mWakefulness=");
-            assertThat(wakeStateBefore.trim()).isEqualTo("mWakefulness=Asleep");
-
             hdmiCecClient.sendCecMessage(
                     LogicalAddress.TV,
                     LogicalAddress.BROADCAST,
                     CecOperand.SET_STREAM_PATH,
                     CecMessage.formatParams(getDumpsysPhysicalAddress(),
                             HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
-
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
-            String wakeStateAfter = device.executeShellCommand(
-                    "dumpsys power | grep mWakefulness=");
-            assertWithMessage("Device should wake up on <Set Stream Path>")
-                    .that(wakeStateAfter.trim()).isEqualTo("mWakefulness=Awake");
+            assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_AWAKE);
         } finally {
             wakeUpDevice();
         }
@@ -150,7 +133,6 @@
      */
     @Test
     public void cect_hf4_6_16_standby_tvBeforeUcp_20() throws Exception {
-        ITestDevice device = getDevice();
         setCec20();
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
@@ -176,7 +158,6 @@
      */
     @Test
     public void cect_hf4_6_19_standby_broadcastBeforeUcp_20() throws Exception {
-        ITestDevice device = getDevice();
         setCec20();
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_BROADCAST);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
index 310d1b1..fd069a7 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
@@ -17,6 +17,7 @@
 package android.hdmicec.cts.playback;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
@@ -26,6 +27,7 @@
 import android.hdmicec.cts.LogicalAddress;
 import android.hdmicec.cts.RemoteControlPassthrough;
 
+import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.Ignore;
@@ -197,4 +199,50 @@
             wakeUpDevice();
         }
     }
+
+    /*
+     * Test to check that the DUT sends volume key press events to the TV when system audio mode is
+     * not turned on.
+     */
+    @Test
+    @Ignore("b/218266432")
+    public void cect_sendVolumeKeyPressToTv() throws Exception {
+        ITestDevice device = getDevice();
+        String ucpMessage;
+        String command = "cmd hdmi_control setsam ";
+
+        simulateCecSinkConnected(device, getTargetLogicalAddress());
+
+        boolean wasSystemAudioModeOn = isSystemAudioModeOn(device);
+        if (wasSystemAudioModeOn) {
+            device.executeShellCommand(command + "off");
+            assertWithMessage("System audio mode is not off")
+                    .that(isSystemAudioModeOn(device))
+                    .isFalse();
+        }
+        try {
+            device.executeShellCommand("input keyevent KEYCODE_VOLUME_UP");
+            ucpMessage =
+                    hdmiCecClient.checkExpectedOutput(
+                            LogicalAddress.TV, CecOperand.USER_CONTROL_PRESSED);
+            assertThat(CecMessage.getParams(ucpMessage))
+                    .isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_UP);
+            device.executeShellCommand("input keyevent KEYCODE_VOLUME_DOWN");
+            ucpMessage =
+                    hdmiCecClient.checkExpectedOutput(
+                            LogicalAddress.TV, CecOperand.USER_CONTROL_PRESSED);
+            assertThat(CecMessage.getParams(ucpMessage))
+                    .isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_DOWN);
+            device.executeShellCommand("input keyevent KEYCODE_VOLUME_MUTE");
+            ucpMessage =
+                    hdmiCecClient.checkExpectedOutput(
+                            LogicalAddress.TV, CecOperand.USER_CONTROL_PRESSED);
+            assertThat(CecMessage.getParams(ucpMessage))
+                    .isEqualTo(HdmiCecConstants.CEC_KEYCODE_MUTE);
+        } finally {
+            if (wasSystemAudioModeOn) {
+                device.executeShellCommand(command + "on");
+            }
+        }
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
index e715f91..65da394 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
@@ -132,7 +132,6 @@
      */
     @Test
     public void cect_11_2_2_4_InactiveSourceOnStandby() throws Exception {
-        ITestDevice device = getDevice();
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_NONE);
         try {
@@ -143,7 +142,7 @@
                     CecOperand.SET_STREAM_PATH,
                     CecMessage.formatParams(dumpsysPhysicalAddress));
             TimeUnit.SECONDS.sleep(5);
-            sendDeviceToSleep();
+            sendDeviceToSleepWithoutWait();
             String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
                     CecOperand.INACTIVE_SOURCE);
             CecMessage.assertPhysicalAddressValid(message, dumpsysPhysicalAddress);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
new file mode 100644
index 0000000..718ae56
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.hdmicec.cts.playback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/** HDMI CEC test verifying system audio control commands (CEC 2.0 CTS Section 7.6) */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecSystemAudioControlTest extends BaseHdmiCecCtsTest {
+
+    public HdmiCecSystemAudioControlTest() {
+        super("-t", "a", "-t", "x");
+    }
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(CecRules.requiresCec(this))
+                    .around(CecRules.requiresLeanback(this))
+                    .around(
+                            CecRules.requiresDeviceType(
+                                    this, HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE))
+                    .around(hdmiCecClient);
+
+    /**
+     * Test HF4-10-5
+     *
+     * <p>Tests that a device forwards all remote control commands to the device that is providing
+     * the audio rendering.
+     */
+    @Test
+    @Ignore("b/218266432")
+    public void cect_hf4_10_5_RemoteControlCommandsWithSystemAudioControlProperty()
+            throws Exception {
+        setCec20();
+
+        ITestDevice device = getDevice();
+        // Broadcast <Set System Audio Mode> ["off"].
+        broadcastSystemAudioModeMessage(false);
+        // All remote control commands should forward to the TV.
+        sendVolumeUpCommandAndCheckForUcp(LogicalAddress.TV);
+
+        // Broadcast <Set System Audio Mode> ["on"].
+        broadcastSystemAudioModeMessage(true);
+        // All remote control commands should forward to the audio rendering device.
+        sendVolumeUpCommandAndCheckForUcp(LogicalAddress.AUDIO_SYSTEM);
+    }
+
+    private void broadcastSystemAudioModeMessage(boolean val) throws Exception {
+        hdmiCecClient.sendCecMessage(
+                hdmiCecClient.getSelfDevice(),
+                LogicalAddress.BROADCAST,
+                CecOperand.SET_SYSTEM_AUDIO_MODE,
+                CecMessage.formatParams(val ? 1 : 0));
+    }
+
+    private void sendVolumeUpCommandAndCheckForUcp(LogicalAddress toDevice) throws Exception {
+        getDevice().executeShellCommand("input keyevent KEYCODE_VOLUME_UP");
+        String message =
+                hdmiCecClient.checkExpectedOutput(toDevice, CecOperand.USER_CONTROL_PRESSED);
+        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_UP);
+        hdmiCecClient.checkExpectedOutput(toDevice, CecOperand.USER_CONTROL_RELEASED);
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
index 7f666e4..125b1e3 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
@@ -16,8 +16,6 @@
 
 package android.hdmicec.cts.playback;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.CecOperand;
@@ -71,13 +69,10 @@
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
+        String previousPowerStateChange = setPowerStateChangeOnActiveSourceLost(
+                HdmiCecConstants.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
         try {
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
-            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
-            hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
-                    CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(OFF));
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
+            simulateCecSinkConnected(device, PLAYBACK_DEVICE);
             hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
                     CecOperand.ACTIVE_SOURCE, CecMessage.formatParams("0000"));
             TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
@@ -89,10 +84,10 @@
                     CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(ON));
             // Verify that device is asleep and <Standby> was sent to TV.
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.STANDBY);
-            String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
-            assertThat(wakeState.trim()).isEqualTo("mWakefulness=Asleep");
+            assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
         } finally {
             setPowerControlMode(previousPowerControlMode);
+            setPowerStateChangeOnActiveSourceLost(previousPowerStateChange);
             wakeUpDevice();
         }
     }
@@ -109,12 +104,7 @@
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
-            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
-            hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
-                    CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(OFF));
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
+            simulateCecSinkConnected(device, PLAYBACK_DEVICE);
             device.executeShellCommand("input keyevent KEYCODE_HOME");
             TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
             hdmiCecClient.clearClientOutput();
@@ -125,8 +115,7 @@
                     CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(ON));
             // Verify that device is asleep and <Standby> was sent to TV.
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.STANDBY);
-            String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
-            assertThat(wakeState.trim()).isEqualTo("mWakefulness=Asleep");
+            assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
         } finally {
             setPowerControlMode(previousPowerControlMode);
             wakeUpDevice();
@@ -145,13 +134,9 @@
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
-            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
-            hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
-                    CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(OFF));
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
+            simulateCecSinkConnected(device, PLAYBACK_DEVICE);
             sendDeviceToSleep();
+            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
             hdmiCecClient.clearClientOutput();
             device.executeShellCommand("input keyevent KEYCODE_TV_POWER");
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
@@ -159,8 +144,7 @@
                     CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(ON));
             // Verify that device is asleep and <Standby> was sent to TV.
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.STANDBY);
-            String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
-            assertThat(wakeState.trim()).isEqualTo("mWakefulness=Asleep");
+            assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP);
         } finally {
             setPowerControlMode(previousPowerControlMode);
             wakeUpDevice();
@@ -179,13 +163,9 @@
         String previousPowerControlMode =
                 setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
-            device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
-            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
-            hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
-                    CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(OFF));
-            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
+            simulateCecSinkConnected(device, PLAYBACK_DEVICE);
             sendDeviceToSleep();
+            TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
             hdmiCecClient.clearClientOutput();
             device.executeShellCommand("input keyevent KEYCODE_TV_POWER");
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
@@ -194,8 +174,7 @@
             // Verify that device is awake and <Text View On> and <Active Source> were sent.
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TEXT_VIEW_ON);
             hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE);
-            String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
-            assertThat(wakeState.trim()).isEqualTo("mWakefulness=Awake");
+            assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_AWAKE);
         } finally {
             setPowerControlMode(previousPowerControlMode);
             wakeUpDevice();
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
index ff2ff00..605ecee 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
@@ -46,7 +46,7 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecRemoteControlPassThroughTest extends BaseHdmiCecCtsTest {
 
-    private static final int WAIT_TIME_MS = 300;
+    private static final int WAIT_TIME_MS = 1000;
 
     private HashMap<String, Integer> remoteControlKeys = new HashMap<String, Integer>();
     private HashMap<String, Integer> remoteControlAudioKeys = new HashMap<String, Integer>();
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
index 1775f5e..a74be84 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
@@ -41,7 +41,7 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest {
 
-    private static final int WAIT_TIME_MS = 300;
+    private static final int WAIT_TIME_MS = 1000;
 
     @Rule
     public RuleChain ruleChain =
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
index 4aba289..e98dce3 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
@@ -40,7 +40,7 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class HdmiCecTvOneTouchPlayTest extends BaseHdmiCecCtsTest {
 
-    private static final int WAIT_TIME_MS = 300;
+    private static final int WAIT_TIME_MS = 1000;
 
     private static final int SLEEP_TIMESTEP_SECONDS = 1;
     private static final int POWER_TRANSITION_WAIT_TIME = 10;
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvStandbyTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvStandbyTest.java
index 4efab29..d3b6d1d 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvStandbyTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvStandbyTest.java
@@ -64,10 +64,6 @@
         try {
             sendDeviceToSleep();
             hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST, CecOperand.STANDBY);
-            String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
-            assertWithMessage("Device is not in standby.")
-                    .that(wakeState.trim())
-                    .isEqualTo("mWakefulness=Asleep");
         } finally {
             wakeUpDevice();
             setHdmiControlDeviceAutoOff(wasOn);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 9b7951f..1c17037 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -19,22 +19,28 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
 import android.content.Context;
 import android.os.Message;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
+import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.style.LocaleSpan;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityRecord;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
@@ -49,45 +55,59 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
-/**
- * Class for testing {@link AccessibilityEvent}.
- */
+/** Class for testing {@link AccessibilityEvent}. */
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityEventTest {
+    private static final long IDLE_TIMEOUT_MS = 500;
+    private static final long DEFAULT_TIMEOUT_MS = 1000;
+
+    // From ViewConfiguration.SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS
+    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100;
 
     private EventReportingLinearLayout mParentView;
     private View mChildView;
-    private AccessibilityManager mAccessibilityManager;
+    private TextView mTextView;
+    private String mPackageName;
 
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
     private final ActivityTestRule<DummyActivity> mActivityRule =
             new ActivityTestRule<>(DummyActivity.class, false, false);
     private final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
             new AccessibilityDumpOnFailureRule();
     private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService>
-            mInstrumentedAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
-            SpeakingAccessibilityService.class, false);
+            mInstrumentedAccessibilityServiceRule =
+                    new InstrumentedAccessibilityServiceTestRule<>(
+                            SpeakingAccessibilityService.class, false);
 
     @Rule
-    public final RuleChain mRuleChain = RuleChain
-            .outerRule(mActivityRule)
-            .around(mInstrumentedAccessibilityServiceRule)
-            .around(mDumpOnFailureRule);
+    public final RuleChain mRuleChain =
+            RuleChain.outerRule(mActivityRule)
+                    .around(mInstrumentedAccessibilityServiceRule)
+                    .around(mDumpOnFailureRule);
 
     @Before
     public void setUp() throws Throwable {
         final Activity activity = mActivityRule.launchActivity(null);
-        mAccessibilityManager = activity.getSystemService(AccessibilityManager.class);
+        mPackageName = activity.getApplicationContext().getPackageName();
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation();
         mInstrumentedAccessibilityServiceRule.enableService();
-        mActivityRule.runOnUiThread(() -> {
-            final LinearLayout grandparent = new LinearLayout(activity);
-            activity.setContentView(grandparent);
-            mParentView = new EventReportingLinearLayout(activity);
-            mChildView = new View(activity);
-            grandparent.addView(mParentView);
-            mParentView.addView(mChildView);
-        });
+        mActivityRule.runOnUiThread(
+                () -> {
+                    final LinearLayout grandparent = new LinearLayout(activity);
+                    activity.setContentView(grandparent);
+                    mParentView = new EventReportingLinearLayout(activity);
+                    mChildView = new View(activity);
+                    mTextView = new TextView(activity);
+                    grandparent.addView(mParentView);
+                    mParentView.addView(mChildView);
+                    mParentView.addView(mTextView);
+                });
+        sUiAutomation.waitForIdle(IDLE_TIMEOUT_MS, DEFAULT_TIMEOUT_MS);
     }
 
     private static class EventReportingLinearLayout extends LinearLayout {
@@ -106,52 +126,66 @@
 
     @Test
     public void testScrollEvent() throws Exception {
-        mChildView.scrollTo(0, 100);
-        Thread.sleep(1000);
-        scrollEventFilter.assertReceivedEventCount(1);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> mChildView.scrollTo(0, 100), new ScrollEventFilter(1), DEFAULT_TIMEOUT_MS);
     }
 
     @Test
     public void testScrollEventBurstCombined() throws Exception {
-        mChildView.scrollTo(0, 100);
-        mChildView.scrollTo(0, 125);
-        mChildView.scrollTo(0, 150);
-        mChildView.scrollTo(0, 175);
-        Thread.sleep(1000);
-        scrollEventFilter.assertReceivedEventCount(1);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    mChildView.scrollTo(0, 100);
+                    mChildView.scrollTo(0, 125);
+                    mChildView.scrollTo(0, 150);
+                    mChildView.scrollTo(0, 175);
+                },
+                new ScrollEventFilter(1),
+                DEFAULT_TIMEOUT_MS);
     }
 
     @Test
     public void testScrollEventsDeliveredInCorrectInterval() throws Exception {
-        mChildView.scrollTo(0, 25);
-        mChildView.scrollTo(0, 50);
-        mChildView.scrollTo(0, 100);
-        Thread.sleep(150);
-        mChildView.scrollTo(0, 150);
-        mChildView.scrollTo(0, 175);
-        Thread.sleep(50);
-        mChildView.scrollTo(0, 200);
-        Thread.sleep(1000);
-        scrollEventFilter.assertReceivedEventCount(2);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    try {
+                        mChildView.scrollTo(0, 25);
+                        mChildView.scrollTo(0, 50);
+                        mChildView.scrollTo(0, 100);
+                        Thread.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2);
+                        mChildView.scrollTo(0, 150);
+                        mChildView.scrollTo(0, 175);
+                        Thread.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS / 2);
+                        mChildView.scrollTo(0, 200);
+                    } catch (InterruptedException e) {
+                        fail("Interrupted while dispatching event bursts.");
+                    }
+                },
+                new ScrollEventFilter(2),
+                DEFAULT_TIMEOUT_MS);
     }
 
-    private AccessibilityEventFilter scrollEventFilter = new AccessibilityEventFilter() {
-        public boolean pass(AccessibilityEvent event) {
-            return event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED;
-        }
-    };
-
     @Test
     public void testScrollEventsClearedOnDetach() throws Throwable {
-        mChildView.scrollTo(0, 25);
-        mChildView.scrollTo(5, 50);
-        mChildView.scrollTo(7, 100);
-        mActivityRule.runOnUiThread(() -> {
-            mParentView.removeView(mChildView);
-            mParentView.addView(mChildView);
-        });
-        mChildView.scrollTo(0, 150);
-        Thread.sleep(1000);
+        ScrollEventFilter scrollEventFilter = new ScrollEventFilter(1);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    mChildView.scrollTo(0, 25);
+                    mChildView.scrollTo(5, 50);
+                    mChildView.scrollTo(7, 100);
+                },
+                scrollEventFilter,
+                DEFAULT_TIMEOUT_MS);
+        mActivityRule.runOnUiThread(
+                () -> {
+                    mParentView.removeView(mChildView);
+                    mParentView.addView(mChildView);
+                });
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    mChildView.scrollTo(0, 150);
+                },
+                scrollEventFilter,
+                DEFAULT_TIMEOUT_MS);
         AccessibilityEvent event = scrollEventFilter.getLastEvent();
         assertEquals(-7, event.getScrollDeltaX());
         assertEquals(50, event.getScrollDeltaY());
@@ -159,10 +193,15 @@
 
     @Test
     public void testScrollEventsCaptureTotalDelta() throws Throwable {
-        mChildView.scrollTo(0, 25);
-        mChildView.scrollTo(5, 50);
-        mChildView.scrollTo(7, 100);
-        Thread.sleep(1000);
+        ScrollEventFilter scrollEventFilter = new ScrollEventFilter(1);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    mChildView.scrollTo(0, 25);
+                    mChildView.scrollTo(5, 50);
+                    mChildView.scrollTo(7, 100);
+                },
+                scrollEventFilter,
+                DEFAULT_TIMEOUT_MS);
         AccessibilityEvent event = scrollEventFilter.getLastEvent();
         assertEquals(7, event.getScrollDeltaX());
         assertEquals(100, event.getScrollDeltaY());
@@ -170,12 +209,24 @@
 
     @Test
     public void testScrollEventsClearDeltaAfterSending() throws Throwable {
-        mChildView.scrollTo(0, 25);
-        mChildView.scrollTo(5, 50);
-        mChildView.scrollTo(7, 100);
-        Thread.sleep(1000);
-        mChildView.scrollTo(0, 150);
-        Thread.sleep(1000);
+        ScrollEventFilter scrollEventFilter = new ScrollEventFilter(2);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    try {
+                        mChildView.scrollTo(0, 25);
+                        mChildView.scrollTo(5, 50);
+                        mChildView.scrollTo(7, 100);
+                        Thread.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2);
+                        mChildView.scrollTo(0, 25);
+                        mChildView.scrollTo(5, 50);
+                        mChildView.scrollTo(7, 100);
+                        mChildView.scrollTo(0, 150);
+                    } catch (InterruptedException e) {
+                        fail("Interrupted while dispatching event bursts.");
+                    }
+                },
+                scrollEventFilter,
+                DEFAULT_TIMEOUT_MS);
         AccessibilityEvent event = scrollEventFilter.getLastEvent();
         assertEquals(-7, event.getScrollDeltaX());
         assertEquals(50, event.getScrollDeltaY());
@@ -183,94 +234,132 @@
 
     @Test
     public void testStateEvent() throws Throwable {
-        sendStateDescriptionChangedEvent(mChildView);
-        Thread.sleep(1000);
-        stateDescriptionEventFilter.assertReceivedEventCount(1);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    sendStateDescriptionChangedEvent(mChildView);
+                },
+                new StateDescriptionEventFilter(1),
+                DEFAULT_TIMEOUT_MS);
     }
 
     @Test
     public void testStateEventBurstCombined() throws Throwable {
-        sendStateDescriptionChangedEvent(mChildView);
-        sendStateDescriptionChangedEvent(mChildView);
-        sendStateDescriptionChangedEvent(mChildView);
-        sendStateDescriptionChangedEvent(mChildView);
-        Thread.sleep(1000);
-        stateDescriptionEventFilter.assertReceivedEventCount(1);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    sendStateDescriptionChangedEvent(mChildView);
+                    sendStateDescriptionChangedEvent(mChildView);
+                    sendStateDescriptionChangedEvent(mChildView);
+                    sendStateDescriptionChangedEvent(mChildView);
+                },
+                new StateDescriptionEventFilter(1),
+                DEFAULT_TIMEOUT_MS);
     }
 
     @Test
     public void testStateEventsDeliveredInCorrectInterval() throws Throwable {
-        sendStateDescriptionChangedEvent(mChildView);
-        sendStateDescriptionChangedEvent(mChildView);
-        sendStateDescriptionChangedEvent(mChildView);
-        Thread.sleep(150);
-        sendStateDescriptionChangedEvent(mChildView);
-        sendStateDescriptionChangedEvent(mChildView);
-        Thread.sleep(50);
-        sendStateDescriptionChangedEvent(mChildView);
-        Thread.sleep(1000);
-        stateDescriptionEventFilter.assertReceivedEventCount(2);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    try {
+                        sendStateDescriptionChangedEvent(mChildView);
+                        sendStateDescriptionChangedEvent(mChildView);
+                        sendStateDescriptionChangedEvent(mChildView);
+                        Thread.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2);
+                        sendStateDescriptionChangedEvent(mChildView);
+                        sendStateDescriptionChangedEvent(mChildView);
+                        Thread.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS / 2);
+                        sendStateDescriptionChangedEvent(mChildView);
+                    } catch (InterruptedException e) {
+                        fail("Interrupted while dispatching event bursts.");
+                    }
+                },
+                new StateDescriptionEventFilter(2),
+                DEFAULT_TIMEOUT_MS);
     }
 
     @Test
     public void testStateEventsHaveLastEventText() throws Throwable {
-        sendStateDescriptionChangedEvent(mChildView, "First state");
+        StateDescriptionEventFilter stateDescriptionEventFilter =
+                new StateDescriptionEventFilter(1);
         String expectedState = "Second state";
-        sendStateDescriptionChangedEvent(mChildView, expectedState);
-        Thread.sleep(1000);
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    sendStateDescriptionChangedEvent(mChildView, "First state");
+                    sendStateDescriptionChangedEvent(mChildView, expectedState);
+                },
+                stateDescriptionEventFilter,
+                DEFAULT_TIMEOUT_MS);
         AccessibilityEvent event = stateDescriptionEventFilter.getLastEvent();
         assertEquals(expectedState, event.getText().get(0));
     }
 
-    private AccessibilityEventFilter stateDescriptionEventFilter = new AccessibilityEventFilter() {
-        public boolean pass(AccessibilityEvent event) {
-            return event.getContentChangeTypes()
-                    == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION;
-        }
-    };
-
-    private abstract class AccessibilityEventFilter {
-        abstract boolean pass(AccessibilityEvent event);
-
-        void assertReceivedEventCount(int count) {
-            assertEquals(count, filteredEventsReceived().size());
-        }
-
-        AccessibilityEvent getLastEvent() {
-            List<AccessibilityEvent> events = filteredEventsReceived();
-            if (events.size() > 0) {
-                return events.get(events.size() - 1);
-            }
-            return null;
-        }
-
-        private List<AccessibilityEvent> filteredEventsReceived() {
-            List<AccessibilityEvent> filteredEvents = new ArrayList<AccessibilityEvent>();
-            List<AccessibilityEvent> receivedEvents = mParentView.mReceivedEvents;
-            for (int i = 0; i < receivedEvents.size(); i++) {
-                if (pass(receivedEvents.get(i))) {
-                    filteredEvents.add(receivedEvents.get(i));
-                }
-            }
-            return filteredEvents;
-        }
-    }
-
     private void sendStateDescriptionChangedEvent(View view) {
         sendStateDescriptionChangedEvent(view, null);
     }
 
     private void sendStateDescriptionChangedEvent(View view, CharSequence text) {
-        AccessibilityEvent event = AccessibilityEvent.obtain(
-                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        AccessibilityEvent event =
+                AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
         event.getText().add(text);
         view.sendAccessibilityEventUnchecked(event);
     }
 
+    @Test
+    public void setText_textChanged_receivesTextEvent() throws Throwable {
+        sUiAutomation.executeAndWaitForEvent(
+                () -> sInstrumentation.runOnMainSync(() -> mTextView.setText("a")),
+                event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
+                DEFAULT_TIMEOUT_MS);
+
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    sInstrumentation.runOnMainSync(
+                            () -> {
+                                mTextView.setText("b");
+                            });
+                },
+                event ->
+                        isExpectedSource(event, mTextView)
+                                && isExpectedChangeType(
+                                        event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
+                DEFAULT_TIMEOUT_MS);
+    }
+
+    @Test
+    public void setText_parcelableSpanChanged_receivesUndefinedEvent() throws Throwable {
+        String text = "a";
+        sUiAutomation.executeAndWaitForEvent(
+                () -> sInstrumentation.runOnMainSync(() -> mTextView.setText(text)),
+                event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
+                DEFAULT_TIMEOUT_MS);
+
+        sUiAutomation.executeAndWaitForEvent(
+                () -> {
+                    sInstrumentation.runOnMainSync(
+                            () -> {
+                                SpannableString spannableString = new SpannableString(text);
+                                spannableString.setSpan(new LocaleSpan(Locale.ENGLISH), 0, 1, 0);
+                                mTextView.setText(spannableString);
+                            });
+                },
+                event ->
+                        isExpectedSource(event, mTextView)
+                                && isExpectedChangeType(
+                                        event, AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED),
+                DEFAULT_TIMEOUT_MS);
+    }
+
+    private static boolean isExpectedSource(AccessibilityEvent event, View view) {
+        return TextUtils.equals(view.getContext().getPackageName(), event.getPackageName())
+                && TextUtils.equals(view.getAccessibilityClassName(), event.getClassName());
+    }
+
+    private static boolean isExpectedChangeType(AccessibilityEvent event, int changeType) {
+        return (event.getContentChangeTypes() & changeType) == changeType;
+    }
+
     /**
-     * Tests whether accessibility events are correctly written and
-     * read from a parcel (version 1).
+     * Tests whether accessibility events are correctly written and read from a parcel (version 1).
      */
     @SmallTest
     @Test
@@ -291,9 +380,7 @@
         parcel.recycle();
     }
 
-    /**
-     * Tests if {@link AccessibilityEvent} are properly reused.
-     */
+    /** Tests if {@link AccessibilityEvent} are properly reused. */
     @SmallTest
     @Test
     public void testReuse() {
@@ -303,9 +390,7 @@
         assertSame("AccessibilityEvent not properly reused", firstEvent, secondEvent);
     }
 
-    /**
-     * Tests if {@link AccessibilityEvent} are properly recycled.
-     */
+    /** Tests if {@link AccessibilityEvent} are properly recycled. */
     @SmallTest
     @Test
     public void testRecycle() {
@@ -321,61 +406,80 @@
         assertAccessibilityEventCleared(recycledEvent);
     }
 
-    /**
-     * Tests whether the event types are correctly converted to strings.
-     */
+    /** Tests whether the event types are correctly converted to strings. */
     @SmallTest
     @Test
     public void testEventTypeToString() {
-        assertEquals("TYPE_NOTIFICATION_STATE_CHANGED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED));
-        assertEquals("TYPE_TOUCH_EXPLORATION_GESTURE_END", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END));
-        assertEquals("TYPE_TOUCH_EXPLORATION_GESTURE_START", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START));
-        assertEquals("TYPE_VIEW_CLICKED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_CLICKED));
-        assertEquals("TYPE_VIEW_FOCUSED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_FOCUSED));
-        assertEquals("TYPE_VIEW_HOVER_ENTER",
+        assertEquals(
+                "TYPE_NOTIFICATION_STATE_CHANGED",
+                AccessibilityEvent.eventTypeToString(
+                        AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED));
+        assertEquals(
+                "TYPE_TOUCH_EXPLORATION_GESTURE_END",
+                AccessibilityEvent.eventTypeToString(
+                        AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END));
+        assertEquals(
+                "TYPE_TOUCH_EXPLORATION_GESTURE_START",
+                AccessibilityEvent.eventTypeToString(
+                        AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START));
+        assertEquals(
+                "TYPE_VIEW_CLICKED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_CLICKED));
+        assertEquals(
+                "TYPE_VIEW_FOCUSED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_FOCUSED));
+        assertEquals(
+                "TYPE_VIEW_HOVER_ENTER",
                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER));
-        assertEquals("TYPE_VIEW_HOVER_EXIT", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT));
-        assertEquals("TYPE_VIEW_LONG_CLICKED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_LONG_CLICKED));
-        assertEquals("TYPE_VIEW_CONTEXT_CLICKED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED));
-        assertEquals("TYPE_VIEW_SCROLLED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_SCROLLED));
-        assertEquals("TYPE_VIEW_SELECTED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_SELECTED));
-        assertEquals("TYPE_VIEW_TEXT_CHANGED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED));
-        assertEquals("TYPE_VIEW_TEXT_SELECTION_CHANGED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED));
-        assertEquals("TYPE_WINDOW_CONTENT_CHANGED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED));
-        assertEquals("TYPE_WINDOW_STATE_CHANGED", AccessibilityEvent.eventTypeToString(
-                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
+        assertEquals(
+                "TYPE_VIEW_HOVER_EXIT",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT));
+        assertEquals(
+                "TYPE_VIEW_LONG_CLICKED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED));
+        assertEquals(
+                "TYPE_VIEW_CONTEXT_CLICKED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED));
+        assertEquals(
+                "TYPE_VIEW_SCROLLED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_SCROLLED));
+        assertEquals(
+                "TYPE_VIEW_SELECTED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_SELECTED));
+        assertEquals(
+                "TYPE_VIEW_TEXT_CHANGED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED));
+        assertEquals(
+                "TYPE_VIEW_TEXT_SELECTION_CHANGED",
+                AccessibilityEvent.eventTypeToString(
+                        AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED));
+        assertEquals(
+                "TYPE_WINDOW_CONTENT_CHANGED",
+                AccessibilityEvent.eventTypeToString(
+                        AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED));
+        assertEquals(
+                "TYPE_WINDOW_STATE_CHANGED",
+                AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
     }
 
-    /**
-     * Tests whether the event describes its contents consistently.
-     */
+    /** Tests whether the event describes its contents consistently. */
     @SmallTest
     @Test
     public void testDescribeContents() {
         AccessibilityEvent event = AccessibilityEvent.obtain();
-        assertSame("Accessibility events always return 0 for this method.", 0,
+        assertSame(
+                "Accessibility events always return 0 for this method.",
+                0,
                 event.describeContents());
         fullyPopulateAccessibilityEvent(event);
-        assertSame("Accessibility events always return 0 for this method.", 0,
+        assertSame(
+                "Accessibility events always return 0 for this method.",
+                0,
                 event.describeContents());
     }
 
     /**
-     * Tests whether accessibility events are correctly written and
-     * read from a parcel (version 2).
+     * Tests whether accessibility events are correctly written and read from a parcel (version 2).
      */
     @SmallTest
     @Test
@@ -429,8 +533,8 @@
 
         final AccessibilityEvent firstEvent = new AccessibilityEvent();
         firstEvent.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-        final AccessibilityEvent secondEvent = new AccessibilityEvent(
-                AccessibilityEvent.TYPE_VIEW_FOCUSED);
+        final AccessibilityEvent secondEvent =
+                new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
 
         assertEqualsAccessibilityEvent(firstEvent, secondEvent);
     }
@@ -475,59 +579,104 @@
     }
 
     /**
-     * Compares all properties of the <code>expectedEvent</code> and the
-     * <code>receivedEvent</code> to verify that the received event is the one
-     * that is expected.
+     * Compares all properties of the <code>expectedEvent</code> and the <code>receivedEvent</code>
+     * to verify that the received event is the one that is expected.
      */
-    private static void assertEqualsAccessibilityEvent(AccessibilityEvent expectedEvent,
-            AccessibilityEvent receivedEvent) {
-        assertEquals("addedCount has incorrect value", expectedEvent.getAddedCount(), receivedEvent
-                .getAddedCount());
-        assertEquals("beforeText has incorrect value", expectedEvent.getBeforeText(), receivedEvent
-                .getBeforeText());
-        assertEquals("checked has incorrect value", expectedEvent.isChecked(), receivedEvent
-                .isChecked());
-        assertEquals("className has incorrect value", expectedEvent.getClassName(), receivedEvent
-                .getClassName());
-        assertEquals("contentDescription has incorrect value", expectedEvent
-                .getContentDescription(), receivedEvent.getContentDescription());
-        assertEquals("currentItemIndex has incorrect value", expectedEvent.getCurrentItemIndex(),
+    private static void assertEqualsAccessibilityEvent(
+            AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent) {
+        assertEquals(
+                "addedCount has incorrect value",
+                expectedEvent.getAddedCount(),
+                receivedEvent.getAddedCount());
+        assertEquals(
+                "beforeText has incorrect value",
+                expectedEvent.getBeforeText(),
+                receivedEvent.getBeforeText());
+        assertEquals(
+                "checked has incorrect value",
+                expectedEvent.isChecked(),
+                receivedEvent.isChecked());
+        assertEquals(
+                "className has incorrect value",
+                expectedEvent.getClassName(),
+                receivedEvent.getClassName());
+        assertEquals(
+                "contentDescription has incorrect value",
+                expectedEvent.getContentDescription(),
+                receivedEvent.getContentDescription());
+        assertEquals(
+                "currentItemIndex has incorrect value",
+                expectedEvent.getCurrentItemIndex(),
                 receivedEvent.getCurrentItemIndex());
-        assertEquals("enabled has incorrect value", expectedEvent.isEnabled(), receivedEvent
-                .isEnabled());
-        assertEquals("eventType has incorrect value", expectedEvent.getEventType(), receivedEvent
-                .getEventType());
-        assertEquals("fromIndex has incorrect value", expectedEvent.getFromIndex(), receivedEvent
-                .getFromIndex());
-        assertEquals("fullScreen has incorrect value", expectedEvent.isFullScreen(), receivedEvent
-                .isFullScreen());
-        assertEquals("itemCount has incorrect value", expectedEvent.getItemCount(), receivedEvent
-                .getItemCount());
-        assertEquals("password has incorrect value", expectedEvent.isPassword(), receivedEvent
-                .isPassword());
-        assertEquals("removedCount has incorrect value", expectedEvent.getRemovedCount(),
+        assertEquals(
+                "enabled has incorrect value",
+                expectedEvent.isEnabled(),
+                receivedEvent.isEnabled());
+        assertEquals(
+                "eventType has incorrect value",
+                expectedEvent.getEventType(),
+                receivedEvent.getEventType());
+        assertEquals(
+                "fromIndex has incorrect value",
+                expectedEvent.getFromIndex(),
+                receivedEvent.getFromIndex());
+        assertEquals(
+                "fullScreen has incorrect value",
+                expectedEvent.isFullScreen(),
+                receivedEvent.isFullScreen());
+        assertEquals(
+                "itemCount has incorrect value",
+                expectedEvent.getItemCount(),
+                receivedEvent.getItemCount());
+        assertEquals(
+                "password has incorrect value",
+                expectedEvent.isPassword(),
+                receivedEvent.isPassword());
+        assertEquals(
+                "removedCount has incorrect value",
+                expectedEvent.getRemovedCount(),
                 receivedEvent.getRemovedCount());
-        assertSame("maxScrollX has incorrect value", expectedEvent.getMaxScrollX(),
+        assertSame(
+                "maxScrollX has incorrect value",
+                expectedEvent.getMaxScrollX(),
                 receivedEvent.getMaxScrollX());
-        assertSame("maxScrollY has incorrect value", expectedEvent.getMaxScrollY(),
+        assertSame(
+                "maxScrollY has incorrect value",
+                expectedEvent.getMaxScrollY(),
                 receivedEvent.getMaxScrollY());
-        assertSame("scrollX has incorrect value", expectedEvent.getScrollX(),
+        assertSame(
+                "scrollX has incorrect value",
+                expectedEvent.getScrollX(),
                 receivedEvent.getScrollX());
-        assertSame("scrollY has incorrect value", expectedEvent.getScrollY(),
+        assertSame(
+                "scrollY has incorrect value",
+                expectedEvent.getScrollY(),
                 receivedEvent.getScrollY());
-        assertSame("scrollDeltaX has incorrect value", expectedEvent.getScrollDeltaX(),
+        assertSame(
+                "scrollDeltaX has incorrect value",
+                expectedEvent.getScrollDeltaX(),
                 receivedEvent.getScrollDeltaX());
-        assertSame("scrollDeltaY has incorrect value", expectedEvent.getScrollDeltaY(),
+        assertSame(
+                "scrollDeltaY has incorrect value",
+                expectedEvent.getScrollDeltaY(),
                 receivedEvent.getScrollDeltaY());
-        assertSame("toIndex has incorrect value", expectedEvent.getToIndex(),
+        assertSame(
+                "toIndex has incorrect value",
+                expectedEvent.getToIndex(),
                 receivedEvent.getToIndex());
-        assertSame("scrollable has incorrect value", expectedEvent.isScrollable(),
+        assertSame(
+                "scrollable has incorrect value",
+                expectedEvent.isScrollable(),
                 receivedEvent.isScrollable());
-        assertSame("granularity has incorrect value", expectedEvent.getMovementGranularity(),
+        assertSame(
+                "granularity has incorrect value",
+                expectedEvent.getMovementGranularity(),
                 receivedEvent.getMovementGranularity());
-        assertSame("action has incorrect value", expectedEvent.getAction(),
-                receivedEvent.getAction());
-        assertSame("windowChangeTypes has incorrect value", expectedEvent.getWindowChanges(),
+        assertSame(
+                "action has incorrect value", expectedEvent.getAction(), receivedEvent.getAction());
+        assertSame(
+                "windowChangeTypes has incorrect value",
+                expectedEvent.getWindowChanges(),
                 receivedEvent.getWindowChanges());
 
         AccessibilityRecordTest.assertEqualsText(expectedEvent.getText(), receivedEvent.getText());
@@ -536,12 +685,14 @@
         assertEqualAppendedRecord(expectedEvent, receivedEvent);
     }
 
-    private static void assertEqualAppendedRecord(AccessibilityEvent expectedEvent,
-            AccessibilityEvent receivedEvent) {
-        assertEquals("recordCount has incorrect value", expectedEvent.getRecordCount(),
+    private static void assertEqualAppendedRecord(
+            AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent) {
+        assertEquals(
+                "recordCount has incorrect value",
+                expectedEvent.getRecordCount(),
                 receivedEvent.getRecordCount());
         if (expectedEvent.getRecordCount() != 0 && receivedEvent.getRecordCount() != 0) {
-            AccessibilityRecord expectedRecord =  expectedEvent.getRecord(0);
+            AccessibilityRecord expectedRecord = expectedEvent.getRecord(0);
             AccessibilityRecord receivedRecord = receivedEvent.getRecord(0);
             AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedRecord, receivedRecord);
         }
@@ -558,4 +709,60 @@
         TestCase.assertEquals("eventType not properly recycled", 0, event.getEventType());
         TestCase.assertNull("packageName not properly recycled", event.getPackageName());
     }
+
+    class ScrollEventFilter extends AccessibilityEventFilter {
+        private int mCount = 0;
+        private int mTargetCount;
+
+        ScrollEventFilter(int count) {
+            mTargetCount = count;
+        }
+
+        public boolean accept(AccessibilityEvent event) {
+            if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+                mCount += 1;
+                mEvents.add(event);
+                return mCount >= mTargetCount;
+            }
+            return false;
+        }
+    }
+
+    class StateDescriptionEventFilter extends AccessibilityEventFilter {
+        private int mCount;
+        private int mTargetCount;
+
+        StateDescriptionEventFilter(int count) {
+            mTargetCount = count;
+        }
+
+        public boolean accept(AccessibilityEvent event) {
+            if (event.getContentChangeTypes()
+                    == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION) {
+                mCount += 1;
+                mEvents.add(event);
+                return mCount >= mTargetCount;
+            }
+            return false;
+        }
+    }
+    ;
+
+    private abstract class AccessibilityEventFilter
+            implements UiAutomation.AccessibilityEventFilter {
+        protected List<AccessibilityEvent> mEvents = new ArrayList<>();
+
+        public abstract boolean accept(AccessibilityEvent event);
+
+        void assertReceivedEventCount(int count) {
+            assertEquals(count, mEvents.size());
+        }
+
+        AccessibilityEvent getLastEvent() {
+            if (mEvents.size() > 0) {
+                return mEvents.get(mEvents.size() - 1);
+            }
+            return null;
+        }
+    }
 }
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
index b7f0ec2..fa6dcde 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
@@ -52,7 +52,10 @@
 import com.android.bedstead.harrier.annotations.Postsubmit;
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.packages.Package;
+import com.android.bedstead.nene.packages.PackageReference;
 import com.android.bedstead.nene.permissions.PermissionContext;
+import com.android.bedstead.nene.users.UnresolvedUser;
+import com.android.bedstead.nene.users.UserReference;
 import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.ClassRule;
@@ -62,7 +65,9 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -383,8 +388,17 @@
     }
 
     private Set<String> getInstalledPackagesOnUser(Set<String> packages, UserHandle user) {
-        return packages.stream().filter(p -> isPackageInstalledOnUser(p, user))
-                .collect(Collectors.toSet());
+        Set<String> installedPackagesOnUser = new HashSet<>();
+
+        UserReference userRef = sTestApis.users().find(user);
+        Collection<PackageReference> packageInUser = sTestApis.packages().installedForUser(userRef);
+        for (PackageReference pkg : packageInUser) {
+            if (packages.contains(pkg.packageName())) {
+                installedPackagesOnUser.add(pkg.packageName());
+            }
+        }
+
+        return installedPackagesOnUser;
     }
 
     private boolean isPackageInstalledOnCurrentUser(String packageName) {
diff --git a/tests/framework/base/biometrics/Android.bp b/tests/framework/base/biometrics/Android.bp
index 369faa7..66c8134 100644
--- a/tests/framework/base/biometrics/Android.bp
+++ b/tests/framework/base/biometrics/Android.bp
@@ -43,6 +43,10 @@
         "ub-uiautomator",
     ],
     srcs: ["src/**/*.java"],
+    data: [
+        ":CtsBiometricServiceTestApp",
+        ":CtsFingerprintServiceTestApp",
+    ],
     sdk_version: "test_current",
     per_testcase_directory: true,
 }
diff --git a/tests/libcore/ojluni/Android.bp b/tests/libcore/ojluni/Android.bp
index 1276621..d6191d4 100644
--- a/tests/libcore/ojluni/Android.bp
+++ b/tests/libcore/ojluni/Android.bp
@@ -43,4 +43,7 @@
         "general-tests",
         "mts-art",
     ],
+    jacoco: {
+        exclude_filter: ["test.**"],
+    }
 }
diff --git a/tests/signature/api-check/Android.bp b/tests/signature/api-check/Android.bp
index b3fd278..c95cf4a 100644
--- a/tests/signature/api-check/Android.bp
+++ b/tests/signature/api-check/Android.bp
@@ -33,6 +33,7 @@
         // androidx.test.runner depends on android.test classes from this library.
         "android.test.base-minus-junit",
         "androidx.test.runner",
+        "compatibility-device-util-axt",
         "cts-signature-common",
     ],
 }
@@ -54,18 +55,6 @@
     compile_multilib: "both",
 }
 
-// Defaults for signature api checks with dynamic config.
-java_defaults {
-    name: "signature-api-check-dynamic-config-defaults",
-    defaults: ["signature-api-check-defaults"],
-    defaults_visibility: [
-        "//cts/tests/signature:__subpackages__",
-    ],
-    static_libs: [
-        "cts-signature-with-dynamic-config",
-    ],
-}
-
 // Filegroup containing the jarjar rules that need to be applied to any test that checks for
 // accessibility (or inaccesibility) of android.test and junit classes from the android.test.base
 // and android.test.runner libraries.
@@ -103,7 +92,7 @@
 // Defaults for hiddenapi blocklist checks.
 java_defaults {
     name: "hiddenapi-blocklist-check-defaults",
-    defaults: ["signature-api-check-dynamic-config-defaults"],
+    defaults: ["signature-api-check-defaults"],
     java_resources: [
         ":platform-bootclasspath{hiddenapi-flags.csv}",
         ":cts-api-hiddenapi-filter-csv"
diff --git a/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java
index 13ea0f1..c940aac 100644
--- a/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java
@@ -16,7 +16,5 @@
 
 package android.signature.cts.api.api27;
 
-import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
-
-public class HiddenApiTest extends DynamicConfigHiddenApiTest {
+public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java
index 091a25f..f85dda3 100644
--- a/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java
@@ -16,7 +16,5 @@
 
 package android.signature.cts.api.api28;
 
-import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
-
-public class HiddenApiTest extends DynamicConfigHiddenApiTest {
+public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java
index 7726489..34f33fe 100644
--- a/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java
@@ -16,7 +16,5 @@
 
 package android.signature.cts.api.current;
 
-import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
-
-public class HiddenApiTest extends DynamicConfigHiddenApiTest {
+public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java
index 9266976..b1a2ee2 100644
--- a/tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java
@@ -17,9 +17,8 @@
 package android.signature.cts.api.blocklist.debug;
 
 import android.signature.cts.DexMemberChecker;
-import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
 
-public class DebugClassHiddenApiTest extends DynamicConfigHiddenApiTest {
+public class DebugClassHiddenApiTest extends android.signature.cts.api.HiddenApiTest {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
diff --git a/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java
index 3fe708c..b30bbcb 100644
--- a/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java
@@ -17,10 +17,9 @@
 package android.signature.cts.api.test;
 
 import android.signature.cts.DexMember;
-import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
 import java.util.Set;
 
-public class HiddenApiTest extends DynamicConfigHiddenApiTest {
+public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
 
     /**
      * Override to match only those members that specify both test-api and blocked.
@@ -30,5 +29,4 @@
         Set<String> flags = member.getHiddenapiFlags();
         return flags.contains("test-api") && flags.contains("blocked");
     }
-
 }
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
index 8150904..7d2946a 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
@@ -32,6 +32,8 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.DynamicConfigDeviceSide;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
@@ -49,6 +51,12 @@
  */
 public class AbstractApiTest extends TestCase {
 
+    /**
+     * The name of the optional instrumentation option that contains the name of the dynamic config
+     * data set that contains the expected failures.
+     */
+    private static final String DYNAMIC_CONFIG_NAME_OPTION = "dynamic-config-name";
+
     private static final String TAG = "SignatureTest";
 
     private TestResultObserver mResultObserver;
@@ -105,17 +113,25 @@
                 new BootClassPathClassesProvider(),
                 name -> name != null && name.startsWith("com.android.internal.R."));
 
+        String dynamicConfigName = instrumentationArgs.getString(DYNAMIC_CONFIG_NAME_OPTION);
+        if (dynamicConfigName != null) {
+            // Get the DynamicConfig.xml contents and extract the expected failures list.
+            DynamicConfigDeviceSide dcds = new DynamicConfigDeviceSide(dynamicConfigName);
+            Collection<String> expectedFailures = dcds.getValues("expected_failures");
+            initExpectedFailures(expectedFailures);
+        }
+
         initializeFromArgs(instrumentationArgs);
     }
 
     /**
      * Initialize the expected failures.
      *
-     * <p>Call from with {@code #initializeFromArgs}</p>
+     * <p>Call from with {@link #setUp()}</p>
      *
      * @param expectedFailures the expected failures.
      */
-    protected void initExpectedFailures(Collection<String> expectedFailures) {
+    private void initExpectedFailures(Collection<String> expectedFailures) {
         this.expectedFailures = expectedFailures;
         String tag = getClass().getName();
         Log.d(tag, "Expected failure count: " + expectedFailures.size());
@@ -129,7 +145,6 @@
     }
 
     protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
-
     }
 
     protected interface RunnableWithResultObserver {
diff --git a/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java b/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java
index bdc5b2a..e4c67a9 100644
--- a/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java
+++ b/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java
@@ -40,7 +40,6 @@
 public class AnnotationTest extends AbstractApiTest {
 
     private static final String TAG = AnnotationTest.class.getSimpleName();
-    private static final String MODULE_NAME = "CtsSystemApiAnnotationTestCases";
 
     private String[] mExpectedApiFiles;
     private String mAnnotationForExactMatch;
@@ -49,11 +48,6 @@
     protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
         mExpectedApiFiles = getCommaSeparatedListRequired(instrumentationArgs, "expected-api-files");
         mAnnotationForExactMatch = instrumentationArgs.getString("annotation-for-exact-match");
-
-        // Get the DynamicConfig.xml contents and extract the expected failures list.
-        DynamicConfigDeviceSide dcds = new DynamicConfigDeviceSide(MODULE_NAME);
-        List<String> expectedFailures = dcds.getValues("expected_failures");
-        initExpectedFailures(expectedFailures);
     }
 
     private Predicate<? super JDiffClassDescription> androidAutoClassesFilter() {
diff --git a/tests/signature/api-check/system-api/Android.bp b/tests/signature/api-check/system-api/Android.bp
index 0c3d2a5..7263c84 100644
--- a/tests/signature/api-check/system-api/Android.bp
+++ b/tests/signature/api-check/system-api/Android.bp
@@ -19,14 +19,12 @@
 
 android_test {
     name: "CtsSystemApiSignatureTestCases",
-    defaults: ["signature-api-check-dynamic-config-defaults"],
+    defaults: ["signature-api-check-defaults"],
     java_resources: [
         ":CtsSystemApiSignatureTestCases_system-all.api",
         ":cts-current-api-gz",
         ":cts-system-current-api-gz",
         ":cts-system-removed-api-gz",
-        ":cts-android-test-mock-current-api-gz",
-        ":cts-android-test-runner-current-api-gz",
     ],
     test_suites: [
         "cts",
diff --git a/tests/signature/api-check/system-api/src/android/signature/cts/api/system/SignatureTest.java b/tests/signature/api-check/system-api/src/android/signature/cts/api/system/SignatureTest.java
index e523152..1997826 100644
--- a/tests/signature/api-check/system-api/src/android/signature/cts/api/system/SignatureTest.java
+++ b/tests/signature/api-check/system-api/src/android/signature/cts/api/system/SignatureTest.java
@@ -16,7 +16,5 @@
 
 package android.signature.cts.api.system;
 
-import java.android.signature.cts.api.dynamic.DynamicConfigSignatureTest;
-
-public class SignatureTest extends DynamicConfigSignatureTest {
+public class SignatureTest extends android.signature.cts.api.SignatureTest  {
 }
diff --git a/tests/signature/api-check/with-dynamic-config/Android.bp b/tests/signature/api-check/with-dynamic-config/Android.bp
deleted file mode 100644
index bdeb878..0000000
--- a/tests/signature/api-check/with-dynamic-config/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.
-
-// Compat.
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_library {
-    name: "cts-signature-with-dynamic-config",
-    visibility: [
-        "//cts/tests/signature:__subpackages__",
-    ],
-    static_libs: [
-        "cts-api-signature-test",
-        "compatibility-device-util-axt",
-    ],
-    srcs: ["src/java/**/*.java"],
-}
diff --git a/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigHiddenApiTest.java b/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigHiddenApiTest.java
deleted file mode 100644
index 1c3dfbc..0000000
--- a/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigHiddenApiTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 android.signature.cts.api.dynamic;
-
-import android.os.Bundle;
-import android.signature.cts.api.HiddenApiTest;
-import android.signature.cts.api.SignatureTest;
-import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.DynamicConfigDeviceSide;
-import java.util.Collection;
-
-/**
- * A hidden API test that supports the use of dynamic config.
- */
-public class DynamicConfigHiddenApiTest extends HiddenApiTest {
-
-    /**
-     * The name of the optional instrumentation option that contains the name of the dynamic config
-     * data set that contains the expected failures.
-     */
-    private static final String DYNAMIC_CONFIG_NAME_OPTION = "dynamic-config-name";
-
-    @Override
-    protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
-        super.initializeFromArgs(instrumentationArgs);
-
-        String dynamicConfigName = instrumentationArgs.getString(DYNAMIC_CONFIG_NAME_OPTION);
-        if (dynamicConfigName != null) {
-            // Get the DynamicConfig.xml contents and extract the expected failures list.
-            DynamicConfigDeviceSide dcds = new DynamicConfigDeviceSide(dynamicConfigName);
-            Collection<String> expectedFailures = dcds.getValues("expected_failures");
-            initExpectedFailures(expectedFailures);
-        }
-    }
-}
diff --git a/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigSignatureTest.java b/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigSignatureTest.java
deleted file mode 100644
index 9c57a1e..0000000
--- a/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigSignatureTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 java.android.signature.cts.api.dynamic;
-
-import android.os.Bundle;
-import android.signature.cts.api.SignatureTest;
-import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.DynamicConfigDeviceSide;
-import java.util.Collection;
-
-/**
- * A signature test that supports the use of dynamic config.
- */
-public class DynamicConfigSignatureTest extends SignatureTest {
-
-    /**
-     * The name of the optional instrumentation option that contains the name of the dynamic config
-     * data set that contains the expected failures.
-     */
-    private static final String DYNAMIC_CONFIG_NAME_OPTION = "dynamic-config-name";
-
-    @Override
-    protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
-        super.initializeFromArgs(instrumentationArgs);
-
-        String dynamicConfigName = instrumentationArgs.getString(DYNAMIC_CONFIG_NAME_OPTION);
-        if (dynamicConfigName != null) {
-            // Get the DynamicConfig.xml contents and extract the expected failures list.
-            DynamicConfigDeviceSide dcds = new DynamicConfigDeviceSide(dynamicConfigName);
-            Collection<String> expectedFailures = dcds.getValues("expected_failures");
-            initExpectedFailures(expectedFailures);
-        }
-    }
-}
diff --git a/tests/tests/binder_ndk/AndroidTest.xml b/tests/tests/binder_ndk/AndroidTest.xml
index 48bf70d..6578635 100644
--- a/tests/tests/binder_ndk/AndroidTest.xml
+++ b/tests/tests/binder_ndk/AndroidTest.xml
@@ -19,6 +19,7 @@
     <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" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
index 7f1a487..a9dea0b 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothStatusCodes;
+import android.content.AttributionSource;
 import android.content.pm.PackageManager;
 import android.test.AndroidTestCase;
 
@@ -66,6 +67,9 @@
         String packageName = mContext.getOpPackageName();
         String deviceAddress = "00:11:22:AA:BB:CC";
 
+        AttributionSource source = AttributionSource.myAttributionSource();
+        assertEquals("android.bluetooth.cts", source.getPackageName());
+
         BluetoothDevice device = mAdapter.getRemoteDevice(deviceAddress);
         // Verifies that when there is no alias, we return the device name
         assertNull(device.getAlias());
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 45b2683..a1cb4bc 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -80,15 +80,6 @@
 
 static const std::string kWebViewPlatSupportLib = "libwebviewchromium_plat_support.so";
 
-static bool is_directory(const char* path) {
-  struct stat sb;
-  if (stat(path, &sb) != -1) {
-    return S_ISDIR(sb.st_mode);
-  }
-
-  return false;
-}
-
 static bool not_accessible(const std::string& err) {
   return err.find("dlopen failed: library \"") == 0 &&
          err.find("is not accessible for the namespace \"classloader-namespace\"") != std::string::npos;
@@ -305,11 +296,19 @@
       }
 
       std::string path = dir + "/" + dp->d_name;
-      if (is_directory(path.c_str())) {
-        dirs.push(path);
-      } else if (!check_lib(env, clazz, path, library_search_paths, public_library_basenames,
-                            test_system_load_library, check_absence, errors)) {
-        success = false;
+      struct stat sb;
+      // Use lstat to not dereference a symlink. If it links out of library_path
+      // it can be ignored because the Bionic linker derefences symlinks before
+      // checking the path. If it links inside library_path we'll get to the
+      // link target anyway.
+      if (lstat(path.c_str(), &sb) != -1) {
+        if (S_ISDIR(sb.st_mode)) {
+          dirs.push(path);
+        } else if (!S_ISLNK(sb.st_mode) &&
+                   !check_lib(env, clazz, path, library_search_paths, public_library_basenames,
+                              test_system_load_library, check_absence, errors)) {
+          success = false;
+        }
       }
     }
   }
diff --git a/tests/tests/keystore/OWNERS b/tests/tests/keystore/OWNERS
index 30685c8..21e2825 100644
--- a/tests/tests/keystore/OWNERS
+++ b/tests/tests/keystore/OWNERS
@@ -2,3 +2,5 @@
 swillden@google.com
 jdanis@google.com
 jbires@google.com
+eranm@google.com
+drysdale@google.com
diff --git a/tests/tests/media/Android.bp b/tests/tests/media/Android.bp
index 7ee7d2a..a465f80 100644
--- a/tests/tests/media/Android.bp
+++ b/tests/tests/media/Android.bp
@@ -26,98 +26,6 @@
     license_text: ["LICENSE_CC_BY"],
 }
 
-java_library {
-    name: "ctsmediautil",
-    srcs: [
-        "common/src/android/media/cts/CodecImage.java",
-        "common/src/android/media/cts/YUVImage.java",
-        "common/src/android/media/cts/CodecUtils.java",
-        "common/src/android/media/cts/CodecState.java",
-        "common/src/android/media/cts/MediaCodecTunneledPlayer.java",
-        "common/src/android/media/cts/MediaTimeProvider.java",
-        "common/src/android/media/cts/NonBlockingAudioTrack.java",
-    ],
-    static_libs: [
-        "compatibility-device-util-axt",
-        "platform-test-annotations",
-    ],
-    platform_apis: true,
-    min_sdk_version: "29",
-    target_sdk_version: "31",
-}
-
-cc_test_library {
-    name: "libctscodecutils_jni",
-    srcs: [
-        "common/jni/codec-utils-jni.cpp",
-        "common/jni/md5_utils.cpp",
-    ],
-    shared_libs: [
-        "libnativehelper_compat_libc++",
-        "liblog",
-    ],
-    header_libs: ["liblog_headers"],
-    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
-    // (revisit if/when we add features to this library that require newer sdk.
-    sdk_version: "29",
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-DEGL_EGLEXT_PROTOTYPES",
-    ],
-    stl: "libc++_static",
-    gtest: false,
-}
-
-cc_test_library {
-    name: "libctsmediacommon_jni",
-    srcs: [
-        "common/jni/NdkInputSurface-jni.cpp",
-        "common/jni/NdkMediaCodec-jni.cpp",
-    ],
-    shared_libs: [
-        "libandroid",
-        "libnativehelper_compat_libc++",
-        "liblog",
-        "libmediandk",
-        "libEGL",
-    ],
-    header_libs: ["liblog_headers"],
-    stl: "libc++_static",
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-DEGL_EGLEXT_PROTOTYPES",
-    ],
-    gtest: false,
-    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
-    // (revisit if/when we add features to this library that require newer sdk.
-    sdk_version: "29",
-}
-
-android_library {
-    name: "cts-media-common",
-    srcs: [
-        "common/src/**/*.java",
-    ],
-    static_libs: [
-        "androidx.heifwriter_heifwriter",
-        "androidx.test.core",
-        "androidx.test.ext.junit",
-        "compatibility-device-util-axt",
-        "junit",
-        "platform-test-annotations",
-    ],
-    platform_apis: true,
-    libs: [
-        "org.apache.http.legacy",
-        "android.test.base",
-        "android.test.runner",
-    ],
-    min_sdk_version: "29",
-    target_sdk_version: "31",
-}
-
 android_test {
     name: "CtsMediaTestCases",
     defaults: ["cts_defaults"],
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 86f744f..087e860 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -47,20 +47,13 @@
     <permission android:name="android.media.cts"
          android:protectionLevel="normal"/>
 
-    <application android:networkSecurityConfig="@xml/network_security_config"
+    <application
          android:requestLegacyExternalStorage="true"
          android:largeHeap="true">
         <uses-library android:name="android.test.runner"/>
         <uses-library android:name="org.apache.http.legacy"
              android:required="false"/>
 
-        <activity android:name="android.media.cts.MediaProjectionActivity"
-             android:label="MediaProjectionActivity"
-             android:screenOrientation="locked"/>
-        <activity android:name="android.media.cts.AudioManagerStub"
-             android:label="AudioManagerStub"/>
-        <activity android:name="android.media.cts.AudioManagerStubHelper"
-             android:label="AudioManagerStubHelper"/>
         <activity android:name="android.media.cts.MediaSessionTestActivity"
              android:label="MediaSessionTestActivity"
              android:screenOrientation="nosensor"
@@ -71,26 +64,6 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
             </intent-filter>
         </activity>
-        <activity android:name="android.media.cts.MediaStubActivity"
-             android:label="MediaStubActivity"
-             android:screenOrientation="nosensor"
-             android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
-             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.media.cts.MediaStubActivity2"
-             android:label="MediaStubActivity2"
-             android:screenOrientation="nosensor"
-             android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
-             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.media.cts.FaceDetectorStub"
              android:label="FaceDetectorStub"/>
         <activity android:name="android.media.cts.ResourceManagerStubActivity"
@@ -124,10 +97,7 @@
                 <action android:name="android.media.MediaSession2Service"/>
             </intent-filter>
         </service>
-        <service android:name="android.media.cts.LocalMediaProjectionService"
-             android:foregroundServiceType="mediaProjection"
-             android:enabled="true">
-        </service>
+
         <service android:name=".StubMediaRoute2ProviderService"
              android:exported="true">
             <intent-filter>
diff --git a/tests/tests/media/audio/AndroidManifest.xml b/tests/tests/media/audio/AndroidManifest.xml
index bc7fc4b..9ee8519 100644
--- a/tests/tests/media/audio/AndroidManifest.xml
+++ b/tests/tests/media/audio/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
diff --git a/tests/tests/media/codec/AndroidManifest.xml b/tests/tests/media/codec/AndroidManifest.xml
index 4ff8f2d..328929a 100644
--- a/tests/tests/media/codec/AndroidManifest.xml
+++ b/tests/tests/media/codec/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
-
+    <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
diff --git a/tests/tests/media/common/Android.bp b/tests/tests/media/common/Android.bp
new file mode 100644
index 0000000..2990ab6
--- /dev/null
+++ b/tests/tests/media/common/Android.bp
@@ -0,0 +1,105 @@
+// 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.
+
+java_library {
+    name: "ctsmediautil",
+    srcs: [
+        "src/android/media/cts/CodecImage.java",
+        "src/android/media/cts/YUVImage.java",
+        "src/android/media/cts/CodecUtils.java",
+        "src/android/media/cts/CodecState.java",
+        "src/android/media/cts/MediaCodecTunneledPlayer.java",
+        "src/android/media/cts/MediaTimeProvider.java",
+        "src/android/media/cts/NonBlockingAudioTrack.java",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "platform-test-annotations",
+    ],
+    platform_apis: true,
+    min_sdk_version: "29",
+    target_sdk_version: "31",
+}
+
+cc_test_library {
+    name: "libctscodecutils_jni",
+    srcs: [
+        "jni/codec-utils-jni.cpp",
+        "jni/md5_utils.cpp",
+    ],
+    shared_libs: [
+        "libnativehelper_compat_libc++",
+        "liblog",
+    ],
+    header_libs: ["liblog_headers"],
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    stl: "libc++_static",
+    gtest: false,
+}
+
+cc_test_library {
+    name: "libctsmediacommon_jni",
+    srcs: [
+        "jni/NdkInputSurface-jni.cpp",
+        "jni/NdkMediaCodec-jni.cpp",
+    ],
+    shared_libs: [
+        "libandroid",
+        "libnativehelper_compat_libc++",
+        "liblog",
+        "libmediandk",
+        "libEGL",
+    ],
+    header_libs: ["liblog_headers"],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    gtest: false,
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
+}
+
+android_library {
+    name: "cts-media-common",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "androidx.heifwriter_heifwriter",
+        "androidx.test.core",
+        "androidx.test.ext.junit",
+        "compatibility-device-util-axt",
+        "junit",
+        "platform-test-annotations",
+    ],
+    platform_apis: true,
+    libs: [
+        "org.apache.http.legacy",
+        "android.test.base",
+        "android.test.runner",
+    ],
+    min_sdk_version: "29",
+    target_sdk_version: "31",
+}
diff --git a/tests/tests/media/common/AndroidManifest.xml b/tests/tests/media/common/AndroidManifest.xml
new file mode 100644
index 0000000..651a22d
--- /dev/null
+++ b/tests/tests/media/common/AndroidManifest.xml
@@ -0,0 +1,62 @@
+<?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.media.cts"
+     android:targetSandboxVersion="2">
+
+    <uses-sdk android:minSdkVersion="29"
+         android:targetSdkVersion="31"/>
+
+    <application android:networkSecurityConfig="@xml/network_security_config"
+        android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+
+        <activity android:name="android.media.cts.MediaProjectionActivity"
+            android:label="MediaProjectionActivity"
+            android:screenOrientation="locked"/>
+        <activity android:name="android.media.cts.AudioManagerStub"
+            android:label="AudioManagerStub"/>
+        <activity android:name="android.media.cts.AudioManagerStubHelper"
+            android:label="AudioManagerStubHelper"/>
+
+        <activity android:name="android.media.cts.MediaStubActivity"
+            android:label="MediaStubActivity"
+            android:screenOrientation="nosensor"
+            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
+            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.media.cts.MediaStubActivity2"
+            android:label="MediaStubActivity2"
+            android:screenOrientation="nosensor"
+            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+            </intent-filter>
+        </activity>
+
+        <service android:name="android.media.cts.LocalMediaProjectionService"
+            android:foregroundServiceType="mediaProjection"
+            android:enabled="true">
+        </service>
+     </application>
+</manifest>
diff --git a/tests/tests/media/res/layout/composition_layout.xml b/tests/tests/media/common/res/layout/composition_layout.xml
similarity index 100%
rename from tests/tests/media/res/layout/composition_layout.xml
rename to tests/tests/media/common/res/layout/composition_layout.xml
diff --git a/tests/tests/media/res/layout/mediacodecplayer.xml b/tests/tests/media/common/res/layout/mediacodecplayer.xml
similarity index 100%
rename from tests/tests/media/res/layout/mediacodecplayer.xml
rename to tests/tests/media/common/res/layout/mediacodecplayer.xml
diff --git a/tests/tests/media/res/layout/mediaplayer.xml b/tests/tests/media/common/res/layout/mediaplayer.xml
similarity index 100%
rename from tests/tests/media/res/layout/mediaplayer.xml
rename to tests/tests/media/common/res/layout/mediaplayer.xml
diff --git a/tests/tests/media/res/layout/test_runner_activity.xml b/tests/tests/media/common/res/layout/test_runner_activity.xml
similarity index 100%
rename from tests/tests/media/res/layout/test_runner_activity.xml
rename to tests/tests/media/common/res/layout/test_runner_activity.xml
diff --git a/tests/tests/media/common/res/raw/sine1khzs40dblong.mp3 b/tests/tests/media/common/res/raw/sine1khzs40dblong.mp3
new file mode 100644
index 0000000..29bc683
--- /dev/null
+++ b/tests/tests/media/common/res/raw/sine1khzs40dblong.mp3
Binary files differ
diff --git a/tests/tests/media/common/res/raw/testmp3.mp3 b/tests/tests/media/common/res/raw/testmp3.mp3
new file mode 100755
index 0000000..657faf7
--- /dev/null
+++ b/tests/tests/media/common/res/raw/testmp3.mp3
Binary files differ
diff --git a/tests/tests/media/res/xml/network_security_config.xml b/tests/tests/media/common/res/xml/network_security_config.xml
similarity index 100%
rename from tests/tests/media/res/xml/network_security_config.xml
rename to tests/tests/media/common/res/xml/network_security_config.xml
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 f9355f5..61acac7 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
@@ -78,7 +78,8 @@
  * test. For decoder test, hw and sw decoders are tested,
  * </p>
  */
-@Presubmit
+// TODO(b/210947256) Enable presubmit once this test works on Pixel 4
+//@Presubmit
 @SmallTest
 @RequiresDevice
 @AppModeFull(reason = "Instant apps cannot access the SD card")
diff --git a/tests/tests/media/extractor/AndroidManifest.xml b/tests/tests/media/extractor/AndroidManifest.xml
index 6911d53..82f45b9 100644
--- a/tests/tests/media/extractor/AndroidManifest.xml
+++ b/tests/tests/media/extractor/AndroidManifest.xml
@@ -17,9 +17,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="android.media.extractor.cts"
      android:targetSandboxVersion="2">
-
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 
diff --git a/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java b/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java
index 972685a..1fe887b 100644
--- a/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java
+++ b/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java
@@ -49,7 +49,7 @@
 public class MediaMuxerTest extends AndroidTestCase {
     private static final String TAG = "MediaMuxerTest";
     private static final boolean VERBOSE = false;
-    private static final int MAX_SAMPLE_SIZE = 256 * 1024;
+    private static final int MAX_SAMPLE_SIZE = 1024 * 1024;
     private static final float LATITUDE = 0.0000f;
     private static final float LONGITUDE  = -180.0f;
     private static final float BAD_LATITUDE = 91.0f;
@@ -83,6 +83,43 @@
     }
 
     /**
+     * Test: make sure the muxer handles dovi profile 8.4 video track only file correctly.
+     */
+    public void testDolbyVisionVideoOnlyP8() throws Exception {
+        final String source = "video_dovi_1920x1080_60fps_dvhe_08_04.mp4";
+        String outputFilePath = File.createTempFile("MediaMuxerTest_dolbyvisionP8videoOnly", ".mp4")
+                .getAbsolutePath();
+        try {
+            cloneAndVerify(source, outputFilePath, 2 /* expectedTrackCount */, 180 /* degrees */,
+                    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4,
+                    MediaMuxerTest::filterOutNonDolbyVisionFormat);
+        } finally {
+            new File(outputFilePath).delete();
+        }
+    }
+
+    /**
+     * Test: make sure the muxer handles dovi profile 9.2 video track only file correctly.
+     */
+    public void testDolbyVisionVideoOnlyP9() throws Exception {
+        final String source = "video_dovi_1920x1080_60fps_dvav_09_02.mp4";
+        String outputFilePath = File.createTempFile("MediaMuxerTest_dolbyvisionP9videoOnly", ".mp4")
+                .getAbsolutePath();
+        try {
+            cloneAndVerify(source, outputFilePath, 2 /* expectedTrackCount */, 180 /* degrees */,
+                    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4,
+                    MediaMuxerTest::filterOutNonDolbyVisionFormat);
+        } finally {
+            new File(outputFilePath).delete();
+        }
+    }
+
+    private static MediaFormat filterOutNonDolbyVisionFormat(MediaFormat format) {
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        return mime.equals(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION) ? format : null;
+    }
+
+    /**
      * Test: makes sure if audio and video muxing using MPEG4Writer works well when there are frame
      * drops as in b/63590381 and b/64949961 while B Frames encoding is enabled.
      */
@@ -433,6 +470,19 @@
      */
     private void cloneAndVerify(final String srcMedia, String outputMediaFile,
             int expectedTrackCount, int degrees, int fmt) throws IOException {
+        cloneAndVerify(srcMedia, outputMediaFile, expectedTrackCount, degrees, fmt,
+                Function.identity());
+    }
+
+    /**
+     * Clones a given file using MediaMuxer and verifies the output matches the input.
+     *
+     * <p>See {@link #cloneMediaUsingMuxer} for information about the parameters.
+     */
+    private void cloneAndVerify(final String srcMedia, String outputMediaFile,
+            int expectedTrackCount, int degrees, int fmt,
+            Function<MediaFormat, MediaFormat> muxerInputTrackFormatTransformer)
+            throws IOException {
         try {
             cloneMediaUsingMuxer(
                     srcMedia,
@@ -440,7 +490,7 @@
                     expectedTrackCount,
                     degrees,
                     fmt,
-                    Function.identity());
+                    muxerInputTrackFormatTransformer);
             if (fmt == MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 ||
                     fmt == MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP) {
                 verifyAttributesMatch(srcMedia, outputMediaFile, degrees);
@@ -454,7 +504,21 @@
         }
     }
 
-    /** Using the MediaMuxer to clone a media file. */
+
+    /**
+     * Clones a given file using MediaMuxer.
+     *
+     * @param srcMedia Input file path passed to extractor
+     * @param dstMediaPath Output file path passed to muxer
+     * @param expectedTrackCount Expected number of tracks in the input file
+     * @param degrees orientation hint in degrees
+     * @param fmt one of the values defined in {@link MediaMuxer.OutputFormat}.
+     * @param muxerInputTrackFormatTransformer Function applied on the MediaMuxer input formats.
+     *                                         If the function returns null for a given MediaFormat,
+     *                                         the corresponding track is discarded and not passed
+     *                                         to MediaMuxer.
+     * @throws IOException if muxer failed to open output file for write.
+     */
     private void cloneMediaUsingMuxer(
             final String srcMedia,
             String dstMediaPath,
@@ -479,10 +543,13 @@
         // Set up the tracks.
         HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount);
         for (int i = 0; i < trackCount; i++) {
-            extractor.selectTrack(i);
             MediaFormat format = extractor.getTrackFormat(i);
-            int dstIndex = muxer.addTrack(muxerInputTrackFormatTransformer.apply(format));
-            indexMap.put(i, dstIndex);
+            MediaFormat muxedFormat = muxerInputTrackFormatTransformer.apply(format);
+            if (muxedFormat != null) {
+                extractor.selectTrack(i);
+                int dstIndex = muxer.addTrack(muxedFormat);
+                indexMap.put(i, dstIndex);
+            }
         }
 
         // Copy the samples from MediaExtractor to MediaMuxer.
diff --git a/tests/tests/media/player/AndroidManifest.xml b/tests/tests/media/player/AndroidManifest.xml
index 050868a..2896da2 100644
--- a/tests/tests/media/player/AndroidManifest.xml
+++ b/tests/tests/media/player/AndroidManifest.xml
@@ -19,10 +19,12 @@
      android:targetSandboxVersion="2">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
-
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
diff --git a/tests/tests/media/recorder/AndroidManifest.xml b/tests/tests/media/recorder/AndroidManifest.xml
index fdfb196..28ddf67 100644
--- a/tests/tests/media/recorder/AndroidManifest.xml
+++ b/tests/tests/media/recorder/AndroidManifest.xml
@@ -21,7 +21,8 @@
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
-
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
diff --git a/tests/tests/permission/OWNERS b/tests/tests/permission/OWNERS
index d401f0d..6b28459 100644
--- a/tests/tests/permission/OWNERS
+++ b/tests/tests/permission/OWNERS
@@ -8,4 +8,5 @@
 per-file NoAudioPermissionTest.java = elaurent@google.com
 per-file MainlineNetworkStackPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS
 per-file Camera2PermissionTest.java = file: platform/frameworks/av:/camera/OWNERS
-per-file NoRollbackPermissionTest.java = mpgroover@google.com
\ No newline at end of file
+per-file NoRollbackPermissionTest.java = mpgroover@google.com
+per-file EthernetManagerPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS
\ No newline at end of file
diff --git a/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
new file mode 100644
index 0000000..ef92763
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.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;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.EthernetManager;
+import android.net.EthernetNetworkUpdateRequest;
+import android.net.NetworkCapabilities;
+import android.net.StaticIpConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+
+/**
+ * Test protected android.net.EthernetManager methods cannot be called without permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class EthernetManagerPermissionTest {
+    private static final String TEST_IFACE = "test123abc789";
+    private EthernetManager mEthernetManager;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mEthernetManager = mContext.getSystemService(EthernetManager.class);
+        // mEthernetManager may be null depending on the device's configuration.
+        assumeNotNull(mEthernetManager);
+    }
+
+    private void callUpdateConfiguration() {
+        final StaticIpConfiguration ipConfig = new StaticIpConfiguration.Builder().build();
+        final NetworkCapabilities networkCapabilities =
+                new NetworkCapabilities.Builder().build();
+        final EthernetNetworkUpdateRequest request =
+                new EthernetNetworkUpdateRequest(ipConfig, networkCapabilities);
+        mEthernetManager.updateConfiguration(TEST_IFACE, request, null, null);
+    }
+
+    /**
+     * Verify that calling {@link EthernetManager#updateConfiguration(String,
+     * EthernetNetworkUpdateRequest, Executor, BiConsumer)} requires permissions.
+     * <p>Tests Permission:
+     *   {@link android.Manifest.permission#MANAGE_ETHERNET_NETWORKS}.
+     */
+    @Test
+    public void testUpdateConfiguration() {
+        assumeTrue(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE));
+        assertThrows("Should not be able to call updateConfiguration without permission",
+                SecurityException.class, () -> callUpdateConfiguration());
+    }
+
+    /**
+     * Verify that calling {@link EthernetManager#connectNetwork(String, Executor, BiConsumer)}
+     * requires permissions.
+     * <p>Tests Permission:
+     *   {@link android.Manifest.permission#MANAGE_ETHERNET_NETWORKS}.
+     */
+    @Test
+    public void testConnectNetwork() {
+        assumeTrue(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE));
+        assertThrows("Should not be able to call connectNetwork without permission",
+                SecurityException.class,
+                () -> mEthernetManager.connectNetwork(TEST_IFACE, null, null));
+    }
+
+    /**
+     * Verify that calling {@link EthernetManager#disconnectNetwork(String, Executor, BiConsumer)}
+     * requires permissions.
+     * <p>Tests Permission:
+     *   {@link android.Manifest.permission#MANAGE_ETHERNET_NETWORKS}.
+     */
+    @Test
+    public void testDisconnectNetwork() {
+        assumeTrue(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE));
+        assertThrows("Should not be able to call disconnectNetwork without permission",
+                SecurityException.class,
+                () -> mEthernetManager.disconnectNetwork(TEST_IFACE, null, null));
+    }
+
+    /**
+     * Verify that calling {@link EthernetManager#updateConfiguration(
+     * String, EthernetNetworkUpdateRequest, Executor, BiConsumer)} requires automotive feature.
+     * <p>Tests Feature:
+     *   {@link PackageManager#FEATURE_AUTOMOTIVE}.
+     */
+    @Test
+    public void testUpdateConfigurationHasAutomotiveFeature() {
+        assumeFalse(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE));
+        assertThrows("Should not be able to call updateConfiguration without automotive feature",
+                UnsupportedOperationException.class, () -> callUpdateConfiguration());
+    }
+
+    /**
+     * Verify that calling {@link EthernetManager#connectNetwork(String, Executor, BiConsumer)}
+     * requires automotive feature.
+     * <p>Tests Feature:
+     *   {@link PackageManager#FEATURE_AUTOMOTIVE}.
+     */
+    @Test
+    public void testConnectNetworkHasAutomotiveFeature() {
+        assumeFalse(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE));
+        assertThrows("Should not be able to call connectNetwork without automotive feature",
+                UnsupportedOperationException.class,
+                () -> mEthernetManager.connectNetwork(TEST_IFACE, null, null));
+    }
+
+    /**
+     * Verify that calling {@link EthernetManager#disconnectNetwork(String, Executor, BiConsumer)}
+     * requires automotive feature.
+     * <p>Tests Feature:
+     *   {@link PackageManager#FEATURE_AUTOMOTIVE}.
+     */
+    @Test
+    public void testDisconnectNetworkHasAutomotiveFeature() {
+        assumeFalse(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE));
+        assertThrows("Should not be able to call disconnectNetwork without automotive feature",
+                UnsupportedOperationException.class,
+                () -> mEthernetManager.disconnectNetwork(TEST_IFACE, null, null));
+    }
+}
diff --git a/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl b/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl
index 2529ada..71924a2 100644
--- a/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl
+++ b/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl
@@ -29,4 +29,5 @@
     boolean isRcsFeatureCreated();
     boolean isMmTelFeatureCreated();
     void resetState();
+    boolean isTelephonyBound();
 }
diff --git a/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java b/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java
index 9101ae5..eaeb6d1 100644
--- a/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java
+++ b/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java
@@ -29,7 +29,7 @@
  */
 
 public class TestExternalImsService extends TestImsService {
-    private static final String TAG = "GtsImsTestDeviceImsService";
+    private static final String TAG = "CtsImsTestDeviceImsService";
     // TODO: Use ImsService.SERVICE_INTERFACE definition when it becomes public.
     private static final String ACTION_BIND_IMS_SERVICE = "android.telephony.ims.ImsService";
 
@@ -56,19 +56,26 @@
         public void resetState() {
             TestExternalImsService.this.resetState();
         }
+
+        public boolean isTelephonyBound() {
+            return TestExternalImsService.this.isTelephonyBound();
+        }
     }
 
     @Override
     public IBinder onBind(Intent intent) {
-        if (ACTION_BIND_IMS_SERVICE.equals(intent.getAction())) {
-            if (ImsUtils.VDBG) {
-                Log.i(TAG, "onBind-Remote");
+        synchronized (mLock) {
+            if (ACTION_BIND_IMS_SERVICE.equals(intent.getAction())) {
+                if (ImsUtils.VDBG) {
+                    Log.i(TAG, "onBind-Remote");
+                }
+                mIsTelephonyBound = true;
+                return super.onBind(intent);
             }
-            return super.onBind(intent);
+            if (ImsUtils.VDBG) {
+                Log.i(TAG, "onBind-Local");
+            }
+            return mBinder;
         }
-        if (ImsUtils.VDBG) {
-            Log.i(TAG, "onBind-Local");
-        }
-        return mBinder;
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java
index 71ffeea..41cd2d2 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java
@@ -318,6 +318,9 @@
             fail("Invalid state found: the test subscription in slot " + sTestSlot + " changed "
                     + "during this test.");
         }
+    }
+
+    public void bindImsService() throws Exception  {
         // Connect to the ImsService with the MmTel feature.
         assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
                 .addFeature(sTestSlot, ImsFeature.FEATURE_MMTEL)
@@ -371,6 +374,8 @@
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
+
+        bindImsService();
         mServiceCallBack = new ServiceCallBack();
         InCallServiceStateValidator.setCallbacks(mServiceCallBack);
 
@@ -407,6 +412,7 @@
             return;
         }
 
+        bindImsService();
         mServiceCallBack = new ServiceCallBack();
         InCallServiceStateValidator.setCallbacks(mServiceCallBack);
 
@@ -449,11 +455,11 @@
     }
 
     @Test
-    public void testIncomingCall() {
+    public void testIncomingCall() throws Exception {
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
-
+        bindImsService();
         mServiceCallBack = new ServiceCallBack();
         InCallServiceStateValidator.setCallbacks(mServiceCallBack);
 
@@ -480,6 +486,45 @@
         waitForUnboundService();
     }
 
+    @Test
+    public void testOutGoingCallForExecutor() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        sServiceConnector.setExecutorTestType(true);
+        bindImsService();
+
+        mServiceCallBack = new ServiceCallBack();
+        InCallServiceStateValidator.setCallbacks(mServiceCallBack);
+
+        TelecomManager telecomManager = (TelecomManager) InstrumentationRegistry
+                .getInstrumentation().getContext().getSystemService(Context.TELECOM_SERVICE);
+
+        final Uri imsUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, String.valueOf(++sCounter), null);
+        Bundle extras = new Bundle();
+
+        // Place outgoing call
+        telecomManager.placeCall(imsUri, extras);
+        assertTrue(callingTestLatchCountdown(LATCH_IS_ON_CALL_ADDED, WAIT_FOR_CALL_STATE));
+
+        Call call = getCall(mCurrentCallId);
+        assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DIALING, WAIT_FOR_CALL_STATE));
+
+        TestImsCallSessionImpl callSession = sServiceConnector.getCarrierService().getMmTelFeature()
+                .getImsCallsession();
+
+        isCallActive(call, callSession);
+
+        callingTestLatchCountdown(LATCH_WAIT, WAIT_FOR_CALL_DISCONNECT);
+        call.disconnect();
+
+        assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE));
+        isCallDisconnected(call, callSession);
+        assertTrue(callingTestLatchCountdown(LATCH_IS_ON_CALL_REMOVED, WAIT_FOR_CALL_STATE));
+        waitForUnboundService();
+    }
+
     public void waitForUnboundService() {
         waitUntilConditionIsTrueOrTimeout(
                 new Condition() {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java
index ae6306a..a994d98 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java
@@ -81,6 +81,12 @@
     private static final String COMMAND_SET_TEST_MODE_ENABLED = "src set-test-enabled ";
     private static final String COMMAND_SET_D2D_ENABLED = "d2d set-device-support ";
 
+    private boolean mIsTestTypeExecutor = false;
+
+    public void setExecutorTestType(boolean type) {
+        mIsTestTypeExecutor = type;
+    }
+
     private class TestCarrierServiceConnection implements ServiceConnection {
 
         private final CountDownLatch mLatch;
@@ -117,7 +123,7 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            mCarrierService = null;
+            mExternalService = null;
         }
     }
 
@@ -143,11 +149,13 @@
             mIsServiceOverridden = true;
             switch (mConnectionType) {
                 case CONNECTION_TYPE_IMS_SERVICE_CARRIER: {
-                    setCarrierImsService("none");
+                    boolean unbindSent = setCarrierImsService("none");
+                    if (unbindSent) waitForCarrierPackageUnbind();
                     break;
                 }
                 case CONNECTION_TYPE_IMS_SERVICE_DEVICE: {
-                    setDeviceImsService("");
+                    boolean unbindSent = setDeviceImsService("");
+                    if (unbindSent) waitForDevicePackageUnbind();
                     break;
                 }
                 case CONNECTION_TYPE_DEFAULT_SMS_APP: {
@@ -157,6 +165,35 @@
             }
         }
 
+        void waitForCarrierPackageUnbind() {
+            TestImsService carrierService = getCarrierService();
+            if (carrierService == null) return;
+            // First unbind the local services
+            removeLocalCarrierServiceConnection();
+            // Then wait for AOSP to unbind if there is still an active binding.
+            boolean isBound = carrierService.isTelephonyBound();
+            if (ImsUtils.VDBG) Log.i(TAG, "waitForCarrierPackageUnbind: isBound=" + isBound);
+            if (isBound) {
+                // Wait for telephony to unbind to local ImsService
+                carrierService.waitForLatchCountdown(TestImsService.LATCH_ON_UNBIND);
+            }
+        }
+
+        void waitForDevicePackageUnbind() throws Exception {
+            // Wait until the ImsService unbinds
+            ITestExternalImsService externalService = getExternalService();
+            if (externalService == null) return;
+            // First unbind the local services
+            removeLocalExternalServiceConnection();
+            // Then wait for AOSP to unbind if there is still an active binding.
+            boolean isBound = externalService.isTelephonyBound();
+            if (ImsUtils.VDBG) Log.i(TAG, "waitForDevicePackageUnbind: isBound=" + isBound);
+            if (isBound) {
+                // Wait for telephony to unbind to external ImsService
+                externalService.waitForLatchCountdown(TestImsService.LATCH_ON_UNBIND);
+            }
+        }
+
         boolean overrideService(ImsFeatureConfiguration config) throws Exception {
             mIsServiceOverridden = true;
             switch (mConnectionType) {
@@ -480,6 +517,11 @@
             return false;
         }
         mCarrierService.resetState();
+        if (mIsTestTypeExecutor) {
+            mCarrierService.setExecutorTestType(mIsTestTypeExecutor);
+            // reset the mIsTestTypeExecutor value
+            mIsTestTypeExecutor = false;
+        }
         return true;
     }
 
@@ -559,17 +601,27 @@
         }
     }
 
-    // Detect and disconnect all active services.
-    void disconnectServices() throws Exception {
-        // Remove local connection
+    void removeLocalCarrierServiceConnection() {
         if (mCarrierServiceConn != null) {
             mInstrumentation.getContext().unbindService(mCarrierServiceConn);
+            mCarrierServiceConn = null;
             mCarrierService = null;
         }
+    }
+
+    void removeLocalExternalServiceConnection() {
         if (mExternalServiceConn != null) {
             mInstrumentation.getContext().unbindService(mExternalServiceConn);
+            mExternalServiceConn = null;
             mExternalService = null;
         }
+    }
+
+    // Detect and disconnect all active services.
+    void disconnectServices() throws Exception {
+        // Remove local connections
+        removeLocalCarrierServiceConnection();
+        removeLocalExternalServiceConnection();
         mDeviceServiceConnection.restoreOriginalPackage();
         mCarrierServiceConnection.restoreOriginalPackage();
         mDefaultSmsAppConnection.restoreOriginalPackage();
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 57dbeed..01361ff 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -60,6 +60,7 @@
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
 import android.telephony.ims.stub.CapabilityExchangeEventListener;
 import android.telephony.ims.stub.ImsConfigImplBase;
@@ -87,6 +88,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -101,10 +103,36 @@
 
     private static ImsServiceConnector sServiceConnector;
 
+    private static final int KEY_VOLTE_PROVISIONING_STATUS =
+            ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
+    private static final int KEY_VT_PROVISIONING_STATUS =
+            ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
+    private static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE =
+            ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
+    private static final int KEY_EAB_PROVISIONING_STATUS =
+            ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+
+    private static final int MMTEL_CAP_VOICE = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+    private static final int MMTEL_CAP_VIDEO = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+    private static final int MMTEL_CAP_UT = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+    private static final int MMTEL_CAP_SMS = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+    private static final int MMTEL_CAP_COMPOSER =
+            MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER;
+
     private static final int RCS_CAP_NONE = RcsImsCapabilities.CAPABILITY_TYPE_NONE;
     private static final int RCS_CAP_OPTIONS = RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
     private static final int RCS_CAP_PRESENCE = RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
 
+    private static final int IMS_REGI_TECH_NONE = ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+    private static final int IMS_REGI_TECH_LTE = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+    private static final int IMS_REGI_TECH_IWLAN = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+    private static final int IMS_REGI_TECH_CROSS_SIM =
+            ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+    private static final int IMS_REGI_TECH_NR = ImsRegistrationImplBase.REGISTRATION_TECH_NR;
+
+    private static final String SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING =
+            "SUPPORT_PROVISION_STATUS_FOR_CAPABILITY";
+
     private static final String MSG_CONTENTS = "hi";
     private static final String EXPECTED_RECEIVED_MESSAGE = "foo5";
     private static final String DEST_NUMBER = "5555554567";
@@ -359,12 +387,14 @@
     @After
     public void afterTest() throws Exception {
         TelephonyUtils.resetCompatCommand(InstrumentationRegistry.getInstrumentation(),
-                TelephonyUtils.CTS_APP_PACKAGE,
-                SUPPORT_PUBLISHING_STATE_STRING);
+                TelephonyUtils.CTS_APP_PACKAGE, SUPPORT_PUBLISHING_STATE_STRING);
+        TelephonyUtils.resetCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                TelephonyUtils.CTS_APP_PACKAGE, SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
-        // Unbind the GTS ImsService after the test completes.
+        // Unbind the ImsService after the test completes.
         if (sServiceConnector != null) {
             sServiceConnector.setSingleRegistrationTestModeEnabled(false);
             sServiceConnector.disconnectCarrierImsService();
@@ -387,6 +417,26 @@
                 TestImsService.LATCH_CREATE_RCS);
         assertNotNull("ImsService created, but ImsService#createRcsFeature was not called!",
                 sServiceConnector.getCarrierService().getRcsFeature());
+        assertTrue("Not expected subId received!",
+                isExpectedSubId(sServiceConnector.getCarrierService().getSubIDs()));
+    }
+
+    @Test
+    public void testCarrierImsServiceBindRcsFeatureForExecutor() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        sServiceConnector.setExecutorTestType(true);
+        // Connect to the ImsService with the RCS feature.
+        assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
+                .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
+                .build()));
+        // The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
+        // Framework did not call it.
+        sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_CREATE_RCS);
+        assertNotNull("ImsService created, but ImsService#createRcsFeature was not called!",
+                sServiceConnector.getCarrierService().getRcsFeature());
     }
 
     @Test
@@ -407,6 +457,8 @@
         // Wait for the framework to set the capabilities on the ImsService
         sServiceConnector.getCarrierService().waitForLatchCountdown(
                 TestImsService.LATCH_MMTEL_CAP_SET);
+        assertTrue("Not expected subId received!",
+                isExpectedSubId(sServiceConnector.getCarrierService().getSubIDs()));
     }
 
     @Test
@@ -436,6 +488,8 @@
         assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
                 TestImsService.LATCH_DISABLE_IMS));
         assertFalse(sServiceConnector.getCarrierService().isEnabled());
+        assertTrue("Not expected subId received!",
+                isExpectedSubId(sServiceConnector.getCarrierService().getSubIDs()));
     }
 
     @Test
@@ -464,6 +518,41 @@
         // Wait for the framework to set the capabilities on the ImsService
         sServiceConnector.getCarrierService().waitForLatchCountdown(
                 TestImsService.LATCH_MMTEL_CAP_SET);
+        assertTrue("Not expected subId received!",
+                isExpectedSubId(sServiceConnector.getCarrierService().getSubIDs()));
+    }
+
+    @Test
+    public void testCarrierImsServiceBindRcsChangeToMmtelCompat() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        // Connect to the ImsService with the RCS feature.
+        ImsFeatureConfiguration config = new ImsFeatureConfiguration.Builder()
+                .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
+                .build();
+        assertTrue(sServiceConnector.connectCarrierImsServiceLocally());
+        sServiceConnector.getCarrierService().resetState();
+        // Set the flag for ImsService compatibility test.
+        sServiceConnector.getCarrierService().setImsServiceCompat();
+        assertTrue(sServiceConnector.triggerFrameworkConnectionToCarrierImsService(config));
+        // The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
+        // Framework did not call it.
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_CREATE_RCS));
+
+        // Change the supported feature to MMTEl
+        sServiceConnector.getCarrierService().getImsServiceCompat().onUpdateSupportedImsFeatures(
+                new ImsFeatureConfiguration.Builder()
+                .addFeature(sTestSlot, ImsFeature.FEATURE_MMTEL).build());
+
+        // createMmTelFeature should be called.
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_CREATE_MMTEL));
+
+        // Wait for the framework to set the capabilities on the ImsService
+        sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_MMTEL_CAP_SET);
     }
 
     @Test
@@ -501,6 +590,8 @@
         // Wait for the framework to set the capabilities on the ImsService
         sServiceConnector.getCarrierService().waitForLatchCountdown(
                 TestImsService.LATCH_MMTEL_CAP_SET);
+        assertTrue("Not expected subId received!",
+                isExpectedSubId(sServiceConnector.getCarrierService().getSubIDs()));
     }
 
     @Test
@@ -944,28 +1035,28 @@
         assertEquals(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, deregResult.getCode());
 
         // Start registration
-        verifyRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, featureTags, mRegQueue,
+        verifyRegistering(IMS_REGI_TECH_LTE, featureTags, mRegQueue,
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
 
         // move to NR
-        verifyRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_NR, featureTags, mRegQueue,
+        verifyRegistering(IMS_REGI_TECH_NR, featureTags, mRegQueue,
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
 
         // move to cross sim
-        verifyRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, featureTags,
+        verifyRegistering(IMS_REGI_TECH_CROSS_SIM, featureTags,
                 mRegQueue, AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
                 ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET);
 
         // Complete registration
-        verifyRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, featureTags, mRegQueue,
+        verifyRegistered(IMS_REGI_TECH_LTE, featureTags, mRegQueue,
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
 
         // move to NR
-        verifyRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_NR, featureTags, mRegQueue,
+        verifyRegistered(IMS_REGI_TECH_NR, featureTags, mRegQueue,
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
 
         // move to cross sim
-        verifyRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, featureTags,
+        verifyRegistered(IMS_REGI_TECH_CROSS_SIM, featureTags,
                 mRegQueue, AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
                 ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET);
 
@@ -1053,17 +1144,17 @@
 
         // Start registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
 
         // Complete registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
 
         // Fail handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                IMS_REGI_TECH_IWLAN,
                 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
                         ImsReasonInfo.CODE_UNSPECIFIED, ""));
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
@@ -1071,7 +1162,7 @@
 
         // Ensure null ImsReasonInfo still results in non-null callback value.
         sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, null);
+                IMS_REGI_TECH_IWLAN, null);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
         assertEquals(ImsReasonInfo.CODE_UNSPECIFIED, waitForIntResult(mQueue));
 
@@ -1193,7 +1284,7 @@
 
         // IMS registers
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
 
         // Framework should not trigger the device capabilities publish when the framework doesn't
         // receive that the RcsUceAdapter.CAPABILITY_TYPE_PRESENCE_UCE is enabled.
@@ -1386,7 +1477,7 @@
         featureTags.add(CHAT_FEATURE_TAG);
         featureTags.add(FILE_TRANSFER_FEATURE_TAG);
         ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE).setFeatureTags(featureTags).build();
+                IMS_REGI_TECH_LTE).setFeatureTags(featureTags).build();
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(attr);
         waitForParam(mQueue, attr);
 
@@ -1500,7 +1591,7 @@
 
         // IMS registers
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
 
         // Notify framework that the RCS capability status is changed and PRESENCE UCE is enabled.
         RcsImsCapabilities capabilities =
@@ -1610,7 +1701,7 @@
 
         // IMS registers
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
 
         // Notify framework that the RCS capability status is changed and PRESENCE UCE is enabled.
         RcsImsCapabilities capabilities =
@@ -1735,7 +1826,7 @@
 
         // IMS registers
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
 
         // Verify the PUBLISH request should not be triggered and the publish state is still
         // NOT_PUBLISHED even the IMS is registered.
@@ -1863,7 +1954,7 @@
 
         // IMS registers
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
 
         // Verify the PUBLISH request should not be triggered and the publish state is still
         // OK even the IMS is registered.
@@ -2002,7 +2093,7 @@
 
         // IMS registers
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
 
         // Notify framework that the RCS capability status is changed and PRESENCE UCE is enabled.
         RcsImsCapabilities capabilities =
@@ -2421,17 +2512,17 @@
 
         // Start registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
 
         // Complete registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
 
         // Fail handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                IMS_REGI_TECH_IWLAN,
                 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
                         ImsReasonInfo.CODE_UNSPECIFIED, ""));
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
@@ -2505,14 +2596,14 @@
 
         // Start registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
         verifyRegistrationState(regManager, RegistrationManager.REGISTRATION_STATE_REGISTERING);
         verifyRegistrationTransportType(regManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
         // Complete registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
         verifyRegistrationState(regManager, RegistrationManager.REGISTRATION_STATE_REGISTERED);
         verifyRegistrationTransportType(regManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
@@ -2520,7 +2611,7 @@
 
         // Fail handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                IMS_REGI_TECH_IWLAN,
                 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
                         ImsReasonInfo.CODE_UNSPECIFIED, ""));
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
@@ -2529,7 +2620,7 @@
 
         // handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+                IMS_REGI_TECH_IWLAN);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
         verifyRegistrationTransportType(regManager, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
 
@@ -2620,35 +2711,35 @@
 
         // Start registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
         verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERING);
         verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
         // Complete registration
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
         verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERED);
         verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
         // Start registration over NR
         sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
-                ImsRegistrationImplBase.REGISTRATION_TECH_NR);
+                IMS_REGI_TECH_NR);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
         verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERING);
         verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
         // Complete registration over NR
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_NR);
+                IMS_REGI_TECH_NR);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
         verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERED);
         verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
         // Fail handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                IMS_REGI_TECH_IWLAN,
                 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
                         ImsReasonInfo.CODE_UNSPECIFIED, ""));
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
@@ -2657,7 +2748,7 @@
 
         // handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+                IMS_REGI_TECH_IWLAN);
         assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
         verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(imsRcsManager,
@@ -2684,7 +2775,7 @@
                 .getMmTelFeature().getCapabilities();
         // Make sure we start off with every capability unavailable
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         sServiceConnector.getCarrierService().getMmTelFeature()
                 .notifyCapabilitiesStatusChanged(new MmTelFeature.MmTelCapabilities());
 
@@ -2696,7 +2787,7 @@
             // Make sure we are tracking voice capability over LTE properly.
             assertEquals(fwCaps.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE),
                     mmTelManager.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                            ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
+                            IMS_REGI_TECH_LTE));
         } finally {
             automan.dropShellPermissionIdentity();
         }
@@ -2745,7 +2836,7 @@
             automan.adoptShellPermissionIdentity();
             assertTrue(ImsUtils.retryUntilTrue(() -> mmTelManager.isAvailable(
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE)));
+                    IMS_REGI_TECH_LTE)));
 
             mmTelManager.unregisterMmTelCapabilityCallback(callback);
         } finally {
@@ -2778,7 +2869,7 @@
                 .getMmTelFeature().getCapabilities();
         // Make sure we start off with every capability unavailable
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         sServiceConnector.getCarrierService().getMmTelFeature()
                 .notifyCapabilitiesStatusChanged(new MmTelFeature.MmTelCapabilities());
 
@@ -2790,7 +2881,7 @@
             // Make sure we are tracking voice capability over LTE properly.
             assertEquals(fwCaps.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE),
                     mmTelManager.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
+                    IMS_REGI_TECH_LTE));
         } finally {
             automan.dropShellPermissionIdentity();
         }
@@ -2835,7 +2926,7 @@
             automan.adoptShellPermissionIdentity();
             assertTrue(ImsUtils.retryUntilTrue(() -> mmTelManager.isAvailable(
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE)));
+                    IMS_REGI_TECH_LTE)));
 
             mmTelManager.unregisterMmTelCapabilityCallback(callback);
         } finally {
@@ -2872,7 +2963,7 @@
 
         // Make sure we start off with every capability unavailable
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         MmTelFeature.MmTelCapabilities stdCapabilities = new MmTelFeature.MmTelCapabilities();
         sServiceConnector.getCarrierService().getMmTelFeature()
                 .notifyCapabilitiesStatusChanged(stdCapabilities);
@@ -2890,7 +2981,7 @@
                 automan.adoptShellPermissionIdentity();
                 boolean isAvailableBeforeStatusChange = mmTelManager.isAvailable(
                         MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                        ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                        IMS_REGI_TECH_LTE);
                 assertFalse(isAvailableBeforeStatusChange);
             } finally {
                 automan.dropShellPermissionIdentity();
@@ -2907,7 +2998,7 @@
                         automan.adoptShellPermissionIdentity();
                         boolean isVoiceAvailable = mmTelManager
                                 .isAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                                        ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                                        IMS_REGI_TECH_LTE);
 
                         voiceIsAvailable.offer(isVoiceAvailable);
                     } finally {
@@ -2963,12 +3054,12 @@
         // Connect to device ImsService with RcsFeature
         triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
 
-        int registrationTech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+        int registrationTech = IMS_REGI_TECH_LTE;
         ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
 
         // Make sure we start off with none-capability
         sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                IMS_REGI_TECH_LTE);
         RcsImsCapabilities noCapabilities = new RcsImsCapabilities(RCS_CAP_NONE);
         sServiceConnector.getCarrierService().getRcsFeature()
                 .notifyCapabilitiesStatusChanged(noCapabilities);
@@ -3030,7 +3121,7 @@
 
         // Verify the callback and the api isAvailable that the capabilities is NONE in the
         // beginning.
-        int radioTechLTE = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+        int radioTechLTE = IMS_REGI_TECH_LTE;
         int capCb = waitForResult(availabilityChanged);
         assertEquals(capCb, RCS_CAP_NONE);
         availabilityChanged.clear();
@@ -3145,6 +3236,537 @@
         }
     }
 
+    @Test
+    public void testProvisioningManagerWhenMmtelProvisionIsRequired() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        // test in case provision for mmtel is required
+        PersistableBundle bundle = new PersistableBundle();
+
+        PersistableBundle innerBundle = new PersistableBundle();
+        innerBundle.putIntArray(
+                CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY,
+                new int[]{IMS_REGI_TECH_LTE, IMS_REGI_TECH_IWLAN}
+        );
+        innerBundle.putIntArray(
+                CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY,
+                new int[]{IMS_REGI_TECH_LTE}
+        );
+
+        bundle.putPersistableBundle(
+                CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE,
+                innerBundle);
+
+        overrideCarrierConfig(bundle);
+
+        triggerFrameworkConnectToCarrierImsService();
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(sTestSub);
+
+        LinkedBlockingQueue<Pair<Integer, Integer>> mIntQueue = new LinkedBlockingQueue<>();
+        LinkedBlockingQueue<Pair<Integer, Pair<Integer, Boolean>>> mOnFeatureChangedQueue =
+                new LinkedBlockingQueue<>();
+
+        ProvisioningManager.Callback callback = new ProvisioningManager.Callback() {
+            @Override
+            public void onProvisioningIntChanged(int item, int value) {
+                mIntQueue.offer(new Pair<>(item, value));
+            }
+        };
+
+        ProvisioningManager.FeatureProvisioningCallback featureProvisioningCallback =
+                new ProvisioningManager.FeatureProvisioningCallback() {
+            @Override
+            public void onFeatureProvisioningChanged(
+                    @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+                    @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+                    boolean isProvisioned) {
+                mOnFeatureChangedQueue.offer(new Pair<>(capability,
+                        new Pair<>(tech, isProvisioned)));
+            }
+        };
+
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
+                    callback);
+            provisioningManager.registerFeatureProvisioningChangedCallback(
+                    getContext().getMainExecutor(), featureProvisioningCallback);
+
+            TelephonyUtils.enableCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
+            // test get/setProvisioningStatusForCapability for VoLTE
+            assertTrue(provisioningManager.isProvisioningRequiredForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+            boolean isProvisioned = provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE);
+            provisioningManager.setProvisioningStatusForCapability(MMTEL_CAP_VOICE,
+                    IMS_REGI_TECH_LTE, !isProvisioned);
+            assertTrue(waitForParam(mOnFeatureChangedQueue,
+                    new Pair<>(MMTEL_CAP_VOICE, new Pair<>(IMS_REGI_TECH_LTE, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VOLTE_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+            assertEquals(!isProvisioned, provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(MMTEL_CAP_VOICE,
+                    IMS_REGI_TECH_LTE, isProvisioned);
+            assertTrue(waitForParam(mOnFeatureChangedQueue,
+                    new Pair<>(MMTEL_CAP_VOICE, new Pair<>(IMS_REGI_TECH_LTE, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VOLTE_PROVISIONING_STATUS, isProvisioned ? 1 : 0)));
+            assertEquals(isProvisioned, provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+
+            // test get/setProvisioningStatusForCapability for VoWIFI
+            assertTrue(provisioningManager.isProvisioningRequiredForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+            isProvisioned = provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN);
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(MMTEL_CAP_VOICE,
+                    IMS_REGI_TECH_IWLAN, !isProvisioned);
+            assertTrue(waitForParam(mOnFeatureChangedQueue,
+                    new Pair<>(MMTEL_CAP_VOICE, new Pair<>(IMS_REGI_TECH_IWLAN, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, !isProvisioned ? 1 : 0)));
+            assertEquals(!isProvisioned, provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(MMTEL_CAP_VOICE,
+                    IMS_REGI_TECH_IWLAN, isProvisioned);
+            assertTrue(waitForParam(mOnFeatureChangedQueue,
+                    new Pair<>(MMTEL_CAP_VOICE, new Pair<>(IMS_REGI_TECH_IWLAN, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, isProvisioned ? 1 : 0)));
+            assertEquals(isProvisioned, provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+
+            // test get/setProvisioningStatusForCapability for VT
+            assertTrue(provisioningManager.isProvisioningRequiredForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+            isProvisioned = provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE);
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(MMTEL_CAP_VIDEO,
+                    IMS_REGI_TECH_LTE, !isProvisioned);
+            assertTrue(waitForParam(mOnFeatureChangedQueue,
+                    new Pair<>(MMTEL_CAP_VIDEO, new Pair<>(IMS_REGI_TECH_LTE, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VT_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+            assertEquals(!isProvisioned, provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(MMTEL_CAP_VIDEO,
+                    IMS_REGI_TECH_LTE, isProvisioned);
+            assertTrue(waitForParam(mOnFeatureChangedQueue,
+                    new Pair<>(MMTEL_CAP_VIDEO, new Pair<>(IMS_REGI_TECH_LTE, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VT_PROVISIONING_STATUS, isProvisioned ? 1 : 0)));
+            assertEquals(isProvisioned, provisioningManager
+                    .getProvisioningStatusForCapability(MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+
+            TelephonyUtils.disableCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
+            // test get/setProvisioningStatusForCapability with lower bounding parameters
+            // when callback is not supported
+
+            isProvisioned = provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE);
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE, !isProvisioned);
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VOLTE_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+            assertEquals(!isProvisioned,
+                    provisioningManager.getProvisioningStatusForCapability(
+                            MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+            isProvisioned = provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN);
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN, !isProvisioned);
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, !isProvisioned ? 1 : 0)));
+            assertEquals(!isProvisioned,
+                    provisioningManager.getProvisioningStatusForCapability(
+                            MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+
+            isProvisioned = provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE);
+            mIntQueue.clear();
+            mOnFeatureChangedQueue.clear();
+            provisioningManager.setProvisioningStatusForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE, !isProvisioned);
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_VT_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+            assertEquals(!isProvisioned,
+                    provisioningManager.getProvisioningStatusForCapability(
+                            MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.unregisterProvisioningChangedCallback(callback);
+            provisioningManager.unregisterFeatureProvisioningChangedCallback(
+                    featureProvisioningCallback);
+        } finally {
+            TelephonyUtils.resetCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+            automan.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testProvisioningManagerWhenMmtelProvisionIsNotRequired() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        // test in case provision for mmtel is required
+        PersistableBundle bundle = new PersistableBundle();
+        PersistableBundle innerBundle = new PersistableBundle();
+
+        bundle.putPersistableBundle(
+                CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE,
+                innerBundle);
+        overrideCarrierConfig(bundle);
+
+        triggerFrameworkConnectToCarrierImsService();
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(sTestSub);
+
+        LinkedBlockingQueue<Pair<Integer, Integer>> mIntQueue = new LinkedBlockingQueue<>();
+        LinkedBlockingQueue<Pair<Integer, Pair<Integer, Boolean>>> mOnFeatureChangedQueue =
+                new LinkedBlockingQueue<>();
+
+
+        ProvisioningManager.Callback callback = new ProvisioningManager.Callback() {};
+
+        ProvisioningManager.FeatureProvisioningCallback featureProvisioningCallback =
+                new ProvisioningManager.FeatureProvisioningCallback() {};
+
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
+                    callback);
+            provisioningManager.registerFeatureProvisioningChangedCallback(
+                    getContext().getMainExecutor(), featureProvisioningCallback);
+
+            // In case provisioning is not required
+            // true will be returned regardless of stored value
+            // ignore set value whatever value is set by app
+            // therefore set different value from current then check if the value has changed
+            // test get/setProvisioningStatusForCapability for VoLTE
+
+            // isProvisioningRequiredForCapability should return false because provision is not
+            // required
+            assertTrue(!provisioningManager.isProvisioningRequiredForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+            // However, getProvisioningStatusForCapability() should return true because it does not
+            // require provision
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+            // put opposite value to check if the key is changed or not
+            provisioningManager.setProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE, false);
+            // key value should not be changed whatever value is set
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_LTE));
+
+            // test case for VoWIFI
+            assertTrue(!provisioningManager.isProvisioningRequiredForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+            provisioningManager.setProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN, false);
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VOICE, IMS_REGI_TECH_IWLAN));
+
+            // test case for VT
+            assertTrue(!provisioningManager.isProvisioningRequiredForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+            provisioningManager.setProvisioningStatusForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE, false);
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    MMTEL_CAP_VIDEO, IMS_REGI_TECH_LTE));
+
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.unregisterProvisioningChangedCallback(callback);
+            provisioningManager.unregisterFeatureProvisioningChangedCallback(
+                    featureProvisioningCallback);
+        } finally {
+            automan.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testProvisioningManagerWhenRcsProvisionIsRequired() throws Exception {
+        if (!ImsUtils.shouldTestImsSingleRegistration()) {
+            return;
+        }
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+                true);
+
+        PersistableBundle innerBundle = new PersistableBundle();
+        innerBundle.putIntArray(
+                CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY,
+                new int[]{IMS_REGI_TECH_LTE, IMS_REGI_TECH_IWLAN, IMS_REGI_TECH_CROSS_SIM,
+                        IMS_REGI_TECH_NR}
+        );
+        bundle.putPersistableBundle(
+                CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE,
+                innerBundle);
+
+        overrideCarrierConfig(bundle);
+
+        triggerFrameworkConnectToImsServiceBindMmTelAndRcsFeature();
+
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(sTestSub);
+
+        LinkedBlockingQueue<Pair<Integer, Integer>> mIntQueue = new LinkedBlockingQueue<>();
+        LinkedBlockingQueue<Pair<Integer, Pair<Integer, Boolean>>> mOnRcsFeatureChangedQueue =
+                new LinkedBlockingQueue<>();
+
+        ProvisioningManager.Callback callback = new ProvisioningManager.Callback() {
+            @Override
+            public void onProvisioningIntChanged(int item, int value) {
+                mIntQueue.offer(new Pair<>(item, value));
+            }
+
+        };
+
+        ProvisioningManager.FeatureProvisioningCallback featureProvisioningCallback =
+                new ProvisioningManager.FeatureProvisioningCallback() {
+            @Override
+            public void onRcsFeatureProvisioningChanged(
+                    @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+                    @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+                    boolean isProvisioned) {
+                mOnRcsFeatureChangedQueue.offer(new Pair<>(capability,
+                        new Pair<>(tech, isProvisioned)));
+            }
+        };
+
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
+                    callback);
+            provisioningManager.registerFeatureProvisioningChangedCallback(
+                    getContext().getMainExecutor(), featureProvisioningCallback);
+
+            TelephonyUtils.enableCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
+            assertTrue(provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_LTE));
+            assertTrue(provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_IWLAN));
+            assertTrue(provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_CROSS_SIM));
+            assertTrue(provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_NR));
+
+            // test get/setRcsProvisioningStatusForCapability for PRESENCE over LTE
+            boolean isProvisioned = provisioningManager.getRcsProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_LTE);
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_LTE, !isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE, new Pair<>(IMS_REGI_TECH_LTE, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_LTE, isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE, new Pair<>(IMS_REGI_TECH_LTE, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, isProvisioned ? 1 : 0)));
+
+            // test get/setRcsProvisioningStatusForCapability for PRESENCE over IWLAN
+            isProvisioned = provisioningManager.getRcsProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_IWLAN);
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_IWLAN, !isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE, new Pair<>(IMS_REGI_TECH_IWLAN, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_IWLAN, isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE, new Pair<>(IMS_REGI_TECH_IWLAN, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, isProvisioned ? 1 : 0)));
+
+            // test get/setRcsProvisioningStatusForCapability for PRESENCE over CROSS SIM
+            isProvisioned = provisioningManager.getRcsProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_CROSS_SIM);
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_CROSS_SIM, !isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE,
+                            new Pair<>(IMS_REGI_TECH_CROSS_SIM, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_CROSS_SIM, isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE,
+                            new Pair<>(IMS_REGI_TECH_CROSS_SIM, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, isProvisioned ? 1 : 0)));
+
+            // test get/setRcsProvisioningStatusForCapability for PRESENCE over NR
+            isProvisioned = provisioningManager.getRcsProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_NR);
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_NR, !isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE, new Pair<>(IMS_REGI_TECH_NR, !isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, !isProvisioned ? 1 : 0)));
+
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    IMS_REGI_TECH_NR, isProvisioned);
+            assertTrue(waitForParam(mOnRcsFeatureChangedQueue,
+                    new Pair<>(RCS_CAP_PRESENCE, new Pair<>(IMS_REGI_TECH_NR, isProvisioned))));
+            assertTrue(waitForParam(mIntQueue,
+                    new Pair<>(KEY_EAB_PROVISIONING_STATUS, isProvisioned ? 1 : 0)));
+
+            // TODO : work for OPTIONS case
+
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.unregisterProvisioningChangedCallback(callback);
+            provisioningManager.unregisterFeatureProvisioningChangedCallback(
+                    featureProvisioningCallback);
+        } finally {
+            TelephonyUtils.resetCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
+            automan.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testProvisioningManagerWhenRcsProvisionIsNotRequired() throws Exception {
+        if (!ImsUtils.shouldTestImsSingleRegistration()) {
+            return;
+        }
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+                true);
+
+        PersistableBundle innerBundle = new PersistableBundle();
+        bundle.putPersistableBundle(
+                CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE,
+                innerBundle);
+
+        overrideCarrierConfig(bundle);
+
+        triggerFrameworkConnectToImsServiceBindMmTelAndRcsFeature();
+
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(sTestSub);
+        ProvisioningManager.Callback callback = new ProvisioningManager.Callback() {};
+        ProvisioningManager.FeatureProvisioningCallback featureProvisioningCallback =
+                new ProvisioningManager.FeatureProvisioningCallback() {};
+
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
+                    callback);
+            provisioningManager.registerFeatureProvisioningChangedCallback(
+                    getContext().getMainExecutor(), featureProvisioningCallback);
+
+            TelephonyUtils.enableCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
+            assertTrue(!provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_LTE));
+            assertTrue(!provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_IWLAN));
+            assertTrue(!provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_CROSS_SIM));
+            assertTrue(!provisioningManager.isRcsProvisioningRequiredForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_NR));
+
+            // However, getProvisioningStatusForCapability() should return true because it does not
+            // require provision
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_LTE));
+            // put opposite value to check if the key is changed or not
+            provisioningManager.setProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_LTE, false);
+            // key value should not be changed whatever value is set
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_LTE));
+
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_IWLAN));
+            provisioningManager.setProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_IWLAN, false);
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_IWLAN));
+
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_CROSS_SIM));
+            provisioningManager.setProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_CROSS_SIM, false);
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_CROSS_SIM));
+
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_NR));
+            provisioningManager.setProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_NR, false);
+            assertTrue(provisioningManager.getProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE, IMS_REGI_TECH_NR));
+
+            // TODO : work for OPTIONS case
+
+            automan.adoptShellPermissionIdentity();
+            provisioningManager.unregisterProvisioningChangedCallback(callback);
+            provisioningManager.unregisterFeatureProvisioningChangedCallback(
+                    featureProvisioningCallback);
+        } finally {
+            TelephonyUtils.resetCompatCommand(InstrumentationRegistry.getInstrumentation(),
+                    TelephonyUtils.CTS_APP_PACKAGE,
+                    SUPPORT_PROVISION_STATUS_FOR_CAPABILITY_STRING);
+
+            automan.dropShellPermissionIdentity();
+        }
+    }
+
     @Ignore("The ProvisioningManager constants were moved back to @hide for now, don't want to "
             + "completely remove test.")
     @Test
@@ -3342,7 +3964,15 @@
 
         PersistableBundle bundle = new PersistableBundle();
         bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, true);
-        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, true);
+
+        PersistableBundle innerBundle = new PersistableBundle();
+        innerBundle.putIntArray(
+                CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_UT_INT_ARRAY,
+                new int[]{IMS_REGI_TECH_LTE}); // UT/LTE
+        bundle.putPersistableBundle(
+                CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE,
+                innerBundle);
+
         overrideCarrierConfig(bundle);
 
         ProvisioningManager provisioningManager =
@@ -3353,22 +3983,22 @@
             automan.adoptShellPermissionIdentity();
             boolean provisioningStatus = provisioningManager.getProvisioningStatusForCapability(
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+                    IMS_REGI_TECH_LTE);
             provisioningManager.setProvisioningStatusForCapability(
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, !provisioningStatus);
+                    IMS_REGI_TECH_LTE, !provisioningStatus);
             // Make sure the change in provisioning status is correctly returned.
             assertEquals(!provisioningStatus,
                     provisioningManager.getProvisioningStatusForCapability(
                             MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
-                            ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
+                            IMS_REGI_TECH_LTE));
             // TODO: Enhance test to make sure the provisioning change is also sent to the
             // ImsService
 
             // set back to current status
             provisioningManager.setProvisioningStatusForCapability(
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, provisioningStatus);
+                    IMS_REGI_TECH_LTE, provisioningStatus);
         } finally {
             automan.dropShellPermissionIdentity();
         }
@@ -3378,7 +4008,7 @@
 
     @Test
     public void testProvisioningManagerRcsProvisioningCaps() throws Exception {
-        if (!ImsUtils.shouldTestImsService()) {
+        if (!ImsUtils.shouldTestImsSingleRegistration()) {
             return;
         }
 
@@ -3390,8 +4020,20 @@
                 true);
         bundle.putBoolean(CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, true);
         bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+                true);
+        PersistableBundle innerBundle = new PersistableBundle();
+        innerBundle.putIntArray(
+                CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY,
+                new int[]{IMS_REGI_TECH_LTE, IMS_REGI_TECH_IWLAN, IMS_REGI_TECH_CROSS_SIM,
+                        IMS_REGI_TECH_NR}
+        );
+        bundle.putPersistableBundle(
+                CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE,
+                innerBundle);
         overrideCarrierConfig(bundle);
 
+
         ProvisioningManager provisioningManager =
                 ProvisioningManager.createForSubscriptionId(sTestSub);
 
@@ -3406,7 +4048,7 @@
             assertEquals(!provisioningStatus,
                     provisioningManager.getRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE));
             // TODO: Enhance test to make sure the provisioning change is also sent to the
-            // ImsService
+            //  ImsService
 
             // set back to current status
             provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
@@ -4393,4 +5035,8 @@
 
         throw new RuntimeException("Invalid hex char '" + c + "'");
     }
+
+    private boolean isExpectedSubId(HashSet<Integer> subIDs) {
+        return (subIDs.size() == 1) && subIDs.contains(sTestSub);
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java
index 10a39d3..3e3b331 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java
@@ -18,15 +18,25 @@
 
 import android.telephony.ims.RcsClientConfiguration;
 import android.telephony.ims.stub.ImsConfigImplBase;
+import android.util.Log;
 
 import java.util.HashMap;
+import java.util.concurrent.Executor;
 
 public class TestImsConfig extends ImsConfigImplBase {
 
+    private static final String TAG = "TestImsConfig";
     private HashMap<Integer, Integer> mIntHashMap = new HashMap<>();
     private HashMap<Integer, String> mStringHashMap = new HashMap<>();
 
     TestImsConfig() {
+        Log.d(TAG, "TestImsConfig with default constructor");
+        TestAcsClient.getInstance().setImsConfigImpl(this);
+    }
+
+    TestImsConfig(Executor executor) {
+        super(executor);
+        Log.d(TAG, "TestImsConfig with Executor constructor");
         TestAcsClient.getInstance().setImsConfigImpl(this);
     }
 
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsRegistration.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsRegistration.java
index afdd3d1..cb3a4d9 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsRegistration.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsRegistration.java
@@ -17,13 +17,26 @@
 package android.telephony.ims.cts;
 
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class TestImsRegistration extends ImsRegistrationImplBase {
 
+    private static final String TAG = "TestImsRegistration";
+
+    public TestImsRegistration() {
+        Log.d(TAG, "TestImsRegistration with default constructor");
+    }
+
+    public TestImsRegistration(Executor executor) {
+        super(executor);
+        Log.d(TAG, "TestImsRegistration with Executor constructor");
+    }
+
     public static class NetworkRegistrationInfo {
         public final int sipCode;
         public final String sipReason;
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
index a012513..97fa8f2 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
@@ -20,7 +20,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
@@ -30,10 +34,12 @@
 import android.telephony.ims.stub.SipTransportImplBase;
 import android.util.Log;
 
-
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.HashSet;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -41,22 +47,27 @@
  */
 public class TestImsService extends Service {
 
-    private static final String TAG = "GtsImsTestImsService";
+    private static final String TAG = "CtsImsTestImsService";
+    private static MessageExecutor sMessageExecutor = null;
 
-    private static final TestImsRegistration sImsRegistrationImplBase =
-            new TestImsRegistration();
-
+    private TestImsRegistration mImsRegistrationImplBase;
     private TestRcsFeature mTestRcsFeature;
     private TestMmTelFeature mTestMmTelFeature;
     private TestImsConfig mTestImsConfig;
     private TestSipTransport mTestSipTransport;
     private ImsService mTestImsService;
+    private ImsService mTestImsServiceCompat;
+    private Executor mExecutor = Runnable::run;
     private boolean mIsEnabled = false;
     private boolean mSetNullRcsBinding = false;
     private boolean mIsSipTransportImplemented = false;
+    private boolean mIsTestTypeExecutor = false;
+    private boolean mIsImsServiceCompat = false;
     private long mCapabilities = 0;
     private ImsFeatureConfiguration mFeatureConfig;
-    private final Object mLock = new Object();
+    protected boolean mIsTelephonyBound = false;
+    private HashSet<Integer> mSubIDs = new HashSet<Integer>();
+    protected final Object mLock = new Object();
 
     public static final int LATCH_FEATURES_READY = 0;
     public static final int LATCH_ENABLE_IMS = 1;
@@ -71,7 +82,8 @@
     public static final int LATCH_RCS_CAP_SET = 10;
     public static final int LATCH_UCE_LISTENER_SET = 11;
     public static final int LATCH_UCE_REQUEST_PUBLISH = 12;
-    private static final int LATCH_MAX = 13;
+    public static final int LATCH_ON_UNBIND = 13;
+    private static final int LATCH_MAX = 14;
     protected static final CountDownLatch[] sLatches = new CountDownLatch[LATCH_MAX];
     static {
         for (int i = 0; i < LATCH_MAX; i++) {
@@ -106,9 +118,193 @@
             if (getBaseContext() == null) {
                 attachBaseContext(context);
             }
-            mTestImsConfig = new TestImsConfig();
-            // For testing, just run on binder thread until required otherwise.
-            mTestSipTransport = new TestSipTransport(Runnable::run);
+
+            if (mIsTestTypeExecutor) {
+                mImsRegistrationImplBase = new TestImsRegistration(mExecutor);
+                mTestSipTransport = new TestSipTransport(mExecutor);
+                mTestImsConfig = new TestImsConfig(mExecutor);
+            } else {
+                mImsRegistrationImplBase = new TestImsRegistration();
+                mTestImsConfig = new TestImsConfig();
+                mTestSipTransport = new TestSipTransport();
+            }
+        }
+
+        @Override
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
+            return getFeatureConfig();
+        }
+
+        @Override
+        public long getImsServiceCapabilities() {
+            return mCapabilities;
+        }
+
+        @Override
+        public void readyForFeatureCreation() {
+            synchronized (mLock) {
+                countDownLatch(LATCH_FEATURES_READY);
+            }
+        }
+
+        @Override
+        public void enableImsForSubscription(int slotId, int subId) {
+            synchronized (mLock) {
+                countDownLatch(LATCH_ENABLE_IMS);
+                mSubIDs.add(subId);
+                setIsEnabled(true);
+            }
+        }
+
+        @Override
+        public void disableImsForSubscription(int slotId, int subId) {
+            synchronized (mLock) {
+                countDownLatch(LATCH_DISABLE_IMS);
+                mSubIDs.add(subId);
+                setIsEnabled(false);
+            }
+        }
+
+        @Override
+        public RcsFeature createRcsFeatureForSubscription(int slotId, int subId) {
+            TestImsService.ReadyListener readyListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_RCS_READY);
+                }
+            };
+
+            TestImsService.RemovedListener removedListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_REMOVE_RCS);
+                    mTestRcsFeature = null;
+                }
+            };
+
+            TestImsService.CapabilitiesSetListener setListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_RCS_CAP_SET);
+                }
+            };
+
+            TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_UCE_LISTENER_SET);
+                }
+            };
+
+            synchronized (mLock) {
+                countDownLatch(LATCH_CREATE_RCS);
+                mSubIDs.add(subId);
+
+                if (mIsTestTypeExecutor) {
+                    mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
+                            setListener, capExchangeEventListener, mExecutor);
+                } else {
+                    mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
+                            setListener, capExchangeEventListener);
+                }
+
+                // Setup UCE request listener
+                mTestRcsFeature.setDeviceCapPublishListener(() -> {
+                    synchronized (mLock) {
+                        countDownLatch(LATCH_UCE_REQUEST_PUBLISH);
+                    }
+                });
+
+                if (mSetNullRcsBinding) {
+                    return null;
+                }
+                return mTestRcsFeature;
+            }
+        }
+
+        @Override
+        public ImsConfigImplBase getConfigForSubscription(int slotId, int subId) {
+            mSubIDs.add(subId);
+            return mTestImsConfig;
+        }
+
+        @Override
+        public MmTelFeature createMmTelFeatureForSubscription(int slotId, int subId) {
+            TestImsService.ReadyListener readyListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_MMTEL_READY);
+                }
+            };
+
+            TestImsService.RemovedListener removedListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_REMOVE_MMTEL);
+                    mTestMmTelFeature = null;
+                }
+            };
+
+            TestImsService.CapabilitiesSetListener capSetListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_MMTEL_CAP_SET);
+                }
+            };
+
+            synchronized (mLock) {
+                countDownLatch(LATCH_CREATE_MMTEL);
+                mSubIDs.add(subId);
+                if (mIsTestTypeExecutor) {
+                    mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
+                            capSetListener, mExecutor);
+                } else {
+                    mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
+                            capSetListener);
+                }
+
+                return mTestMmTelFeature;
+            }
+        }
+
+        @Override
+        public ImsRegistrationImplBase getRegistrationForSubscription(int slotId, int subId) {
+            mSubIDs.add(subId);
+            return mImsRegistrationImplBase;
+        }
+
+        @Nullable
+        @Override
+        public SipTransportImplBase getSipTransport(int slotId) {
+            if (mIsSipTransportImplemented) {
+                return mTestSipTransport;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public @NonNull Executor getExecutor() {
+            if (mIsTestTypeExecutor) {
+                return mExecutor;
+            } else {
+                mExecutor = Runnable::run;
+                return mExecutor;
+            }
+        }
+    }
+
+    private class ImsServiceUT_compat extends ImsService {
+
+        ImsServiceUT_compat(Context context) {
+            // As explained above, ImsServiceUT is created in order to get around classloader
+            // restrictions. Attach the base context from the wrapper ImsService.
+            if (getBaseContext() == null) {
+                attachBaseContext(context);
+            }
+
+            if (mIsTestTypeExecutor) {
+                mImsRegistrationImplBase = new TestImsRegistration(mExecutor);
+                mTestSipTransport = new TestSipTransport(mExecutor);
+                mTestImsConfig = new TestImsConfig(mExecutor);
+            } else {
+                mImsRegistrationImplBase = new TestImsRegistration();
+                mTestImsConfig = new TestImsConfig();
+                mTestSipTransport = new TestSipTransport();
+            }
         }
 
         @Override
@@ -146,33 +342,42 @@
 
         @Override
         public RcsFeature createRcsFeature(int slotId) {
+
+            TestImsService.ReadyListener readyListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_RCS_READY);
+                }
+            };
+
+            TestImsService.RemovedListener removedListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_REMOVE_RCS);
+                    mTestRcsFeature = null;
+                }
+            };
+
+            TestImsService.CapabilitiesSetListener setListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_RCS_CAP_SET);
+                }
+            };
+
+            TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_UCE_LISTENER_SET);
+                }
+            };
+
             synchronized (mLock) {
                 countDownLatch(LATCH_CREATE_RCS);
-                mTestRcsFeature = new TestRcsFeature(getBaseContext(),
-                        //onReady
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_RCS_READY);
-                            }
-                        },
-                        //onRemoved
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_REMOVE_RCS);
-                                mTestRcsFeature = null;
-                            }
-                        },
-                        //onCapabilitiesSet
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_RCS_CAP_SET);
-                            }
-                        },
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_UCE_LISTENER_SET);
-                        }
-                        });
+
+                if (mIsTestTypeExecutor) {
+                    mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
+                            setListener, capExchangeEventListener, mExecutor);
+                } else {
+                    mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
+                            setListener, capExchangeEventListener);
+                }
 
                 // Setup UCE request listener
                 mTestRcsFeature.setDeviceCapPublishListener(() -> {
@@ -195,36 +400,42 @@
 
         @Override
         public MmTelFeature createMmTelFeature(int slotId) {
+            TestImsService.ReadyListener readyListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_MMTEL_READY);
+                }
+            };
+
+            TestImsService.RemovedListener removedListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_REMOVE_MMTEL);
+                    mTestMmTelFeature = null;
+                }
+            };
+
+            TestImsService.CapabilitiesSetListener capSetListener = () -> {
+                synchronized (mLock) {
+                    countDownLatch(LATCH_MMTEL_CAP_SET);
+                }
+            };
+
             synchronized (mLock) {
                 countDownLatch(LATCH_CREATE_MMTEL);
-                mTestMmTelFeature = new TestMmTelFeature(
-                        //onReady
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_MMTEL_READY);
-                            }
-                        },
-                        //onRemoved
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_REMOVE_MMTEL);
-                                mTestMmTelFeature = null;
-                            }
-                        },
-                        //onCapabilitiesSet
-                        () -> {
-                            synchronized (mLock) {
-                                countDownLatch(LATCH_MMTEL_CAP_SET);
-                            }
-                        }
-                        );
+                if (mIsTestTypeExecutor) {
+                    mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
+                            capSetListener, mExecutor);
+                } else {
+                    mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
+                            capSetListener);
+                }
+
                 return mTestMmTelFeature;
             }
         }
 
         @Override
         public ImsRegistrationImplBase getRegistration(int slotId) {
-            return sImsRegistrationImplBase;
+            return mImsRegistrationImplBase;
         }
 
         @Nullable
@@ -236,6 +447,62 @@
                 return null;
             }
         }
+
+        @Override
+        public @NonNull Executor getExecutor() {
+            if (mIsTestTypeExecutor) {
+                return mExecutor;
+            } else {
+                mExecutor = Runnable::run;
+                return mExecutor;
+            }
+        }
+    }
+
+    private static Looper createLooper(String name) {
+        HandlerThread thread = new HandlerThread(name);
+        thread.start();
+
+        Looper looper = thread.getLooper();
+
+        if (looper == null) {
+            return Looper.getMainLooper();
+        }
+        return looper;
+    }
+
+    /**
+     * Executes the tasks in the other thread rather than the calling thread.
+     */
+    public class MessageExecutor extends Handler implements Executor {
+        public MessageExecutor(String name) {
+            super(createLooper(name));
+        }
+
+        @Override
+        public void execute(Runnable r) {
+            Message m = Message.obtain(this, 0, r);
+            m.sendToTarget();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.obj instanceof Runnable) {
+                executeInternal((Runnable) msg.obj);
+            } else {
+                Log.d(TAG, "[MessageExecutor] handleMessage :: "
+                        + "Not runnable object; ignore the msg=" + msg);
+            }
+        }
+
+        private void executeInternal(Runnable r) {
+            try {
+                r.run();
+            } catch (Throwable t) {
+                Log.d(TAG, "[MessageExecutor] executeInternal :: run task=" + r);
+                t.printStackTrace();
+            }
+        }
     }
 
     private final LocalBinder mBinder = new LocalBinder();
@@ -256,18 +523,53 @@
         }
     }
 
+    protected ImsService getImsServiceCompat() {
+        synchronized (mLock) {
+            if (mTestImsServiceCompat != null) {
+                return mTestImsServiceCompat;
+            }
+            mTestImsServiceCompat = new ImsServiceUT_compat(this);
+            return mTestImsServiceCompat;
+        }
+    }
+
     @Override
     public IBinder onBind(Intent intent) {
-        if ("android.telephony.ims.ImsService".equals(intent.getAction())) {
-            if (ImsUtils.VDBG) {
-                Log.d(TAG, "onBind-Remote");
+        synchronized (mLock) {
+            if ("android.telephony.ims.ImsService".equals(intent.getAction())) {
+                mIsTelephonyBound = true;
+                if (mIsImsServiceCompat) {
+                    if (ImsUtils.VDBG) {
+                        Log.d(TAG, "onBind-Remote-Compat");
+                    }
+                    return getImsServiceCompat().onBind(intent);
+                } else {
+                    if (ImsUtils.VDBG) {
+                        Log.d(TAG, "onBind-Remote");
+                    }
+                    return getImsService().onBind(intent);
+                }
             }
-            return getImsService().onBind(intent);
+            if (ImsUtils.VDBG) {
+                Log.i(TAG, "onBind-Local");
+            }
+            return mBinder;
         }
-        if (ImsUtils.VDBG) {
-            Log.i(TAG, "onBind-Local");
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        synchronized (mLock) {
+            if ("android.telephony.ims.ImsService".equals(intent.getAction())) {
+                if (ImsUtils.VDBG)  Log.i(TAG, "onUnbind-Remote");
+                mIsTelephonyBound = false;
+                countDownLatch(LATCH_ON_UNBIND);
+            } else {
+                if (ImsUtils.VDBG)  Log.i(TAG, "onUnbind-Local");
+            }
+            // return false so that onBind is called next time.
+            return false;
         }
-        return mBinder;
     }
 
     public void resetState() {
@@ -277,10 +579,38 @@
             mIsEnabled = false;
             mSetNullRcsBinding = false;
             mIsSipTransportImplemented = false;
+            mIsTestTypeExecutor = false;
+            mIsImsServiceCompat = false;
             mCapabilities = 0;
             for (int i = 0; i < LATCH_MAX; i++) {
                 sLatches[i] = new CountDownLatch(1);
             }
+
+            if (sMessageExecutor != null) {
+                sMessageExecutor.getLooper().quit();
+                sMessageExecutor = null;
+            }
+            mSubIDs.clear();
+        }
+    }
+
+    public boolean isTelephonyBound() {
+        return mIsTelephonyBound;
+    }
+
+    public void setExecutorTestType(boolean type) {
+        mIsTestTypeExecutor = type;
+        if (mIsTestTypeExecutor) {
+            if (sMessageExecutor == null) {
+                sMessageExecutor = new MessageExecutor("TestImsService");
+            }
+            mExecutor = sMessageExecutor;
+        }
+    }
+
+    public void setImsServiceCompat() {
+        synchronized (mLock) {
+            mIsImsServiceCompat = true;
         }
     }
 
@@ -329,20 +659,7 @@
     }
 
     public boolean waitForLatchCountdown(int latchIndex) {
-        boolean complete = false;
-        try {
-            CountDownLatch latch;
-            synchronized (mLock) {
-                latch = sLatches[latchIndex];
-            }
-            complete = latch.await(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            // complete == false
-        }
-        synchronized (mLock) {
-            sLatches[latchIndex] = new CountDownLatch(1);
-        }
-        return complete;
+        return waitForLatchCountdown(latchIndex, ImsUtils.TEST_TIMEOUT_MS);
     }
 
     public boolean waitForLatchCountdown(int latchIndex, long waitMs) {
@@ -352,7 +669,12 @@
             synchronized (mLock) {
                 latch = sLatches[latchIndex];
             }
+            long startTime = System.currentTimeMillis();
             complete = latch.await(waitMs, TimeUnit.MILLISECONDS);
+            if (ImsUtils.VDBG) {
+                Log.i(TAG, "Latch " + latchIndex + " took "
+                        + (System.currentTimeMillis() - startTime) + " ms to count down.");
+            }
         } catch (InterruptedException e) {
             // complete == false
         }
@@ -388,11 +710,15 @@
 
     public TestImsRegistration getImsRegistration() {
         synchronized (mLock) {
-            return sImsRegistrationImplBase;
+            return mImsRegistrationImplBase;
         }
     }
 
     public ImsConfigImplBase getConfig() {
         return mTestImsConfig;
     }
+
+    public HashSet<Integer> getSubIDs() {
+        return mSubIDs;
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java
index d38a63a..c365ecb 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java
@@ -52,6 +52,21 @@
     TestMmTelFeature(TestImsService.ReadyListener readyListener,
             TestImsService.RemovedListener removedListener,
             TestImsService.CapabilitiesSetListener setListener) {
+        Log.d(TAG, "TestMmTelFeature with default constructor");
+        mReadyListener = readyListener;
+        mRemovedListener = removedListener;
+        mCapSetListener = setListener;
+        mSmsImpl = new TestImsSmsImpl();
+        // Must set the state to READY in the constructor - onFeatureReady depends on the state
+        // being ready.
+        setFeatureState(STATE_READY);
+    }
+
+    TestMmTelFeature(TestImsService.ReadyListener readyListener,
+            TestImsService.RemovedListener removedListener,
+            TestImsService.CapabilitiesSetListener setListener, Executor executor) {
+        super(executor);
+        Log.d(TAG, "TestMmTelFeature with Executor constructor");
         mReadyListener = readyListener;
         mRemovedListener = removedListener;
         mCapSetListener = setListener;
@@ -154,7 +169,7 @@
     }
 
     public boolean isCallSessionCreated() {
-        return (mCallSession != null) ? true : false;
+        return (mCallSession != null);
     }
 
     public void onIncomingCallReceived(Bundle extras) {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
index 08eaa85..3915fc5 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
@@ -16,7 +16,6 @@
 
 package android.telephony.ims.cts;
 
-import android.content.Context;
 import android.telephony.ims.feature.CapabilityChangeRequest;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.CapabilityExchangeEventListener;
@@ -43,13 +42,30 @@
     private CapabilityExchangeEventListener mCapEventListener;
     private TestImsService.DeviceCapPublishListener mDeviceCapPublishListener;
 
-    TestRcsFeature(Context context,
-            TestImsService.ReadyListener readyListener,
+    TestRcsFeature(TestImsService.ReadyListener readyListener,
             TestImsService.RemovedListener removedListener,
             TestImsService.CapabilitiesSetListener setListener,
             TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener) {
-        super(context.getMainExecutor());
+        super();
+        Log.d(TAG, "TestRcsFeature with default constructor");
+        mReadyListener = readyListener;
+        mRemovedListener = removedListener;
+        mCapExchangeEventListener = capExchangeEventListener;
 
+        mRcsCapabilityChangedListener = setListener;
+        mRcsCapabilitiesLte = new RcsImsCapabilities(RcsImsCapabilities.CAPABILITY_TYPE_NONE);
+        mRcsCapabilitiesIWan = new RcsImsCapabilities(RcsImsCapabilities.CAPABILITY_TYPE_NONE);
+
+        setFeatureState(STATE_READY);
+    }
+
+    TestRcsFeature(TestImsService.ReadyListener readyListener,
+            TestImsService.RemovedListener removedListener,
+            TestImsService.CapabilitiesSetListener setListener,
+            TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener,
+            Executor executor) {
+        super(executor);
+        Log.d(TAG, "TestRcsFeature with Executor constructor");
         mReadyListener = readyListener;
         mRemovedListener = removedListener;
         mCapExchangeEventListener = capExchangeEventListener;
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java
index 0ea195c..88dcc2a 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java
@@ -23,6 +23,7 @@
 import android.telephony.ims.DelegateStateCallback;
 import android.telephony.ims.stub.SipDelegate;
 import android.telephony.ims.stub.SipTransportImplBase;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 
@@ -34,6 +35,7 @@
 
 public class TestSipTransport extends SipTransportImplBase {
 
+    private static final String TAG = "TestSipTransport";
     public static final String ONE_TO_ONE_CHAT_TAG =
             "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.oma.cpm.msg\"";
     public static final String GROUP_CHAT_TAG =
@@ -54,8 +56,13 @@
     private final ArrayList<TestSipDelegate> mDelegates = new ArrayList<>();
     private final Object mLock = new Object();
 
+    public TestSipTransport() {
+        Log.d(TAG, "TestSipTransport with default constructor");
+    }
+
     public TestSipTransport(Executor executor) {
         super(executor);
+        Log.d(TAG, "TestSipTransport with Executor constructor");
     }
 
     @Override