Merge "mediav2: fix ByteBuffer mode image comparison"
diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp
index 01c579e..69ef63f 100644
--- a/apps/CtsVerifier/Android.bp
+++ b/apps/CtsVerifier/Android.bp
@@ -2,3 +2,66 @@
     name: "CtsVerifierMockVrListenerServiceFiles",
     srcs: ["src/com/android/cts/verifier/vr/MockVrListenerService.java"],
 }
+
+android_test {
+    name: "CtsVerifier",
+    defaults: ["cts_error_prone_rules_tests"],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/I*.aidl",
+    ],
+
+    aidl: {
+        include_dirs: ["frameworks/native/aidl/gui"],
+    },
+
+    static_libs: [
+        "android-ex-camera2",
+        "compatibility-common-util-devicesidelib",
+        "cts-sensors-tests",
+        "cts-camera-performance-tests",
+        "ctstestrunner-axt",
+        "apache-commons-math",
+        "androidplot",
+        "ctsverifier-opencv",
+        "core-tests-support",
+        "androidx.legacy_legacy-support-v4",
+        "mockito-target-minus-junit4",
+        "mockwebserver",
+        "compatibility-device-util-axt",
+        "platform-test-annotations",
+        "cts-security-test-support-library",
+        "cts-midi-lib",
+        "cbor-java",
+        "CtsCameraUtils",
+        "androidx.legacy_legacy-support-v4",
+        "CtsForceStopHelper-constants",
+    ],
+
+    libs: ["telephony-common"] + ["android.test.runner.stubs"] + ["android.test.base.stubs"] + ["android.test.mock.stubs"] + ["android.car"] + ["voip-common"] + ["truth-prebuilt"],
+
+    platform_apis: true,
+
+    jni_libs: [
+        "libctsverifier_jni",
+        "libctsnativemidi_jni",
+        "libaudioloopback_jni",
+    ],
+
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+
+    dex_preopt: {
+        enabled: false,
+    },
+}
+
+// opencv library
+java_import {
+    name: "ctsverifier-opencv",
+    jars: ["libs/opencv3-android.jar"],
+}
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 8e10b97..007be8c 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -15,66 +15,6 @@
 #
 
 LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_MULTILIB := both
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src) \
-                    ../ForceStopHelperApp/src/com/android/cts/forcestophelper/Constants.java
-
-LOCAL_AIDL_INCLUDES := \
-    frameworks/native/aidl/gui
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2 \
-                               compatibility-common-util-devicesidelib \
-                               cts-sensors-tests \
-                               cts-camera-performance-tests \
-                               ctstestrunner-axt \
-                               apache-commons-math \
-                               androidplot \
-                               ctsverifier-opencv \
-                               core-tests-support \
-                               androidx.legacy_legacy-support-v4  \
-                               mockito-target-minus-junit4 \
-                               mockwebserver \
-                               compatibility-device-util-axt \
-                               platform-test-annotations \
-                               cts-security-test-support-library \
-                               cts-midi-lib \
-                               cbor-java \
-                               CtsCameraUtils
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.legacy_legacy-support-v4
-
-LOCAL_JAVA_LIBRARIES += telephony-common
-LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
-LOCAL_JAVA_LIBRARIES += android.test.base.stubs
-LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
-LOCAL_JAVA_LIBRARIES += android.car
-LOCAL_JAVA_LIBRARIES += voip-common
-LOCAL_JAVA_LIBRARIES += truth-prebuilt
-
-LOCAL_PACKAGE_NAME := CtsVerifier
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_JNI_SHARED_LIBRARIES := \
-	libctsverifier_jni \
-	libctsnativemidi_jni \
-	libaudioloopback_jni \
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_DEX_PREOPT := false
--include cts/error_prone_rules_tests.mk
-include $(BUILD_PACKAGE)
-
 # Build CTS verifier framework as a libary.
 
 include $(CLEAR_VARS)
@@ -101,14 +41,6 @@
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
-# opencv library
-include $(CLEAR_VARS)
-
-LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
-        ctsverifier-opencv:libs/opencv3-android.jar
-
-include $(BUILD_MULTI_PREBUILT)
-
 pre-installed-apps := \
     CtsEmptyDeviceAdmin \
     CtsEmptyDeviceOwner \
diff --git a/apps/ForceStopHelperApp/Android.bp b/apps/ForceStopHelperApp/Android.bp
index f438b87..85a2f6d 100644
--- a/apps/ForceStopHelperApp/Android.bp
+++ b/apps/ForceStopHelperApp/Android.bp
@@ -25,3 +25,8 @@
         "general-tests",
     ],
 }
+
+java_library {
+    name: "CtsForceStopHelper-constants",
+    srcs: ["src/com/android/cts/forcestophelper/Constants.java"],
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 2a087dc..5c26531 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -135,6 +135,9 @@
             getDevice().uninstallPackage(WRITE_PKG);
             installPackage(WRITE_APK);
 
+            // Make sure user initialization is complete before testing
+            waitForBroadcastIdle();
+
             for (int user : mUsers) {
                 runDeviceTests(WRITE_PKG, WRITE_CLASS, "testExternalStorageRename", user);
             }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt
index ba62f2f..4972c68 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt
@@ -30,11 +30,13 @@
 import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters
 import android.cts.host.utils.DeviceJUnit4Parameterized
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import java.lang.AssertionError
 
 /**
  * This test verifies protection for an exploit where any app could set the installer package
@@ -197,7 +199,9 @@
     }
 
     private fun assertPermission(granted: Boolean, permission: String) {
-        assertThat(getPermissionString(permission)).contains("granted=$granted")
+        val dump = device.executeShellCommand("dumpsys package $TARGET_PKG")
+        assertWithMessage(dump).that(getPermissionString(dump, permission))
+                .contains("granted=$granted")
     }
 
     private fun grantPermission(permission: String) {
@@ -209,40 +213,50 @@
     }
 
     private fun assertGrantState(state: GrantState, permission: String) {
-        val output = getPermissionString(permission)
+        val dump = device.executeShellCommand("dumpsys package $TARGET_PKG")
+        val output = getPermissionString(dump, permission)
 
         when (state) {
             GrantState.TRUE -> {
-                assertThat(output).contains("granted=true")
-                assertThat(output).doesNotContain("RESTRICTION")
-                assertThat(output).doesNotContain("EXEMPT")
+                assertWithMessage(dump).that(output).contains("granted=true")
+                assertWithMessage(dump).that(output).doesNotContain("RESTRICTION")
+                assertWithMessage(dump).that(output).doesNotContain("EXEMPT")
             }
             GrantState.TRUE_EXEMPT -> {
-                assertThat(output).contains("granted=true")
-                assertThat(output).contains("RESTRICTION_INSTALLER_EXEMPT")
+                assertWithMessage(dump).that(output).contains("granted=true")
+                assertWithMessage(dump).that(output).contains("RESTRICTION_INSTALLER_EXEMPT")
             }
             GrantState.TRUE_RESTRICTED -> {
-                assertThat(output).contains("granted=true")
-                assertThat(output).contains("APPLY_RESTRICTION")
-                assertThat(output).doesNotContain("EXEMPT")
+                assertWithMessage(dump).that(output).contains("granted=true")
+                assertWithMessage(dump).that(output).contains("APPLY_RESTRICTION")
+                assertWithMessage(dump).that(output).doesNotContain("EXEMPT")
             }
             GrantState.FALSE -> {
-                assertThat(output).contains("granted=false")
+                assertWithMessage(dump).that(output).contains("granted=false")
             }
         }
     }
 
-    private fun getPermissionString(permission: String) =
-            device.executeShellCommand("dumpsys package $TARGET_PKG")
-                    .lineSequence()
-                    .dropWhile { !it.startsWith("Packages:") } // Wait for package header
-                    .drop(1) // Drop the package header itself
-                    .takeWhile { it.isEmpty() || it.first().isWhitespace() } // Until next header
-                    .dropWhile { !it.trim().startsWith("User $mPrimaryUserId:") } // Find user
-                    .drop(1) // Drop the user header itself
-                    .takeWhile { !it.trim().startsWith("User") } // Until next user
-                    .filter { it.contains("$permission: granted=") }
-                    .single()
+    private fun getPermissionString(output: String, permission: String) = retry {
+        output.lineSequence()
+                .dropWhile { !it.startsWith("Packages:") } // Wait for package header
+                .drop(1) // Drop the package header itself
+                .takeWhile { it.isEmpty() || it.first().isWhitespace() } // Until next header
+                .dropWhile { !it.trim().startsWith("User $mPrimaryUserId:") } // Find user
+                .drop(1) // Drop the user header itself
+                .takeWhile { !it.trim().startsWith("User") } // Until next user
+                .filter { it.contains("$permission: granted=") }
+                .firstOrNull()
+    }
+
+    private fun <T> retry(block: () -> T?): T {
+        repeat(10) {
+            block()?.let { return it }
+            Thread.sleep(1000)
+        }
+
+        throw AssertionError("Never succeeded")
+    }
 
     enum class GrantState {
         // Granted in full, unrestricted
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
index d18a9fa..2f7444d 100755
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
@@ -376,7 +376,7 @@
     private void validateDeviceIdAttestationData(Certificate leaf,
             String expectedSerial, String expectedImei, String expectedMeid)
             throws CertificateParsingException {
-        Attestation attestationRecord = new Attestation((X509Certificate) leaf);
+        Attestation attestationRecord = Attestation.loadFromCertificate((X509Certificate) leaf);
         AuthorizationList teeAttestation = attestationRecord.getTeeEnforced();
         assertThat(teeAttestation).isNotNull();
         validateBrandAttestationRecord(teeAttestation);
@@ -402,7 +402,7 @@
         assertThat(attestation).isNotNull();
         assertThat(attestation.size()).isGreaterThan(1);
         X509Certificate leaf = (X509Certificate) attestation.get(0);
-        Attestation attestationRecord = new Attestation(leaf);
+        Attestation attestationRecord = Attestation.loadFromCertificate(leaf);
         assertThat(attestationRecord.getAttestationChallenge()).isEqualTo(providedChallenge);
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java
index 71566d0..88286f3 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java
@@ -24,7 +24,7 @@
 
 /**
  * Wrapper over <code>DevicePolicyEvent</code> atom as defined in
- * <code>frameworks/base/cmds/statsd/src/atoms.proto</code>.
+ * <code>frameworks/proto_logging/stats/atoms.proto</code>.
  * @see Builder
  */
 public final class DevicePolicyEventWrapper {
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
index bbba1c4..f18268e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
@@ -99,14 +99,17 @@
     /**
      * Test 11.2.2-4
      * Tests that the device sends a <INACTIVE_SOURCE> message when put on standby.
-     * This test depends on One Touch Play, and will pass only if One Touch Play passes.
      */
     @Test
     public void cect_11_2_2_4_InactiveSourceOnStandby() throws Exception {
         ITestDevice device = getDevice();
         try {
             int dumpsysPhysicalAddress = getDumpsysPhysicalAddress();
-            device.executeShellCommand("input keyevent KEYCODE_HOME");
+            hdmiCecClient.sendCecMessage(
+                    LogicalAddress.TV,
+                    LogicalAddress.BROADCAST,
+                    CecOperand.SET_STREAM_PATH,
+                    CecMessage.formatParams(dumpsysPhysicalAddress));
             device.executeShellCommand("input keyevent KEYCODE_SLEEP");
             String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
                     CecOperand.INACTIVE_SOURCE);
diff --git a/hostsidetests/rollback/TEST_MAPPING b/hostsidetests/rollback/TEST_MAPPING
index a353a74..acec493 100644
--- a/hostsidetests/rollback/TEST_MAPPING
+++ b/hostsidetests/rollback/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "CtsRollbackManagerHostTestCases"
     }
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index f1a8035..47b45ed 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -3048,12 +3048,28 @@
         assertThat(readPfd.getStatSize()).isEqualTo(writePfd.getStatSize());
     }
 
+    private void assertStartsWith(String actual, String prefix, boolean expected) throws Exception {
+        String message = "String \"" + actual + "\" should start with \"" + prefix + "\"";
+
+        if (expected) {
+            assertTrue(message, actual.startsWith(prefix));
+        } else {
+            assertFalse(message, actual.startsWith(prefix));
+        }
+    }
+
     private void assertLowerFsFd(ParcelFileDescriptor pfd) throws Exception {
-        assertThat(Os.readlink("/proc/self/fd/" + pfd.getFd()).startsWith("/storage")).isTrue();
+        String path = Os.readlink("/proc/self/fd/" + pfd.getFd());
+        String prefix = "/storage";
+
+        assertStartsWith(path, prefix, true);
     }
 
     private void assertUpperFsFd(ParcelFileDescriptor pfd) throws Exception {
-        assertThat(Os.readlink("/proc/self/fd/" + pfd.getFd()).startsWith("/mnt/user")).isTrue();
+        String path = Os.readlink("/proc/self/fd/" + pfd.getFd());
+        String prefix = "/mnt/user";
+
+        assertStartsWith(path, prefix, true);
     }
 
     private static void assertCanCreateFile(File file) throws IOException {
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13232/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13232/poc.cpp
index 121d5a9..419f4c6 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13232/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13232/poc.cpp
@@ -79,7 +79,7 @@
   data.writeInt32(1);
   audio_attributes_t attr;
   memset(&attr, 0xff, sizeof(attr));
-  attr.flags = 0;
+  attr.flags = AUDIO_FLAG_NONE;
   memset(attr.tags, 0x41, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
   data.write(&attr, sizeof(attr));
   binder->transact(GET_OUTPUT_FOR_ATTR, data, &reply, 0);
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/Android.bp
index 0a19bec..0e9fbdd 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/Android.bp
@@ -29,12 +29,14 @@
         "frameworks/native/include/media/openmax",
         "frameworks/av/media/libstagefright",
         "frameworks/native/include/media/hardware",
-        "frameworks/av/media/libstagefright/codecs/mp3dec/include",
-        "frameworks/av/media/libstagefright/codecs/mp3dec/src",
         "frameworks/av/media/libmedia/include",
         "frameworks/av/media/libstagefright/xmlparser/include",
     ],
 
+    header_libs: [
+        "libstagefright_mp3dec",
+    ],
+
     shared_libs: [
         "libstagefright",
         "libstagefright_omx",
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/poc.cpp
index a559537..1ac7b76 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9313/poc.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
+#include <s_tmp3dec_file.h> // from the mp3dec library
+
 #include "../includes/common.h"
 #include "../includes/memutils_track.h"
 #include "../includes/omxUtils.h"
-#include "codecs/mp3dec/src/s_tmp3dec_file.h"
 #include "media/omx/1.0/WOmx.h"
 #include "omx/include/media/stagefright/omx/1.0/Omx.h"
 
diff --git a/hostsidetests/userspacereboot/TEST_MAPPING b/hostsidetests/userspacereboot/TEST_MAPPING
index 40da059..cf97bfa 100644
--- a/hostsidetests/userspacereboot/TEST_MAPPING
+++ b/hostsidetests/userspacereboot/TEST_MAPPING
@@ -1,8 +1,7 @@
 {
-  "postsubmit" : [
+  "presubmit" : [
     {
-      "name": "CtsUserspaceRebootHostSideTestCases",
-      "keywords": ["primary-device"]
+      "name": "CtsUserspaceRebootHostSideTestCases"
     }
   ]
 }
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index ff2fed4..cdc8a64 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -65,6 +65,7 @@
         <activity
             android:label="Full screen activity for gesture dispatch testing"
             android:name=".AccessibilityGestureDispatchTest$GestureDispatchActivity"
+            android:theme="@style/Theme_NoSwipeDismiss"
             android:screenOrientation="locked" />
 
         <activity
diff --git a/tests/accessibilityservice/res/values/styles.xml b/tests/accessibilityservice/res/values/styles.xml
new file mode 100644
index 0000000..77c0405
--- /dev/null
+++ b/tests/accessibilityservice/res/values/styles.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<resources>
+    <style name="Theme_NoSwipeDismiss">
+        <item name="android:windowSwipeToDismiss">false</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 7663cdb..ea984ee 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -590,6 +590,8 @@
                             || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
                 });
 
+        sUiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, DEFAULT_TIMEOUT_MS);
+
         assertTrue(
                 sUiAutomation.performGlobalAction(
                         AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN));
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index c3fcf4c..fe194f2 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -101,7 +101,7 @@
     private static final int EXIT_CODE = 123;
     private static final int CRASH_SIGNAL = OsConstants.SIGSEGV;
 
-    private static final int WAITFOR_MSEC = 5000;
+    private static final int WAITFOR_MSEC = 10000;
     private static final int WAITFOR_SETTLE_DOWN = 2000;
 
     private static final int CMD_PID = 1;
diff --git a/tests/app/TEST_MAPPING b/tests/app/TEST_MAPPING
index ca2dd6c..213ad1e 100644
--- a/tests/app/TEST_MAPPING
+++ b/tests/app/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "CtsAppTestCases",
       "options": [
diff --git a/tests/app/app/src/android/app/stubs/OrientationTestUtils.java b/tests/app/app/src/android/app/stubs/OrientationTestUtils.java
index 410c98d..29b71e0 100644
--- a/tests/app/app/src/android/app/stubs/OrientationTestUtils.java
+++ b/tests/app/app/src/android/app/stubs/OrientationTestUtils.java
@@ -20,6 +20,7 @@
 
 import android.app.Activity;
 import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
 import android.view.DisplayInfo;
 
 import java.util.concurrent.CountDownLatch;
@@ -71,8 +72,15 @@
 
     /** Checks whether the display dimension is close to square. */
     public static boolean isCloseToSquareDisplay(final Activity activity) {
-        final float closeToSquareMaxAspectRatio = activity.getResources().getFloat(
-                com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio);
+        final Resources resources = activity.getResources();
+        final float closeToSquareMaxAspectRatio;
+        try {
+            closeToSquareMaxAspectRatio = resources.getFloat(resources.getIdentifier(
+                    "config_closeToSquareDisplayMaxAspectRatio", "dimen", "android"));
+        } catch (Resources.NotFoundException e) {
+            // Assume device is not close to square.
+            return false;
+        }
         final DisplayInfo displayInfo = new DisplayInfo();
         activity.getDisplay().getDisplayInfo(displayInfo);
         final int w = displayInfo.logicalWidth;
diff --git a/tests/camera/Android.bp b/tests/camera/Android.bp
new file mode 100644
index 0000000..bf39750
--- /dev/null
+++ b/tests/camera/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2015 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.
+
+// Reusable Camera performance test classes and helpers
+android_library {
+    name: "cts-camera-performance-tests",
+
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "mockito-target-minus-junit4",
+        "CtsCameraUtils",
+        "truth-prebuilt",
+        "androidx.test.rules",
+    ],
+
+    manifest: "AndroidManifest-lib.xml",
+    resource_dirs: ["res"],
+    srcs: [
+        "src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java",
+        "src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java",
+        "src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java",
+        "src/android/hardware/camera2/cts/PerformanceTest.java",
+        "src/android/hardware/cts/CameraPerformanceTestHelper.java",
+        "src/android/hardware/cts/LegacyCameraPerformanceTest.java",
+        "src/android/hardware/camera2/cts/RecordingTest.java",
+    ],
+
+    sdk_version: "test_current",
+
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+}
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index 1dc75f3..749fed5 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -14,40 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-# Reusable Camera performance test classes and helpers
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := cts-camera-performance-tests
-
-LOCAL_MODULE_TAGS := tests
-
-# Include both the 32 and 64 bit versions
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt \
-	ctstestrunner-axt \
-	mockito-target-minus-junit4 \
-	CtsCameraUtils \
-	truth-prebuilt \
-	androidx.test.rules
-
-LOCAL_MANIFEST_FILE := AndroidManifest-lib.xml
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SRC_FILES := \
-	src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java \
-	src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java \
-	src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java \
-	src/android/hardware/camera2/cts/PerformanceTest.java \
-	src/android/hardware/cts/CameraPerformanceTestHelper.java \
-	src/android/hardware/cts/LegacyCameraPerformanceTest.java \
-	src/android/hardware/camera2/cts/RecordingTest.java
-
-LOCAL_SDK_VERSION := test_current
-
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # CtsCameraTestCases package
 
 include $(CLEAR_VARS)
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
index 2552686..b8b7f25 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
@@ -136,8 +136,12 @@
             assertThat(featureRect.top).isAtLeast(0);
             assertThat(featureRect.right).isAtLeast(0);
             assertThat(featureRect.bottom).isAtLeast(0);
-            assertThat(featureRect.right).isAtMost(mActivity.getWidth());
-            assertThat(featureRect.bottom).isAtMost(mActivity.getHeight());
+
+            final Rect activityBounds =
+                    mActivity.getWindowManager().getCurrentWindowMetrics().getBounds();
+
+            assertThat(featureRect.right).isAtMost(activityBounds.width());
+            assertThat(featureRect.bottom).isAtMost(activityBounds.height());
         }
     }
 
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/wrapper/sidecarwrapperimpl/TestSidecarWindowLayoutInfo.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/wrapper/sidecarwrapperimpl/TestSidecarWindowLayoutInfo.java
index e784351..31e496ba 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/wrapper/sidecarwrapperimpl/TestSidecarWindowLayoutInfo.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/wrapper/sidecarwrapperimpl/TestSidecarWindowLayoutInfo.java
@@ -73,11 +73,55 @@
             return false;
         }
         final TestSidecarWindowLayoutInfo other = (TestSidecarWindowLayoutInfo) obj;
-        return mSidecarWindowLayoutInfo.equals(other.mSidecarWindowLayoutInfo);
+        return areSidecarWindowLayoutInfoEqual(mSidecarWindowLayoutInfo,
+                other.mSidecarWindowLayoutInfo);
     }
 
     @Override
     public int hashCode() {
         return mSidecarWindowLayoutInfo.hashCode();
     }
+
+    /**
+     * Compares two {@link SidecarWindowLayoutInfo} with respect to their core data. This method is
+     * necessary because {@link SidecarWindowLayoutInfo} did not implement {@code equals}. Also
+     * Sidecar has been deprecated and frozen, so this method is stable.
+     *
+     * @param lhs {@link SidecarWindowLayoutInfo} to be compared.
+     * @param rhs {@link SidecarWindowLayoutInfo} to be compared.
+     * @return {@code true} if objects are equal with respect to data otherwise return
+     * {@code false}.
+     */
+    private static boolean areSidecarWindowLayoutInfoEqual(@NonNull SidecarWindowLayoutInfo lhs,
+            @NonNull SidecarWindowLayoutInfo rhs) {
+        if (lhs.displayFeatures == rhs.displayFeatures) {
+            return true;
+        }
+        if (lhs.displayFeatures == null || rhs.displayFeatures == null
+                || lhs.displayFeatures.size() != rhs.displayFeatures.size()) {
+            return false;
+        }
+        for (int i = 0; i < lhs.displayFeatures.size(); i++) {
+            if (!areSidecarDisplayFeatureEqual(lhs.displayFeatures.get(i),
+                    rhs.displayFeatures.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Compares two {@link SidecarDisplayFeature} with respect to their core data.  This method is
+     * necessary because {@link SidecarWindowLayoutInfo} did not implement {@code equals}.  Also
+     * Sidecar has been deprecated and frozen, so this method is stable.
+     *
+     * @param lhs {@link SidecarDisplayFeature} to be compared.
+     * @param rhs {@link SidecarDisplayFeature} to be compared.
+     * @return {@code true} if objects are equal with respect to data otherwise return
+     * {@code false}.
+     */
+    private static boolean areSidecarDisplayFeatureEqual(@NonNull SidecarDisplayFeature lhs,
+            @NonNull SidecarDisplayFeature rhs) {
+        return lhs.getType() == rhs.getType() && lhs.getRect().equals(rhs.getRect());
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index cfc9d0a..0ac11b7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -832,7 +832,7 @@
 
         // Move activity back to docked stack.
         separateTestJournal();
-        setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        moveTaskToPrimarySplitScreen(mWmState.getTaskByActivity(activityName).mTaskId);
         final SizeInfo finalDockedSizes = getActivityDisplaySize(activityName);
 
         // After activity configuration was changed twice it must report same size as original one.
diff --git a/tests/media/jni/NativeCodecUnitTest.cpp b/tests/media/jni/NativeCodecUnitTest.cpp
index 724bf7b..aa27619 100644
--- a/tests/media/jni/NativeCodecUnitTest.cpp
+++ b/tests/media/jni/NativeCodecUnitTest.cpp
@@ -745,11 +745,12 @@
         AMediaFormat* dupFormat = AMediaCodec_getInputFormat(mCodec);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (!dupMime || strcmp(dupMime, mime) != 0) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getInputFormat fails in initialized state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
     }
     return !hasSeenError();
@@ -768,11 +769,12 @@
         AMediaFormat* dupFormat = AMediaCodec_getInputFormat(mCodec);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (!dupMime || strcmp(dupMime, mime) != 0) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getInputFormat fails in running state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
     }
     return !hasSeenError();
@@ -789,22 +791,24 @@
         AMediaFormat* dupFormat = AMediaCodec_getInputFormat(mCodec);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getInputFormat succeeds in uninitialized state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
         CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
         dupFormat = AMediaCodec_getInputFormat(mCodec);
         dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getInputFormat succeeds in stopped state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
     }
     return !hasSeenError();
 }
@@ -918,11 +922,12 @@
         AMediaFormat* dupFormat = AMediaCodec_getOutputFormat(mCodec);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (!dupMime || strcmp(dupMime, mime) != 0) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getOutputFormat fails in initialized state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
     }
     return !hasSeenError();
@@ -941,11 +946,12 @@
         AMediaFormat* dupFormat = AMediaCodec_getOutputFormat(mCodec);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (!dupMime || strcmp(dupMime, mime) != 0) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getOutputFormat fails in running state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
     }
     return !hasSeenError();
@@ -960,22 +966,24 @@
         AMediaFormat* dupFormat = AMediaCodec_getOutputFormat(mCodec);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getOutputFormat succeeds in uninitialized state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
         CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
         dupFormat = AMediaCodec_getOutputFormat(mCodec);
         dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("getOutputFormat succeeds in stopped state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
     }
     return !hasSeenError();
 }
@@ -1373,11 +1381,12 @@
         AMediaFormat* dupFormat = AMediaCodec_getBufferFormat(mCodec, 0);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("GetBufferFormat succeeds in initialized state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
     }
     return !hasSeenError();
@@ -1397,11 +1406,12 @@
         AMediaFormat* dupFormat = AMediaCodec_getBufferFormat(mCodec, -1);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("GetBufferFormat succeeds for bad buffer index -1");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         if (!queueEOS()) return false;
         if (!hasSeenError()) {
             int bufferIndex = 0;
@@ -1414,11 +1424,12 @@
                         dupFormat = AMediaCodec_getBufferFormat(mCodec, bufferIndex);
                         dupMime = nullptr;
                         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-                        AMediaFormat_delete(dupFormat);
                         if (!dupMime || strcmp(dupMime, mime) != 0) {
+                            AMediaFormat_delete(dupFormat);
                             ALOGE("GetBufferFormat fails in running state");
                             return false;
                         }
+                        AMediaFormat_delete(dupFormat);
                         isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
                     }
                 } else {
@@ -1427,11 +1438,12 @@
                         dupFormat = AMediaCodec_getBufferFormat(mCodec, bufferIndex);
                         dupMime = nullptr;
                         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-                        AMediaFormat_delete(dupFormat);
                         if (!dupMime || strcmp(dupMime, mime) != 0) {
+                            AMediaFormat_delete(dupFormat);
                             ALOGE("GetBufferFormat fails in running state");
                             return false;
                         }
+                        AMediaFormat_delete(dupFormat);
                         isOk = dequeueOutput(bufferIndex, &outInfo);
                     }
                 }
@@ -1443,11 +1455,12 @@
             dupFormat = AMediaCodec_getBufferFormat(mCodec, bufferIndex);
             dupMime = nullptr;
             AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-            AMediaFormat_delete(dupFormat);
             if (dupMime) {
+                AMediaFormat_delete(dupFormat);
                 ALOGE("GetBufferFormat succeeds for buffer index not owned by client");
                 return false;
             }
+            AMediaFormat_delete(dupFormat);
         } else {
             ALOGE("Got unexpected error");
             return false;
@@ -1466,22 +1479,24 @@
         AMediaFormat* dupFormat = AMediaCodec_getBufferFormat(mCodec, 0);
         const char* dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("GetBufferFormat succeeds in uninitialized state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
         if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
         CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
         CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
         dupFormat = AMediaCodec_getBufferFormat(mCodec, 0);
         dupMime = nullptr;
         AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMime);
-        AMediaFormat_delete(dupFormat);
         if (dupMime) {
+            AMediaFormat_delete(dupFormat);
             ALOGE("GetBufferFormat succeeds in stopped state");
             return false;
         }
+        AMediaFormat_delete(dupFormat);
     }
     return !hasSeenError();
 }
diff --git a/tests/media/jni/NativeExtractorTest.cpp b/tests/media/jni/NativeExtractorTest.cpp
index e5c391a..de0fae8 100644
--- a/tests/media/jni/NativeExtractorTest.cpp
+++ b/tests/media/jni/NativeExtractorTest.cpp
@@ -93,7 +93,7 @@
                 setSampleInfo(refExtractor, &refSampleInfo);
                 setSampleInfo(testExtractor, &testSampleInfo);
                 if (!isSampleInfoValidAndIdentical(&refSampleInfo, &testSampleInfo)) {
-                    ALOGD(" Mime: %s mismatch for sample: %d", refMime, frameCount);
+                    ALOGD(" Mime: %s mismatch for sample: %d", mime, frameCount);
                     ALOGD(" flags exp/got: %d / %d", refSampleInfo.flags, testSampleInfo.flags);
                     ALOGD(" size exp/got: %d / %d ", refSampleInfo.size, testSampleInfo.size);
                     ALOGD(" ts exp/got: %" PRId64 " / %" PRId64 "",
@@ -104,55 +104,53 @@
                 ssize_t refSz =
                         AMediaExtractor_readSampleData(refExtractor, refBuffer, maxSampleSize);
                 if (refSz != refSampleInfo.size) {
-                    ALOGD("Mime: %s Size exp/got:  %d / %zd ", refMime, refSampleInfo.size, refSz);
+                    ALOGD("Mime: %s Size exp/got:  %d / %zd ", mime, refSampleInfo.size, refSz);
                     areTracksIdentical = false;
                     break;
                 }
                 ssize_t testSz =
                         AMediaExtractor_readSampleData(testExtractor, testBuffer, maxSampleSize);
                 if (testSz != testSampleInfo.size) {
-                    ALOGD("Mime: %s Size exp/got:  %d / %zd ", refMime, testSampleInfo.size,
-                          testSz);
+                    ALOGD("Mime: %s Size exp/got:  %d / %zd ", mime, testSampleInfo.size, testSz);
                     areTracksIdentical = false;
                     break;
                 }
                 int trackIndex = AMediaExtractor_getSampleTrackIndex(refExtractor);
                 if (trackIndex != refTrackID) {
-                    ALOGD("Mime: %s TrackID exp/got: %zu / %d", refMime, refTrackID, trackIndex);
+                    ALOGD("Mime: %s TrackID exp/got: %zu / %d", mime, refTrackID, trackIndex);
                     areTracksIdentical = false;
                     break;
                 }
                 trackIndex = AMediaExtractor_getSampleTrackIndex(testExtractor);
                 if (trackIndex != testTrackID) {
-                    ALOGD("Mime: %s  TrackID exp/got %zd / %d : ", refMime, testTrackID,
-                          trackIndex);
+                    ALOGD("Mime: %s  TrackID exp/got %zd / %d : ", mime, testTrackID, trackIndex);
                     areTracksIdentical = false;
                     break;
                 }
                 if (memcmp(refBuffer, testBuffer, refSz)) {
-                    ALOGD("Mime: %s Mismatch in sample data", refMime);
+                    ALOGD("Mime: %s Mismatch in sample data", mime);
                     areTracksIdentical = false;
                     break;
                 }
                 bool haveRefSamples = AMediaExtractor_advance(refExtractor);
                 bool haveTestSamples = AMediaExtractor_advance(testExtractor);
                 if (haveRefSamples != haveTestSamples) {
-                    ALOGD("Mime: %s Mismatch in sampleCount", refMime);
+                    ALOGD("Mime: %s Mismatch in sampleCount", mime);
                     areTracksIdentical = false;
                     break;
                 }
 
                 if (!haveRefSamples && !isExtractorOKonEOS(refExtractor)) {
-                    ALOGD("Mime: %s calls post advance() are not OK", refMime);
+                    ALOGD("Mime: %s calls post advance() are not OK", mime);
                     areTracksIdentical = false;
                     break;
                 }
                 if (!haveTestSamples && !isExtractorOKonEOS(testExtractor)) {
-                    ALOGD("Mime: %s calls post advance() are not OK", refMime);
+                    ALOGD("Mime: %s calls post advance() are not OK", mime);
                     areTracksIdentical = false;
                     break;
                 }
-                ALOGV("Mime: %s Sample: %d flags: %d size: %d ts: % " PRId64 "", refMime,
+                ALOGV("Mime: %s Sample: %d flags: %d size: %d ts: % " PRId64 "", mime,
                       frameCount, refSampleInfo.flags, refSampleInfo.size,
                       refSampleInfo.presentationTimeUs);
                 if (!haveRefSamples || frameCount >= sampleLimit) {
@@ -423,6 +421,7 @@
         const char* currMime = nullptr;
         bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &currMime);
         if (!hasKey || strcmp(currMime, mime) != 0) {
+            AMediaFormat_delete(format);
             continue;
         }
         AMediaExtractor_selectTrack(extractor, trackID);
diff --git a/tests/media/jni/NativeMediaCommon.cpp b/tests/media/jni/NativeMediaCommon.cpp
index 8cccbc0..90540e0 100644
--- a/tests/media/jni/NativeMediaCommon.cpp
+++ b/tests/media/jni/NativeMediaCommon.cpp
@@ -47,8 +47,6 @@
 const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
 
 bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat) {
-    const char* mime;
-    AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &mime);
     for (int i = 0;; i++) {
         std::pair<void*, size_t> refCsd;
         std::pair<void*, size_t> testCsd;
diff --git a/tests/media/jni/NativeMuxerTest.cpp b/tests/media/jni/NativeMuxerTest.cpp
index cea162c7..b9eae64 100644
--- a/tests/media/jni/NativeMuxerTest.cpp
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -271,6 +271,8 @@
         AMediaFormat* thisFormat = mFormat[i];
         const char* thisMime = nullptr;
         AMediaFormat_getString(thisFormat, AMEDIAFORMAT_KEY_MIME, &thisMime);
+        int tolerance = !strncmp(thisMime, "video/", strlen("video/")) ? STTS_TOLERANCE_US : 0;
+        tolerance += 1; // rounding error
         int j = 0;
         for (; j < that->mTrackCount; j++) {
             AMediaFormat* thatFormat = that->mFormat[j];
@@ -279,9 +281,6 @@
             if (thisMime != nullptr && thatMime != nullptr && !strcmp(thisMime, thatMime)) {
                 if (!isFormatSimilar(thisFormat, thatFormat)) continue;
                 if (mBufferInfo[i].size() == that->mBufferInfo[j].size()) {
-                    int tolerance =
-                            !strncmp(thisMime, "video/", strlen("video/")) ? STTS_TOLERANCE_US : 0;
-                    tolerance += 1; // rounding error
                     int k = 0;
                     for (; k < mBufferInfo[i].size(); k++) {
                         AMediaCodecBufferInfo* thisInfo = mBufferInfo[i][k];
@@ -305,6 +304,7 @@
             }
         }
         if (j == that->mTrackCount) {
+            AMediaFormat_getString(thisFormat, AMEDIAFORMAT_KEY_MIME, &thisMime);
             ALOGV("For mime %s, Couldn't find a match", thisMime);
             return false;
         }
diff --git a/tests/security/Android.bp b/tests/security/Android.bp
index d9786a8..12168cc 100644
--- a/tests/security/Android.bp
+++ b/tests/security/Android.bp
@@ -17,6 +17,7 @@
     static_libs: [
         "bouncycastle-unbundled",
         "bouncycastle-bcpkix-unbundled",
+        "cbor-java",
         "guava",
         "truth-prebuilt",
         "testng",
diff --git a/tests/security/src/android/keystore/cts/Asn1Attestation.java b/tests/security/src/android/keystore/cts/Asn1Attestation.java
new file mode 100644
index 0000000..b454130
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/Asn1Attestation.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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.keystore.cts;
+
+import org.bouncycastle.asn1.ASN1Sequence;
+
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+
+public class Asn1Attestation extends Attestation {
+    static final int ATTESTATION_VERSION_INDEX = 0;
+    static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+    static final int KEYMASTER_VERSION_INDEX = 2;
+    static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+    static final int ATTESTATION_CHALLENGE_INDEX = 4;
+    static final int UNIQUE_ID_INDEX = 5;
+    static final int SW_ENFORCED_INDEX = 6;
+    static final int TEE_ENFORCED_INDEX = 7;
+
+    int attestationSecurityLevel;
+
+    /**
+     * Constructs an {@code Asn1Attestation} object from the provided {@link X509Certificate},
+     * extracting the attestation data from the attestation extension.
+     *
+     * @throws CertificateParsingException if the certificate does not contain a properly-formatted
+     *     attestation extension.
+     */
+    public Asn1Attestation(X509Certificate x509Cert) throws CertificateParsingException {
+        super(x509Cert);
+        ASN1Sequence seq = getAttestationSequence(x509Cert);
+
+        attestationVersion =
+                Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
+        attestationSecurityLevel =
+                Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
+        keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
+        keymasterSecurityLevel =
+                Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
+
+        attestationChallenge =
+                Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX));
+
+        uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(UNIQUE_ID_INDEX));
+
+        softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
+        teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
+    }
+
+    ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
+            throws CertificateParsingException {
+        byte[] attestationExtensionBytes = x509Cert.getExtensionValue(Attestation.ASN1_OID);
+        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+            throw new CertificateParsingException("Did not find extension with OID " + ASN1_OID);
+        }
+        return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes);
+    }
+
+    public int getAttestationSecurityLevel() {
+        return attestationSecurityLevel;
+    }
+
+    public RootOfTrust getRootOfTrust() {
+        return teeEnforced.getRootOfTrust();
+    }
+}
diff --git a/tests/security/src/android/keystore/cts/Attestation.java b/tests/security/src/android/keystore/cts/Attestation.java
index 2285cad..7414908 100644
--- a/tests/security/src/android/keystore/cts/Attestation.java
+++ b/tests/security/src/android/keystore/cts/Attestation.java
@@ -16,71 +16,69 @@
 
 package android.keystore.cts;
 
+import co.nstant.in.cbor.CborException;
+
 import com.google.common.base.CharMatcher;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.BaseEncoding;
 
-import org.bouncycastle.asn1.ASN1Sequence;
-
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * Parses an attestation certificate and provides an easy-to-use interface for examining the
  * contents.
  */
-public class Attestation {
-    static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
-    static final String KEY_USAGE_OID = "2.5.29.15";  // Standard key usage extension.
-    static final int ATTESTATION_VERSION_INDEX = 0;
-    static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
-    static final int KEYMASTER_VERSION_INDEX = 2;
-    static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
-    static final int ATTESTATION_CHALLENGE_INDEX = 4;
-    static final int UNIQUE_ID_INDEX = 5;
-    static final int SW_ENFORCED_INDEX = 6;
-    static final int TEE_ENFORCED_INDEX = 7;
+public abstract class Attestation {
+    static final String EAT_OID = "1.3.6.1.4.1.11129.2.1.25";
+    static final String ASN1_OID = "1.3.6.1.4.1.11129.2.1.17";
+    static final String KEY_USAGE_OID = "2.5.29.15"; // Standard key usage extension.
 
     public static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
     public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
     public static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
 
-    private final int attestationVersion;
-    private final int attestationSecurityLevel;
-    private final int keymasterVersion;
-    private final int keymasterSecurityLevel;
-    private final byte[] attestationChallenge;
-    private final byte[] uniqueId;
-    private final AuthorizationList softwareEnforced;
-    private final AuthorizationList teeEnforced;
-    private final Set<String> unexpectedExtensionOids;
-
+    int attestationVersion;
+    int keymasterVersion;
+    int keymasterSecurityLevel;
+    byte[] attestationChallenge;
+    byte[] uniqueId;
+    AuthorizationList softwareEnforced;
+    AuthorizationList teeEnforced;
+    Set<String> unexpectedExtensionOids;
 
     /**
      * Constructs an {@code Attestation} object from the provided {@link X509Certificate},
      * extracting the attestation data from the attestation extension.
      *
+     * <p>This method ensures that at most one attestation extension is included in the certificate.
+     *
      * @throws CertificateParsingException if the certificate does not contain a properly-formatted
-     *                                     attestation extension.
+     *     attestation extension, if it contains multiple attestation extensions, or if the
+     *     attestation extension can not be parsed.
      */
-    public Attestation(X509Certificate x509Cert) throws CertificateParsingException {
-        ASN1Sequence seq = getAttestationSequence(x509Cert);
+    public static Attestation loadFromCertificate(X509Certificate x509Cert)
+            throws CertificateParsingException {
+        if (x509Cert.getExtensionValue(EAT_OID) == null
+                && x509Cert.getExtensionValue(ASN1_OID) == null) {
+            throw new CertificateParsingException("No attestation extensions found");
+        }
+        if (x509Cert.getExtensionValue(EAT_OID) != null) {
+            if (x509Cert.getExtensionValue(ASN1_OID) != null) {
+                throw new CertificateParsingException("Multiple attestation extensions found");
+            }
+            try {
+                return new EatAttestation(x509Cert);
+            } catch (CborException cbe) {
+                throw new CertificateParsingException("Unable to parse EAT extension", cbe);
+            }
+        }
+        return new Asn1Attestation(x509Cert);
+    }
+
+    Attestation(X509Certificate x509Cert) {
         unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert);
-
-        attestationVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
-        attestationSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
-        keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
-        keymasterSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
-
-        attestationChallenge =
-                Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.ATTESTATION_CHALLENGE_INDEX));
-
-        uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.UNIQUE_ID_INDEX));
-
-        softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
-        teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
     }
 
     public static String securityLevelToString(int attestationSecurityLevel) {
@@ -100,9 +98,9 @@
         return attestationVersion;
     }
 
-    public int getAttestationSecurityLevel() {
-        return attestationSecurityLevel;
-    }
+    public abstract int getAttestationSecurityLevel();
+
+    public abstract RootOfTrust getRootOfTrust();
 
     public int getKeymasterVersion() {
         return keymasterVersion;
@@ -135,13 +133,15 @@
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();
-        s.append("Attest version: " + attestationVersion);
-        s.append("\nAttest security: " + securityLevelToString(attestationSecurityLevel));
+        s.append("Extension type: " + getClass());
+        s.append("\nAttest version: " + attestationVersion);
+        s.append("\nAttest security: " + securityLevelToString(getAttestationSecurityLevel()));
         s.append("\nKM version: " + keymasterVersion);
         s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel));
 
         s.append("\nChallenge");
-        String stringChallenge = new String(attestationChallenge);
+        String stringChallenge =
+                attestationChallenge != null ? new String(attestationChallenge) : "null";
         if (CharMatcher.ascii().matchesAllOf(stringChallenge)) {
             s.append(": [" + stringChallenge + "]");
         } else {
@@ -159,26 +159,16 @@
         return s.toString();
     }
 
-    private ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
-            throws CertificateParsingException {
-        byte[] attestationExtensionBytes = x509Cert.getExtensionValue(KEY_DESCRIPTION_OID);
-        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
-            throw new CertificateParsingException(
-                    "Did not find extension with OID " + KEY_DESCRIPTION_OID);
-        }
-        return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes);
-    }
-
-    private Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) {
+    Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) {
         return new ImmutableSet.Builder<String>()
-                .addAll(x509Cert.getCriticalExtensionOIDs()
-                        .stream()
-                        .filter(s -> !KEY_USAGE_OID.equals(s))
-                        .iterator())
-                .addAll(x509Cert.getNonCriticalExtensionOIDs()
-                        .stream()
-                        .filter(s -> !KEY_DESCRIPTION_OID.equals(s))
-                        .iterator())
+                .addAll(
+                        x509Cert.getCriticalExtensionOIDs().stream()
+                                .filter(s -> !KEY_USAGE_OID.equals(s))
+                                .iterator())
+                .addAll(
+                        x509Cert.getNonCriticalExtensionOIDs().stream()
+                                .filter(s -> !ASN1_OID.equals(s) && !EAT_OID.equals(s))
+                                .iterator())
                 .build();
     }
 }
diff --git a/tests/security/src/android/keystore/cts/AuthorizationList.java b/tests/security/src/android/keystore/cts/AuthorizationList.java
index 85ac115..dce9b2a 100644
--- a/tests/security/src/android/keystore/cts/AuthorizationList.java
+++ b/tests/security/src/android/keystore/cts/AuthorizationList.java
@@ -19,6 +19,10 @@
 import static com.google.common.base.Functions.forMap;
 import static com.google.common.collect.Collections2.transform;
 
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Number;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -166,6 +170,7 @@
             .put(KM_PURPOSE_VERIFY, "VERIFY")
             .build();
 
+    private Integer securityLevel;
     private Set<Integer> purposes;
     private Integer algorithm;
     private Integer keySize;
@@ -331,6 +336,126 @@
 
     }
 
+    public AuthorizationList(co.nstant.in.cbor.model.Map submodMap)
+            throws CertificateParsingException {
+        for (DataItem key : submodMap.getKeys()) {
+            int keyInt = ((Number) key).getValue().intValue();
+            switch (keyInt) {
+                default:
+                    throw new CertificateParsingException("Unknown EAT tag: " + key);
+
+                case EatClaim.SECURITY_LEVEL:
+                    securityLevel = eatSecurityLevelToKeymasterSecurityLevel(
+                            CborUtils.getInt(submodMap, key));
+                    break;
+                case EatClaim.PURPOSE:
+                    purposes = CborUtils.getIntSet(submodMap, key);
+                    break;
+                case EatClaim.ALGORITHM:
+                    algorithm = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.KEY_SIZE:
+                    keySize = CborUtils.getInt(submodMap, key);
+                    Log.i("Attestation", "Found KEY SIZE, value: " + keySize);
+                    break;
+                case EatClaim.DIGEST:
+                    digests = CborUtils.getIntSet(submodMap, key);
+                    break;
+                case EatClaim.PADDING:
+                    paddingModes = CborUtils.getIntSet(submodMap, key);
+                    break;
+                case EatClaim.RSA_PUBLIC_EXPONENT:
+                    rsaPublicExponent = CborUtils.getLong(submodMap, key);
+                    break;
+                case EatClaim.NO_AUTH_REQUIRED:
+                    noAuthRequired = true;
+                    break;
+                case EatClaim.IAT:
+                    creationDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.ORIGIN:
+                    origin = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.OS_VERSION:
+                    osVersion = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.OS_PATCHLEVEL:
+                    osPatchLevel = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.VENDOR_PATCHLEVEL:
+                    vendorPatchLevel = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.BOOT_PATCHLEVEL:
+                    bootPatchLevel = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.ACTIVE_DATETIME:
+                    activeDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.ORIGINATION_EXPIRE_DATETIME:
+                    originationExpireDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.USAGE_EXPIRE_DATETIME:
+                    usageExpireDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.ROLLBACK_RESISTANT:
+                    rollbackResistant = true;
+                    break;
+                case EatClaim.ROLLBACK_RESISTANCE:
+                    rollbackResistance = true;
+                    break;
+                case EatClaim.AUTH_TIMEOUT:
+                    authTimeout = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.ALLOW_WHILE_ON_BODY:
+                    allowWhileOnBody = true;
+                    break;
+                case EatClaim.EC_CURVE:
+                    ecCurve = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.USER_AUTH_TYPE:
+                    userAuthType = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_APPLICATION_ID:
+                    // TODO: The attestation application ID is currently still encoded as an ASN.1
+                    // structure. Parse a CBOR structure when it's available instead.
+                    attestationApplicationId = new AttestationApplicationId(
+                        Asn1Utils.getAsn1EncodableFromBytes(CborUtils.getBytes(submodMap, key)));
+                    break;
+                case EatClaim.ATTESTATION_ID_BRAND:
+                    brand = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_DEVICE:
+                    device = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_PRODUCT:
+                    product = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_SERIAL:
+                    serialNumber = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.UEID:
+                    // TODO: Parse depending on encoding chosen in attestation_record.cpp.
+                    imei = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_MEID:
+                    meid = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_MANUFACTURER:
+                    manufacturer = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_MODEL:
+                    model = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.USER_PRESENCE_REQUIRED:
+                    userPresenceRequired = CborUtils.getBoolean(submodMap, key);
+                    break;
+                case EatClaim.TRUSTED_CONFIRMATION_REQUIRED:
+                    confirmationRequired = true;
+                    break;
+            }
+        }
+    }
+
     public static String algorithmToString(int algorithm) {
         switch (algorithm) {
             case KM_ALGORITHM_RSA:
@@ -415,6 +540,10 @@
         }
     }
 
+    public Integer getSecurityLevel() {
+        return securityLevel;
+    }
+
     public Set<Integer> getPurposes() {
         return purposes;
     }
@@ -607,6 +736,19 @@
         return confirmationRequired;
     }
 
+    static int eatSecurityLevelToKeymasterSecurityLevel(int eatSecurityLevel) {
+        switch(eatSecurityLevel) {
+            case EatClaim.SECURITY_LEVEL_UNRESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_SOFTWARE;
+            case EatClaim.SECURITY_LEVEL_SECURE_RESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+            case EatClaim.SECURITY_LEVEL_HARDWARE:
+                return Attestation.KM_SECURITY_LEVEL_STRONG_BOX;
+            default:
+                throw new RuntimeException("Invalid EAT security level: " + eatSecurityLevel);
+        }
+    }
+
     private String getStringFromAsn1Value(ASN1Primitive value) throws CertificateParsingException {
         try {
             return Asn1Utils.getStringFromAsn1OctetStreamAssumingUTF8(value);
diff --git a/tests/security/src/android/keystore/cts/CborUtils.java b/tests/security/src/android/keystore/cts/CborUtils.java
new file mode 100644
index 0000000..eeac9b5
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/CborUtils.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 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.keystore.cts;
+
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.NegativeInteger;
+import co.nstant.in.cbor.model.Number;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.SimpleValueType;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+import java.nio.charset.StandardCharsets;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+class CborUtils {
+    public static Number toNumber(long l) {
+        return l >= 0 ? new UnsignedInteger(l) : new NegativeInteger(l);
+    }
+
+    public static int getInt(Map map, long index) {
+        DataItem item = map.get(CborUtils.toNumber(index));
+        return ((Number) item).getValue().intValue();
+    }
+
+    public static int getInt(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        return ((Number) item).getValue().intValue();
+    }
+
+    public static long getLong(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        return ((Number) item).getValue().longValue();
+    }
+
+    public static Set<Integer> getIntSet(Map map, DataItem index) {
+        Array array = (Array) map.get(index);
+        Set<Integer> result = new HashSet();
+        for (DataItem item : array.getDataItems()) {
+            result.add(((Number) item).getValue().intValue());
+        }
+        return result;
+    }
+
+    public static Boolean getBoolean(Map map, DataItem index) {
+        SimpleValueType value = ((SimpleValue) map.get(index)).getSimpleValueType();
+        if (value != SimpleValueType.TRUE && value != SimpleValueType.FALSE) {
+            throw new RuntimeException("Only expecting boolean values for " + index);
+        }
+        return (value == SimpleValueType.TRUE);
+    }
+
+    public static List<Boolean> getBooleanList(Map map, DataItem index) {
+        Array array = (Array) map.get(index);
+        List<Boolean> result = new ArrayList();
+        for (DataItem item : array.getDataItems()) {
+            SimpleValueType value = ((SimpleValue) item).getSimpleValueType();
+            if (value == SimpleValueType.FALSE) {
+                result.add(false);
+            } else if (value == SimpleValueType.TRUE) {
+                result.add(true);
+            } else {
+                throw new RuntimeException("Map contains more than booleans: " + map);
+            }
+        }
+        return result;
+    }
+
+    public static Date getDate(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        long epochMillis = ((Number) item).getValue().longValue();
+        return new Date(epochMillis);
+    }
+
+    public static byte[] getBytes(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        return ((ByteString) item).getBytes();
+    }
+
+    public static String getString(Map map, DataItem index) {
+        byte[] bytes = getBytes(map, index);
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+}
diff --git a/tests/security/src/android/keystore/cts/EatAttestation.java b/tests/security/src/android/keystore/cts/EatAttestation.java
new file mode 100644
index 0000000..21ef0db
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/EatAttestation.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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.keystore.cts;
+
+import android.util.Log;
+
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.Number;
+import co.nstant.in.cbor.model.UnicodeString;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+public class EatAttestation extends Attestation {
+    static final String TAG = "EatAttestation";
+    final Map extension;
+    final RootOfTrust rootOfTrust;
+
+    /**
+     * Constructs an {@code EatAttestation} object from the provided {@link X509Certificate},
+     * extracting the attestation data from the attestation extension.
+     *
+     * @throws CertificateParsingException if the certificate does not contain a properly-formatted
+     *     attestation extension.
+     */
+    public EatAttestation(X509Certificate x509Cert)
+            throws CertificateParsingException, CborException {
+        super(x509Cert);
+        extension = getEatExtension(x509Cert);
+
+        RootOfTrust.Builder rootOfTrustBuilder = new RootOfTrust.Builder();
+        List<Boolean> bootState = null;
+        boolean officialBuild = false;
+
+        for (DataItem key : extension.getKeys()) {
+            int keyInt = ((Number) key).getValue().intValue();
+            switch (keyInt) {
+                default:
+                    throw new CertificateParsingException(
+                            "Unknown EAT tag: " + key + "\n in EAT extension:\n" + toString());
+
+                case EatClaim.ATTESTATION_VERSION:
+                    attestationVersion = CborUtils.getInt(extension, key);
+                    break;
+                case EatClaim.KEYMASTER_VERSION:
+                    keymasterVersion = CborUtils.getInt(extension, key);
+                    break;
+                case EatClaim.SECURITY_LEVEL:
+                    keymasterSecurityLevel =
+                            eatSecurityLevelToKeymintSecurityLevel(
+                                    CborUtils.getInt(extension, key));
+                    break;
+                case EatClaim.SUBMODS:
+                    Map submods = (Map) extension.get(key);
+                    softwareEnforced =
+                            new AuthorizationList(
+                                    (Map) submods.get(new UnicodeString(EatClaim.SUBMOD_SOFTWARE)));
+                    teeEnforced =
+                            new AuthorizationList(
+                                    (Map) submods.get(new UnicodeString(EatClaim.SUBMOD_TEE)));
+                    break;
+                case EatClaim.VERIFIED_BOOT_KEY:
+                    rootOfTrustBuilder.setVerifiedBootKey(CborUtils.getBytes(extension, key));
+                    break;
+                case EatClaim.DEVICE_LOCKED:
+                    rootOfTrustBuilder.setDeviceLocked(CborUtils.getBoolean(extension, key));
+                    break;
+                case EatClaim.BOOT_STATE:
+                    bootState = CborUtils.getBooleanList(extension, key);
+                    break;
+                case EatClaim.OFFICIAL_BUILD:
+                    officialBuild = CborUtils.getBoolean(extension, key);
+                    break;
+                case EatClaim.NONCE:
+                    attestationChallenge = CborUtils.getBytes(extension, key);
+                    break;
+                case EatClaim.CTI:
+                    Log.i(TAG, "Got CTI claim: " + CborUtils.getBytes(extension, key));
+                    uniqueId = CborUtils.getBytes(extension, key);
+                    break;
+                case EatClaim.VERIFIED_BOOT_HASH:
+                    // TODO: ignored for now, as this is not checked in original ASN.1 tests
+                    break;
+            }
+        }
+
+        if (bootState != null) {
+            rootOfTrustBuilder.setVerifiedBootState(
+                    eatBootStateTypeToVerifiedBootState(bootState, officialBuild));
+        }
+        rootOfTrust = rootOfTrustBuilder.build();
+    }
+
+    /** Find the submod containing the key information, and return its security level. */
+    public int getAttestationSecurityLevel() {
+        if (teeEnforced != null && teeEnforced.getAlgorithm() != null) {
+            return teeEnforced.getSecurityLevel();
+        } else if (softwareEnforced != null && softwareEnforced.getAlgorithm() != null) {
+            return softwareEnforced.getSecurityLevel();
+        } else {
+            return -1;
+        }
+    }
+
+    public RootOfTrust getRootOfTrust() {
+        return rootOfTrust;
+    }
+
+    public String toString() {
+        return super.toString() + "\nEncoded CBOR: " + extension;
+    }
+
+    Map getEatExtension(X509Certificate x509Cert)
+            throws CertificateParsingException, CborException {
+        byte[] attestationExtensionBytes = x509Cert.getExtensionValue(Attestation.EAT_OID);
+        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+            throw new CertificateParsingException("Did not find extension with OID " + EAT_OID);
+        }
+        ASN1Encodable asn1 = Asn1Utils.getAsn1EncodableFromBytes(attestationExtensionBytes);
+        byte[] cborBytes = Asn1Utils.getByteArrayFromAsn1(asn1);
+        List<DataItem> cbor = CborDecoder.decode(cborBytes);
+        return (Map) cbor.get(0);
+    }
+
+    static int eatSecurityLevelToKeymintSecurityLevel(int eatSecurityLevel) {
+        switch (eatSecurityLevel) {
+            case EatClaim.SECURITY_LEVEL_UNRESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_SOFTWARE;
+            case EatClaim.SECURITY_LEVEL_SECURE_RESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+            case EatClaim.SECURITY_LEVEL_HARDWARE:
+                return Attestation.KM_SECURITY_LEVEL_STRONG_BOX;
+            default:
+                throw new RuntimeException("Invalid EAT security level: " + eatSecurityLevel);
+        }
+    }
+
+    static int eatBootStateTypeToVerifiedBootState(List<Boolean> bootState, Boolean officialBuild) {
+        if (bootState.size() != 5) {
+            throw new RuntimeException("Boot state map has unexpected size: " + bootState.size());
+        }
+        if (bootState.get(4)) {
+            throw new RuntimeException("debug-permanent-disable must never be true: " + bootState);
+        }
+        boolean verifiedOrSelfSigned = bootState.get(0);
+        if (verifiedOrSelfSigned != bootState.get(1)
+                && verifiedOrSelfSigned != bootState.get(2)
+                && verifiedOrSelfSigned != bootState.get(3)) {
+            throw new RuntimeException("Unexpected boot state: " + bootState);
+        }
+
+        if (officialBuild) {
+            if (!verifiedOrSelfSigned) {
+                throw new AssertionError("Non-verified official build");
+            }
+            return RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
+        } else {
+            return verifiedOrSelfSigned
+                    ? RootOfTrust.KM_VERIFIED_BOOT_SELF_SIGNED
+                    : RootOfTrust.KM_VERIFIED_BOOT_UNVERIFIED;
+        }
+    }
+}
diff --git a/tests/security/src/android/keystore/cts/EatClaim.java b/tests/security/src/android/keystore/cts/EatClaim.java
new file mode 100644
index 0000000..1130037
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/EatClaim.java
@@ -0,0 +1,112 @@
+package android.keystore.cts;
+
+import android.security.keymaster.KeymasterDefs;
+
+class EatClaim {
+    public static final int IAT = 6;
+    public static final int CTI = 7;
+
+    public static final int NONCE = -75008;
+    public static final int UEID = -75009;
+
+    public static final int SECURITY_LEVEL = -76002;
+    public static final int SECURITY_LEVEL_UNRESTRICTED = 1;
+    public static final int SECURITY_LEVEL_SECURE_RESTRICTED = 3;
+    public static final int SECURITY_LEVEL_HARDWARE = 4;
+
+    public static final int BOOT_STATE = -76003;
+    public static final int SUBMODS = -76000;
+
+    private static final int PRIVATE_BASE = -80000;
+
+    public static final int PURPOSE = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_PURPOSE);
+    public static final int ALGORITHM =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ALGORITHM);
+    public static final int KEY_SIZE = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_KEY_SIZE);
+    public static final int BLOCK_MODE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_BLOCK_MODE);
+    public static final int DIGEST = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_DIGEST);
+    public static final int PADDING = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_PADDING);
+    public static final int CALLER_NONCE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_CALLER_NONCE);
+    public static final int MIN_MAC_LENGTH =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_MIN_MAC_LENGTH);
+    public static final int KDF = PRIVATE_BASE - 9;
+
+    public static final int EC_CURVE = PRIVATE_BASE - 10;
+    public static final int EAT_EC_CURVE_P_224 = 0;
+    public static final int EAT_EC_CURVE_P_256 = 1;
+    public static final int EAT_EC_CURVE_P_384 = 2;
+    public static final int EAT_EC_CURVE_P_521 = 3;
+
+    public static final int RSA_PUBLIC_EXPONENT =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT);
+
+    public static final int ROLLBACK_RESISTANCE = PRIVATE_BASE - 303;
+    public static final int EARLY_BOOT_ONLY = PRIVATE_BASE - 305;
+
+    public static final int ACTIVE_DATETIME =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
+    public static final int ORIGINATION_EXPIRE_DATETIME =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
+    public static final int USAGE_EXPIRE_DATETIME =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
+
+    public static final int NO_AUTH_REQUIRED =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+    public static final int USER_AUTH_TYPE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_USER_AUTH_TYPE);
+    public static final int AUTH_TIMEOUT =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_AUTH_TIMEOUT);
+    public static final int ALLOW_WHILE_ON_BODY =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
+    public static final int USER_PRESENCE_REQUIRED = PRIVATE_BASE - 507;
+    public static final int TRUSTED_CONFIRMATION_REQUIRED =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+    public static final int UNLOCKED_DEVICE_REQUIRED =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
+
+    public static final int APPLICATION_ID =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_APPLICATION_ID);
+
+    public static final int ORIGIN = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ORIGIN);
+    // TODO: hardcoded while KeymasterDefs uses the same value for
+    // ROLLBACK_RESISTANCE and ROLLBACK_RESISTANT
+    public static final int ROLLBACK_RESISTANT = PRIVATE_BASE - 703;
+    public static final int OS_VERSION = PRIVATE_BASE - 705;
+    public static final int OS_PATCHLEVEL = PRIVATE_BASE - 706;
+    public static final int ATTESTATION_APPLICATION_ID = PRIVATE_BASE - 709;
+    public static final int ATTESTATION_ID_BRAND =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND);
+    public static final int ATTESTATION_ID_DEVICE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE);
+    public static final int ATTESTATION_ID_PRODUCT =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT);
+    public static final int ATTESTATION_ID_SERIAL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL);
+    public static final int ATTESTATION_ID_MEID =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID);
+    public static final int ATTESTATION_ID_MANUFACTURER =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER);
+    public static final int ATTESTATION_ID_MODEL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL);
+    public static final int VENDOR_PATCHLEVEL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_VENDOR_PATCHLEVEL);
+    public static final int BOOT_PATCHLEVEL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_BOOT_PATCHLEVEL);
+    public static final int DEVICE_UNIQUE_ATTESTATION =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION);
+    public static final int IDENTITY_CREDENTIAL_KEY = PRIVATE_BASE - 721;
+
+    private static final int NON_KM_BASE = PRIVATE_BASE - 2000;
+
+    public static final int VERIFIED_BOOT_KEY = NON_KM_BASE - 1;
+    public static final int DEVICE_LOCKED = NON_KM_BASE - 2;
+    public static final int VERIFIED_BOOT_HASH = NON_KM_BASE - 3;
+    public static final int ATTESTATION_VERSION = NON_KM_BASE - 4;
+    public static final int KEYMASTER_VERSION = NON_KM_BASE - 5;
+    public static final int OFFICIAL_BUILD = NON_KM_BASE - 6;
+
+    public static final String SUBMOD_SOFTWARE = "software";
+    public static final String SUBMOD_TEE = "tee";
+}
diff --git a/tests/security/src/android/keystore/cts/RootOfTrust.java b/tests/security/src/android/keystore/cts/RootOfTrust.java
index 9957702..a115874 100644
--- a/tests/security/src/android/keystore/cts/RootOfTrust.java
+++ b/tests/security/src/android/keystore/cts/RootOfTrust.java
@@ -51,6 +51,13 @@
                 Asn1Utils.getIntegerFromAsn1(sequence.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
     }
 
+
+    RootOfTrust(byte[] verifiedBootKey, boolean deviceLocked, int verifiedBootState) {
+        this.verifiedBootKey = verifiedBootKey;
+        this.deviceLocked = deviceLocked;
+        this.verifiedBootState = verifiedBootState;
+    }
+
     public static String verifiedBootStateToString(int verifiedBootState) {
         switch (verifiedBootState) {
             case KM_VERIFIED_BOOT_VERIFIED:
@@ -82,11 +89,35 @@
     public String toString() {
         return new StringBuilder()
                 .append("\nVerified boot Key: ")
-                .append(BaseEncoding.base64().encode(verifiedBootKey))
+                .append(verifiedBootKey != null ?
+                            BaseEncoding.base64().encode(verifiedBootKey) :
+                            "null")
                 .append("\nDevice locked: ")
                 .append(deviceLocked)
                 .append("\nVerified boot state: ")
                 .append(verifiedBootStateToString(verifiedBootState))
                 .toString();
     }
+
+    public static class Builder {
+        private byte[] verifiedBootKey;
+        private boolean deviceLocked = false;
+        private int verifiedBootState = -1;
+
+        public Builder setVerifiedBootKey(byte[] verifiedBootKey) {
+            this.verifiedBootKey = verifiedBootKey;
+            return this;
+        }
+        public Builder setDeviceLocked(boolean deviceLocked) {
+            this.deviceLocked = deviceLocked;
+            return this;
+        }
+        public Builder setVerifiedBootState(int verifiedBootState) {
+            this.verifiedBootState = verifiedBootState;
+            return this;
+        }
+        public RootOfTrust build() {
+            return new RootOfTrust(verifiedBootKey, deviceLocked, verifiedBootState);
+        }
+    }
 }
diff --git a/tests/sensor/Android.bp b/tests/sensor/Android.bp
new file mode 100644
index 0000000..4930c6f
--- /dev/null
+++ b/tests/sensor/Android.bp
@@ -0,0 +1,100 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//
+// Reusable Sensor test classes and helpers
+//
+java_library {
+    name: "cts-sensors-tests",
+    defaults: ["cts_error_prone_rules_tests"],
+
+    static_libs: ["compatibility-device-util-axt"],
+
+    libs: [
+        "platform-test-annotations",
+        "android.test.base.stubs",
+    ],
+
+    sdk_version: "test_current",
+
+    srcs: ["src/**/*.java"],
+}
+
+//
+// JNI components for testing NDK
+//
+cc_library_shared {
+    name: "libcts-sensors-ndk-jni",
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+
+    srcs: [
+        "jni/SensorTest.cpp",
+        "jni/SensorTestCases.cpp",
+        "jni/android_hardware_cts_SensorDirectReportTest.cpp",
+        "jni/android_hardware_cts_SensorNativeTest.cpp",
+        "jni/nativeTestHelper.cpp",
+    ],
+
+    header_libs: ["jni_headers"],
+
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+
+    sdk_version: "current",
+
+    stl: "c++_shared",
+}
+
+//
+// CtsSensorTestCases package
+//
+android_test {
+    name: "CtsSensorTestCases",
+    defaults: [
+        "cts_defaults",
+    ],
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+
+    // include both the 32 and 64 bit versions
+    compile_multilib: "both",
+
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "cts-sensors-tests",
+    ],
+
+    jni_libs: ["libcts-sensors-ndk-jni"],
+
+    sdk_version: "test_current",
+
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+
+    stl: "c++_shared",
+}
diff --git a/tests/sensor/Android.mk b/tests/sensor/Android.mk
deleted file mode 100644
index d198b04..0000000
--- a/tests/sensor/Android.mk
+++ /dev/null
@@ -1,96 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-#
-# Reusable Sensor test classes and helpers
-#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := cts-sensors-tests
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt
-
-LOCAL_JAVA_LIBRARIES := platform-test-annotations android.test.base.stubs
-
-LOCAL_SDK_VERSION := test_current
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
--include cts/error_prone_rules_tests.mk
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
-# JNI components for testing NDK
-#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libcts-sensors-ndk-jni
-
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
-    jni/SensorTest.cpp \
-    jni/SensorTestCases.cpp \
-    jni/android_hardware_cts_SensorDirectReportTest.cpp \
-    jni/android_hardware_cts_SensorNativeTest.cpp \
-    jni/nativeTestHelper.cpp
-
-LOCAL_HEADER_LIBRARIES := jni_headers
-
-LOCAL_SHARED_LIBRARIES := libandroid liblog
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_NDK_STL_VARIANT := c++_shared
--include cts/error_prone_rules_tests.mk
-include $(BUILD_SHARED_LIBRARY)
-
-#
-# CtsSensorTestCases package
-#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-# include both the 32 and 64 bit versions
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util-axt \
-    ctstestrunner-axt \
-    cts-sensors-tests \
-
-LOCAL_JNI_SHARED_LIBRARIES := libcts-sensors-ndk-jni
-
-LOCAL_PACKAGE_NAME := CtsSensorTestCases
-
-LOCAL_SDK_VERSION := test_current
-
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
-
-LOCAL_NDK_STL_VARIANT := c++_shared
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/sensor/src/android/hardware/cts/SensorTest.java b/tests/sensor/src/android/hardware/cts/SensorTest.java
index f48ed77..8b2e234 100644
--- a/tests/sensor/src/android/hardware/cts/SensorTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorTest.java
@@ -87,7 +87,8 @@
 
         mAndroidSensorList = new ArrayList<>();
         for (Sensor s : mSensorList) {
-            if (s.getType() < Sensor.TYPE_DEVICE_PRIVATE_BASE) {
+            if (s.getType() < Sensor.TYPE_DEVICE_PRIVATE_BASE &&
+                    (!context.getPackageManager().isInstantApp() || s.getType() != Sensor.TYPE_HEART_RATE)) {
                 mAndroidSensorList.add(s);
             }
         }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
index f2b551c..ddb6e8b 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
@@ -31,4 +31,16 @@
   test_package.LongEnum[] shouldContainTwoLongFoos;
   @nullable String[] g;
   @nullable test_package.SimpleUnion u;
+  int shouldSetBit0AndBit2;
+  @nullable test_package.SimpleUnion shouldBeConstS1;
+  const int kZero = 0;
+  const int kOne = 1;
+  const int kOnes = -1;
+  const byte kByteOne = 1;
+  const long kLongOnes = -1;
+  const String kEmpty = "";
+  const String kFoo = "foo";
+  const int BIT0 = 1;
+  const int BIT1 = 2;
+  const int BIT2 = 4;
 }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
index 7c1564a..e7fc95c 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
@@ -23,4 +23,12 @@
   test_package.ByteEnum d;
   test_package.ByteEnum[] e;
   @nullable test_package.Bar f;
+  const int kZero = 0;
+  const int kOne = 1;
+  const int kOnes = -1;
+  const byte kByteOne = 1;
+  const long kLongOnes = -1;
+  const String kEmpty = "";
+  const String kFoo = "foo";
+  const String S1 = "a string constant";
 }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
index 85e5599..e346e2b 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
@@ -202,7 +202,7 @@
   EXPECT_EQ(getuid(), res);
 }
 
-TEST_P(NdkBinderTest_Aidl, Constants) {
+TEST_P(NdkBinderTest_Aidl, ConstantsInInterface) {
   ASSERT_EQ(0, ITest::kZero);
   ASSERT_EQ(1, ITest::kOne);
   ASSERT_EQ(0xffffffff, ITest::kOnes);
@@ -212,6 +212,26 @@
   ASSERT_EQ(std::string("foo"), ITest::kFoo);
 }
 
+TEST_P(NdkBinderTest_Aidl, ConstantsInParcelable) {
+  ASSERT_EQ(0, Foo::kZero);
+  ASSERT_EQ(1, Foo::kOne);
+  ASSERT_EQ(0xffffffff, Foo::kOnes);
+  ASSERT_EQ(1, Foo::kByteOne);
+  ASSERT_EQ(0xffffffffffffffff, Foo::kLongOnes);
+  ASSERT_EQ(std::string(""), Foo::kEmpty);
+  ASSERT_EQ(std::string("foo"), Foo::kFoo);
+}
+
+TEST_P(NdkBinderTest_Aidl, ConstantsInUnion) {
+  ASSERT_EQ(0, SimpleUnion::kZero);
+  ASSERT_EQ(1, SimpleUnion::kOne);
+  ASSERT_EQ(0xffffffff, SimpleUnion::kOnes);
+  ASSERT_EQ(1, SimpleUnion::kByteOne);
+  ASSERT_EQ(0xffffffffffffffff, SimpleUnion::kLongOnes);
+  ASSERT_EQ(std::string(""), SimpleUnion::kEmpty);
+  ASSERT_EQ(std::string("foo"), SimpleUnion::kFoo);
+}
+
 TEST_P(NdkBinderTest_Aidl, RepeatPrimitiveInt) {
   int32_t out;
   ASSERT_OK(iface->RepeatInt(3, &out));
@@ -545,6 +565,8 @@
   foo.shouldContainTwoIntFoos = {IntEnum::FOO, IntEnum::FOO};
   foo.shouldContainTwoLongFoos = {LongEnum::FOO, LongEnum::FOO};
   foo.u = SimpleUnion::make<SimpleUnion::c>("hello");
+  foo.shouldSetBit0AndBit2 = Foo::BIT0 | Foo::BIT2;
+  foo.shouldBeConstS1 = SimpleUnion::S1;
 
   Foo retFoo;
 
@@ -561,6 +583,8 @@
   EXPECT_EQ(foo.shouldContainTwoIntFoos, retFoo.shouldContainTwoIntFoos);
   EXPECT_EQ(foo.shouldContainTwoLongFoos, retFoo.shouldContainTwoLongFoos);
   EXPECT_EQ(foo.u, retFoo.u);
+  EXPECT_EQ(foo.shouldSetBit0AndBit2, retFoo.shouldSetBit0AndBit2);
+  EXPECT_EQ(foo.shouldBeConstS1, retFoo.shouldBeConstS1);
 }
 
 TEST_P(NdkBinderTest_Aidl, RepeatGenericBar) {
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
index 9ecb97e..e6b6285 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
@@ -7,6 +7,14 @@
 import test_package.SimpleUnion;
 
 parcelable Foo {
+    const int kZero = 0;
+    const int kOne = 1;
+    const int kOnes = 0xffffffff;
+    const byte kByteOne = 1;
+    const long kLongOnes = 0xffffffffffffffff;
+    const String kEmpty = "";
+    const String kFoo = "foo";
+
     String a="FOO";
     int b=42;
     float c=3.14f;
@@ -21,4 +29,13 @@
     LongEnum[] shouldContainTwoLongFoos;
     @nullable String[] g;
     @nullable SimpleUnion u;
+
+    // example: using int constants
+    const int BIT0 = 0x1;
+    const int BIT1 = 0x1 << 1;
+    const int BIT2 = 0x1 << 2;
+    int shouldSetBit0AndBit2;
+
+    // example: using a String constant of union
+    @nullable SimpleUnion shouldBeConstS1;
 }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl
index 5fa906a..7bd33d4 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl
@@ -4,10 +4,20 @@
 import test_package.ByteEnum;
 
 union SimpleUnion {
+    const int kZero = 0;
+    const int kOne = 1;
+    const int kOnes = 0xffffffff;
+    const byte kByteOne = 1;
+    const long kLongOnes = 0xffffffffffffffff;
+    const String kEmpty = "";
+    const String kFoo = "foo";
+
     int a = 42;
     int[] b;
     String c;
     ByteEnum d;
     ByteEnum[] e;
     @nullable Bar f;
+
+    const String S1 = "a string constant";
 }
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
index 79f231a..2d57d82 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
@@ -607,6 +607,9 @@
 
         foo.u = SimpleUnion.e(new byte[]{ByteEnum.FOO, ByteEnum.FOO});
 
+        foo.shouldSetBit0AndBit2 = Foo.BIT0 | Foo.BIT2;
+        foo.shouldBeConstS1 = SimpleUnion.c(SimpleUnion.S1);
+
         Foo repeatedFoo = mInterface.repeatFoo(foo);
 
         assertEquals(foo.a, repeatedFoo.a);
@@ -620,6 +623,8 @@
         Assert.assertArrayEquals(foo.shouldContainTwoIntFoos, repeatedFoo.shouldContainTwoIntFoos);
         Assert.assertArrayEquals(foo.shouldContainTwoLongFoos, repeatedFoo.shouldContainTwoLongFoos);
         Assert.assertArrayEquals(foo.u.getE(), repeatedFoo.u.getE());
+        assertEquals(foo.shouldSetBit0AndBit2, repeatedFoo.shouldSetBit0AndBit2);
+        assertEquals(foo.shouldBeConstS1.getC(), repeatedFoo.shouldBeConstS1.getC());
     }
 
     @Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
index 371a637..55a7ac1 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
@@ -32,6 +32,7 @@
 import android.graphics.Canvas;
 import android.graphics.ColorSpace;
 import android.graphics.Rect;
+import android.media.MediaFormat;
 import android.os.ParcelFileDescriptor;
 
 import androidx.test.InstrumentationRegistry;
@@ -40,6 +41,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.BitmapUtils;
+import com.android.compatibility.common.util.MediaUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -641,6 +643,10 @@
 
     @Test
     public void testHeif() throws IOException {
+        if (!MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            // HEIF support is optional when HEVC decoder is not supported.
+            return;
+        }
         InputStream is = obtainInputStream(R.raw.heifwriter_input);
         BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
         Bitmap region = decoder.decodeRegion(new Rect(0, 0, TILE_SIZE, TILE_SIZE), null);
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 867f8a3..121af43 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -43,6 +43,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
+import android.media.MediaFormat;
 import android.net.Uri;
 import android.util.DisplayMetrics;
 import android.util.Size;
@@ -53,6 +54,7 @@
 import androidx.test.filters.LargeTest;
 
 import com.android.compatibility.common.util.BitmapUtils;
+import com.android.compatibility.common.util.MediaUtils;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,6 +67,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.concurrent.Callable;
 import java.util.function.IntFunction;
 import java.util.function.Supplier;
@@ -98,19 +102,24 @@
 
     private static final ColorSpace sSRGB = ColorSpace.get(ColorSpace.Named.SRGB);
 
-    static Object[] getRecords() {
-        return new Record[] {
-            new Record(R.drawable.baseline_jpeg, 1280, 960, "image/jpeg", false, false, sSRGB),
-            new Record(R.drawable.grayscale_jpg, 128, 128, "image/jpeg", true, false, sSRGB),
-            new Record(R.drawable.png_test, 640, 480, "image/png", false, false, sSRGB),
-            new Record(R.drawable.gif_test, 320, 240, "image/gif", false, false, sSRGB),
-            new Record(R.drawable.bmp_test, 320, 240, "image/bmp", false, false, sSRGB),
-            new Record(R.drawable.webp_test, 640, 480, "image/webp", false, false, sSRGB),
-            new Record(R.drawable.google_chrome, 256, 256, "image/x-ico", false, true, sSRGB),
-            new Record(R.drawable.color_wheel, 128, 128, "image/x-ico", false, true, sSRGB),
-            new Record(R.raw.sample_1mp, 600, 338, "image/x-adobe-dng", false, false, sSRGB),
-            new Record(R.raw.heifwriter_input, 1920, 1080, "image/heif", false, false, sSRGB),
-        };
+    static Record[] getRecords() {
+        ArrayList<Record> records = new ArrayList<>(Arrays.asList(new Record[] {
+                new Record(R.drawable.baseline_jpeg, 1280, 960, "image/jpeg", false, false, sSRGB),
+                new Record(R.drawable.grayscale_jpg, 128, 128, "image/jpeg", true, false, sSRGB),
+                new Record(R.drawable.png_test, 640, 480, "image/png", false, false, sSRGB),
+                new Record(R.drawable.gif_test, 320, 240, "image/gif", false, false, sSRGB),
+                new Record(R.drawable.bmp_test, 320, 240, "image/bmp", false, false, sSRGB),
+                new Record(R.drawable.webp_test, 640, 480, "image/webp", false, false, sSRGB),
+                new Record(R.drawable.google_chrome, 256, 256, "image/x-ico", false, true, sSRGB),
+                new Record(R.drawable.color_wheel, 128, 128, "image/x-ico", false, true, sSRGB),
+                new Record(R.raw.sample_1mp, 600, 338, "image/x-adobe-dng", false, false, sSRGB)
+        }));
+        if (MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            // HEIF support is optional when HEVC decoder is not supported.
+            records.add(new Record(R.raw.heifwriter_input, 1920, 1080, "image/heif", false, false,
+                                   sSRGB));
+        }
+        return records.toArray(new Record[] {});
     }
 
     // offset is how many bytes to offset the beginning of the image.
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index d457995..1af904f 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -234,7 +234,8 @@
                 assertEquals(1, certificates.length);
 
                 X509Certificate attestationCert = (X509Certificate) certificates[0];
-                assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+                assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID));
+                assertNull(attestationCert.getExtensionValue(Attestation.EAT_OID));
             } finally {
                 keyStore.deleteEntry(keystoreAlias);
             }
@@ -272,7 +273,7 @@
             verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            checkDeviceLocked(new Attestation(attestationCert));
+            checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -402,7 +403,7 @@
                 assertEquals(1, certificates.length);
 
                 X509Certificate attestationCert = (X509Certificate) certificates[0];
-                assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+                assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID));
             } finally {
                 keyStore.deleteEntry(keystoreAlias);
             }
@@ -441,7 +442,7 @@
             verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            checkDeviceLocked(new Attestation(attestationCert));
+            checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -559,12 +560,13 @@
             verifyCertificateChain(certificates, false /* expectStrongBox */);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            Attestation attestation = new Attestation(attestationCert);
+            Attestation attestation = Attestation.loadFromCertificate(attestationCert);
 
-            checkRsaKeyDetails(attestation, keySize, purposes, ImmutableSet.copyOf(paddingModes));
+            checkRsaKeyDetails(attestation, keySize, purposes,
+                ImmutableSet.copyOf(paddingModes));
             checkKeyUsage(attestationCert, purposes);
-            checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
-                    devicePropertiesAttestation, attestation);
+            checkKeyIndependentAttestationInfo(challenge, purposes, startTime,
+                includeValidityDates, devicePropertiesAttestation, attestation);
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -618,12 +620,12 @@
             verifyCertificateChain(certificates, false /* expectStrongBox */);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            Attestation attestation = new Attestation(attestationCert);
+            Attestation attestation = Attestation.loadFromCertificate(attestationCert);
 
             checkEcKeyDetails(attestation, ecCurve, keySize);
             checkKeyUsage(attestationCert, purposes);
-            checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
-                    devicePropertiesAttestation, attestation);
+            checkKeyIndependentAttestationInfo(challenge, purposes, startTime,
+                includeValidityDates, devicePropertiesAttestation, attestation);
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -635,6 +637,7 @@
         int kmVersion = attestation.getKeymasterVersion();
         assertNull(attestation.getTeeEnforced().getAttestationApplicationId());
         aaid = attestation.getSoftwareEnforced().getAttestationApplicationId();
+
         if (kmVersion >= 3) {
             // must be present and correct
             assertNotNull(aaid);
@@ -695,9 +698,11 @@
         checkUnexpectedOids(attestation);
         checkAttestationSecurityLevelDependentParams(attestation);
         assertNotNull(attestation.getAttestationChallenge());
-        assertTrue(Arrays.equals(challenge, attestation.getAttestationChallenge()));
-        assertNotNull(attestation.getUniqueId());
-        assertEquals(0, attestation.getUniqueId().length);
+        assertThat(attestation.getAttestationChallenge(), is(challenge));
+        // In EAT, this is null if not filled in. In ASN.1, this is an array with length 0.
+        if (attestation.getUniqueId() != null) {
+            assertEquals(0, attestation.getUniqueId().length);
+        }
         checkPurposes(attestation, purposes);
         checkDigests(attestation,
                 ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512));
@@ -919,8 +924,8 @@
 
     @SuppressWarnings("unchecked")
     private void checkAttestationSecurityLevelDependentParams(Attestation attestation) {
-        assertThat("Attestation version must be 1, 2, 3, or 4", attestation.getAttestationVersion(),
-               either(is(1)).or(is(2)).or(is(3)).or(is(4)));
+        assertThat("Attestation version must be 1, 2, 3, 4 or 5", attestation.getAttestationVersion(),
+               either(is(1)).or(is(2)).or(is(3)).or(is(4)).or(is(5)));
 
         AuthorizationList teeEnforced = attestation.getTeeEnforced();
         AuthorizationList softwareEnforced = attestation.getSoftwareEnforced();
@@ -997,7 +1002,7 @@
     }
 
     private void checkRootOfTrust(Attestation attestation, boolean requireLocked) {
-        RootOfTrust rootOfTrust = attestation.getTeeEnforced().getRootOfTrust();
+        RootOfTrust rootOfTrust = attestation.getRootOfTrust();
         assertNotNull(rootOfTrust);
         assertNotNull(rootOfTrust.getVerifiedBootKey());
         assertTrue("Verified boot key is only " + rootOfTrust.getVerifiedBootKey().length +
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 24a598a..7b25b26 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -1033,6 +1033,11 @@
     }
 
     public void testGetImageAtIndex() throws Exception {
+        if (!MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            MediaUtils.skipTest("no video decoders for resource");
+            return;
+        }
+
         testGetImage("heifwriter_input.heic", 1920, 1080, 0 /*rotation*/,
                 4 /*imageCount*/, 3 /*primary*/, true /*useGrid*/, true /*checkColor*/);
     }
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index a89ba85..cbcaf24 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3011,6 +3011,12 @@
     <permission android:name="android.permission.DUMP"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- Allows an application to start tracing for InputMethod and WindowManager.
+    <p>Not for use by third-party applications.
+    @hide -->
+    <permission android:name="android.permission.CONTROL_UI_TRACING"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- Allows an application to read the low-level system log files.
     <p>Not for use by third-party applications, because
     Log entries can contain the user's private information. -->
diff --git a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt
index 2a61e12..89bb1da 100644
--- a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt
+++ b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt
@@ -1,6 +1,10 @@
 package android.permission3.cts.usepermission
 
 import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
 import android.os.Bundle
 import android.view.WindowManager
 
@@ -14,5 +18,15 @@
                 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
                 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+
+        registerReceiver(object : BroadcastReceiver() {
+            override fun onReceive(context: Context?, intent: Intent?) {
+                if (intent?.action != RequestPermissionsActivity.ACTION_HIDE_OVERLAY) {
+                    return
+                }
+
+                finish()
+            }
+        }, IntentFilter(RequestPermissionsActivity.ACTION_HIDE_OVERLAY))
     }
 }
\ No newline at end of file
diff --git a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
index 0d29202..54155f6 100644
--- a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
+++ b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
@@ -17,15 +17,29 @@
 package android.permission3.cts.usepermission
 
 import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
 import android.content.Intent
+import android.content.IntentFilter
 import android.os.Bundle
 import android.os.Handler
 
 class RequestPermissionsActivity : Activity() {
+
     var paused = false
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        registerReceiver(object : BroadcastReceiver() {
+            override fun onReceive(context: Context?, intent: Intent?) {
+                if (intent?.action != ACTION_SHOW_OVERLAY) {
+                    return
+                }
+
+                startActivity(Intent(context, OverlayActivity::class.java)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
+            }
+        }, IntentFilter(ACTION_SHOW_OVERLAY))
         Handler(mainLooper).post(this::eventuallyRequestPermission)
     }
 
@@ -34,11 +48,7 @@
      * due to rapid install/uninstall tests do
      */
     private fun eventuallyRequestPermission() {
-        if (paused) {
-            // Grant dialog should be in front at this point
-            startActivity(Intent(this, OverlayActivity::class.java)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
-        } else {
+        if (!paused) {
             val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
             requestPermissions(permissions, 1)
             Handler(mainLooper).postDelayed(this::eventuallyRequestPermission, 200)
@@ -68,4 +78,9 @@
         paused = false
         super.onResume()
     }
+
+    companion object {
+        const val ACTION_SHOW_OVERLAY = "android.permission3.cts.usepermission.ACTION_SHOW_OVERLAY"
+        const val ACTION_HIDE_OVERLAY = "android.permission3.cts.usepermission.ACTION_HIDE_OVERLAY"
+    }
 }
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index 8aedb6d..cb181e3 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -88,108 +88,54 @@
     protected val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
 
     private val platformResources = context.createPackageContext("android", 0).resources
-    private val permissionToLabelResNameMap =
-        if (!packageManager.arePermissionsIndividuallyControlled()) {
-            mapOf(
-                // Contacts
-                android.Manifest.permission.READ_CONTACTS
+    private val permissionToLabelResNameMap = mapOf(
+            // Contacts
+            android.Manifest.permission.READ_CONTACTS
                     to "@android:string/permgrouplab_contacts",
-                android.Manifest.permission.WRITE_CONTACTS
+            android.Manifest.permission.WRITE_CONTACTS
                     to "@android:string/permgrouplab_contacts",
-                // Calendar
-                android.Manifest.permission.READ_CALENDAR
+            // Calendar
+            android.Manifest.permission.READ_CALENDAR
                     to "@android:string/permgrouplab_calendar",
-                android.Manifest.permission.WRITE_CALENDAR
+            android.Manifest.permission.WRITE_CALENDAR
                     to "@android:string/permgrouplab_calendar",
-                // SMS
-                android.Manifest.permission.SEND_SMS to "@android:string/permgrouplab_sms",
-                android.Manifest.permission.RECEIVE_SMS to "@android:string/permgrouplab_sms",
-                android.Manifest.permission.READ_SMS to "@android:string/permgrouplab_sms",
-                android.Manifest.permission.RECEIVE_WAP_PUSH to "@android:string/permgrouplab_sms",
-                android.Manifest.permission.RECEIVE_MMS to "@android:string/permgrouplab_sms",
-                "android.permission.READ_CELL_BROADCASTS" to "@android:string/permgrouplab_sms",
-                // Storage
-                android.Manifest.permission.READ_EXTERNAL_STORAGE
+            // SMS
+            android.Manifest.permission.SEND_SMS to "@android:string/permgrouplab_sms",
+            android.Manifest.permission.RECEIVE_SMS to "@android:string/permgrouplab_sms",
+            android.Manifest.permission.READ_SMS to "@android:string/permgrouplab_sms",
+            android.Manifest.permission.RECEIVE_WAP_PUSH to "@android:string/permgrouplab_sms",
+            android.Manifest.permission.RECEIVE_MMS to "@android:string/permgrouplab_sms",
+            "android.permission.READ_CELL_BROADCASTS" to "@android:string/permgrouplab_sms",
+            // Storage
+            android.Manifest.permission.READ_EXTERNAL_STORAGE
                     to "@android:string/permgrouplab_storage",
-                android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+            android.Manifest.permission.WRITE_EXTERNAL_STORAGE
                     to "@android:string/permgrouplab_storage",
-                // Location
-                android.Manifest.permission.ACCESS_FINE_LOCATION
+            // Location
+            android.Manifest.permission.ACCESS_FINE_LOCATION
                     to "@android:string/permgrouplab_location",
-                android.Manifest.permission.ACCESS_COARSE_LOCATION
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
                     to "@android:string/permgrouplab_location",
-                // Phone
-                android.Manifest.permission.READ_PHONE_STATE
+            // Phone
+            android.Manifest.permission.READ_PHONE_STATE
                     to "@android:string/permgrouplab_phone",
-                android.Manifest.permission.CALL_PHONE to "@android:string/permgrouplab_phone",
-                "android.permission.ACCESS_IMS_CALL_SERVICE"
+            android.Manifest.permission.CALL_PHONE to "@android:string/permgrouplab_phone",
+            "android.permission.ACCESS_IMS_CALL_SERVICE"
                     to "@android:string/permgrouplab_phone",
-                android.Manifest.permission.READ_CALL_LOG to "@android:string/permgrouplab_phone",
-                android.Manifest.permission.WRITE_CALL_LOG to "@android:string/permgrouplab_phone",
-                android.Manifest.permission.ADD_VOICEMAIL to "@android:string/permgrouplab_phone",
-                android.Manifest.permission.USE_SIP to "@android:string/permgrouplab_phone",
-                android.Manifest.permission.PROCESS_OUTGOING_CALLS
+            android.Manifest.permission.READ_CALL_LOG to "@android:string/permgrouplab_phone",
+            android.Manifest.permission.WRITE_CALL_LOG to "@android:string/permgrouplab_phone",
+            android.Manifest.permission.ADD_VOICEMAIL to "@android:string/permgrouplab_phone",
+            android.Manifest.permission.USE_SIP to "@android:string/permgrouplab_phone",
+            android.Manifest.permission.PROCESS_OUTGOING_CALLS
                     to "@android:string/permgrouplab_phone",
-                // Microphone
-                android.Manifest.permission.RECORD_AUDIO
+            // Microphone
+            android.Manifest.permission.RECORD_AUDIO
                     to "@android:string/permgrouplab_microphone",
-                // Camera
-                android.Manifest.permission.CAMERA to "@android:string/permgrouplab_camera",
-                // Body sensors
-                android.Manifest.permission.BODY_SENSORS to "@android:string/permgrouplab_sensors"
-            )
-        } else {
-            mapOf(
-                // Contacts
-                android.Manifest.permission.READ_CONTACTS to "@android:string/permlab_readContacts",
-                android.Manifest.permission.WRITE_CONTACTS
-                    to "@android:string/permlab_writeContacts",
-                // Calendar
-                android.Manifest.permission.READ_CALENDAR
-                    to "@android:string/permgrouplab_calendar",
-                android.Manifest.permission.WRITE_CALENDAR
-                    to "@android:string/permgrouplab_calendar",
-                // SMS
-                android.Manifest.permission.SEND_SMS to "@android:string/permlab_sendSms",
-                android.Manifest.permission.RECEIVE_SMS to "@android:string/permlab_receiveSms",
-                android.Manifest.permission.READ_SMS to "@android:string/permlab_readSms",
-                android.Manifest.permission.RECEIVE_WAP_PUSH
-                    to "@android:string/permlab_receiveWapPush",
-                android.Manifest.permission.RECEIVE_MMS to "@android:string/permlab_receiveMms",
-                "android.permission.READ_CELL_BROADCASTS"
-                    to "@android:string/permlab_readCellBroadcasts",
-                // Storage
-                android.Manifest.permission.READ_EXTERNAL_STORAGE
-                    to "@android:string/permgrouplab_storage",
-                android.Manifest.permission.WRITE_EXTERNAL_STORAGE
-                    to "@android:string/permgrouplab_storage",
-                // Location
-                android.Manifest.permission.ACCESS_FINE_LOCATION
-                    to "@android:string/permgrouplab_location",
-                android.Manifest.permission.ACCESS_COARSE_LOCATION
-                    to "@android:string/permgrouplab_location",
-                // Phone
-                android.Manifest.permission.READ_PHONE_STATE
-                    to "@android:string/permlab_readPhoneState",
-                android.Manifest.permission.CALL_PHONE to "@android:string/permlab_callPhone",
-                "android.permission.ACCESS_IMS_CALL_SERVICE"
-                    to "@android:string/permlab_accessImsCallService",
-                android.Manifest.permission.READ_CALL_LOG to "@android:string/permlab_readCallLog",
-                android.Manifest.permission.WRITE_CALL_LOG
-                    to "@android:string/permlab_writeCallLog",
-                android.Manifest.permission.ADD_VOICEMAIL to "@android:string/permlab_addVoicemail",
-                android.Manifest.permission.USE_SIP to "@android:string/permlab_use_sip",
-                android.Manifest.permission.PROCESS_OUTGOING_CALLS
-                    to "@android:string/permlab_processOutgoingCalls",
-                // Microphone
-                android.Manifest.permission.RECORD_AUDIO
-                    to "@android:string/permgrouplab_microphone",
-                // Camera
-                android.Manifest.permission.CAMERA to "@android:string/permgrouplab_camera",
-                // Body sensors
-                android.Manifest.permission.BODY_SENSORS to "@android:string/permgrouplab_sensors"
-            )
-        }
+            // Camera
+            android.Manifest.permission.CAMERA to "@android:string/permgrouplab_camera",
+            // Body sensors
+            android.Manifest.permission.BODY_SENSORS to "@android:string/permgrouplab_sensors"
+    )
 
     @Before
     @After
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
index f0b1f80..6b8678d 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
@@ -28,6 +28,7 @@
 import androidx.test.runner.AndroidJUnit4
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
+import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,6 +37,12 @@
 
 @RunWith(AndroidJUnit4::class)
 class PermissionReviewTest : BaseUsePermissionTest() {
+
+    @Before
+    fun assumeNotIndividuallyControlled() {
+        Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+    }
+
     @Before
     fun installApp22CalendarOnly() {
         installPackage(APP_APK_PATH_22_CALENDAR_ONLY)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
index a5f8939..5360f1d 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
@@ -17,6 +17,7 @@
 package android.permission3.cts
 
 import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.content.Intent
 import android.content.pm.PackageManager
 import android.support.test.uiautomator.By
 import com.android.compatibility.common.util.SystemUtil
@@ -42,26 +43,46 @@
         assertAppHasPermission(ACCESS_FINE_LOCATION, false)
         requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
 
+        val buttonCenter = waitFindObject(By.text(
+                getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT))).visibleCenter
+
         // Wait for overlay to hide the dialog
+        context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY))
         waitFindObject(By.res("android.permission3.cts.usepermission:id/overlay_description"))
+
         try {
             // Try to grant the permission, this should fail
             SystemUtil.eventually({
                 if (packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
                         PackageManager.PERMISSION_DENIED) {
-                    clickPermissionRequestAllowForegroundButton(100)
+                    uiDevice.click(buttonCenter.x, buttonCenter.y)
+                    Thread.sleep(100)
                 }
                 assertAppHasPermission(ACCESS_FINE_LOCATION, true)
             }, 10000)
         } catch (e: RuntimeException) {
             // expected
         }
-        // Permission should not be granted and dialog should still be showing
+        // Permission should not be granted
         assertAppHasPermission(ACCESS_FINE_LOCATION, false)
 
         // On Automotive the dialog gets closed by the tapjacking activity popping up
         if (!isAutomotive) {
-            clickPermissionRequestAllowForegroundButton()
+            // Verify that clicking the dialog without the overlay still works
+            context.sendBroadcast(Intent(ACTION_HIDE_OVERLAY))
+            SystemUtil.eventually({
+                if (packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
+                        PackageManager.PERMISSION_DENIED) {
+                    uiDevice.click(buttonCenter.x, buttonCenter.y)
+                    Thread.sleep(100)
+                }
+                assertAppHasPermission(ACCESS_FINE_LOCATION, true)
+            }, 10000)
         }
     }
-}
\ No newline at end of file
+
+    companion object {
+        const val ACTION_SHOW_OVERLAY = "android.permission3.cts.usepermission.ACTION_SHOW_OVERLAY"
+        const val ACTION_HIDE_OVERLAY = "android.permission3.cts.usepermission.ACTION_HIDE_OVERLAY"
+    }
+}
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest22.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest22.kt
old mode 100644
new mode 100755
index 7d06770..8fdddfb
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest22.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest22.kt
@@ -16,6 +16,7 @@
 
 package android.permission3.cts
 
+import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 
@@ -23,8 +24,11 @@
  * Runtime permission behavior tests for apps targeting API 22.
  */
 class PermissionTest22 : BaseUsePermissionTest() {
+
     @Before
     fun installApp22AndApprovePermissionReview() {
+        Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
         installPackage(APP_APK_PATH_22)
         approvePermissionReview()
     }
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
index 59cb9aa..31e6f3a 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
@@ -17,6 +17,7 @@
 package android.permission3.cts
 
 import androidx.test.filters.FlakyTest
+import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 
@@ -232,6 +233,8 @@
     @Test(timeout = 120000)
     @FlakyTest
     fun testNoResidualPermissionsOnUninstall() {
+        Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
         // Grant all permissions
         grantAppPermissions(
             android.Manifest.permission.WRITE_CALENDAR,
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionUpgradeTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionUpgradeTest.kt
index fcfc1bb..de58d43 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionUpgradeTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionUpgradeTest.kt
@@ -16,6 +16,7 @@
 
 package android.permission3.cts
 
+import org.junit.Assume
 import org.junit.Test
 
 /**
@@ -25,6 +26,8 @@
 
     @Test
     fun testUpgradeKeepsPermissions() {
+        Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
         installPackage(APP_APK_PATH_22)
 
         approvePermissionReview()
@@ -80,6 +83,8 @@
 
     @Test
     fun testRevokePropagatedOnUpgradeOldToNewModel() {
+        Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
         installPackage(APP_APK_PATH_22)
 
         approvePermissionReview()
diff --git a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index a7281fe..6ec352c 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -12,7 +12,6 @@
 import java.util.Collections;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
-import org.junit.Assert;
 
 abstract class SELinuxTargetSdkTestBase extends AndroidTestCase
 {
@@ -20,8 +19,6 @@
         System.loadLibrary("ctsselinux_jni");
     }
 
-    static final byte[] ANONYMIZED_HARDWARE_ADDRESS = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
     protected static String getFile(String filename) throws IOException {
         BufferedReader in = null;
         try {
@@ -62,20 +59,36 @@
         }
     }
 
-    protected static void noNetlinkRouteGetlink() throws IOException {
-        assertEquals(
-                "RTM_GETLINK is not allowed on netlink route sockets. Verify that the"
-                    + " following patch has been applied to your kernel: "
-                    + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
-                13,
-                checkNetlinkRouteGetlink());
+    protected static void checkNetlinkRouteGetlink(boolean expectAllowed) throws IOException {
+        if (!expectAllowed) {
+            assertEquals(
+                    "RTM_GETLINK is not allowed on a netlink route sockets. Verify that the"
+                        + " following patch has been applied to your kernel: "
+                        + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
+                    13,
+                    checkNetlinkRouteGetlink());
+        } else {
+            assertEquals(
+                    "RTM_GETLINK should be allowed netlink route sockets for apps with "
+                            + "targetSdkVersion <= Q",
+                    -1,
+                    checkNetlinkRouteGetlink());
+        }
     }
 
-    protected static void noNetlinkRouteBind() throws IOException {
-        assertEquals(
-                "bind() is not allowed on netlink route sockets",
-                13,
-                checkNetlinkRouteBind());
+    protected static void checkNetlinkRouteBind(boolean expectAllowed) throws IOException {
+        if (!expectAllowed) {
+            assertEquals(
+                    "Bind() is not allowed on a netlink route sockets",
+                    13,
+                    checkNetlinkRouteBind());
+        } else {
+            assertEquals(
+                    "Bind() should succeed for netlink route sockets for apps with "
+                            + "targetSdkVersion <= Q",
+                    -1,
+                    checkNetlinkRouteBind());
+        }
     }
 
     /**
@@ -156,17 +169,16 @@
     }
 
     /**
-     * Verify that apps having targetSdkVersion <= 29 get an anonymized MAC
-     * address (02:00:00:00:00:00) instead of a null MAC for ethernet interfaces.
+     * Verify that apps having targetSdkVersion <= 29 are able to see MAC
+     * addresses of ethernet devices.
      * The counterpart of this test (testing for targetSdkVersion > 29) is
      * {@link libcore.java.net.NetworkInterfaceTest#testGetHardwareAddress_returnsNull()}.
      */
-    protected static void checkNetworkInterface_returnsAnonymizedHardwareAddresses()
-        throws Exception {
+    protected static void checkNetworkInterface_returnsHardwareAddresses() throws Exception {
         assertNotNull(NetworkInterface.getNetworkInterfaces());
         for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) {
             if (isEthernet(nif.getName())) {
-                Assert.assertArrayEquals(ANONYMIZED_HARDWARE_ADDRESS, nif.getHardwareAddress());
+                assertEquals(6, nif.getHardwareAddress().length);
             }
         }
     }
diff --git a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
index 9153b8a..1ed366e 100644
--- a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
@@ -79,10 +79,10 @@
     }
 
     public void testNoNetlinkRouteGetlink() throws IOException {
-        noNetlinkRouteGetlink();
+        checkNetlinkRouteGetlink(false);
     }
 
     public void testNoNetlinkRouteBind() throws IOException {
-        noNetlinkRouteBind();
+        checkNetlinkRouteBind(false);
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
index 5e2f75d..a784464 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
@@ -66,6 +66,6 @@
     }
 
     public void testNetworkInterface() throws Exception {
-        checkNetworkInterface_returnsAnonymizedHardwareAddresses();
+        checkNetworkInterface_returnsHardwareAddresses();
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
index 29b68db..880ae1a 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
@@ -66,6 +66,6 @@
     }
 
     public void testNetworkInterface() throws Exception {
-        checkNetworkInterface_returnsAnonymizedHardwareAddresses();
+        checkNetworkInterface_returnsHardwareAddresses();
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
index 76fc772..1f1eaaa 100644
--- a/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
@@ -43,12 +43,12 @@
         }
     }
 
-    public void testNoNetlinkRouteGetlink() throws IOException {
-        noNetlinkRouteGetlink();
+    public void testNetlinkRouteGetlinkSucceeds() throws IOException {
+        checkNetlinkRouteGetlink(true);
     }
 
-    public void testNoNetlinkRouteBind() throws IOException {
-        noNetlinkRouteBind();
+    public void testNetlinkRouteBindSucceeds() throws IOException {
+        checkNetlinkRouteBind(true);
     }
 
     public void testCanNotExecuteFromHomeDir() throws Exception {
@@ -86,6 +86,6 @@
     }
 
     public void testNetworkInterface() throws Exception {
-        checkNetworkInterface_returnsAnonymizedHardwareAddresses();
+        checkNetworkInterface_returnsHardwareAddresses();
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
index 859864e..05fad31 100644
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
@@ -34,11 +34,11 @@
     }
 
     public void testNoNetlinkRouteGetlink() throws IOException {
-        noNetlinkRouteGetlink();
+        checkNetlinkRouteGetlink(false);
     }
 
     public void testNoNetlinkRouteBind() throws IOException {
-        noNetlinkRouteBind();
+        checkNetlinkRouteBind(false);
     }
 
     public void testCanNotExecuteFromHomeDir() throws Exception {
diff --git a/tests/tests/telephony/current/Android.bp b/tests/tests/telephony/current/Android.bp
index 046587c..8bd31d9 100644
--- a/tests/tests/telephony/current/Android.bp
+++ b/tests/tests/telephony/current/Android.bp
@@ -21,6 +21,8 @@
         "src/android/telephony/ims/cts/TestImsSmsImpl.java",
         "src/android/telephony/ims/cts/TestImsConfig.java",
         "src/android/telephony/ims/cts/TestSipTransport.java",
+        "src/android/telephony/ims/cts/TestSipDelegate.java",
+        "src/android/telephony/ims/cts/TestSipDelegateConnection.java",
         "src/android/telephony/ims/cts/ImsUtils.java",
     ],
     path: "src/",
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index da70f25..41d7070 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -58,6 +58,7 @@
 import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
+import android.telephony.DataThrottlingRequest;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.PinResult;
@@ -67,6 +68,7 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ThermalMitigationRequest;
 import android.telephony.UiccCardInfo;
 import android.telephony.UiccSlotInfo;
 import android.telephony.data.ApnSetting;
@@ -1074,6 +1076,14 @@
         // succeeds, so just make sure nothing crashes.
         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
                 tp -> tp.setSystemSelectionChannels(Collections.emptyList()));
+
+        // getSystemSelectionChannels was added in IRadio 1.6, so ensure it returns
+        // the value that was set by setSystemSelectionChannels.
+        if (mRadioVersion >= RADIO_HAL_VERSION_1_6) {
+            assertEquals(Collections.emptyList(),
+                    ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                    TelephonyManager::getSystemSelectionChannels));
+        }
     }
 
     @Test
@@ -3138,6 +3148,115 @@
         }
     }
 
+    @Test
+    public void testSendThermalMitigationRequest() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        long arbitraryCompletionWindowSecs = 1L;
+
+
+        // Test a proper data throttling thermal mitigation request.
+        int thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
+                        new ThermalMitigationRequest.Builder()
+                                .setThermalMitigationAction(ThermalMitigationRequest
+                                        .THERMAL_MITIGATION_ACTION_DATA_THROTTLING)
+                                .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+                                        .setDataThrottlingAction(DataThrottlingRequest
+                                                .DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER)
+                                        .setCompletionDurationMillis(arbitraryCompletionWindowSecs)
+                                .build())
+                        .build()));
+        // Only verify the result for supported devices on IRadio 1.6+
+        if (mRadioVersion >= RADIO_HAL_VERSION_1_6) {
+            assertEquals(thermalMitigationResult,
+                    TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS);
+        }
+        // Test negative completionDurationSecs is an invalid parameter.
+        try {
+            thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
+                            new ThermalMitigationRequest.Builder()
+                                    .setThermalMitigationAction(ThermalMitigationRequest
+                                            .THERMAL_MITIGATION_ACTION_DATA_THROTTLING)
+                                    .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+                                            .setDataThrottlingAction(DataThrottlingRequest
+                                                    .DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER
+                                            )
+                                            .setCompletionDurationMillis(-1)
+                                            .build())
+                                    .build()));
+        } catch (IllegalArgumentException e) {
+        }
+
+        // Test non-zero completionDurationSecs is an invalid parameter for data throttling hold.
+        try {
+            thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
+                            new ThermalMitigationRequest.Builder()
+                                    .setThermalMitigationAction(ThermalMitigationRequest
+                                            .THERMAL_MITIGATION_ACTION_DATA_THROTTLING)
+                                    .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+                                            .setDataThrottlingAction(
+                                                    DataThrottlingRequest
+                                                            .DATA_THROTTLING_ACTION_HOLD)
+                                            .setCompletionDurationMillis(
+                                                    arbitraryCompletionWindowSecs)
+                                            .build())
+                                    .build()));
+        } catch (IllegalArgumentException e) {
+        }
+
+        // Test null DataThrottlingParams is an invalid parameter for data throttling request.
+        try {
+            thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
+                            new ThermalMitigationRequest.Builder()
+                                    .setThermalMitigationAction(ThermalMitigationRequest
+                                            .THERMAL_MITIGATION_ACTION_DATA_THROTTLING)
+                                    .build()));
+        } catch (IllegalArgumentException e) {
+        }
+
+        // Test non-null DataThrottlingParams is an invalid parameter for voice only request.
+        try {
+            thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
+                            new ThermalMitigationRequest.Builder()
+                                    .setThermalMitigationAction(
+                                            ThermalMitigationRequest
+                                                    .THERMAL_MITIGATION_ACTION_VOICE_ONLY)
+                                    .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+                                            .setDataThrottlingAction(
+                                                    DataThrottlingRequest
+                                                    .DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER
+                                            )
+                                            .setCompletionDurationMillis(-1)
+                                            .build())
+                            .build()));
+        } catch (IllegalArgumentException e) {
+        }
+
+        // Test non-null DataThrottlingParams is an invalid parameter for radio off request.
+        try {
+            thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
+                            new ThermalMitigationRequest.Builder()
+                                    .setThermalMitigationAction(
+                                            ThermalMitigationRequest
+                                                    .THERMAL_MITIGATION_ACTION_RADIO_OFF)
+                                    .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+                                            .setDataThrottlingAction(DataThrottlingRequest
+                                                    .DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER
+                                            )
+                                            .setCompletionDurationMillis(-1)
+                                            .build())
+                            .build()));
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
     /**
      * Validate Emergency Number address that only contains the dialable character.
      *
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 d5ec9e0..6c207c4 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
@@ -56,6 +56,7 @@
     private static final String COMMAND_BASE = "cmd phone ";
     private static final String COMMAND_SET_IMS_SERVICE = "ims set-ims-service ";
     private static final String COMMAND_GET_IMS_SERVICE = "ims get-ims-service ";
+    private static final String COMMAND_CLEAR_SERVICE_OVERRIDE = "ims clear-ims-service-override";
     private static final String COMMAND_CARRIER_SERVICE_IDENTIFIER = "-c ";
     private static final String COMMAND_DEVICE_SERVICE_IDENTIFIER = "-d ";
     private static final String COMMAND_SLOT_IDENTIFIER = "-s ";
@@ -140,6 +141,7 @@
         }
 
         boolean overrideService(ImsFeatureConfiguration config) throws Exception {
+            mIsServiceOverridden = true;
             switch (mConnectionType) {
                 case CONNECTION_TYPE_IMS_SERVICE_CARRIER: {
                     return bindCarrierImsService(config, PACKAGE_NAME);
@@ -148,8 +150,7 @@
                     return bindDeviceImsService(config, EXTERNAL_PACKAGE_NAME);
                 }
                 case CONNECTION_TYPE_DEFAULT_SMS_APP: {
-                    setDefaultSmsApp(PACKAGE_NAME);
-                    break;
+                    return setDefaultSmsApp(PACKAGE_NAME);
                 }
             }
             return false;
@@ -159,6 +160,7 @@
             if (!mIsServiceOverridden) {
                 return;
             }
+            mIsServiceOverridden = false;
 
             if (mOrigRcsServicePackage == null) {
                 mOrigRcsServicePackage = "";
@@ -170,8 +172,7 @@
 
             switch (mConnectionType) {
                 case CONNECTION_TYPE_IMS_SERVICE_CARRIER: {
-                    setCarrierImsService(mOrigMmTelServicePackage, ImsFeature.FEATURE_MMTEL);
-                    setCarrierImsService(mOrigRcsServicePackage, ImsFeature.FEATURE_RCS);
+                    clearCarrierImsServiceOverride();
                     break;
                 }
                 case CONNECTION_TYPE_IMS_SERVICE_DEVICE: {
@@ -255,7 +256,8 @@
             mFeatureTypeToPackageOverrideMap.put(ImsFeature.FEATURE_RCS, packageName);
             String result = TelephonyUtils.executeShellCommand(mInstrumentation,
                     constructSetImsServiceOverrideCommand(true, packageName, new int[] {
-                            ImsFeature.FEATURE_MMTEL, ImsFeature.FEATURE_RCS}));
+                            ImsFeature.FEATURE_EMERGENCY_MMTEL, ImsFeature.FEATURE_MMTEL,
+                            ImsFeature.FEATURE_RCS}));
             if (ImsUtils.VDBG) {
                 Log.d(TAG, "setCarrierMmTelImsService result: " + result);
             }
@@ -284,7 +286,19 @@
             return "true".equals(result);
         }
 
-        private void setDefaultSmsApp(String packageName) throws Exception {
+        private boolean clearCarrierImsServiceOverride() throws Exception {
+            String result = TelephonyUtils.executeShellCommand(mInstrumentation,
+                    constructClearCarrierImsServiceOverrideCommand());
+            if (ImsUtils.VDBG) {
+                Log.d(TAG, "clearCarrierImsServiceOverride result: " + result);
+            }
+            return "true".equals(result);
+        }
+
+        private boolean setDefaultSmsApp(String packageName) throws Exception {
+            if (packageName == null) {
+                return false;
+            }
             RoleManager roleManager = mInstrumentation.getContext()
                     .getSystemService(RoleManager.class);
             Boolean result;
@@ -298,6 +312,7 @@
             if (ImsUtils.VDBG) {
                 Log.d(TAG, "setDefaultSmsApp result: " + result);
             }
+            return result;
         }
 
         private String getDefaultSmsApp() throws Exception {
@@ -378,6 +393,11 @@
                     + COMMAND_FEATURE_IDENTIFIER + featureType;
         }
 
+        private String constructClearCarrierImsServiceOverrideCommand() {
+            return COMMAND_BASE + COMMAND_CLEAR_SERVICE_OVERRIDE + COMMAND_SLOT_IDENTIFIER
+                    + mSlotId;
+        }
+
         private String getFeatureTypesString(int[] featureTypes) {
             if (featureTypes.length == 0) return "";
             StringBuilder builder = new StringBuilder();
@@ -459,8 +479,12 @@
         return mDeviceServiceConnection.overrideService(config);
     }
 
-    void setDefaultSmsApp() throws Exception {
-        mDefaultSmsAppConnection.overrideService(null);
+    boolean setDefaultSmsApp() throws Exception {
+        return mDefaultSmsAppConnection.overrideService(null);
+    }
+
+    void restoreDefaultSmsApp() throws Exception {
+        mDefaultSmsAppConnection.restoreOriginalPackage();
     }
 
     void disconnectCarrierImsService() throws Exception {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
index 2c09de8..e691e51 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
@@ -24,6 +24,7 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.SipMessage;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -43,6 +44,9 @@
     public static final int ITEM_NON_COMPRESSED = 2000;
     // Id for compressed auto configuration xml.
     public static final int ITEM_COMPRESSED = 2001;
+    // TODO Replace with a real sip message once that logic is in.
+    public static final String TEST_TRANSACTION_ID = "z9hG4bK.TeSt";
+    public static final SipMessage TEST_SIP_MESSAGE = new SipMessage("A", "B", new byte[0]);
 
     public static boolean shouldTestTelephony() {
         final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext()
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java
index cb02a0c..3f786fa 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java
@@ -19,6 +19,7 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
@@ -26,16 +27,23 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.util.ArraySet;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -49,20 +57,27 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * CTS tests for {@link SipDelegateManager} API.
  */
 @RunWith(AndroidJUnit4.class)
 public class SipDelegateManagerTest {
-
-    private static int sTestSlot = 0;
-    private static int sTestSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private static ImsServiceConnector sServiceConnector;
-    private static CarrierConfigReceiver sReceiver;
+    private static final String MMTEL_TAG =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
+    private static final String ONE_TO_ONE_CHAT_TAG =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.oma.cpm.msg\"";
+    private static final String GROUP_CHAT_TAG =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.oma.cpm.session\"";
+    private static final String FILE_TRANSFER_HTTP_TAG =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.fthttp\"";
 
     private static class CarrierConfigReceiver extends BroadcastReceiver {
         private CountDownLatch mLatch = new CountDownLatch(1);
@@ -91,6 +106,11 @@
         }
     }
 
+    private static int sTestSlot = 0;
+    private static int sTestSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private static ImsServiceConnector sServiceConnector;
+    private static CarrierConfigReceiver sReceiver;
+
     @BeforeClass
     public static void beforeAllTests() throws Exception {
         // First, only populate test slot/sub
@@ -179,6 +199,7 @@
         // is enabled.
         if (sServiceConnector != null) {
             sServiceConnector.disconnectCarrierImsService();
+            sServiceConnector.restoreDefaultSmsApp();
         }
     }
 
@@ -196,6 +217,14 @@
         } catch (SecurityException e) {
             //expected
         }
+        DelegateRequest d = new DelegateRequest(Collections.emptySet());
+        TestSipDelegateConnection c = new TestSipDelegateConnection(d);
+        try {
+            manager.createSipDelegate(d, Runnable::run, c, c);
+            fail("createSipDelegate requires MODIFY_PHONE_STATE");
+        } catch (SecurityException e) {
+            //expected
+        }
     }
 
     @Test
@@ -354,7 +383,7 @@
         overrideCarrierConfig(b);
 
         assertTrue(sServiceConnector.connectCarrierImsServiceLocally());
-        // NoytImplemented/capable
+        // Not Implemented/capable
         ImsFeatureConfiguration c = getConfigForMmTelAndRcs();
         assertTrue(sServiceConnector.triggerFrameworkConnectionToCarrierImsService(c));
         verifyImsServiceState(c);
@@ -368,6 +397,506 @@
                 + "set as capable in ImsService#getImsServiceCapabilities", result);
     }
 
+    @Test
+    public void testCreateDestroyDelegateNotDefaultMessagingApp() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        connectTestImsServiceWithSipTransportAndConfig();
+
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+
+        // wait for onCreated and registration state change to be called.
+        createSipDelegateConnectionNoDelegateExpected(manager, delegateConn, transportImpl);
+
+        // TODO deal with this case better when we can filter messages.
+        delegateConn.sendMessageAndVerifyFailure(ImsUtils.TEST_SIP_MESSAGE,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+
+        destroySipDelegateConnectionNoDelegate(manager, delegateConn);
+    }
+
+    @Test
+    public void testCreateDelegateBasicUseCases() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        connectTestImsServiceWithSipTransportAndConfig();
+
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+
+        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
+                transportImpl, Collections.emptySet(), 0);
+        assertNotNull(delegate);
+
+        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
+                .build();
+        verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
+                Collections.emptySet(), c);
+
+        sendMessageAndVerifyAck(delegateConn, delegate);
+        receiveMessageAndVerifyAck(delegateConn, delegate);
+
+        destroySipDelegateAndVerify(manager, transportImpl, delegateConn, delegate,
+                request.getFeatureTags());
+        assertEquals("There should be no more delegates", 0,
+                transportImpl.getDelegates().size());
+    }
+
+    @Test
+    public void testDelegateRegistrationChanges() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        connectTestImsServiceWithSipTransportAndConfig();
+
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+
+        // Construct registered tags and denied tags, vendor denied FT tag.
+        Set<String> registeredTags = new ArraySet<>(request.getFeatureTags());
+        registeredTags.remove(FILE_TRANSFER_HTTP_TAG);
+        Set<FeatureTagState> deniedTags = new ArraySet<>(1);
+        deniedTags.add(new FeatureTagState(FILE_TRANSFER_HTTP_TAG,
+                SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
+                transportImpl, deniedTags, 0);
+        assertNotNull(delegate);
+
+        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
+                .build();
+        verifyRegisteredAndSendSipConfig(delegateConn, delegate, registeredTags, deniedTags, c);
+
+        // TODO verify messages can be sent on registered tags, but generate error for denied tags.
+
+        // move reg state to deregistering and then deregistered
+        delegateConn.setOperationCountDownLatch(1);
+        DelegateRegistrationState s = getDeregisteringState(registeredTags,
+                DelegateRegistrationState.DEREGISTERING_REASON_PDN_CHANGE);
+        delegate.notifyImsRegistrationUpdate(s);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyRegistrationStateEquals(s);
+
+        delegateConn.setOperationCountDownLatch(1);
+        s = getRegisteredRegistrationState(registeredTags);
+        delegate.notifyImsRegistrationUpdate(s);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyRegistrationStateEquals(s);
+
+        destroySipDelegateAndVerify(manager, transportImpl, delegateConn, delegate,
+                registeredTags);
+        assertEquals("There should be no more delegates", 0,
+                transportImpl.getDelegates().size());
+    }
+
+    @Test
+    public void testCreateMultipleDelegates() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        connectTestImsServiceWithSipTransportAndConfig();
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+
+        DelegateRequest request1 = getChatOnlyRequest();
+        TestSipDelegateConnection delegateConn1 = new TestSipDelegateConnection(request1);
+        Set<String> registeredTags1 = new ArraySet<>(request1.getFeatureTags());
+        TestSipDelegate delegate1 = createSipDelegateConnectionAndVerify(manager, delegateConn1,
+                transportImpl, Collections.emptySet(), 0);
+        assertNotNull(delegate1);
+
+        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
+                .build();
+        verifyRegisteredAndSendSipConfig(delegateConn1, delegate1, registeredTags1,
+                Collections.emptySet(), c);
+
+        // This will only be granted File transfer FT
+        DelegateRequest request2 = getDefaultRequest();
+        TestSipDelegateConnection delegateConn2 = new TestSipDelegateConnection(request2);
+        Set<String> registeredTags2 = new ArraySet<>();
+        registeredTags2.add(FILE_TRANSFER_HTTP_TAG);
+        TestSipDelegate delegate2 = createSipDelegateConnectionAndVerify(manager, delegateConn2,
+                transportImpl, Collections.emptySet(), 1);
+        assertNotNull(delegate2);
+        Set<FeatureTagState> deniedSet = generateDeniedSetFromRequest(request1.getFeatureTags(),
+                request2.getFeatureTags(),
+                SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE);
+        verifyRegisteredAndSendSipConfig(delegateConn2, delegate2, registeredTags2,
+                deniedSet, c);
+
+        // Destroying delegate 1 will transfer all feature tags over to delegate 2
+        delegateConn2.setOperationCountDownLatch(1);
+        destroySipDelegateAndVerify(manager, transportImpl, delegateConn1, delegate1,
+                registeredTags1);
+        delegateConn2.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        // This internally triggers the destruction of the internal delegate2 and then recreation
+        // of another delegate with the new feature set that it supports.
+        verifySipDelegateDestroyed(transportImpl, delegateConn2, delegate2, registeredTags2,
+                DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
+        delegate2 = getSipDelegate(transportImpl, Collections.emptySet(), 0);
+        verifyRegisteredAndSendSipConfig(delegateConn2, delegate2, request2.getFeatureTags(),
+                Collections.emptySet(), c);
+
+        destroySipDelegateAndVerify(manager, transportImpl, delegateConn2, delegate2,
+                request2.getFeatureTags());
+        assertEquals("There should be no more delegates", 0,
+                transportImpl.getDelegates().size());
+    }
+
+    @Test
+    public void testCreateDelegateMessagingAppChangesToApp() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        // start with no features granted
+        connectTestImsServiceWithSipTransportAndConfig();
+
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+
+        // wait for onCreated and registration state change to be called.
+        createSipDelegateConnectionNoDelegateExpected(manager, delegateConn, transportImpl);
+
+        // Make this app the DMA
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        TestSipDelegate delegate = getSipDelegate(transportImpl, Collections.emptySet(), 0);
+        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
+                .build();
+        verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
+                Collections.emptySet(), c);
+        destroySipDelegateAndVerify(manager, transportImpl, delegateConn, delegate,
+                request.getFeatureTags());
+        assertEquals("There should be no more delegates", 0,
+                transportImpl.getDelegates().size());
+    }
+
+    @Test
+    public void testCreateDelegateMessagingAppChangesAwayFromApp() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        // Make this app the DMA
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        connectTestImsServiceWithSipTransportAndConfig();
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
+                transportImpl, Collections.emptySet(), 0);
+        assertNotNull(delegate);
+
+        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
+                .build();
+        verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
+                Collections.emptySet(), c);
+
+
+        // Move DMA to another app, we should receive a registration update.
+        delegateConn.setOperationCountDownLatch(1);
+        sServiceConnector.restoreDefaultSmsApp();
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        // we should get another reg update with all tags denied.
+        delegateConn.setOperationCountDownLatch(1);
+        verifySipDelegateDestroyed(transportImpl, delegateConn, delegate, request.getFeatureTags(),
+                DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyRegistrationStateEmpty();
+        // All requested features should have been denied due to the app not being the default sms
+        // app.
+        delegateConn.verifyAllDenied(SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+        // There should not be any delegates left, as the only delegate should have been cleaned up.
+        assertEquals("SipDelegate should not have any delegates", 0,
+                transportImpl.getDelegates().size());
+
+        destroySipDelegateConnectionNoDelegate(manager, delegateConn);
+    }
+    @Test
+    public void testParcelUnparcelDelegateRequest() {
+        ArraySet<String> testTags = new ArraySet<>();
+        testTags.add(MMTEL_TAG);
+        testTags.add(ONE_TO_ONE_CHAT_TAG);
+        testTags.add(GROUP_CHAT_TAG);
+        testTags.add(FILE_TRANSFER_HTTP_TAG);
+        DelegateRequest r = new DelegateRequest(testTags);
+        Parcel p = Parcel.obtain();
+        r.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        DelegateRequest unparcelled = DelegateRequest.CREATOR.createFromParcel(p);
+        assertEquals(r, unparcelled);
+        assertEquals(r.getFeatureTags(), unparcelled.getFeatureTags());
+    }
+
+    @Test
+    public void testParcelUnparcelFeatureTagState() {
+        FeatureTagState f = new FeatureTagState(MMTEL_TAG,
+                DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED);
+        Parcel p = Parcel.obtain();
+        f.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        FeatureTagState unparcelled = FeatureTagState.CREATOR.createFromParcel(p);
+        assertEquals(f, unparcelled);
+        assertEquals(f.getFeatureTag(), unparcelled.getFeatureTag());
+        assertEquals(f.getState(), unparcelled.getState());
+    }
+
+    @Test
+    public void testParcelUnparcelRegistrationState() {
+        ArraySet<String> regTags = new ArraySet<>();
+        regTags.add(MMTEL_TAG);
+        DelegateRegistrationState s = new DelegateRegistrationState.Builder()
+                .addRegisteredFeatureTags(regTags)
+                .addRegisteredFeatureTag(ONE_TO_ONE_CHAT_TAG)
+                .addDeregisteringFeatureTag(GROUP_CHAT_TAG,
+                        DelegateRegistrationState.DEREGISTERING_REASON_PDN_CHANGE)
+                .addDeregisteredFeatureTag(FILE_TRANSFER_HTTP_TAG,
+                        DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED)
+                .build();
+        Parcel p = Parcel.obtain();
+        s.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        DelegateRegistrationState unparcel = DelegateRegistrationState.CREATOR.createFromParcel(p);
+        assertEquals(s, unparcel);
+        assertEquals(s.getRegisteredFeatureTags(), unparcel.getRegisteredFeatureTags());
+        assertEquals(s.getDeregisteringFeatureTags(), unparcel.getDeregisteringFeatureTags());
+        assertEquals(s.getDeregisteredFeatureTags(), unparcel.getDeregisteredFeatureTags());
+    }
+
+    @Test
+    public void testParcelUnparcelImsConfiguration() {
+        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1 /*version*/)
+                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, true)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, 1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
+                .build();
+        Parcel p = Parcel.obtain();
+        c.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        SipDelegateImsConfiguration unparcel =
+                SipDelegateImsConfiguration.CREATOR.createFromParcel(p);
+        assertEquals(c.getVersion(), unparcel.getVersion());
+        assertEquals(c.getBoolean(
+                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false),
+                unparcel.getBoolean(
+                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false));
+        assertEquals(c.getInt(
+                SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, -1),
+                unparcel.getInt(
+                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
+                        -1));
+        assertEquals(c.getString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING),
+                unparcel.getString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING));
+    }
+
+    @Test
+    public void testParcelUnparcelSipMessage() {
+        byte[] bytes = new byte[1];
+        bytes[0] = 'a';
+        SipMessage m = new SipMessage("A", "B", bytes);
+        Parcel p = Parcel.obtain();
+        m.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        SipMessage unparcel = SipMessage.CREATOR.createFromParcel(p);
+        assertEquals(m, unparcel);
+        assertEquals(m.getStartLine(), unparcel.getStartLine());
+        assertEquals(m.getHeaderSection(), unparcel.getHeaderSection());
+        assertTrue(Arrays.equals(m.getContent(), unparcel.getContent()));
+    }
+
+    private void createSipDelegateConnectionNoDelegateExpected(SipDelegateManager manager,
+            TestSipDelegateConnection conn, TestSipTransport transport) throws Exception {
+        // wait for onCreated and reg state changed
+        conn.setOperationCountDownLatch(2);
+        conn.connect(manager);
+        conn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        conn.verifyDelegateCreated();
+        conn.verifyRegistrationStateEmpty();
+        // All requested features should have been denied due to the app not being the default sms
+        // app.
+        conn.verifyAllDenied(SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+        // There should not have been a call to create a SipDelegate on the service side, since all
+        // features were denied due to permissions issues.
+        assertEquals("SipDelegate should not have been created", 0,
+                transport.getDelegates().size());
+    }
+
+    private void destroySipDelegateConnectionNoDelegate(SipDelegateManager manager,
+            TestSipDelegateConnection delegateConn) throws Exception {
+        delegateConn.setOperationCountDownLatch(1);
+        delegateConn.disconnect(manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+    }
+
+    private void destroySipDelegateAndVerify(SipDelegateManager manager,
+            TestSipTransport transportImpl, TestSipDelegateConnection delegateConn,
+            TestSipDelegate delegate, Set<String> registeredTags) throws Exception {
+        // wait for registration change upon disconnecting state change
+        delegateConn.setOperationCountDownLatch(1);
+        delegateConn.disconnect(manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        // verify we move to deregistering for registered tags.
+        DelegateRegistrationState s = getDeregisteringState(registeredTags,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
+        delegateConn.verifyRegistrationStateEquals(s);
+        // wait for on destroyed
+        delegateConn.setOperationCountDownLatch(1);
+        transportImpl.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+    }
+
+    private void verifySipDelegateDestroyed(TestSipTransport transportImpl,
+            TestSipDelegateConnection delegateConn, TestSipDelegate delegate,
+            Set<String> registeredTags, int deregReason) {
+        // verify we move to deregistering for registered tags.
+        DelegateRegistrationState s = getDeregisteringState(registeredTags, deregReason);
+        delegateConn.verifyRegistrationStateEquals(s);
+        transportImpl.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+    }
+
+    private TestSipDelegate createSipDelegateConnectionAndVerify(SipDelegateManager manager,
+            TestSipDelegateConnection conn, TestSipTransport transport,
+            Set<FeatureTagState>  deniedTags, int delegateIndex) throws Exception {
+        conn.setOperationCountDownLatch(1);
+        conn.connect(manager);
+        TestSipDelegate d = getSipDelegate(transport, deniedTags, delegateIndex);
+        conn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        conn.verifyDelegateCreated();
+        return d;
+    }
+
+    private TestSipDelegate getSipDelegate(TestSipTransport transport,
+            Set<FeatureTagState> deniedTags, int delegateIndex) {
+        transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_CREATE_DELEGATE);
+        // There must have been a call to create a SipDelegate on the service side.
+        assertEquals("SipDelegate should have been created", delegateIndex + 1,
+                transport.getDelegates().size());
+        TestSipDelegate d = transport.getDelegates().get(delegateIndex);
+        d.notifyOnCreated(deniedTags);
+        return d;
+    }
+
+    private void verifyRegisteredAndSendSipConfig(TestSipDelegateConnection delegateConn,
+            TestSipDelegate delegate, Set<String> registeredTags,
+            Set<FeatureTagState> deniedTags, SipDelegateImsConfiguration sipConfig) {
+        // wait for reg change to be called
+        delegateConn.setOperationCountDownLatch(1);
+        DelegateRegistrationState s = getRegisteredRegistrationState(registeredTags);
+        delegate.notifyImsRegistrationUpdate(s);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyRegistrationStateRegistered(registeredTags);
+        delegateConn.verifyDenied(deniedTags);
+
+        // send config change as well.
+        sendConfigChange(sipConfig, delegateConn, delegate);
+    }
+
+    private Set<FeatureTagState> generateDeniedSetFromRequest(Set<String> grantedTags,
+            Set<String> newTags, int reason) {
+        // Deny features from newTags that are already granted in grantedTags.
+        return grantedTags.stream().filter(newTags::contains)
+                .map(s -> new FeatureTagState(s, reason))
+                .collect(Collectors.toSet());
+    }
+
+    private void sendMessageAndVerifyAck(TestSipDelegateConnection delegateConn,
+            TestSipDelegate delegate) throws Exception {
+        // Send a message and ensure it gets received on the other end as well as acked
+        delegateConn.sendMessageAndVerifyCompletedSuccessfully(ImsUtils.TEST_SIP_MESSAGE);
+        delegate.verifyMessageSend(ImsUtils.TEST_SIP_MESSAGE);
+        // send a message and notify connection that it failed
+        delegate.setSendMessageDenyReason(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+        delegateConn.sendMessageAndVerifyFailure(ImsUtils.TEST_SIP_MESSAGE,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+        delegate.verifyMessageSend(ImsUtils.TEST_SIP_MESSAGE);
+    }
+
+    private void receiveMessageAndVerifyAck(TestSipDelegateConnection delegateConn,
+            TestSipDelegate delegate) throws Exception {
+        // Receive a message and ensure it gets received on the other end as well as acked
+        delegate.receiveMessageAndVerifyReceivedCalled(ImsUtils.TEST_SIP_MESSAGE);
+        delegateConn.verifyMessageReceived(ImsUtils.TEST_SIP_MESSAGE);
+        // Receive a message and have connection notify that it didn't complete
+        delegateConn.setReceivedMessageErrorResponseReason(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT);
+        delegate.receiveMessageAndVerifyReceiveErrorCalled(ImsUtils.TEST_SIP_MESSAGE,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT);
+    }
+
+    private void sendConfigChange(SipDelegateImsConfiguration c,
+            TestSipDelegateConnection delegateConn, TestSipDelegate delegate) {
+        delegateConn.setOperationCountDownLatch(1);
+        delegate.notifyImsConfigurationUpdate(c);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyConfigEquals(c);
+    }
+
+    private DelegateRegistrationState getRegisteredRegistrationState(Set<String> registered) {
+        return new DelegateRegistrationState.Builder().addRegisteredFeatureTags(registered).build();
+    }
+
+    private DelegateRegistrationState getDeregisteringState(Set<String> deregisterTags,
+            int reason) {
+        DelegateRegistrationState.Builder b = new DelegateRegistrationState.Builder();
+        for (String t : deregisterTags) {
+            b.addDeregisteringFeatureTag(t, reason);
+        }
+        return b.build();
+    }
+
+    private DelegateRegistrationState getDeregistedState(Set<String> deregisterTags,
+            int reason) {
+        DelegateRegistrationState.Builder b = new DelegateRegistrationState.Builder();
+        for (String t : deregisterTags) {
+            b.addDeregisteredFeatureTag(t, reason);
+        }
+        return b.build();
+    }
+
+    private void connectTestImsServiceWithSipTransportAndConfig() throws Exception {
+        PersistableBundle b = new PersistableBundle();
+        b.putBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+        overrideCarrierConfig(b);
+
+        assertTrue(sServiceConnector.connectCarrierImsServiceLocally());
+        sServiceConnector.getCarrierService().addCapabilities(
+                ImsService.CAPABILITY_SIP_DELEGATE_CREATION);
+        sServiceConnector.getCarrierService().setSipTransportImplemented();
+        ImsFeatureConfiguration c = getConfigForMmTelAndRcs();
+        assertTrue(sServiceConnector.triggerFrameworkConnectionToCarrierImsService(c));
+        verifyImsServiceState(c);
+    }
+
+
     private void connectTestImsServiceWithSipTransport() throws Exception {
         assertTrue(sServiceConnector.connectCarrierImsServiceLocally());
         sServiceConnector.getCarrierService().addCapabilities(
@@ -422,6 +951,21 @@
         return null;
     }
 
+    private DelegateRequest getDefaultRequest() {
+        ArraySet<String> features = new ArraySet<>(3);
+        features.add(TestSipTransport.ONE_TO_ONE_CHAT_TAG);
+        features.add(TestSipTransport.GROUP_CHAT_TAG);
+        features.add(TestSipTransport.FILE_TRANSFER_HTTP_TAG);
+        return new DelegateRequest(features);
+    }
+
+    private DelegateRequest getChatOnlyRequest() {
+        ArraySet<String> features = new ArraySet<>(3);
+        features.add(TestSipTransport.ONE_TO_ONE_CHAT_TAG);
+        features.add(TestSipTransport.GROUP_CHAT_TAG);
+        return new DelegateRequest(features);
+    }
+
     private ImsFeatureConfiguration getConfigForMmTelAndRcs() {
         return new ImsFeatureConfiguration.Builder()
                 .addFeature(sTestSlot, ImsFeature.FEATURE_EMERGENCY_MMTEL)
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 89019b7..029b8c3 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
@@ -342,6 +342,12 @@
         }
     }
 
+    public TestSipTransport getSipTransport() {
+        synchronized (mLock) {
+            return mTestSipTransport;
+        }
+    }
+
     public ImsRegistrationImplBase getImsRegistration() {
         synchronized (mLock) {
             return sImsRegistrationImplBase;
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java
new file mode 100644
index 0000000..3d06dce
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 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.telephony.ims.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class TestSipDelegate implements SipDelegate {
+    private static final String LOG_TAG = "CtsImsSipDelegate";
+
+    public final int subId;
+    public final DelegateRequest delegateRequest;
+    private final DelegateStateCallback mStateCallback;
+    private final DelegateMessageCallback mMessageCallback;
+
+    private final LinkedBlockingQueue<SipMessage> mIncomingMessages = new LinkedBlockingQueue<>();
+    // Pair is <transactionId, error reason>
+    private final LinkedBlockingQueue<Pair<String, Integer>> mReceivedMessageAcks =
+            new LinkedBlockingQueue<>();
+    private int mSendMessageDenyReason = -1;
+
+    public TestSipDelegate(int sub, DelegateRequest request, DelegateStateCallback cb,
+            DelegateMessageCallback mc) {
+        subId = sub;
+        delegateRequest = request;
+        mStateCallback = cb;
+        mMessageCallback = mc;
+    }
+
+    @Override
+    public void sendMessage(@NonNull SipMessage message, long configVersion) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "sendMessage");
+        mIncomingMessages.offer(message);
+        if (mSendMessageDenyReason > -1) {
+            mMessageCallback.onMessageSendFailure(ImsUtils.TEST_TRANSACTION_ID,
+                    mSendMessageDenyReason);
+        } else {
+            mMessageCallback.onMessageSent(ImsUtils.TEST_TRANSACTION_ID);
+        }
+    }
+
+    @Override
+    public void closeDialog(@NonNull String callId) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "closeDialog");
+        // TODO: Test once dialogs are tracked in AOSP.
+    }
+
+    @Override
+    public void notifyMessageReceived(@NonNull String viaTransactionId) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "notifyMessageReceived");
+        mReceivedMessageAcks.offer(new Pair<>(viaTransactionId, -1));
+    }
+
+    @Override
+    public void notifyMessageReceiveError(@NonNull String viaTransactionId, int reason) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "notifyMessageReceiveError");
+        mReceivedMessageAcks.offer(new Pair<>(viaTransactionId, reason));
+    }
+
+    public void verifyMessageSend(SipMessage messageToVerify) throws Exception {
+        SipMessage m = mIncomingMessages.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertEquals(messageToVerify, m);
+    }
+
+    public void setSendMessageDenyReason(int reason) {
+        mSendMessageDenyReason = reason;
+    }
+
+    public void receiveMessageAndVerifyReceivedCalled(SipMessage m) throws Exception {
+        mMessageCallback.onMessageReceived(m);
+        Pair<String, Integer> transactionAndIdPair = mReceivedMessageAcks.poll(
+                ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertEquals(ImsUtils.TEST_TRANSACTION_ID, transactionAndIdPair.first);
+        assertNotNull(transactionAndIdPair.second);
+        assertEquals(-1, transactionAndIdPair.second.intValue());
+    }
+
+    public void receiveMessageAndVerifyReceiveErrorCalled(SipMessage m, int reason)
+            throws Exception {
+        mMessageCallback.onMessageReceived(m);
+        Pair<String, Integer> transactionAndIdPair = mReceivedMessageAcks.poll(
+                ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertEquals(ImsUtils.TEST_TRANSACTION_ID, transactionAndIdPair.first);
+        assertNotNull(transactionAndIdPair.second);
+        assertEquals(reason, transactionAndIdPair.second.intValue());
+    }
+
+    public void notifyImsRegistrationUpdate(DelegateRegistrationState state) {
+        mStateCallback.onFeatureTagRegistrationChanged(state);
+    }
+
+    public void notifyImsConfigurationUpdate(SipDelegateImsConfiguration config) {
+        mStateCallback.onImsConfigurationChanged(config);
+    }
+
+    public void notifyOnCreated(Set<FeatureTagState> deniedTags) {
+        mStateCallback.onCreated(this, deniedTags);
+    }
+
+    public void notifyOnDestroyed(int reason) {
+        mStateCallback.onDestroyed(reason);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java
new file mode 100644
index 0000000..6b3b134
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2020 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.telephony.ims.cts;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.os.PersistableBundle;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+public class TestSipDelegateConnection implements DelegateConnectionStateCallback,
+        DelegateConnectionMessageCallback {
+
+    private interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+
+    private static final String LOG_TAG = "CtsImsSipDelegateC";
+
+    public int destroyReason = -1;
+    public SipDelegateConnection connection;
+    public Set<FeatureTagState> deniedTags;
+    public DelegateRegistrationState regState;
+    public SipDelegateImsConfiguration sipConfig;
+    public final DelegateRequest delegateRequest;
+
+    private int mReceivedMessageErrorResponseReason = -1;
+    private CountDownLatch mLatch;
+    // Pair is <transactionId, error reason>
+    private final LinkedBlockingQueue<SipMessage> mReceivedMessages = new LinkedBlockingQueue<>();
+    private final LinkedBlockingQueue<Pair<String, Integer>> mSentMessageAcks =
+            new LinkedBlockingQueue<>();
+
+    public TestSipDelegateConnection(DelegateRequest request) {
+        delegateRequest = request;
+    }
+
+    public void connect(SipDelegateManager manager) throws Exception {
+        callUntilImsServiceIsAvailableNoReturn(() ->
+                ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+                        manager, (m) -> m.createSipDelegate(delegateRequest, Runnable::run, this,
+                                this), ImsException.class,
+                        "android.permission.MODIFY_PHONE_STATE"));
+    }
+
+    public void disconnect(SipDelegateManager manager, int reason) throws Exception {
+        ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+                manager, (m) -> m.destroySipDelegate(connection, reason),
+                ImsException.class, "android.permission.MODIFY_PHONE_STATE");
+    }
+
+    @Override
+    public void onMessageReceived(@NonNull SipMessage message) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onMessageReceived");
+        mReceivedMessages.offer(message);
+        if (mReceivedMessageErrorResponseReason > -1) {
+            connection.notifyMessageReceiveError(ImsUtils.TEST_TRANSACTION_ID,
+                    mReceivedMessageErrorResponseReason);
+        } else {
+            connection.notifyMessageReceived(ImsUtils.TEST_TRANSACTION_ID);
+        }
+    }
+
+    @Override
+    public void onMessageSent(@NonNull String viaTransactionId) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onMessageSent");
+        mSentMessageAcks.offer(new Pair<>(viaTransactionId, -1));
+    }
+
+    @Override
+    public void onMessageSendFailure(@NonNull String viaTransactionId, int reason) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onMessageSendFailure");
+        mSentMessageAcks.offer(new Pair<>(viaTransactionId, reason));
+    }
+
+    @Override
+    public void onCreated(@NonNull SipDelegateConnection c) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onCreated");
+        connection = c;
+        mLatch.countDown();
+    }
+
+    @Override
+    public void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState,
+            @NonNull Set<FeatureTagState> deniedFeatureTags) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onFeatureTagStatusChanged");
+        regState = registrationState;
+        deniedTags = deniedFeatureTags;
+        mLatch.countDown();
+    }
+
+    @Override
+    public void onImsConfigurationChanged(
+            @NonNull SipDelegateImsConfiguration registeredSipConfig) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onImsConfigurationChanged");
+        sipConfig = registeredSipConfig;
+        mLatch.countDown();
+    }
+
+    @Override
+    public void onDestroyed(int reason) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onDestroyed");
+        connection = null;
+        destroyReason = reason;
+        mLatch.countDown();
+    }
+
+    public void sendMessageAndVerifyCompletedSuccessfully(SipMessage messageToSend)
+            throws Exception {
+        assertNotNull("SipDelegate was null when sending message", connection);
+        connection.sendMessage(messageToSend, sipConfig.getVersion());
+        Pair<String, Integer> ack = mSentMessageAcks.poll(ImsUtils.TEST_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS);
+        assertNotNull(ack);
+        assertEquals(ImsUtils.TEST_TRANSACTION_ID, ack.first);
+        assertNotNull(ack.second);
+        assertEquals(-1, ack.second.intValue());
+    }
+
+    public void sendMessageAndVerifyFailure(SipMessage messageToSend, int expectedReason)
+            throws Exception {
+        assertNotNull("SipDelegate was null when sending message", connection);
+        // send invalid version if it was not sent.
+        long version = (sipConfig != null) ? sipConfig.getVersion() : -1;
+        connection.sendMessage(messageToSend, version);
+        Pair<String, Integer> ack = mSentMessageAcks.poll(ImsUtils.TEST_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS);
+        assertNotNull(ack);
+        // TODO actually check this, but for now the platform can not inspect SipMessages and send
+        // the real transaction ID. So, just ensure it is null.
+        //assertEquals(ImsUtils.TEST_TRANSACTION_ID, ack.first);
+        assertNotNull(ack.second);
+        assertEquals(expectedReason, ack.second.intValue());
+    }
+
+    public void verifyMessageReceived(SipMessage messageToVerify)
+            throws Exception {
+        SipMessage m = mReceivedMessages.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertEquals(messageToVerify, m);
+    }
+
+    public void setReceivedMessageErrorResponseReason(int reason) {
+        mReceivedMessageErrorResponseReason = reason;
+    }
+
+    public void verifyDelegateCreated() {
+        assertNotNull("SipDelegate is null when it should have been created", connection);
+    }
+
+    public void verifyConfigEquals(SipDelegateImsConfiguration config) {
+        assertNotNull("SIP configuration should not be null", sipConfig);
+        assertEquals("IMS config version is not correct", config.getVersion(),
+                sipConfig.getVersion());
+        PersistableBundle b = config.copyBundle();
+        for (String key : b.keySet()) {
+            assertTrue("tracked sip config does not contain the key [" + key + "}",
+                    sipConfig.containsKey(key));
+            // Not a true equality check, but close enough for the purposes of this test.
+            assertEquals(config.getString(key), sipConfig.getString(key));
+            assertEquals(config.getInt(key, -1), sipConfig.getInt(key, -1));
+            assertEquals(config.getBoolean(key, false),
+                    sipConfig.getBoolean(key, false));
+        }
+    }
+
+    public void verifyRegistrationStateRegistered() {
+        verifyRegistrationStateRegistered(delegateRequest.getFeatureTags());
+    }
+
+    public void verifyRegistrationStateRegistered(Set<String> tags) {
+        assertNotNull(regState);
+        assertFalse("No registered features found",
+                regState.getRegisteredFeatureTags().isEmpty());
+        ArraySet<String> notRegistered = new ArraySet<>(tags);
+        notRegistered.removeAll(regState.getRegisteredFeatureTags());
+        assertTrue("Not all requested features were registered: " + notRegistered,
+                notRegistered.isEmpty());
+        assertTrue(regState.getDeregisteringFeatureTags().isEmpty());
+        assertTrue(regState.getDeregisteredFeatureTags().isEmpty());
+    }
+
+    public void verifyRegistrationStateEmpty() {
+        assertNotNull(regState);
+        assertTrue(regState.getRegisteredFeatureTags().isEmpty());
+        assertTrue(regState.getDeregisteringFeatureTags().isEmpty());
+        assertTrue(regState.getDeregisteredFeatureTags().isEmpty());
+    }
+
+    public void verifyRegistrationStateEquals(DelegateRegistrationState s) {
+        assertEquals("unexpected registered tags", s.getRegisteredFeatureTags(),
+                regState.getRegisteredFeatureTags());
+        assertEquals("unexpected deregistering tags", s.getDeregisteringFeatureTags(),
+                regState.getDeregisteringFeatureTags());
+        assertEquals("unexpected deregistered tags", s.getDeregisteredFeatureTags(),
+                regState.getDeregisteredFeatureTags());
+    }
+
+
+    public void verifyNoneDenied() {
+        assertNotNull(deniedTags);
+        assertTrue(deniedTags.isEmpty());
+    }
+
+    public void verifyDenied(Set<FeatureTagState> denied) {
+        assertNotNull(deniedTags);
+        assertEquals(denied, deniedTags);
+    }
+
+    public void verifyAllDenied(int reason) {
+        assertNotNull(deniedTags);
+        // Ensure that if the request is empty, the denied tags are also empty.
+        if (delegateRequest.getFeatureTags().isEmpty()) {
+            assertTrue(deniedTags.isEmpty());
+        }
+        // All should be denied with the same reason.
+        FeatureTagState incorrectReason = deniedTags.stream().filter((t) -> t.getState() != reason)
+                .findAny().orElse(null);
+        Set<String> deniedFeatures = deniedTags.stream().map(FeatureTagState::getFeatureTag)
+                .collect(Collectors.toSet());
+        assertNull(incorrectReason);
+
+        Set<String> requestedTags = new ArraySet<>(delegateRequest.getFeatureTags());
+        requestedTags.removeAll(deniedFeatures);
+        assertTrue("Not all tags denied: " + requestedTags, requestedTags.isEmpty());
+    }
+
+    public void verifyDestroyed(int reason) {
+        assertEquals(reason, destroyReason);
+    }
+
+    /**
+     * Set the number of operations that are expected to happen. Use {@link #waitForCountDown(long)}
+     * to wait for the operations to occur.
+     */
+    public void setOperationCountDownLatch(int operationCount) {
+        mLatch = new CountDownLatch(operationCount);
+    }
+
+    /**
+     * Wait for the latch set in {@link #setOperationCountDownLatch(int)} to complete.
+     * @param timeoutMs The time to wait before giving up.
+     * @return {@code true} if the latch successfully counted down, {@code false} if time elaptsed
+     * before it counted down.
+     */
+    public boolean waitForCountDown(long timeoutMs) {
+        while (mLatch.getCount() > 0) {
+            try {
+                return mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException ignore) { }
+        }
+        return true;
+    }
+
+    /**
+     * Wait up to five seconds (retrying a command 1 time per second) until ImsExceptions due to the
+     * ImsService not being available go away.
+     */
+    private void callUntilImsServiceIsAvailableNoReturn(ExceptionRunnable command)
+            throws Exception {
+        int retry = 0;
+        while (retry < 5) {
+            try {
+                command.run();
+                return;
+            } catch (ImsException e) {
+                // we want to absorb only the unavailable error, as telephony may still be
+                // internally setting up. Any other type of ImsException is unexpected.
+                if (e.getCode() != ImsException.CODE_ERROR_SERVICE_UNAVAILABLE) {
+                    throw e;
+                }
+            }
+            Thread.sleep(1000);
+            retry++;
+        }
+    }
+}
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 b7db4c8..ed5a07b 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
@@ -16,13 +16,103 @@
 
 package android.telephony.ims.cts;
 
+import static org.junit.Assert.fail;
+
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.stub.SipDelegate;
 import android.telephony.ims.stub.SipTransportImplBase;
 
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 
 public class TestSipTransport extends SipTransportImplBase {
 
+    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 =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.oma.cpm.session\"";
+    public static final String FILE_TRANSFER_HTTP_TAG =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.fthttp\"";
+
+    public static final int LATCH_CREATE_DELEGATE = 0;
+    public static final int LATCH_DESTROY_DELEGATE = 1;
+    private static final int LATCH_MAX = 2;
+    protected static final CountDownLatch[] sLatches = new CountDownLatch[LATCH_MAX];
+    static {
+        for (int i = 0; i < LATCH_MAX; i++) {
+            sLatches[i] = new CountDownLatch(1);
+        }
+    }
+
+    private final ArrayList<TestSipDelegate> mDelegates = new ArrayList<>();
+    private final Object mLock = new Object();
+
     public TestSipTransport(Executor executor) {
         super(executor);
     }
+
+    @Override
+    public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request,
+            @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
+        TestSipDelegate d = new TestSipDelegate(subscriptionId, request, dc, mc);
+        synchronized (mLock) {
+            mDelegates.add(d);
+        }
+        countDownLatch(LATCH_CREATE_DELEGATE);
+    }
+
+    @Override
+    public void destroySipDelegate(@NonNull SipDelegate delegate, int reason) {
+        if (delegate instanceof TestSipDelegate) {
+            synchronized (mLock) {
+                mDelegates.remove(delegate);
+            }
+            countDownLatch(LATCH_DESTROY_DELEGATE);
+        } else {
+            fail("unknown delegate passed in!");
+        }
+    }
+
+    public List<TestSipDelegate> getDelegates() {
+        synchronized (mLock) {
+            return mDelegates;
+        }
+    }
+
+    public TestSipDelegate getDelegate(DelegateRequest request) {
+        synchronized (mLock) {
+            return mDelegates.stream().filter((d) -> d.delegateRequest.equals(request))
+                    .findFirst().orElse(null);
+        }
+    }
+
+    public boolean waitForLatchCountdownAndReset(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;
+    }
+
+    private void countDownLatch(int latchIndex) {
+        synchronized (mLock) {
+            sLatches[latchIndex].countDown();
+        }
+    }
 }
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index c30c71c..85d4fc8 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -291,7 +291,7 @@
         </activity>
 
         <activity android:name="android.widget.cts.PopupWindowCtsActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout"
             android:label="PopupWindowCtsActivity"
             android:theme="@style/Theme.PopupWindowCtsActivity">
             <intent-filter>
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index e8c5c27..115d02e 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -2100,13 +2100,17 @@
         boolean isStaApConcurrencySupported = mWifiManager.isStaApConcurrencySupported();
         // start local only hotspot.
         TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
-        if (isStaApConcurrencySupported) {
-            assertTrue(mWifiManager.isWifiEnabled());
-        } else {
-            // no concurrency, wifi should be disabled.
-            assertFalse(mWifiManager.isWifiEnabled());
+        try {
+            if (isStaApConcurrencySupported) {
+                assertTrue(mWifiManager.isWifiEnabled());
+            } else {
+                // no concurrency, wifi should be disabled.
+                assertFalse(mWifiManager.isWifiEnabled());
+            }
+        } finally {
+            // clean up local only hotspot no matter if assertion passed or failed
+            stopLocalOnlyHotspot(callback, true);
         }
-        stopLocalOnlyHotspot(callback, true);
 
         assertTrue(mWifiManager.isWifiEnabled());
     }
diff --git a/tools/cts-tradefed/res/config/cts-automated.xml b/tools/cts-tradefed/res/config/cts-automated.xml
index 150f8b9..80bcea7 100644
--- a/tools/cts-tradefed/res/config/cts-automated.xml
+++ b/tools/cts-tradefed/res/config/cts-automated.xml
@@ -15,6 +15,9 @@
 -->
 <configuration description="Runs CTS with common options set for an automated run on userdebug/eng builds">
 
+    <!-- template hook to allow users to attach additional target preparers -->
+    <template-include name="preparers" default="empty" />
+
     <include name="cts" />
 
     <option name="plan" value="cts" />
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index e369dfa..0aad033 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -255,4 +255,7 @@
 
     <!-- b/153032202: CtsSystemUiTestCases (10_r3 waiver) -->
     <option name="compatibility:exclude-filter" value="CtsSystemUiTestCases android.systemui.cts.WindowInsetsBehaviorTests#swipeOutsideLimit_systemUiVisible_allEventsCanceled"/>
+
+    <!-- b/173662175: CtsStatsdHostTestCases due to insufficient processes running -->
+    <option name="compatibility:exclude-filter" value="CtsStatsdHostTestCases android.cts.statsd.validation.ProcStatsValidationTests#testProcessStatePssValue"/>
 </configuration>
diff --git a/tools/vm-tests-tf/TEST_MAPPING b/tools/vm-tests-tf/TEST_MAPPING
index a566107..4fbca3b 100644
--- a/tools/vm-tests-tf/TEST_MAPPING
+++ b/tools/vm-tests-tf/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "vm-tests-tf"
     }