Merge "Snap for 6533464 from 1b19277298b490ffd0ff6a4e34d14f4832cdec5a to sdk-release" into sdk-release
diff --git a/apps/CameraITS/Android.mk b/apps/CameraITS/Android.mk
index 8f7ed7c..b0cabc7 100644
--- a/apps/CameraITS/Android.mk
+++ b/apps/CameraITS/Android.mk
@@ -13,6 +13,8 @@
 # limitations under the License.
 #
 
+LOCAL_PATH := $(call my-dir)
+
 its-dir-name := CameraITS
 its-dir := $(HOST_OUT)/$(its-dir-name)
 its-build-stamp := $(its-dir)/build_stamp
@@ -21,11 +23,11 @@
 
 .PHONY: camera-its
 
-$(its-dir): $(its-build-stamp)
-
-$(its-build-stamp): $(ACP)
-	echo $(its_dir)
-	mkdir -p $(its-dir)
-	$(ACP) -rfp cts/apps/$(its-dir-name)/* $(its-dir)
-	rm $(its-dir)/Android.mk
+$(its-build-stamp): PRIVATE_PATH := $(LOCAL_PATH)
+$(its-build-stamp): PRIVATE_OUT := $(its-dir)
+$(its-build-stamp): $(ACP) $(call find-files-in-subdirs,.,*,$(LOCAL_PATH))
+	rm -rf $(PRIVATE_OUT)
+	mkdir -p $(PRIVATE_OUT)
+	$(ACP) -rfp $(PRIVATE_PATH)/* $(PRIVATE_OUT)/
+	rm $(PRIVATE_OUT)/Android.mk
 	touch $@
diff --git a/apps/CameraITS/tests/scene1/test_multi_camera_match.py b/apps/CameraITS/tests/scene1/test_multi_camera_match.py
index 311a36f..2429837 100644
--- a/apps/CameraITS/tests/scene1/test_multi_camera_match.py
+++ b/apps/CameraITS/tests/scene1/test_multi_camera_match.py
@@ -41,6 +41,7 @@
         for i in ids:
             physical_props = cam.get_camera_properties_by_id(i)
             its.caps.skip_unless(not its.caps.mono_camera(physical_props))
+            its.caps.skip_unless(its.caps.backward_compatible(physical_props))
             yuv_sizes[i] = its.objects.get_available_output_sizes(
                     'yuv', physical_props)
             if i == ids[0]:  # get_available_output_sizes returns sorted list
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 7885d4f..6802f8d 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -171,7 +171,7 @@
 #	$(hide) $(ACP) -fp cts/apps/CtsVerifier/assets/scripts/execute_power_tests.py $@
 
 cts : $(verifier-zip)
-$(verifier-zip) : $(HOST_OUT)/CameraITS
+$(verifier-zip) : $(HOST_OUT)/CameraITS/build_stamp
 $(verifier-zip) : $(foreach app,$(apps-to-include),$(call apk-location-for,$(app)))
 $(verifier-zip) : $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk | $(ACP)
 		$(hide) mkdir -p $(verifier-dir)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/admin/OWNERS
new file mode 100644
index 0000000..79d60e5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/OWNERS
@@ -0,0 +1,5 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204
+alexkershaw@google.com
+eranm@google.com
+rubinxu@google.com
+sandness@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OWNERS
new file mode 100644
index 0000000..e303d2b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 142675
+set noparent
+suprabh@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/audio/OWNERS
new file mode 100644
index 0000000..eb8de3b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 48436
+elaurent@google.com
+pmclean@google.com
+philburk@google.com
+per-file HifiUltrasound*.java = xlythe@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/OWNERS
new file mode 100644
index 0000000..52531aa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 27441
+zachoverflow@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
new file mode 100644
index 0000000..8ebd7b8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 41727
+include platform/frameworks/av:/camera/OWNER
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/OWNERS
new file mode 100644
index 0000000..78b6baa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 31568
+jplemieux@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/dialer/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/dialer/OWNERS
new file mode 100644
index 0000000..a7c1795
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/dialer/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 20868
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+xiaotonj@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/OWNERS
new file mode 100644
index 0000000..869f601
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 187788
+dimuthu@google.com
+leegao@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/OWNERS
new file mode 100644
index 0000000..79d60e5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/OWNERS
@@ -0,0 +1,5 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204
+alexkershaw@google.com
+eranm@google.com
+rubinxu@google.com
+sandness@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/net/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/net/OWNERS
new file mode 100644
index 0000000..733502a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/net/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 31808
+lorenzo@google.com
+satk@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/OWNERS
new file mode 100644
index 0000000..cbc5f31
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 78930
+juliacr@google.com
+kozynski@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java
index d80924c..f17c9ee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java
@@ -24,6 +24,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
@@ -38,6 +39,7 @@
  * able to be answered.
  */
 public class IncomingCallTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = "TelecomIncomingCall";
 
     private Button mRegisterAndEnablePhoneAccount;
     private Button mConfirmPhoneAccountEnabled;
@@ -77,6 +79,7 @@
 
                 mConfirmPhoneAccountEnabled.setEnabled(true);
             } else {
+                Log.w(TAG, "Step 1 fail - couldn't register phone account");
                 mStep1Status.setImageResource(R.drawable.fs_error);
             }
         });
@@ -91,9 +94,11 @@
             PhoneAccount account = PhoneAccountUtils.getPhoneAccount(this);
             if (account != null && account.isEnabled()) {
                 getPassButton().setEnabled(true);
+                Log.i(TAG, "Step 1 pass - account is enabled.");
                 mStep1Status.setImageResource(R.drawable.fs_good);
                 mConfirmPhoneAccountEnabled.setEnabled(false);
             } else {
+                Log.w(TAG, "Step 1 fail - account is not enabled.");
                 mStep1Status.setImageResource(R.drawable.fs_error);
             }
         });
@@ -112,9 +117,11 @@
             TelecomManager telecomManager =
                     (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
             if (telecomManager == null) {
+                Log.w(TAG, "Step 2 fail - telecom service is null");
                 mStep2Status.setImageResource(R.drawable.fs_error);
                 return;
             }
+            Log.i(TAG, "Step 2 pass - adding new incoming call");
             telecomManager.addNewIncomingCall(PhoneAccountUtils.TEST_PHONE_ACCOUNT_HANDLE, extras);
             mStep2Status.setImageResource(R.drawable.fs_good);
         });
@@ -126,8 +133,10 @@
         }
         mConfirmIncomingCallAnswered.setOnClickListener(v -> {
             if (confirmIncomingCall()) {
+                Log.i(TAG, "Step 3 pass - new incoming call answered");
                 mStep3Status.setImageResource(R.drawable.fs_good);
             } else {
+                Log.w(TAG, "Step 3 fail - failed to answer new incoming call");
                 mStep3Status.setImageResource(R.drawable.fs_error);
             }
             PhoneAccountUtils.unRegisterTestPhoneAccount(this);
@@ -148,13 +157,16 @@
         List<CtsConnection> ongoingConnections =
                 CtsConnectionService.getConnectionService().getConnections();
         if (ongoingConnections == null || ongoingConnections.size() != 1) {
+            Log.w(TAG, "Step 3 fail - no ongoing call found");
             return false;
         }
         CtsConnection incomingConnection = ongoingConnections.get(0);
         if (!incomingConnection.isIncomingCall()) {
+            Log.w(TAG, "Step 3 fail - ongoing call isn't incoming");
             return false;
         }
         if (incomingConnection.getState() != Connection.STATE_ACTIVE) {
+            Log.w(TAG, "Step 3 fail - ongoing call is not active");
             return false;
         }
         incomingConnection.onDisconnect();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OWNERS
new file mode 100644
index 0000000..0cfd686
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 151185
+tgunn@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java
index 5f5c605..9fc0381 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java
@@ -22,6 +22,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
@@ -36,6 +37,7 @@
  * the CtsConnectionService.
  */
 public class OutgoingCallTestActivity extends PassFailButtons.Activity {
+    private final static String TAG = "TelecomOutgoingCall";
 
     private Button mRegisterAndEnablePhoneAccount;
     private Button mConfirmPhoneAccountEnabled;
@@ -71,9 +73,10 @@
                 // the default.
                 Intent intent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
                 startActivity(intent);
-
+                Log.i(TAG, "Step 1 - phone account registered");
                 mConfirmPhoneAccountEnabled.setEnabled(true);
             } else {
+                Log.w(TAG, "Step 1 fail - phone account registration failed");
                 mStep1Status.setImageResource(R.drawable.fs_error);
             }
         });
@@ -90,9 +93,11 @@
             if (account != null && account.isEnabled() &&
                     PhoneAccountUtils.TEST_PHONE_ACCOUNT_HANDLE.equals(defaultOutgoingAccount)) {
                 getPassButton().setEnabled(true);
+                Log.i(TAG, "Step 1 pass - phone account registration confirmed");
                 mStep1Status.setImageResource(R.drawable.fs_good);
                 mConfirmPhoneAccountEnabled.setEnabled(false);
             } else {
+                Log.w(TAG, "Step 1 fail - phone account registration failed");
                 mStep1Status.setImageResource(R.drawable.fs_error);
             }
         });
@@ -106,6 +111,7 @@
             Intent intent = new Intent(Intent.ACTION_DIAL);
             intent.setData(TEST_DIAL_NUMBER);
             startActivity(intent);
+            Log.i(TAG, "Step 1 pass - dial intent sent");
             mStep2Status.setImageResource(R.drawable.fs_good);
         });
 
@@ -115,8 +121,10 @@
         }
         mConfirmOutgoingCall.setOnClickListener(v -> {
             if (confirmOutgoingCall()) {
+                Log.i(TAG, "Step 3 pass - call confirmed");
                 mStep3Status.setImageResource(R.drawable.fs_good);
             } else {
+                Log.w(TAG, "Step 3 fail - failed to confirm call");
                 mStep3Status.setImageResource(R.drawable.fs_error);
             }
             PhoneAccountUtils.unRegisterTestPhoneAccount(this);
@@ -137,10 +145,12 @@
         List<CtsConnection> ongoingConnections =
                 CtsConnectionService.getConnectionService().getConnections();
         if (ongoingConnections == null || ongoingConnections.size() != 1) {
+            Log.w(TAG, "Step 3 fail - no ongoing connections");
             return false;
         }
         CtsConnection outgoingConnection = ongoingConnections.get(0);
         if (outgoingConnection.isIncomingCall()) {
+            Log.w(TAG, "Step 3 fail - call is not outgoing");
             return false;
         }
         outgoingConnection.onDisconnect();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
index a8c2caa..e273616 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
@@ -23,6 +23,7 @@
 import android.telecom.Connection;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
@@ -37,6 +38,7 @@
  * call or when there is an ongoing self-managed call in another app.
  */
 public class SelfManagedIncomingCallTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = "SelfManagedIncomingCall";
     private Uri TEST_DIAL_NUMBER_1 = Uri.fromParts("tel", "6505551212", null);
     private Uri TEST_DIAL_NUMBER_2 = Uri.fromParts("tel", "4085551212", null);
 
@@ -51,6 +53,7 @@
         @Override
         void onShowIncomingCallUi(CtsConnection connection) {
             // The system should have displayed the incoming call UI; this is a fail.
+            Log.w(TAG, "Step 3 fail - got unexpected onShowIncomingCallUi");
             mStep3Status.setImageResource(R.drawable.fs_error);
             getPassButton().setEnabled(false);
         };
@@ -58,6 +61,7 @@
         @Override
         void onAnswer(CtsConnection connection, int videoState) {
             // Call was answered, so disconnect it now.
+            Log.i(TAG, "Step 3 - Incoming call answered.");
             connection.onDisconnect();
         };
 
@@ -71,10 +75,11 @@
                     (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
             if (telecomManager == null || !telecomManager.isInManagedCall()) {
                 // Should still be in a managed call; only one would need to be disconnected.
+                Log.w(TAG, "Step 3 fail - not in managed call as expected.");
                 mStep3Status.setImageResource(R.drawable.fs_error);
                 return;
             }
-
+            Log.i(TAG, "Step 3 pass - call disconnected");
             mStep3Status.setImageResource(R.drawable.fs_good);
             getPassButton().setEnabled(true);
         }
@@ -100,8 +105,10 @@
                     account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
                 mRegisterPhoneAccount.setEnabled(false);
                 mVerifyCall.setEnabled(true);
+                Log.i(TAG, "Step 1 pass - account registered");
                 mStep1Status.setImageResource(R.drawable.fs_good);
             } else {
+                Log.w(TAG, "Step 1 fail - account not registered");
                 mStep1Status.setImageResource(R.drawable.fs_error);
             }
         });
@@ -112,10 +119,12 @@
             TelecomManager telecomManager =
                     (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
             if (telecomManager == null || !telecomManager.isInManagedCall()) {
+                Log.w(TAG, "Step 2 fail - expected to be in a managed call");
                 mStep2Status.setImageResource(R.drawable.fs_error);
                 mPlaceCall.setEnabled(false);
             } else {
                 mStep2Status.setImageResource(R.drawable.fs_good);
+                Log.i(TAG, "Step 2 pass - device in a managed call");
                 mVerifyCall.setEnabled(false);
                 mPlaceCall.setEnabled(true);
             }
@@ -135,6 +144,7 @@
                         TelecomManager telecomManager =
                                 (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
                         if (telecomManager == null) {
+                            Log.w(TAG, "Step 2 fail - telecom manager null");
                             mStep2Status.setImageResource(R.drawable.fs_error);
                             return new Throwable("Could not get telecom service.");
                         }
@@ -144,12 +154,14 @@
                         CtsConnectionService ctsConnectionService =
                                 CtsConnectionService.waitForAndGetConnectionService();
                         if (ctsConnectionService == null) {
+                            Log.w(TAG, "Step 2 fail - ctsConnectionService null");
                             mStep2Status.setImageResource(R.drawable.fs_error);
                             return new Throwable("Could not get connection service.");
                         }
 
                         CtsConnection connection = ctsConnectionService.waitForAndGetConnection();
                         if (connection == null) {
+                            Log.w(TAG, "Step 2 fail - could not get connection");
                             mStep2Status.setImageResource(R.drawable.fs_error);
                             return new Throwable("Could not get connection.");
                         }
@@ -167,6 +179,7 @@
                         int capabilities = connection.getConnectionCapabilities();
                         capabilities &= ~Connection.CAPABILITY_HOLD;
                         connection.setConnectionCapabilities(capabilities);
+                        Log.w(TAG, "Step 2 - connection added");
                         return null;
                     } catch (Throwable t) {
                         return t;
@@ -179,6 +192,7 @@
                         mStep2Status.setImageResource(R.drawable.fs_good);
                         mPlaceCall.setEnabled(false);
                     } else {
+                        Log.i(TAG, "Step 2 pass - connection added");
                         mStep2Status.setImageResource(R.drawable.fs_error);
                     }
                 }
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index c478961..ac30d80 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -100,9 +100,12 @@
 
             store.addResult(IS_ACTIVE_ADMIN, deviceAdminPackages.contains(pkg.packageName));
 
-            final boolean isDefaultAccessibilityComponent = pkg.packageName.equals(
-                    defaultAccessibilityComponent.getPackageName()
-            );
+            boolean isDefaultAccessibilityComponent = false;
+            if (defaultAccessibilityComponent != null) {
+              isDefaultAccessibilityComponent = pkg.packageName.equals(
+                      defaultAccessibilityComponent.getPackageName()
+              );
+            }
             store.addResult(DEFAULT_ACCESSIBILITY_SERVICE, isDefaultAccessibilityComponent);
 
             String sha256_cert = PackageUtil.computePackageSignatureDigest(pkg.packageName);
diff --git a/hostsidetests/appcompat/OWNERS b/hostsidetests/appcompat/OWNERS
index 4e3ec29..11a1173 100644
--- a/hostsidetests/appcompat/OWNERS
+++ b/hostsidetests/appcompat/OWNERS
@@ -4,6 +4,5 @@
 platform-compat-eng+reviews@google.com
 
 andreionea@google.com
-atrost@google.com
 mathewi@google.com
-satayev@google.com
\ No newline at end of file
+satayev@google.com
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
index 699af1a..2f8790f 100644
--- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
@@ -43,6 +43,11 @@
         installPackage(TEST_APK, true);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        uninstallPackage(TEST_PKG, true);
+    }
+
     public void testIsChangeEnabled() throws Exception {
         runDeviceCompatTest(TEST_PKG, ".CompatChangesTest", "isChangeEnabled_changeEnabled",
                 /*enabledChanges*/ImmutableSet.of(CTS_SYSTEM_API_CHANGEID),
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
index bd17748..1eb58ef 100644
--- a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
@@ -35,7 +35,22 @@
     public void testAccessSerialPermissionNeeded() throws Exception {
         // Build.SERIAL should not provide the device serial for modern apps.
         // We don't know the serial but know that it should be the dummy
-        // value returned to unauthorized callers, so make sure that value is returned.
+        // value returned to unauthorized callers, so make sure that value
+        assertTrue("Build.SERIAL must not work for modern apps",
+                Build.UNKNOWN.equals(Build.SERIAL));
+
+        // We don't have the read phone state permission, so this should throw
+        try {
+            Build.getSerial();
+            fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+
+        // Now grant ourselves READ_PHONE_STATE
+        grantReadPhoneStatePermission();
+
+        // Build.SERIAL should not provide the device serial for modern apps.
         assertTrue("Build.SERIAL must not work for modern apps",
                 Build.UNKNOWN.equals(Build.SERIAL));
 
@@ -53,4 +68,10 @@
                     + e);
         }
     }
+
+    private void grantReadPhoneStatePermission() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+                InstrumentationRegistry.getContext().getPackageName(),
+                android.Manifest.permission.READ_PHONE_STATE);
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 78f2fff..3763797 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -61,6 +61,7 @@
 import java.io.OutputStream;
 import java.util.HashSet;
 import java.util.concurrent.Callable;
+import java.util.concurrent.TimeoutException;
 
 @RunWith(AndroidJUnit4.class)
 public class MediaStorageTest {
@@ -75,6 +76,9 @@
     private ContentResolver mContentResolver;
     private int mUserId;
 
+    private static int currentAttempt = 0;
+    private static final int MAX_NUMBER_OF_ATTEMPT = 10;
+
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
@@ -441,9 +445,19 @@
         runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
     }
 
-    static File stageFile(File file) throws IOException {
-        file.getParentFile().mkdirs();
-        file.createNewFile();
-        return file;
+    static File stageFile(File file) throws Exception {
+        // Sometimes file creation fails due to slow permission update, try more times 
+        while(currentAttempt < MAX_NUMBER_OF_ATTEMPT) {
+            try {
+                file.getParentFile().mkdirs();
+                file.createNewFile();
+                return file;
+            } catch(IOException e) {
+                currentAttempt++;
+                // wait 500ms
+                Thread.sleep(500);
+            }
+        } 
+        throw new TimeoutException("File creation failed due to slow permission update");
     }
 }
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index d287311..a02ac15 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -45,6 +45,7 @@
 import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiScrollable;
 import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
 
@@ -100,6 +101,8 @@
         device.waitForIdle();
 
         if (!isTV(getContext())) {
+            UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+            uiScrollable.scrollTextIntoView("internal storage");
             device.findObject(new UiSelector().textContains("internal storage")).click();
             device.waitForIdle();
         }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
index fc7a232..7f85baa 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -87,6 +87,12 @@
         mUiDevice.pressHome();
     }
 
+    public void testLockTaskIsActive() throws Exception {
+        Log.d(TAG, "testLockTaskIsActive on host-driven test");
+        waitAndCheckLockedActivityIsResumed();
+        checkLockedActivityIsRunning();
+    }
+
     /**
      * On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
      * assume it's finished. Therefore, only use it once in a given test.
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 2e9ae39..c4efeda 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -956,7 +956,7 @@
     }
 
     public void testSetMeteredDataDisabledPackages() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
             return;
         }
         installAppAsUser(METERED_DATA_APP_APK, mUserId);
@@ -1128,6 +1128,9 @@
             // Wait for the LockTask starting
             waitForBroadcastIdle();
 
+            // Make sure that the LockTaskUtilityActivityIfWhitelisted was started.
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "testLockTaskIsActive");
+
             // Try to open settings via adb
             executeShellCommand("am start -a android.settings.SETTINGS");
 
diff --git a/hostsidetests/hdmicec/app/Android.bp b/hostsidetests/hdmicec/app/Android.bp
index b0f8f71..6d713d3 100644
--- a/hostsidetests/hdmicec/app/Android.bp
+++ b/hostsidetests/hdmicec/app/Android.bp
@@ -16,5 +16,9 @@
     name: "HdmiCecHelperApp",
     defaults: ["cts_defaults"],
     srcs: ["src/**/*.java"],
-    sdk_version: "current",
+    static_libs: [
+        "services.core",
+        "guava",
+        "androidx.test.runner",
+    ],
 }
diff --git a/hostsidetests/hdmicec/app/AndroidManifest.xml b/hostsidetests/hdmicec/app/AndroidManifest.xml
index edf28a8..bb19e50 100644
--- a/hostsidetests/hdmicec/app/AndroidManifest.xml
+++ b/hostsidetests/hdmicec/app/AndroidManifest.xml
@@ -32,6 +32,7 @@
                 <action android:name="android.hdmicec.app.UNMUTE" />
                 <action android:name="android.hdmicec.app.REPORT_VOLUME" />
                 <action android:name="android.hdmicec.app.SET_VOLUME" />
+                <action android:name="android.hdmicec.app.GET_SUPPORTED_SAD_FORMATS" />
                 <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
             </intent-filter>
         </activity>
diff --git a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
index 058442a..f04490f 100644
--- a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
+++ b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
@@ -22,6 +22,10 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import com.android.server.hdmi.SadConfigurationReaderTest;
+
+import org.junit.runner.JUnitCore;
+
 /**
  * A simple app that can be used to mute, unmute, set volume or get the volume status of a device.
  * The actions supported are:
@@ -79,6 +83,10 @@
                 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
                 Log.i(TAG, "Set volume to " + volume + " (" + percentVolume + "%)");
                 break;
+            case "android.hdmicec.app.GET_SUPPORTED_SAD_FORMATS":
+                JUnitCore junit = new JUnitCore();
+                junit.run(SadConfigurationReaderTest.class);
+                break;
             default:
                 Log.w(TAG, "Unknown intent!");
         }
diff --git a/hostsidetests/hdmicec/app/src/com/android/server/hdmi/SadConfigurationReaderTest.java b/hostsidetests/hdmicec/app/src/com/android/server/hdmi/SadConfigurationReaderTest.java
new file mode 100644
index 0000000..48665ac
--- /dev/null
+++ b/hostsidetests/hdmicec/app/src/com/android/server/hdmi/SadConfigurationReaderTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.android.server.hdmi;
+
+import android.util.Log;
+
+import com.android.server.hdmi.HdmiUtils.CodecSad;
+import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+import com.google.common.hash.HashCode;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+/**
+ * Reads short audio descriptors from a configuration file and outputs to a log
+ * for use by host side tests.
+ */
+public class SadConfigurationReaderTest {
+
+    private static final String TAG = "SadConfigurationReaderTest";
+
+    // Variable should be copy of SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH in
+    // frameworks/base/services/core/java/com/android/server/hdmi/
+    //  HdmiCecLocalDeviceAudioSystem.java
+    private final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
+
+    @Test
+    public void parseSadConfigXML() {
+        List<DeviceConfig> deviceConfigs = null;
+        File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH);
+        if (file.exists()) {
+            try {
+                InputStream in = new FileInputStream(file);
+                deviceConfigs = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in);
+                in.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Error reading file: " + file.getAbsolutePath(), e);
+            } catch (XmlPullParserException e) {
+                Log.e(TAG, "Unable to parse file: " + file.getAbsolutePath(), e);
+            }
+        } else {
+            Log.e(TAG, "No config file present at " + file.getAbsolutePath());
+            return;
+        }
+        DeviceConfig deviceConfigToUse = null;
+        if (deviceConfigs != null && deviceConfigs.size() > 0) {
+            for (DeviceConfig deviceConfig : deviceConfigs) {
+                if (deviceConfig.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) {
+                    deviceConfigToUse = deviceConfig;
+                    break;
+                }
+            }
+            if (deviceConfigToUse == null) {
+                Log.w(TAG, "sadConfig.xml does not have required device info for "
+                                   + "VX_AUDIO_DEVICE_IN_HDMI_ARC");
+                return;
+            }
+            List<Integer> audioCodecFormats = new ArrayList<>();
+            List<String> shortAudioDescriptors = new ArrayList<>();
+            for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) {
+                audioCodecFormats.add(codecSad.audioCodec);
+                shortAudioDescriptors.add(HashCode.fromBytes(codecSad.sad).toString());
+            }
+            String audioCodecFormatsString = audioCodecFormats.toString();
+            String shortAudioDescriptorsString = shortAudioDescriptors.toString();
+            Log.i(TAG, "Supported Audio Formats");
+            Log.i(TAG, audioCodecFormatsString.substring(1, audioCodecFormatsString.length() - 1));
+            Log.i(TAG, shortAudioDescriptorsString
+                               .substring(1, shortAudioDescriptorsString.length() - 1));
+        }
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
index bf5ff8d..bce34e1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -16,64 +16,152 @@
 
 package android.hdmicec.cts;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
-public enum CecMessage {
-    FEATURE_ABORT(0x00),
-    TEXT_VIEW_ON(0x0d),
-    SET_MENU_LANGUAGE(0x32),
-    STANDBY(0x36),
-    USER_CONTROL_PRESSED(0x44),
-    USER_CONTROL_RELEASED(0x45),
-    GIVE_OSD_NAME(0x46),
-    SET_OSD_NAME(0x47),
-    SYSTEM_AUDIO_MODE_REQUEST(0x70),
-    GIVE_AUDIO_STATUS(0x71),
-    SET_SYSTEM_AUDIO_MODE(0x72),
-    REPORT_AUDIO_STATUS(0x7a),
-    GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
-    SYSTEM_AUDIO_MODE_STATUS(0x7e),
-    ACTIVE_SOURCE(0x82),
-    GIVE_PHYSICAL_ADDRESS(0x83),
-    REPORT_PHYSICAL_ADDRESS(0x84),
-    REQUEST_ACTIVE_SOURCE(0x85),
-    SET_STREAM_PATH(0x86),
-    DEVICE_VENDOR_ID(0x87),
-    VENDOR_COMMAND(0x89),
-    GIVE_DEVICE_VENDOR_ID(0x8c),
-    GIVE_POWER_STATUS(0x8f),
-    REPORT_POWER_STATUS(0x90),
-    GET_MENU_LANGUAGE(0x91),
-    INACTIVE_SOURCE(0x9d),
-    CEC_VERSION(0x9e),
-    GET_CEC_VERSION(0x9f),
-    INITIATE_ARC(0xc0),
-    ARC_INITIATED(0xc1),
-    REQUEST_ARC_INITIATION(0xc3),
-    REQUEST_ARC_TERMINATION(0xc4),
-    TERMINATE_ARC(0xc5),
-    ABORT(0xff);
+public class CecMessage {
 
-    private final int messageId;
-    private static Map messageMap = new HashMap<>();
+    private static final int HEXADECIMAL_RADIX = 16;
 
-    static {
-        for (CecMessage message : CecMessage.values()) {
-            messageMap.put(message.messageId, message);
+    /** Gets the hexadecimal ASCII character values of a string. */
+    public static String getHexAsciiString(String string) {
+        String asciiString = "";
+        byte[] ascii = string.trim().getBytes();
+
+        for (byte b : ascii) {
+            asciiString.concat(Integer.toHexString(b));
         }
+
+        return asciiString;
     }
 
-    public static CecMessage getMessage(int messageId) {
-        return (CecMessage) messageMap.get(messageId);
+    public static String formatParams(String rawParams) {
+        StringBuilder params = new StringBuilder("");
+        int position = 0;
+        int endPosition = 2;
+
+        do {
+            params.append(":" + rawParams.substring(position, endPosition));
+            position = endPosition;
+            endPosition += 2;
+        } while (endPosition <= rawParams.length());
+        return params.toString();
     }
 
-    @Override
-    public String toString() {
-        return String.format("%02x", messageId);
+    public static String formatParams(long rawParam) {
+        StringBuilder params = new StringBuilder("");
+
+        do {
+            params.insert(0, ":" + String.format("%02x", rawParam % 256));
+            rawParam >>= 8;
+        } while (rawParam > 0);
+
+        return params.toString();
     }
 
-    private CecMessage(int messageId) {
-        this.messageId = messageId;
+    /**
+     * Formats the rawParam into CEC message parameters. The parameters will be at least
+     * minimumNibbles long.
+     */
+    public static String formatParams(long rawParam, int minimumNibbles) {
+        StringBuilder params = new StringBuilder("");
+
+        do {
+            params.insert(0, ":" + String.format("%02x", rawParam % 256));
+            rawParam >>= 8;
+            minimumNibbles -= 2;
+        } while (rawParam > 0 || minimumNibbles > 0);
+
+        return params.toString();
+    }
+
+    public static int hexStringToInt(String message) {
+        return Integer.parseInt(message, HEXADECIMAL_RADIX);
+    }
+
+    public static String getAsciiString(String message) {
+        String params = getNibbles(message).substring(4);
+        StringBuilder builder = new StringBuilder();
+
+        for (int i = 2; i <= params.length(); i += 2) {
+            builder.append((char) hexStringToInt(params.substring(i - 2, i)));
+        }
+
+        return builder.toString();
+    }
+
+    public static String getParamsAsString(String message) {
+        return getNibbles(message).substring(4);
+    }
+
+    /** Gets the params from a CEC message. */
+    public static int getParams(String message) {
+        return hexStringToInt(getNibbles(message).substring(4));
+    }
+
+    /** Gets the first 'numNibbles' number of param nibbles from a CEC message. */
+    public static int getParams(String message, int numNibbles) {
+        int paramStart = 4;
+        int end = numNibbles + paramStart;
+        return hexStringToInt(getNibbles(message).substring(paramStart, end));
+    }
+
+    /**
+     * From the params of a CEC message, gets the nibbles from position start to position end.
+     * The start and end are relative to the beginning of the params. For example, in the following
+     * message - 4F:82:10:00:04, getParamsFromMessage(message, 0, 4) will return 0x1000 and
+     * getParamsFromMessage(message, 4, 6) will return 0x04.
+     */
+    public static int getParams(String message, int start, int end) {
+        return hexStringToInt(getNibbles(message).substring(4).substring(start, end));
+    }
+
+    /**
+     * Gets the source logical address from a CEC message.
+     */
+    public static LogicalAddress getSource(String message) {
+        String param = getNibbles(message).substring(0, 1);
+        return LogicalAddress.getLogicalAddress(hexStringToInt(param));
+    }
+
+    /** Gets the destination logical address from a CEC message. */
+    public static LogicalAddress getDestination(String message) {
+        String param = getNibbles(message).substring(1, 2);
+        return LogicalAddress.getLogicalAddress(hexStringToInt(param));
+    }
+
+    /** Gets the operand from a CEC message. */
+    public static CecOperand getOperand(String message) {
+        String param = getNibbles(message).substring(2, 4);
+        return CecOperand.getOperand(hexStringToInt(param));
+    }
+
+    /**
+     * Converts ascii characters to hexadecimal numbers that can be appended to a CEC message as
+     * params. For example, "spa" will be converted to ":73:70:61"
+     */
+    public static String convertStringToHexParams(String rawParams) {
+        StringBuilder params = new StringBuilder("");
+        for (int i = 0; i < rawParams.length(); i++) {
+            params.append(String.format(":%02x", (int) rawParams.charAt(i)));
+        }
+        return params.toString();
+    }
+
+    private static String getNibbles(String message) {
+        final String tag1 = "group1";
+        final String tag2 = "group2";
+        String paramsPattern = "(?:.*[>>|<<].*?)" +
+                "(?<" + tag1 + ">[\\p{XDigit}{2}:]+)" +
+                "(?<" + tag2 + ">\\p{XDigit}{2})" +
+                "(?:.*?)";
+        String nibbles = "";
+
+        Pattern p = Pattern.compile(paramsPattern);
+        Matcher m = p.matcher(message);
+        if (m.matches()) {
+            nibbles = m.group(tag1).replace(":", "") + m.group(tag2);
+        }
+        return nibbles;
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
new file mode 100644
index 0000000..5aa547e
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
@@ -0,0 +1,81 @@
+/*
+ * 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.hdmicec.cts;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum CecOperand {
+    FEATURE_ABORT(0x00),
+    TEXT_VIEW_ON(0x0d),
+    SET_MENU_LANGUAGE(0x32),
+    STANDBY(0x36),
+    USER_CONTROL_PRESSED(0x44),
+    USER_CONTROL_RELEASED(0x45),
+    GIVE_OSD_NAME(0x46),
+    SET_OSD_NAME(0x47),
+    SYSTEM_AUDIO_MODE_REQUEST(0x70),
+    GIVE_AUDIO_STATUS(0x71),
+    SET_SYSTEM_AUDIO_MODE(0x72),
+    REPORT_AUDIO_STATUS(0x7a),
+    GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
+    SYSTEM_AUDIO_MODE_STATUS(0x7e),
+    ACTIVE_SOURCE(0x82),
+    GIVE_PHYSICAL_ADDRESS(0x83),
+    REPORT_PHYSICAL_ADDRESS(0x84),
+    REQUEST_ACTIVE_SOURCE(0x85),
+    SET_STREAM_PATH(0x86),
+    DEVICE_VENDOR_ID(0x87),
+    VENDOR_COMMAND(0x89),
+    GIVE_DEVICE_VENDOR_ID(0x8c),
+    GIVE_POWER_STATUS(0x8f),
+    REPORT_POWER_STATUS(0x90),
+    GET_MENU_LANGUAGE(0x91),
+    INACTIVE_SOURCE(0x9d),
+    CEC_VERSION(0x9e),
+    GET_CEC_VERSION(0x9f),
+    REPORT_SHORT_AUDIO_DESCRIPTOR(0xa3),
+    REQUEST_SHORT_AUDIO_DESCRIPTOR(0xa4),
+    INITIATE_ARC(0xc0),
+    ARC_INITIATED(0xc1),
+    REQUEST_ARC_INITIATION(0xc3),
+    REQUEST_ARC_TERMINATION(0xc4),
+    TERMINATE_ARC(0xc5),
+    ABORT(0xff);
+
+    private final int operandCode;
+    private static Map operandMap = new HashMap<>();
+
+    static {
+        for (CecOperand operand : CecOperand.values()) {
+            operandMap.put(operand.operandCode, operand);
+        }
+    }
+
+    public static CecOperand getOperand(int messageId) {
+        return (CecOperand) operandMap.get(messageId);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%02x", operandCode);
+    }
+
+    private CecOperand(int operandCode) {
+        this.operandCode = operandCode;
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index 570ed15..51da280 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -16,10 +16,6 @@
 
 package android.hdmicec.cts;
 
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.util.RunUtil;
@@ -32,10 +28,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.junit.Rule;
 import org.junit.rules.ExternalResource;
 
 /** Class that helps communicate with the cec-client */
@@ -44,8 +38,6 @@
     private static final String CEC_CONSOLE_READY = "waiting for input";
     private static final int MILLISECONDS_TO_READY = 10000;
     private static final int DEFAULT_TIMEOUT = 20000;
-    private static final String HDMI_CEC_FEATURE = "feature:android.hardware.hdmi.cec";
-    private static final int HEXADECIMAL_RADIX = 16;
     private static final int BUFFER_SIZE = 1024;
 
     private Process mCecClient;
@@ -53,29 +45,16 @@
     private BufferedReader mInputConsole;
     private boolean mCecClientInitialised = false;
 
-    private CecDevice targetDevice;
-    private BaseHostJUnit4Test testObject;
+    private LogicalAddress targetDevice;
     private String clientParams[];
 
-    public HdmiCecClientWrapper(CecDevice targetDevice, BaseHostJUnit4Test testObject,
-            String ...clientParams) {
+    public HdmiCecClientWrapper(LogicalAddress targetDevice, String ...clientParams) {
         this.targetDevice = targetDevice;
-        this.testObject = testObject;
         this.clientParams = clientParams;
     }
 
     @Override
     protected void before() throws Throwable {
-        ITestDevice testDevice;
-        testDevice = testObject.getDevice();
-        assertWithMessage("Device not set").that(testDevice).isNotNull();
-
-        assertThat(isHdmiCecFeatureSupported(testDevice)).isTrue();
-
-        String deviceTypeCsv = testDevice.executeShellCommand("getprop ro.hdmi.device_type").trim();
-        List<String> deviceType = Arrays.asList(deviceTypeCsv.replaceAll("\\s+", "").split(","));
-        assertThat(deviceType.contains(targetDevice.getDeviceType())).isTrue();
-
         this.init();
     };
 
@@ -84,20 +63,9 @@
         this.killCecProcess();
     };
 
-    /**
-     * Checks if the HDMI CEC feature is running on the device. Call this function before running
-     * any HDMI CEC tests.
-     * This could throw a DeviceNotAvailableException.
-     */
-    private static boolean isHdmiCecFeatureSupported(ITestDevice device) throws Exception {
-        return device.hasFeature(HDMI_CEC_FEATURE);
-    }
-
     /** Initialise the client */
     private void init() throws Exception {
-        boolean gotExpectedOut = false;
         List<String> commands = new ArrayList();
-        int seconds = 0;
 
         commands.add("cec-client");
         /* "-p 2" starts the client as if it is connected to HDMI port 2, taking the physical
@@ -138,15 +106,15 @@
      * Sends a CEC message with source marked as broadcast to the device passed in the constructor
      * through the output console of the cec-communication channel.
      */
-    public void sendCecMessage(CecMessage message) throws Exception {
-        sendCecMessage(CecDevice.BROADCAST, targetDevice, message, "");
+    public void sendCecMessage(CecOperand message) throws Exception {
+        sendCecMessage(LogicalAddress.BROADCAST, targetDevice, message, "");
     }
 
     /**
      * Sends a CEC message from source device to the device passed in the constructor through the
      * output console of the cec-communication channel.
      */
-    public void sendCecMessage(CecDevice source, CecMessage message) throws Exception {
+    public void sendCecMessage(LogicalAddress source, CecOperand message) throws Exception {
         sendCecMessage(source, targetDevice, message, "");
     }
 
@@ -154,8 +122,8 @@
      * Sends a CEC message from source device to a destination device through the output console of
      * the cec-communication channel.
      */
-    public void sendCecMessage(CecDevice source, CecDevice destination,
-        CecMessage message) throws Exception {
+    public void sendCecMessage(LogicalAddress source, LogicalAddress destination,
+        CecOperand message) throws Exception {
         sendCecMessage(source, destination, message, "");
     }
 
@@ -163,8 +131,8 @@
      * Sends a CEC message from source device to a destination device through the output console of
      * the cec-communication channel with the appended params.
      */
-    public void sendCecMessage(CecDevice source, CecDevice destination,
-            CecMessage message, String params) throws Exception {
+    public void sendCecMessage(LogicalAddress source, LogicalAddress destination,
+            CecOperand message, String params) throws Exception {
         checkCecClient();
         mOutputConsole.write("tx " + source + destination + ":" + message + params);
         mOutputConsole.newLine();
@@ -175,13 +143,13 @@
      * Sends a <USER_CONTROL_PRESSED> and <USER_CONTROL_RELEASED> from source to destination
      * through the output console of the cec-communication channel with the mentioned keycode.
      */
-    public void sendUserControlPressAndRelease(CecDevice source, CecDevice destination,
+    public void sendUserControlPressAndRelease(LogicalAddress source, LogicalAddress destination,
             int keycode, boolean holdKey) throws Exception {
         sendUserControlPress(source, destination, keycode, holdKey);
         /* Sleep less than 200ms between press and release */
         TimeUnit.MILLISECONDS.sleep(100);
         mOutputConsole.write("tx " + source + destination + ":" +
-                              CecMessage.USER_CONTROL_RELEASED);
+                              CecOperand.USER_CONTROL_RELEASED);
         mOutputConsole.flush();
     }
 
@@ -190,11 +158,11 @@
      * cec-communication channel with the mentioned keycode. If holdKey is true, the method will
      * send multiple <UCP> messages to simulate a long press. No <UCR> will be sent.
      */
-    public void sendUserControlPress(CecDevice source, CecDevice destination,
+    public void sendUserControlPress(LogicalAddress source, LogicalAddress destination,
             int keycode, boolean holdKey) throws Exception {
         String key = String.format("%02x", keycode);
         String command = "tx " + source + destination + ":" +
-                CecMessage.USER_CONTROL_PRESSED + ":" + key;
+                CecOperand.USER_CONTROL_PRESSED + ":" + key;
 
         if (holdKey) {
             /* Repeat once between 200ms and 450ms for at least 5 seconds. Since message will be
@@ -218,7 +186,8 @@
      * of the cec-communication channel immediately followed by <UCP> [secondKeycode]. No <UCR>
      *  message is sent.
      */
-    public void sendUserControlInterruptedPressAndHold(CecDevice source, CecDevice destination,
+    public void sendUserControlInterruptedPressAndHold(
+        LogicalAddress source, LogicalAddress destination,
             int firstKeycode, int secondKeycode, boolean holdKey) throws Exception {
         sendUserControlPress(source, destination, firstKeycode, holdKey);
         /* Sleep less than 200ms between press and release */
@@ -262,8 +231,8 @@
     /** Gets all the messages received from the given source device during a period of duration
      * seconds.
      */
-    public List<CecMessage> getAllMessages(CecDevice source, int duration) throws Exception {
-        List<CecMessage> receivedMessages = new ArrayList<>();
+    public List<CecOperand> getAllMessages(LogicalAddress source, int duration) throws Exception {
+        List<CecOperand> receivedOperands = new ArrayList<>();
         long startTime = System.currentTimeMillis();
         long endTime = startTime;
         Pattern pattern = Pattern.compile("(.*>>)(.*?)" +
@@ -274,15 +243,15 @@
             if (mInputConsole.ready()) {
                 String line = mInputConsole.readLine();
                 if (pattern.matcher(line).matches()) {
-                    CecMessage message = getOperandFromMessage(line);
-                    if (!receivedMessages.contains(message)) {
-                        receivedMessages.add(message);
+                    CecOperand operand = CecMessage.getOperand(line);
+                    if (!receivedOperands.contains(operand)) {
+                        receivedOperands.add(operand);
                     }
                 }
             }
             endTime = System.currentTimeMillis();
         }
-        return receivedMessages;
+        return receivedOperands;
     }
 
 
@@ -291,8 +260,8 @@
      * returns the first line that contains that message within default timeout. If the CEC message
      * is not found within the timeout, an exception is thrown.
      */
-    public String checkExpectedOutput(CecMessage expectedMessage) throws Exception {
-        return checkExpectedOutput(CecDevice.BROADCAST, expectedMessage, DEFAULT_TIMEOUT);
+    public String checkExpectedOutput(CecOperand expectedMessage) throws Exception {
+        return checkExpectedOutput(LogicalAddress.BROADCAST, expectedMessage, DEFAULT_TIMEOUT);
     }
 
     /**
@@ -300,8 +269,8 @@
      * communication channel and returns the first line that contains that message within
      * default timeout. If the CEC message is not found within the timeout, an exception is thrown.
      */
-    public String checkExpectedOutput(CecDevice toDevice,
-                                      CecMessage expectedMessage) throws Exception {
+    public String checkExpectedOutput(LogicalAddress toDevice,
+                                      CecOperand expectedMessage) throws Exception {
         return checkExpectedOutput(toDevice, expectedMessage, DEFAULT_TIMEOUT);
     }
 
@@ -310,9 +279,9 @@
      * returns the first line that contains that message within timeoutMillis. If the CEC message
      * is not found within the timeout, an exception is thrown.
      */
-    public String checkExpectedOutput(CecMessage expectedMessage,
+    public String checkExpectedOutput(CecOperand expectedMessage,
                                       long timeoutMillis) throws Exception {
-        return checkExpectedOutput(CecDevice.BROADCAST, expectedMessage, timeoutMillis);
+        return checkExpectedOutput(LogicalAddress.BROADCAST, expectedMessage, timeoutMillis);
     }
 
     /**
@@ -320,7 +289,7 @@
      * communication channel and returns the first line that contains that message within
      * timeoutMillis. If the CEC message is not found within the timeout, an exception is thrown.
      */
-    public String checkExpectedOutput(CecDevice toDevice, CecMessage expectedMessage,
+    public String checkExpectedOutput(LogicalAddress toDevice, CecOperand expectedMessage,
                                        long timeoutMillis) throws Exception {
         checkCecClient();
         long startTime = System.currentTimeMillis();
@@ -349,8 +318,8 @@
      * within the default timeout. If the CEC message is not found within the timeout, function
      * returns without error.
      */
-    public void checkOutputDoesNotContainMessage(CecDevice toDevice,
-            CecMessage incorrectMessage) throws Exception {
+    public void checkOutputDoesNotContainMessage(LogicalAddress toDevice,
+            CecOperand incorrectMessage) throws Exception {
         checkOutputDoesNotContainMessage(toDevice, incorrectMessage, DEFAULT_TIMEOUT);
      }
 
@@ -360,7 +329,7 @@
      * within timeoutMillis. If the CEC message is not found within the timeout, function returns
      * without error.
      */
-    public void checkOutputDoesNotContainMessage(CecDevice toDevice, CecMessage incorrectMessage,
+    public void checkOutputDoesNotContainMessage(LogicalAddress toDevice, CecOperand incorrectMessage,
             long timeoutMillis) throws Exception {
 
         checkCecClient();
@@ -377,168 +346,13 @@
                 if (pattern.matcher(line).matches()) {
                     CLog.v("Found " + incorrectMessage.name() + " in " + line);
                     throw new Exception("Found " + incorrectMessage.name() + " to " + toDevice +
-                            " with params " + getParamsFromMessage(line));
+                            " with params " + CecMessage.getParamsAsString(line));
                 }
             }
             endTime = System.currentTimeMillis();
         }
      }
 
-    /** Gets the hexadecimal ASCII character values of a string. */
-    public String getHexAsciiString(String string) {
-        String asciiString = "";
-        byte[] ascii = string.trim().getBytes();
-
-        for (byte b : ascii) {
-            asciiString.concat(Integer.toHexString(b));
-        }
-
-        return asciiString;
-    }
-
-    public String formatParams(String rawParams) {
-        StringBuilder params = new StringBuilder("");
-        int position = 0;
-        int endPosition = 2;
-
-        do {
-            params.append(":" + rawParams.substring(position, endPosition));
-            position = endPosition;
-            endPosition += 2;
-        } while (endPosition <= rawParams.length());
-        return params.toString();
-    }
-
-    public String formatParams(long rawParam) {
-        StringBuilder params = new StringBuilder("");
-
-        do {
-            params.insert(0, ":" + String.format("%02x", rawParam % 256));
-            rawParam >>= 8;
-        } while (rawParam > 0);
-
-        return params.toString();
-    }
-
-    /** Formats the rawParam into CEC message parameters. The parameters will be at least
-     * minimumNibbles long. */
-    public String formatParams(long rawParam, int minimumNibbles) {
-        StringBuilder params = new StringBuilder("");
-
-        do {
-            params.insert(0, ":" + String.format("%02x", rawParam % 256));
-            rawParam >>= 8;
-            minimumNibbles -= 2;
-        } while (rawParam > 0 || minimumNibbles > 0);
-
-        return params.toString();
-    }
-
-    /** Formats a CEC message in the hex colon format (sd:op:xx:xx). */
-    public String formatMessage(CecDevice source, CecDevice destination, CecMessage message,
-            int params) {
-        StringBuilder cecMessage = new StringBuilder("" + source + destination + ":" + message);
-
-        cecMessage.append(formatParams(params));
-
-        return cecMessage.toString();
-    }
-
-    public static int hexStringToInt(String message) {
-        return Integer.parseInt(message, HEXADECIMAL_RADIX);
-    }
-
-    public String getAsciiStringFromMessage(String message) {
-        String params = getNibbles(message).substring(4);
-        StringBuilder builder = new StringBuilder();
-
-        for (int i = 2; i <= params.length(); i += 2) {
-            builder.append((char) hexStringToInt(params.substring(i - 2, i)));
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * Gets the params from a CEC message.
-     */
-    public int getParamsFromMessage(String message) {
-        return hexStringToInt(getNibbles(message).substring(4));
-    }
-
-    /**
-     * Gets the first 'numNibbles' number of param nibbles from a CEC message.
-     */
-    public int getParamsFromMessage(String message, int numNibbles) {
-        int paramStart = 4;
-        int end = numNibbles + paramStart;
-        return hexStringToInt(getNibbles(message).substring(paramStart, end));
-    }
-
-    /**
-     * From the params of a CEC message, gets the nibbles from position start to position end.
-     * The start and end are relative to the beginning of the params. For example, in the following
-     * message - 4F:82:10:00:04, getParamsFromMessage(message, 0, 4) will return 0x1000 and
-     * getParamsFromMessage(message, 4, 6) will return 0x04.
-     */
-    public int getParamsFromMessage(String message, int start, int end) {
-        return hexStringToInt(getNibbles(message).substring(4).substring(start, end));
-    }
-
-    /**
-     * Gets the source logical address from a CEC message.
-     */
-    public CecDevice getSourceFromMessage(String message) {
-        String param = getNibbles(message).substring(0, 1);
-        return CecDevice.getDevice(hexStringToInt(param));
-    }
-
-    /**
-     * Converts ascii characters to hexadecimal numbers that can be appended to a CEC message as
-     * params. For example, "spa" will be converted to ":73:70:61"
-     */
-    public static String convertStringToHexParams(String rawParams) {
-        StringBuilder params = new StringBuilder("");
-        for (int i = 0; i < rawParams.length(); i++) {
-            params.append(String.format(":%02x", (int) rawParams.charAt(i)));
-        }
-        return params.toString();
-    }
-
-
-    /**
-     * Gets the destination logical address from a CEC message.
-     */
-    public CecDevice getDestinationFromMessage(String message) {
-        String param = getNibbles(message).substring(1, 2);
-        return CecDevice.getDevice(hexStringToInt(param));
-    }
-
-    /**
-     * Gets the operand from a CEC message.
-     */
-    public CecMessage getOperandFromMessage(String message) {
-        String param = getNibbles(message).substring(2, 4);
-        return CecMessage.getMessage(hexStringToInt(param));
-    }
-
-    private String getNibbles(String message) {
-        final String tag1 = "group1";
-        final String tag2 = "group2";
-        String paramsPattern = "(?:.*[>>|<<].*?)" +
-                               "(?<" + tag1 + ">[\\p{XDigit}{2}:]+)" +
-                               "(?<" + tag2 + ">\\p{XDigit}{2})" +
-                               "(?:.*?)";
-        String nibbles = "";
-
-        Pattern p = Pattern.compile(paramsPattern);
-        Matcher m = p.matcher(message);
-        if (m.matches()) {
-            nibbles = m.group(tag1).replace(":", "") + m.group(tag2);
-        }
-        return nibbles;
-    }
-
     /**
      * Kills the cec-client process that was created in init().
      */
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index 0565eae..8af4a8b 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -47,4 +47,11 @@
     public static final int CEC_DEVICE_TYPE_PLAYBACK_DEVICE = 4;
     public static final int CEC_DEVICE_TYPE_AUDIO_SYSTEM = 5;
 
+    /** Feature Abort Reasons */
+    public static final int ABORT_UNRECOGNIZED_MODE = 0;
+    public static final int ABORT_NOT_IN_CORRECT_MODE = 1;
+    public static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
+    public static final int ABORT_INVALID_OPERAND = 3;
+    public static final int ABORT_REFUSED = 4;
+    public static final int ABORT_UNABLE_TO_DETERMINE = 5;
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecDevice.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/LogicalAddress.java
similarity index 73%
rename from hostsidetests/hdmicec/src/android/hdmicec/cts/CecDevice.java
rename to hostsidetests/hdmicec/src/android/hdmicec/cts/LogicalAddress.java
index 3aeb318..5bcf5d1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecDevice.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/LogicalAddress.java
@@ -19,7 +19,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
-public enum CecDevice {
+public enum LogicalAddress {
     TV(0x0),
     RECORDER_1(0x1),
     RECORDER_2(0x2),
@@ -37,17 +37,24 @@
     SPECIFIC_USE(0xe),
     BROADCAST(0xf);
 
-    private final int playerId;
+    private final int address;
     private static Map deviceMap = new HashMap<>();
 
+    // CEC Device feature list
+    public static final String HDMI_CEC_FEATURE = "feature:android.hardware.hdmi.cec";
+    public static final String LEANBACK_FEATURE = "feature:android.software.leanback";
+
+    // CEC Device property list
+    public static final String HDMI_DEVICE_TYPE_PROPERTY = "ro.hdmi.device_type";
+
     @Override
     public String toString() {
-        return Integer.toHexString(this.playerId);
+        return Integer.toHexString(this.address);
     }
 
     static {
-        for (CecDevice device : CecDevice.values()) {
-            deviceMap.put(device.playerId, device);
+        for (LogicalAddress device : LogicalAddress.values()) {
+            deviceMap.put(device.address, device);
         }
     }
 
@@ -75,11 +82,11 @@
         }
     }
 
-    public static CecDevice getDevice(int playerId) {
-        return (CecDevice) deviceMap.get(playerId);
+    public static LogicalAddress getLogicalAddress(int address) {
+        return (LogicalAddress) deviceMap.get(address);
     }
 
-    private CecDevice(int playerId) {
-        this.playerId = playerId;
+    private LogicalAddress(int address) {
+        this.address = address;
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredFeatureRule.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredFeatureRule.java
new file mode 100644
index 0000000..8fb70ec
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredFeatureRule.java
@@ -0,0 +1,57 @@
+/*
+ * 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.hdmicec.cts;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule to check if the required device feature is available on device.
+ */
+public class RequiredFeatureRule implements TestRule {
+
+    private final BaseHostJUnit4Test mTest;
+    private final String mFeature;
+
+    public RequiredFeatureRule(BaseHostJUnit4Test test, String feature) {
+        mTest = test;
+        mFeature = feature;
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                ITestDevice testDevice = mTest.getDevice();
+                // Checks if the device is available.
+                assumeTrue("Test device is not available", testDevice != null);
+                // Checks if the requested feature is available on the device.
+                assumeTrue(mFeature + " not present in DUT " + testDevice.getSerialNumber(),
+                    testDevice.hasFeature(mFeature));
+                base.evaluate();
+            }
+        };
+    }
+}
+
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredPropertyRule.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredPropertyRule.java
new file mode 100644
index 0000000..f3998adb
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredPropertyRule.java
@@ -0,0 +1,104 @@
+/*
+ * 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.hdmicec.cts;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule class that allows for checking device property value against required values.
+ * Static functions in this class can be used to check properties as a list or single value.
+ */
+public class RequiredPropertyRule implements TestRule {
+
+    // Do not allow instantiation.
+    private RequiredPropertyRule(){}
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+            }
+        };
+    }
+
+    private static String getDevicePropertyValue(BaseHostJUnit4Test test, String propertyName)
+        throws Throwable {
+        ITestDevice testDevice = test.getDevice();
+        // Checks if the device is available.
+        assumeTrue("Test device is not available", testDevice != null);
+        return testDevice.executeShellCommand("getprop " + propertyName).trim();
+
+    }
+
+    public static RequiredPropertyRule isEqualTo(final BaseHostJUnit4Test test,
+        final String propertyName, final String propertyValue) {
+        return new RequiredPropertyRule() {
+            @Override
+            public Statement apply(final Statement base, final Description description) {
+                return new Statement() {
+                    @Override
+                    public void evaluate() throws Throwable {
+                        String deviceProperty = getDevicePropertyValue(test, propertyName);
+                        assumeTrue("Required property " + propertyName + " = " + propertyValue
+                                + " is not present in " + deviceProperty
+                                + " of device " + test.getDevice().getSerialNumber(),
+                            deviceProperty.equals(propertyValue));
+                        base.evaluate();
+                    }
+                };
+            }
+        };
+    }
+
+    public static RequiredPropertyRule asCsvContainsValue(final BaseHostJUnit4Test test,
+        final String propertyName, final String propertyValue) {
+        return new RequiredPropertyRule() {
+            @Override
+            public Statement apply(final Statement base, final Description description) {
+                return new Statement() {
+                    @Override
+                    public void evaluate() throws Throwable {
+                        List<String> deviceProperties =
+                            Arrays.asList(getDevicePropertyValue(test, propertyName)
+                                .replaceAll("\\s+", "")
+                                .split(","));
+                        assumeTrue(
+                            "Required property " + propertyName + " = " + propertyValue
+                                + " is not present in " + deviceProperties.toString()
+                                + " of device " + test.getDevice().getSerialNumber(),
+                            deviceProperties.contains(propertyValue));
+                        base.evaluate();
+                    }
+                };
+            }
+        };
+    }
+}
+
+
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
index eaaf6df..897a35e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
@@ -18,31 +18,46 @@
 
 import static org.junit.Assume.assumeNoException;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /** HDMI CEC test to test audio return channel control (Section 11.2.17) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecAudioReturnChannelControlTest extends BaseHostJUnit4Test {
-    private static final CecDevice AUDIO_DEVICE = CecDevice.AUDIO_SYSTEM;
+
+    private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE);
 
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                AUDIO_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
 
     private void checkArcIsInitiated(){
         try {
-            hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                    CecMessage.REQUEST_ARC_INITIATION);
-            hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
+            hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                    CecOperand.REQUEST_ARC_INITIATION);
+            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
         } catch(Exception e) {
             assumeNoException(e);
         }
@@ -55,13 +70,13 @@
      */
     @Test
     public void cect_11_2_17_1_InitiateArc() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                CecMessage.REPORT_PHYSICAL_ADDRESS,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                CecOperand.REPORT_PHYSICAL_ADDRESS,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
                 HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
         getDevice().executeShellCommand("reboot");
         getDevice().waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
     }
 
     /**
@@ -72,13 +87,13 @@
     @Test
     public void cect_11_2_17_2_TerminateArc() throws Exception {
         checkArcIsInitiated();
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                CecMessage.REPORT_PHYSICAL_ADDRESS,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                CecOperand.REPORT_PHYSICAL_ADDRESS,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
                         HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
         getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
         try {
-            hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.TERMINATE_ARC);
+            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TERMINATE_ARC);
         } finally {
             getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
         }
@@ -91,13 +106,13 @@
      */
     @Test
     public void cect_11_2_17_3_RequestToInitiateArc() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                CecMessage.REPORT_PHYSICAL_ADDRESS,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                CecOperand.REPORT_PHYSICAL_ADDRESS,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
                 HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.REQUEST_ARC_INITIATION);
-        hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.REQUEST_ARC_INITIATION);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
     }
 
     /**
@@ -108,12 +123,12 @@
     @Test
     public void cect_11_2_17_4_RequestToTerminateArc() throws Exception {
         checkArcIsInitiated();
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                CecMessage.REPORT_PHYSICAL_ADDRESS,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                CecOperand.REPORT_PHYSICAL_ADDRESS,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
                         HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.REQUEST_ARC_TERMINATION);
-        hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.TERMINATE_ARC);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.REQUEST_ARC_TERMINATION);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TERMINATE_ARC);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
new file mode 100644
index 0000000..f7644f2
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
@@ -0,0 +1,357 @@
+/*
+ * 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.hdmicec.cts.audio;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+
+/** HDMI CEC test to verify that device ignores invalid messages (Section 12) */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecInvalidMessagesTest extends BaseHostJUnit4Test {
+
+    private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
+    private static final String PROPERTY_LOCALE = "persist.sys.locale";
+
+    /** The package name of the APK. */
+    private static final String PACKAGE = "android.hdmicec.app";
+
+    /** The class name of the main activity in the APK. */
+    private static final String CLASS = "HdmiCecKeyEventCapture";
+
+    /** The command to launch the main activity. */
+    private static final String START_COMMAND = String.format(
+            "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+
+    /** The command to clear the main activity. */
+    private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE);
+
+    private static final int WAIT_TIME = 10;
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE);
+
+    @Rule
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                AUDIO_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
+
+    private String getSystemLocale() throws Exception {
+        ITestDevice device = getDevice();
+        return device.executeShellCommand("getprop " + PROPERTY_LOCALE).trim();
+    }
+
+    private void setSystemLocale(String locale) throws Exception {
+        ITestDevice device = getDevice();
+        device.executeShellCommand("setprop " + PROPERTY_LOCALE + " " + locale);
+    }
+
+    private boolean isLanguageEditable() throws Exception {
+        String val = getDevice().executeShellCommand("getprop ro.hdmi.set_menu_language");
+        return val.trim().equals("true") ? true : false;
+    }
+
+    private static String extractLanguage(String locale) {
+        return locale.split("[^a-zA-Z]")[0];
+    }
+
+    private void checkArcIsInitiated(){
+        try {
+            hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.REQUEST_ARC_INITIATION);
+            hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
+        } catch(Exception e) {
+            assumeNoException(e);
+        }
+    }
+
+    private void logShouldNotContain(String expectedOut) throws Exception {
+        ITestDevice device = getDevice();
+        TimeUnit.SECONDS.sleep(WAIT_TIME);
+        String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
+        // Search for string.
+        String testString = "";
+        Scanner in = new Scanner(logs);
+        while (in.hasNextLine()) {
+            String line = in.nextLine();
+            if(line.startsWith("I/" + CLASS)) {
+                testString = line.split(":")[1].trim();
+                break;
+            }
+        }
+        device.executeAdbCommand("logcat", "-c");
+        assertThat(testString).doesNotContain(expectedOut);
+    }
+
+    /**
+     * Test 12-1
+     * Tests that the device ignores every broadcast only message that is received as
+     * directly addressed.
+     */
+    @Test
+    public void cect_12_1_BroadcastReceivedAsDirectlyAddressed() throws Exception {
+        /* <Set Menu Language> */
+        assumeTrue(isLanguageEditable());
+        final String locale = getSystemLocale();
+        final String originalLanguage = extractLanguage(locale);
+        final String language = originalLanguage.equals("spa") ? "eng" : "spa";
+        try {
+            hdmiCecClient.sendCecMessage(
+                    LogicalAddress.TV,
+                    AUDIO_DEVICE,
+                    CecOperand.SET_MENU_LANGUAGE,
+                    CecMessage.convertStringToHexParams(language));
+            assertThat(originalLanguage).isEqualTo(extractLanguage(getSystemLocale()));
+        } finally {
+            // If the language was incorrectly changed during the test, restore it.
+            setSystemLocale(locale);
+        }
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GET_CEC_VERSION> if received as
+     * a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_getCecVersion() throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GET_CEC_VERSION);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.TV,
+                CecOperand.CEC_VERSION);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GIVE_PHYSICAL_ADDRESS> if received
+     * as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePhysicalAddress()
+        throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GIVE_PHYSICAL_ADDRESS);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.BROADCAST,
+                CecOperand.REPORT_PHYSICAL_ADDRESS);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GIVE_AUDIO_STATUS> if received as
+     * a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveAudioStatus() throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GIVE_AUDIO_STATUS);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.TV,
+                CecOperand.REPORT_AUDIO_STATUS);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GIVE_POWER_STATUS> if received as
+     * a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePowerStatus() throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GIVE_POWER_STATUS);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.TV,
+                CecOperand.REPORT_POWER_STATUS);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GIVE_DEVICE_VENDOR_ID> if received
+     * as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveDeviceVendorId()
+        throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GIVE_DEVICE_VENDOR_ID);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.BROADCAST,
+                CecOperand.DEVICE_VENDOR_ID);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GIVE_OSD_NAME> if received as
+     * a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveOsdName() throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GIVE_OSD_NAME);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.TV,
+                CecOperand.SET_OSD_NAME);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <GIVE_SYSTEM_AUDIO_MODE_STATUS> if
+     * received as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveSystemAudioModeStatus()
+        throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.TV,
+                CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <REQUEST_SHORT_AUDIO_DESCRIPTOR> if
+     * received as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_requestShortAudioDescriptor()
+        throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR,
+                CecMessage.formatParams("01020304"));
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.TV,
+                CecOperand.REPORT_SHORT_AUDIO_DESCRIPTOR);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <SYSTEM_AUDIO_MODE_REQUEST> if
+     * received as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_systemAudioModeRequest()
+        throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.BROADCAST,
+                CecOperand.SET_SYSTEM_AUDIO_MODE);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <REQUEST_ARC_INITIATION> if
+     * received as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_requestArcInitiation()
+        throws Exception {
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.REQUEST_ARC_INITIATION);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.BROADCAST,
+                CecOperand.INITIATE_ARC);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <REQUEST_ARC_TERMINATION> if
+     * received as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_requestArcTermination()
+        throws Exception {
+        checkArcIsInitiated();
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                CecOperand.REQUEST_ARC_TERMINATION);
+        hdmiCecClient.checkOutputDoesNotContainMessage(
+                LogicalAddress.BROADCAST,
+                CecOperand.TERMINATE_ARC);
+    }
+
+    /**
+     * Test 12-2
+     * Tests that the device ignores directly addressed message <USER_CONTROL_PRESSED> if received
+     * as a broadcast message
+     */
+    @Test
+    public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_userControlPressed()
+        throws Exception {
+        ITestDevice device = getDevice();
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND);
+        hdmiCecClient.sendUserControlPressAndRelease(
+                LogicalAddress.TV,
+                LogicalAddress.BROADCAST,
+                HdmiCecConstants.CEC_CONTROL_UP,
+                false);
+        logShouldNotContain("Short press KEYCODE_DPAD_UP");
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
index c81b550..d0f052d 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
@@ -18,26 +18,41 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /** HDMI CEC test to verify logical address after device reboot (Section 10.2.5) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecLogicalAddressTest extends BaseHostJUnit4Test {
-    private static final CecDevice AUDIO_DEVICE = CecDevice.AUDIO_SYSTEM;
+
+    private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE);
 
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                AUDIO_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 10.2.5-1
@@ -49,7 +64,7 @@
         ITestDevice device = getDevice();
         device.executeShellCommand("reboot");
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
-        assertThat(hdmiCecClient.getSourceFromMessage(message)).isEqualTo(AUDIO_DEVICE);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+        assertThat(CecMessage.getSource(message)).isEqualTo(AUDIO_DEVICE);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
index 9931e26..e7e0be4 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
@@ -18,15 +18,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -51,9 +54,18 @@
 
     private static final int WAIT_TIME = 10;
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.AUDIO_SYSTEM);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-            new HdmiCecClientWrapper(CecDevice.AUDIO_SYSTEM, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.AUDIO_SYSTEM.getDeviceType()))
+            .around(hdmiCecClient);
 
     private void lookForLog(String expectedOut) throws Exception {
         ITestDevice device = getDevice();
@@ -87,22 +99,22 @@
         device.executeAdbCommand("logcat", "-c");
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_UP, false);
         lookForLog("Short press KEYCODE_DPAD_UP");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_DOWN, false);
         lookForLog("Short press KEYCODE_DPAD_DOWN");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_LEFT, false);
         lookForLog("Short press KEYCODE_DPAD_LEFT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_RIGHT, false);
         lookForLog("Short press KEYCODE_DPAD_RIGHT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_SELECT, false);
         lookForLog("Short press KEYCODE_DPAD_CENTER");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_BACK, false);
         lookForLog("Short press KEYCODE_BACK");
     }
@@ -121,22 +133,22 @@
         device.executeAdbCommand("logcat", "-c");
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_UP, true);
         lookForLog("Long press KEYCODE_DPAD_UP");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_DOWN, true);
         lookForLog("Long press KEYCODE_DPAD_DOWN");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_LEFT, true);
         lookForLog("Long press KEYCODE_DPAD_LEFT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_RIGHT, true);
         lookForLog("Long press KEYCODE_DPAD_RIGHT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_SELECT, true);
         lookForLog("Long press KEYCODE_DPAD_CENTER");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_BACK, true);
         lookForLog("Long press KEYCODE_BACK");
     }
@@ -155,22 +167,22 @@
         device.executeAdbCommand("logcat", "-c");
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
-        hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_UP, true);
         lookForLog("Long press KEYCODE_DPAD_UP");
-        hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_DOWN, true);
         lookForLog("Long press KEYCODE_DPAD_DOWN");
-        hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_LEFT, true);
         lookForLog("Long press KEYCODE_DPAD_LEFT");
-        hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_RIGHT, true);
         lookForLog("Long press KEYCODE_DPAD_RIGHT");
-        hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_SELECT, true);
         lookForLog("Long press KEYCODE_DPAD_CENTER");
-        hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+        hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
                 HdmiCecConstants.CEC_CONTROL_BACK, true);
         lookForLog("Long press KEYCODE_BACK");
     }
@@ -190,28 +202,28 @@
         device.executeAdbCommand("logcat", "-c");
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
-        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
-                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_UP,
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+                LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_UP,
                 HdmiCecConstants.CEC_CONTROL_BACK, true);
         lookForLog("Long press KEYCODE_DPAD_UP");
-        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
-                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_DOWN,
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+                LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_DOWN,
                 HdmiCecConstants.CEC_CONTROL_UP, true);
         lookForLog("Long press KEYCODE_DPAD_DOWN");
-        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
-                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_LEFT,
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+                LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_LEFT,
                 HdmiCecConstants.CEC_CONTROL_DOWN, true);
         lookForLog("Long press KEYCODE_DPAD_LEFT");
-        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
-                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_RIGHT,
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+                LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_RIGHT,
                 HdmiCecConstants.CEC_CONTROL_LEFT, true);
         lookForLog("Long press KEYCODE_DPAD_RIGHT");
-        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
-                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_SELECT,
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+                LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_SELECT,
                 HdmiCecConstants.CEC_CONTROL_RIGHT, true);
         lookForLog("Long press KEYCODE_DPAD_CENTER");
-        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
-                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_BACK,
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+                LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_BACK,
                 HdmiCecConstants.CEC_CONTROL_SELECT, true);
         lookForLog("Long press KEYCODE_BACK");
     }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
index fe4ccf5..4cb7b97 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
@@ -18,13 +18,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assume.assumeTrue;
 
+import com.google.common.collect.Lists;
 import com.google.common.collect.Range;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -32,11 +37,17 @@
 
 import org.junit.After;
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Scanner;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /** HDMI CEC test to test system audio mode (Section 11.2.15) */
 @RunWith(DeviceJUnit4ClassRunner.class)
@@ -48,6 +59,9 @@
     /** The class name of the main activity in the APK. */
     private static final String CLASS = "HdmiCecAudioManager";
 
+    /** The tag of the SadConfigurationReaderTest launched by the APK. */
+    private static final String SAD_READER = "SadConfigurationReaderTest";
+
     /** The command to launch the main activity. */
     private static final String START_COMMAND = String.format(
             "am start -n %s/%s.%s -a ", PACKAGE, PACKAGE, CLASS);
@@ -56,13 +70,27 @@
     private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE);
 
     private static final int WAIT_TIME = 10;
-    private static final CecDevice AUDIO_DEVICE = CecDevice.AUDIO_SYSTEM;
+    private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
     private static final int ON = 0x1;
     private static final int OFF = 0x0;
+    private static final int MAX_AUDIO_FORMATS = 4;
+    private static final int MAX_VALID_AUDIO_FORMATS = 2;
+    private static final String SAD_CONFIGURATION_MARKER = "Supported Audio Formats";
+
+    private List<Integer> mSupportedAudioFormats = null;
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE, "-t", "t");
 
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-            new HdmiCecClientWrapper(AUDIO_DEVICE, this, "-t", "t");
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                AUDIO_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
 
     private void lookForLogFromHdmiCecAudioManager(String expectedOut) throws Exception {
         ITestDevice device = getDevice();
@@ -82,6 +110,71 @@
         assertThat(testString).isEqualTo(expectedOut);
     }
 
+    private void lookForLogFromSadConfigurationReaderTest(String expectedOut) throws Exception {
+        ITestDevice device = getDevice();
+        TimeUnit.SECONDS.sleep(WAIT_TIME);
+        String logs =
+                device.executeAdbCommand("logcat", "-v", "brief", "-d", SAD_READER + ":I", "*:S");
+        // Search for string.
+        String testString = "";
+        Scanner in = new Scanner(logs);
+        while (in.hasNextLine()) {
+            String line = in.nextLine();
+            if (line.startsWith("I/" + SAD_READER)) {
+                testString = line.split(":")[1].trim();
+                if (testString.equals(SAD_CONFIGURATION_MARKER)) {
+                    List<String> mFormatsLine =
+                            Arrays.asList(in.nextLine().split(":")[1].trim().split(", "));
+                    List<String> mCodecSADsLine =
+                            Arrays.asList(in.nextLine().split(":")[1].trim().split(", "));
+                    mSupportedAudioFormats =
+                            Lists.transform(mFormatsLine, fl -> Integer.parseInt(fl));
+                    break;
+                }
+            }
+        }
+        device.executeAdbCommand("logcat", "-c");
+        assumeTrue(testString.equals(expectedOut));
+    }
+
+    private String getRequestSadFormatsParams(boolean sendValidFormats) throws Exception {
+        ITestDevice device = getDevice();
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND + "android.hdmicec.app.GET_SUPPORTED_SAD_FORMATS");
+        lookForLogFromSadConfigurationReaderTest(SAD_CONFIGURATION_MARKER);
+
+        // Create a list of all the audio format codes according to CEA-861-D. Remove the supported
+        // audio format codes from it, to get the unsupported audio format codes.
+        List<Integer> mAllCodecFormats =
+                IntStream.range(1, 15).boxed().collect(Collectors.toList());
+        List<Integer> unsupportedAudioFormats = new ArrayList<>();
+        unsupportedAudioFormats.addAll(mAllCodecFormats);
+        unsupportedAudioFormats.removeAll(mSupportedAudioFormats);
+        // Create params message for REQUEST_SHORT_AUDIO_DESCRIPTOR
+        String messageParams = "";
+        int i = 0;
+        int listIndex = 0;
+        if (sendValidFormats) {
+            while (i < Math.min(MAX_VALID_AUDIO_FORMATS, mSupportedAudioFormats.size())) {
+                messageParams +=
+                        CecMessage.formatParams(mSupportedAudioFormats.get(listIndex), 2);
+                i++;
+                listIndex++;
+            }
+            listIndex = 0;
+        }
+        while (i < Math.min(MAX_AUDIO_FORMATS, unsupportedAudioFormats.size())) {
+            messageParams += CecMessage.formatParams(unsupportedAudioFormats.get(listIndex), 2);
+            i++;
+            listIndex++;
+        }
+        return messageParams;
+    }
+
     private void muteDevice() throws Exception {
         ITestDevice device = getDevice();
         // Clear activity
@@ -126,85 +219,50 @@
     }
 
     public void sendSystemAudioModeTermination() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST);
     }
 
     public void sendSystemAudioModeInitiation() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
-                HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+                    HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
     }
 
     private int getDutAudioStatus() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.GIVE_AUDIO_STATUS);
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                CecMessage.REPORT_AUDIO_STATUS);
-        return hdmiCecClient.getParamsFromMessage(message);
-    }
-
-    private void reportAudioStatus_0_unmuted() throws  Exception {
-        unmuteDevice();
-        setDeviceVolume(0);
-        int reportedVolume = getDutAudioStatus();
-        /* Allow for a range of volume, since the actual volume set will depend on the device's
-        volume resolution. */
-        assertThat(reportedVolume).isEqualTo(0);
-    }
-
-    private void reportAudioStatus_50_unmuted() throws  Exception {
-        unmuteDevice();
-        setDeviceVolume(50);
-        int reportedVolume = (getDutAudioStatus() * 100) / 127;
-        /* Allow for a range of volume, since the actual volume set will depend on the device's
-        volume resolution. */
-        assertThat(reportedVolume).isIn(Range.closed(40, 60));
-    }
-
-    private void reportAudioStatus_100_unmuted() throws  Exception {
-        unmuteDevice();
-        setDeviceVolume(100);
-        int reportedVolume = getDutAudioStatus();
-        /* Allow for a range of volume, since the actual volume set will depend on the device's
-        volume resolution. */
-        assertThat(reportedVolume).isEqualTo(100);
-    }
-
-    private void reportAudioStatusMuted() throws  Exception {
-        muteDevice();
-        int reportedVolume = getDutAudioStatus();
-        /* If device is muted, the 8th bit of CEC message parameters is set and the volume will
-        be greater than 127. */
-        assertWithMessage("Device not muted").that(reportedVolume).isGreaterThan(127);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.GIVE_AUDIO_STATUS);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                CecOperand.REPORT_AUDIO_STATUS);
+        return CecMessage.getParams(message);
     }
 
     private void initateSystemAudioModeFromTuner() throws Exception {
         getDevice().reboot();
-        hdmiCecClient.sendCecMessage(CecDevice.TUNER_1, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+        hdmiCecClient.sendCecMessage(LogicalAddress.TUNER_1, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
                         HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
         handleSetSystemAudioModeOnToTv();
     }
 
     private void handleSetSystemAudioModeOnToTv() throws Exception {
-        hdmiCecClient.checkExpectedOutput(CecMessage.REQUEST_ACTIVE_SOURCE);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST, CecMessage.ACTIVE_SOURCE,
-                hdmiCecClient.formatParams("2000"));
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        hdmiCecClient.checkExpectedOutput(CecOperand.REQUEST_ACTIVE_SOURCE);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE,
+                CecMessage.formatParams("2000"));
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
     }
 
     private void initiateSystemAudioModeFromDut() throws Exception {
         getDevice().reboot();
-        hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
-                CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
-        hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
-                CecMessage.ARC_INITIATED);
+        hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
+                CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
+                CecOperand.ARC_INITIATED);
         handleSetSystemAudioModeOnToTv();
     }
 
@@ -220,18 +278,18 @@
      */
     @Test
     public void cect_11_2_15_1_SystemAudioModeRequestAsFollower() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
 
         /* Repeat test for device 0x3 (TUNER_1) */
-        hdmiCecClient.sendCecMessage(CecDevice.TUNER_1, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
-        message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TUNER_1, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
+        message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
     }
 
     /**
@@ -242,8 +300,8 @@
     @Test
     public void cect_11_2_15_2_SystemAudioModeWithFeatureInitiation() throws Exception {
         initiateSystemAudioModeFromDut();
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
     }
 
     /**
@@ -255,10 +313,10 @@
     @Test
     public void cect_11_2_15_3_SystemAudioModeWithFeatureAbort() throws Exception {
         initiateSystemAudioModeFromDut();
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.FEATURE_ABORT,
-                hdmiCecClient.formatParams(CecMessage.SET_SYSTEM_AUDIO_MODE + "04"));
-        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
-                CecMessage.SET_SYSTEM_AUDIO_MODE);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.FEATURE_ABORT,
+                CecMessage.formatParams(CecOperand.SET_SYSTEM_AUDIO_MODE + "04"));
+        hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+                CecOperand.SET_SYSTEM_AUDIO_MODE);
         //The DUT will need a reboot here so it'll forget the feature abort from the previous
         // message. Else it may not respond correctly with a SET_SYSTEM_AUDIO_MODE message
         // in future tests.
@@ -273,13 +331,13 @@
     @Test
     public void cect_11_2_15_4_SystemAudioModeStatusOn() throws Exception {
         sendSystemAudioModeInitiation();
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
-        message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                CecMessage.SYSTEM_AUDIO_MODE_STATUS);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
     }
 
     /**
@@ -290,11 +348,11 @@
     @Test
     public void cect_11_2_15_5_SetSystemAudioModeOff() throws Exception {
         sendSystemAudioModeInitiation();
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
         sendSystemAudioModeTermination();
-        message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+        message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
     }
 
     /**
@@ -307,11 +365,11 @@
         try {
             getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
             sendSystemAudioModeInitiation();
-            String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-            assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+            String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+            assertThat(CecMessage.getParams(message)).isEqualTo(ON);
             getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
-            message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-            assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+            message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+            assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
         } finally {
             getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
         }
@@ -324,13 +382,13 @@
     */
    @Test
     public void cect_11_2_15_7_SystemAudioModeStatusOff() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SET_SYSTEM_AUDIO_MODE, hdmiCecClient.formatParams(OFF));
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                CecMessage.SYSTEM_AUDIO_MODE_STATUS);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SET_SYSTEM_AUDIO_MODE, CecMessage.formatParams(OFF));
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+        assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
     }
 
     /**
@@ -341,36 +399,113 @@
     @Test
     public void cect_11_2_15_8_HandleUcpMute() throws Exception {
         unmuteDevice();
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, AUDIO_DEVICE,
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, AUDIO_DEVICE,
                 HdmiCecConstants.CEC_CONTROL_MUTE, false);
         assertWithMessage("Device is not muted").that(isDeviceMuted()).isTrue();
     }
 
     /**
      * Test 11.2.15-9
-     * Tests that the device responds with a <Report Audio Status> message to a
-     * <Give Audio Status> message.
+     * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+     * to a <Give Audio Status> message when volume is set to 0% and not muted.
      */
     @Test
-    public void cect_11_2_15_9_ReportAudioStatus() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
-                HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
-        /** Set volume to 0 % and check the <Report Audio Status> */
-        reportAudioStatus_0_unmuted();
+    public void cect_11_2_15_9_ReportAudioStatus_0_unmuted() throws Exception {
+        sendSystemAudioModeInitiation();
+        unmuteDevice();
+        setDeviceVolume(0);
+        int reportedVolume = getDutAudioStatus();
+        assertThat(reportedVolume).isEqualTo(0);
+    }
 
-        /** Set volume to 50 % and check the <Report Audio Status> */
-        reportAudioStatus_50_unmuted();
+    /**
+     * Test 11.2.15-9
+     * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+     * to a <Give Audio Status> message when volume is set to 50% and not muted.
+     */
+    @Test
+    public void cect_11_2_15_9_ReportAudioStatus_50_unmuted() throws Exception {
+        sendSystemAudioModeInitiation();
+        unmuteDevice();
+        setDeviceVolume(50);
+        int reportedVolume = getDutAudioStatus();
+        /* Allow for a range of volume, since the actual volume set will depend on the device's
+        volume resolution. */
+        assertThat(reportedVolume).isIn(Range.closed(46, 54));
+    }
 
-        /** Set volume to 100 % and check the <Report Audio Status> */
-        reportAudioStatus_100_unmuted();
+    /**
+     * Test 11.2.15-9
+     * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+     * to a <Give Audio Status> message when volume is set to 100% and not muted.
+     */
+    @Test
+    public void cect_11_2_15_9_ReportAudioStatus_100_unmuted() throws Exception {
+        sendSystemAudioModeInitiation();
+        unmuteDevice();
+        setDeviceVolume(100);
+        int reportedVolume = getDutAudioStatus();
+        assertThat(reportedVolume).isEqualTo(100);
+    }
 
-        /** Mute volume and check the <Report Audio Status> */
-        reportAudioStatusMuted();
+    /**
+     * Test 11.2.15-9
+     * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+     * to a <Give Audio Status> message when volume is muted.
+     */
+    @Test
+    public void cect_11_2_15_9_ReportAudioStatusMuted() throws Exception {
+        sendSystemAudioModeInitiation();
+        muteDevice();
+        int reportedVolume = getDutAudioStatus();
+        /* If device is muted, the 8th bit of CEC message parameters is set and the volume will
+        be greater than 127. */
+        assertWithMessage("Device not muted").that(reportedVolume).isGreaterThan(127);
+    }
+
+    /**
+     * Test 11.2.15-13
+     * Tests that the device responds to a <Request Short Audio Descriptor> message with a
+     * <Report Short Audio Descriptor> message with only those audio descriptors that are supported.
+     */
+    @Test
+    public void cect_11_2_15_13_ValidShortAudioDescriptor() throws Exception {
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR, getRequestSadFormatsParams(true));
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                CecOperand.REPORT_SHORT_AUDIO_DESCRIPTOR);
+        /* Each Short Audio Descriptor is 3 bytes long. In the first byte of the params, bits 3-6
+         * will have the audio format. Bit 7 will always 0 for audio format defined in CEA-861-D.
+         * Bits 0-2 represent (Max number of channels - 1). Discard bits 0-2 and check for the
+         * format.
+         * Iterate the params by 3 bytes(6 nibbles) and extract only the first byte(2 nibbles).
+         */
+        for (int i = 0; i < Math.min(mSupportedAudioFormats.size(), MAX_VALID_AUDIO_FORMATS); i++) {
+            int audioFormat =
+                    CecMessage.getParams(message, 6 * i, 6 * i + 2) >>> 3;
+            assertWithMessage("Could not find audio format " + audioFormat)
+                    .that(mSupportedAudioFormats).contains(audioFormat);
+        }
+    }
+
+    /**
+     * Test 11.2.15-14
+     * Tests that the device responds to a <Request Short Audio Descriptor> message with a
+     * <Feature Abort> [“Invalid Operand”] when a <Request Short Audio Descriptor> message is
+     * received with a single [Audio Format ID][Audio Format Code] pair that is not supported.
+     */
+    @Test
+    public void cect_11_2_15_14_InvalidShortAudioDescriptor() throws Exception {
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR, getRequestSadFormatsParams(false));
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.FEATURE_ABORT);
+        assertThat(CecOperand.getOperand(CecMessage.getParams(message, 2)))
+                .isEqualTo(CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR);
+        assertThat(CecMessage.getParams(message, 2, 4))
+                .isEqualTo(HdmiCecConstants.ABORT_INVALID_OPERAND);
     }
 
     /**
@@ -382,14 +517,14 @@
     public void cect_11_2_15_16_UnmuteForSystemAudioRequestOn() throws Exception {
         muteDevice();
         sendSystemAudioModeTermination();
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
-                HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
-        message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+                    HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
+        message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
         assertWithMessage("Device muted").that(isDeviceMuted()).isFalse();
     }
 
@@ -400,15 +535,15 @@
      */
     @Test
     public void cect_11_2_15_17_MuteForSystemAudioRequestOff() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
-                CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
-                hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
-                HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+                CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+                CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+                    HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
         sendSystemAudioModeTermination();
-        message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+        message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
         assertWithMessage("Device not muted").that(isDeviceMuted()).isTrue();
     }
 
@@ -421,10 +556,10 @@
     @Test
     public void cect_11_2_15_18_SystemAudioModeWithFeatureAbortWithinTime() throws Exception {
         initiateSystemAudioModeFromDut();
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.FEATURE_ABORT,
-                hdmiCecClient.formatParams(CecMessage.SET_SYSTEM_AUDIO_MODE + "04"));
-        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
-                CecMessage.SET_SYSTEM_AUDIO_MODE);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.FEATURE_ABORT,
+                CecMessage.formatParams(CecOperand.SET_SYSTEM_AUDIO_MODE + "04"));
+        hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+                CecOperand.SET_SYSTEM_AUDIO_MODE);
     }
 
     /**
@@ -437,9 +572,9 @@
     @Test
     public void cect_11_2_15_19_SystemAudioModeWithFeatureAbortWithinTime() throws Exception {
         initateSystemAudioModeFromTuner();
-        hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.FEATURE_ABORT,
-                hdmiCecClient.formatParams(CecMessage.SET_SYSTEM_AUDIO_MODE + "04"));
-        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
-                CecMessage.SET_SYSTEM_AUDIO_MODE);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.FEATURE_ABORT,
+                CecMessage.formatParams(CecOperand.SET_SYSTEM_AUDIO_MODE + "04"));
+        hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+            CecOperand.SET_SYSTEM_AUDIO_MODE);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java
index 05b2071..ef10425 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java
@@ -18,26 +18,40 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /** HDMI CEC tests related to the device reporting the device OSD name (Section 11.2.11) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecDeviceOsdNameTest extends BaseHostJUnit4Test {
-    private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+    private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
 
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.11-1a
@@ -53,9 +67,9 @@
         if (deviceName.length() > nameLength) {
             deviceName = deviceName.substring(0, nameLength).trim();
         }
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_OSD_NAME);
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.SET_OSD_NAME);
-        assertThat(hdmiCecClient.getAsciiStringFromMessage(message)).isEqualTo(deviceName);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_OSD_NAME);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.SET_OSD_NAME);
+        assertThat(CecMessage.getAsciiString(message)).isEqualTo(deviceName);
     }
 
     /**
@@ -70,10 +84,10 @@
         String originalName = device.executeShellCommand("settings get global device_name").trim();
         try {
             device.executeShellCommand("settings put global device_name '" + testName + "'");
-            hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_OSD_NAME);
-            String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                    CecMessage.SET_OSD_NAME);
-            assertThat(hdmiCecClient.getAsciiStringFromMessage(message)).isEqualTo(testName);
+            hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_OSD_NAME);
+            String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                    CecOperand.SET_OSD_NAME);
+            assertThat(CecMessage.getAsciiString(message)).isEqualTo(testName);
         } finally {
             device.executeShellCommand("settings put global device_name '" + originalName + "'");
         }
@@ -85,11 +99,11 @@
      */
     @Test
     public void cect_11_2_11_2_UnregisteredDeviceGiveOsdNameTest() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.PLAYBACK_1, CecMessage.GIVE_OSD_NAME);
-        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.PLAYBACK_1,
-                CecMessage.SET_OSD_NAME);
-        hdmiCecClient.sendCecMessage(CecDevice.BROADCAST, CecMessage.GIVE_OSD_NAME);
-        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
-                CecMessage.SET_OSD_NAME);
+        hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1, CecOperand.GIVE_OSD_NAME);
+        hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.PLAYBACK_1,
+                CecOperand.SET_OSD_NAME);
+        hdmiCecClient.sendCecMessage(LogicalAddress.BROADCAST, CecOperand.GIVE_OSD_NAME);
+        hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+                CecOperand.SET_OSD_NAME);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java
index 89d5304..d48a6cd 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java
@@ -18,27 +18,41 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /** HDMI CEC test to verify physical address after device reboot (Section 10.2.3) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecLogicalAddressTest extends BaseHostJUnit4Test {
-    private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+    private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
 
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 10.2.3-1
@@ -50,7 +64,7 @@
         ITestDevice device = getDevice();
         device.executeShellCommand("reboot");
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
-        assertThat(hdmiCecClient.getSourceFromMessage(message)).isEqualTo(PLAYBACK_DEVICE);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+        assertThat(CecMessage.getSource(message)).isEqualTo(PLAYBACK_DEVICE);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java
index 1b86b12..56a7df2 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java
@@ -18,15 +18,19 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -36,9 +40,32 @@
 
     private static final int PHYSICAL_ADDRESS = 0x1000;
 
+    /** Intent to launch the remote pairing activity */
+    private static final String ACTION_CONNECT_INPUT_NORMAL =
+            "com.google.android.intent.action.CONNECT_INPUT";
+
+    /** Package name of the Settings app */
+    private static final String SETTINGS_PACKAGE =
+            "com.android.tv.settings";
+
+    /** The command to broadcast an intent. */
+    private static final String START_COMMAND = "am start -a ";
+
+    /** The command to stop an app. */
+    private static final String FORCE_STOP_COMMAND = "am force-stop ";
+
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.1-1
@@ -48,9 +75,25 @@
     @Test
     public void cect_11_2_1_1_OneTouchPlay() throws Exception {
         ITestDevice device = getDevice();
+        device.reboot();
         device.executeShellCommand("input keyevent KEYCODE_HOME");
-        hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.TEXT_VIEW_ON);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.ACTIVE_SOURCE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TEXT_VIEW_ON);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
+    }
+
+    /**
+     * Tests that the device sends a <TEXT_VIEW_ON> when the pairing activity is started on
+     * device, followed by a <ACTIVE_SOURCE> message.
+     */
+    @Test
+    public void cect_PairingActivity_OneTouchPlay() throws Exception {
+        ITestDevice device = getDevice();
+        device.reboot();
+        device.executeShellCommand(START_COMMAND + ACTION_CONNECT_INPUT_NORMAL);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TEXT_VIEW_ON);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
+        device.executeShellCommand(FORCE_STOP_COMMAND + SETTINGS_PACKAGE);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java
index de28c23..bae8490 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java
@@ -18,16 +18,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -35,9 +39,18 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecPhysicalAddressTest extends BaseHostJUnit4Test {
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 10.1.2-1
@@ -49,8 +62,8 @@
         ITestDevice device = getDevice();
         device.executeShellCommand("reboot");
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
-        int physicalAddress = hdmiCecClient.getParamsFromMessage(message,
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+        int physicalAddress = CecMessage.getParams(message,
             HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH);
         assertThat(HdmiCecConstants.PHYSICAL_ADDRESS).isEqualTo(physicalAddress);
     }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
index 46ac18a..b1bfb1e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
@@ -18,16 +18,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -42,9 +46,18 @@
 
     private static final int WAIT_TIME = 5;
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.14-1
@@ -56,10 +69,10 @@
         ITestDevice device = getDevice();
         /* Make sure the device is not booting up/in standby */
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_POWER_STATUS);
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                                                            CecMessage.REPORT_POWER_STATUS);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+            CecOperand.REPORT_POWER_STATUS);
+        assertThat(CecMessage.getParams(message)).isEqualTo(ON);
     }
 
     /**
@@ -73,14 +86,14 @@
         try {
             /* Make sure the device is not booting up/in standby */
             device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-            // b/154737276 - Required for Yukawa
+            /* Home Key to prevent device from going to deep suspend state */
             device.executeShellCommand("input keyevent KEYCODE_HOME");
             device.executeShellCommand("input keyevent KEYCODE_SLEEP");
             TimeUnit.SECONDS.sleep(WAIT_TIME);
-            hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_POWER_STATUS);
-            String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                                                              CecMessage.REPORT_POWER_STATUS);
-            assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+            hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
+            String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                CecOperand.REPORT_POWER_STATUS);
+            assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
         } finally {
             /* Wake up the device */
             device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
index 1981759..160e80f5 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
@@ -18,15 +18,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -57,11 +60,30 @@
 
     private static final int WAIT_TIME = 10;
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-            new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     private void lookForLog(String expectedOut) throws Exception {
+        String testString = getKeyPressLog();
+        assertThat(testString).isEqualTo(expectedOut);
+    }
+
+    private void lookForLog(String expectedOut1, String expectedOut2) throws Exception {
+        String testString = getKeyPressLog();
+        assertThat(testString).isAnyOf(expectedOut1, expectedOut2);
+    }
+
+    private String getKeyPressLog() throws Exception {
         ITestDevice device = getDevice();
         TimeUnit.SECONDS.sleep(WAIT_TIME);
         String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
@@ -76,7 +98,7 @@
             }
         }
         device.executeAdbCommand("logcat", "-c");
-        assertThat(testString).isEqualTo(expectedOut);
+        return testString;
     }
 
     /**
@@ -93,22 +115,22 @@
         device.executeAdbCommand("logcat", "-c");
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_UP, false);
         lookForLog("Short press KEYCODE_DPAD_UP");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_DOWN, false);
         lookForLog("Short press KEYCODE_DPAD_DOWN");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_LEFT, false);
         lookForLog("Short press KEYCODE_DPAD_LEFT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_RIGHT, false);
         lookForLog("Short press KEYCODE_DPAD_RIGHT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_SELECT, false);
-        lookForLog("Short press KEYCODE_DPAD_CENTER");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        lookForLog("Short press KEYCODE_DPAD_CENTER", "Short press KEYCODE_ENTER");
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_BACK, false);
         lookForLog("Short press KEYCODE_BACK");
     }
@@ -127,22 +149,22 @@
         device.executeAdbCommand("logcat", "-c");
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_UP, true);
         lookForLog("Long press KEYCODE_DPAD_UP");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_DOWN, true);
         lookForLog("Long press KEYCODE_DPAD_DOWN");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_LEFT, true);
         lookForLog("Long press KEYCODE_DPAD_LEFT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_RIGHT, true);
         lookForLog("Long press KEYCODE_DPAD_RIGHT");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_SELECT, true);
-        lookForLog("Long press KEYCODE_DPAD_CENTER");
-        hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+        lookForLog("Long press KEYCODE_DPAD_CENTER", "Long press KEYCODE_ENTER");
+        hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
                 HdmiCecConstants.CEC_CONTROL_BACK, true);
         lookForLog("Long press KEYCODE_BACK");
     }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
index f75925f..8bec365 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
@@ -18,16 +18,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -39,9 +43,18 @@
 
     private static final int PHYSICAL_ADDRESS = 0x1000;
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-            new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.2-1
@@ -52,14 +65,14 @@
     public void cect_11_2_2_1_SetStreamPathToDut() throws Exception {
         final long hdmi2Address = 0x2000;
         /* Switch to HDMI2. Setup assumes DUT is connected to HDMI1. */
-        hdmiCecClient.sendCecMessage(CecDevice.PLAYBACK_2, CecDevice.BROADCAST,
-                CecMessage.ACTIVE_SOURCE, hdmiCecClient.formatParams(hdmi2Address));
+        hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_2, LogicalAddress.BROADCAST,
+                CecOperand.ACTIVE_SOURCE, CecMessage.formatParams(hdmi2Address));
         TimeUnit.SECONDS.sleep(3);
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                CecMessage.SET_STREAM_PATH,
-                hdmiCecClient.formatParams(HdmiCecConstants.PHYSICAL_ADDRESS));
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.ACTIVE_SOURCE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                CecOperand.SET_STREAM_PATH,
+                CecMessage.formatParams(HdmiCecConstants.PHYSICAL_ADDRESS));
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
     }
 
     /**
@@ -71,10 +84,10 @@
     public void cect_11_2_2_2_RequestActiveSource() throws Exception {
         ITestDevice device = getDevice();
         device.executeShellCommand("input keyevent KEYCODE_HOME");
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-            CecMessage.REQUEST_ACTIVE_SOURCE);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.ACTIVE_SOURCE);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+            CecOperand.REQUEST_ACTIVE_SOURCE);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
     }
 
     /**
@@ -88,9 +101,9 @@
         try {
             device.executeShellCommand("input keyevent KEYCODE_HOME");
             device.executeShellCommand("input keyevent KEYCODE_SLEEP");
-            String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                    CecMessage.INACTIVE_SOURCE);
-            assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+            String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                    CecOperand.INACTIVE_SOURCE);
+            assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
         } finally {
             /* Wake up the device */
             device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java
index 8305fab..06b62f9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java
@@ -20,10 +20,12 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static org.junit.Assume.assumeTrue;
 
-import android.hdmicec.cts.CecDevice;
-import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -33,6 +35,7 @@
 import com.google.common.collect.ImmutableList;
 import javax.annotation.Nullable;
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -44,18 +47,28 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecStartupTest extends BaseHostJUnit4Test {
 
-  private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
-  private static final ImmutableList<CecMessage> necessaryMessages =
-      new ImmutableList.Builder<CecMessage>()
-          .add(CecMessage.REPORT_PHYSICAL_ADDRESS, CecMessage.CEC_VERSION,
-              CecMessage.DEVICE_VENDOR_ID, CecMessage.GIVE_POWER_STATUS).build();
-  private static final ImmutableList<CecMessage> permissibleMessages =
-      new ImmutableList.Builder<CecMessage>()
-          .add(CecMessage.VENDOR_COMMAND, CecMessage.GIVE_DEVICE_VENDOR_ID,
-              CecMessage.SET_OSD_NAME, CecMessage.GIVE_OSD_NAME).build();
+  private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+  private static final ImmutableList<CecOperand> necessaryMessages =
+      new ImmutableList.Builder<CecOperand>()
+          .add(CecOperand.REPORT_PHYSICAL_ADDRESS, CecOperand.CEC_VERSION,
+              CecOperand.DEVICE_VENDOR_ID, CecOperand.GIVE_POWER_STATUS).build();
+  private static final ImmutableList<CecOperand> permissibleMessages =
+      new ImmutableList.Builder<CecOperand>()
+          .add(CecOperand.VENDOR_COMMAND, CecOperand.GIVE_DEVICE_VENDOR_ID,
+              CecOperand.SET_OSD_NAME, CecOperand.GIVE_OSD_NAME).build();
+
+  public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
 
   @Rule
-  public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+  public RuleChain ruleChain =
+      RuleChain
+          .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+          .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+          .around(RequiredPropertyRule.asCsvContainsValue(
+              this,
+              LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+              PLAYBACK_DEVICE.getDeviceType()))
+          .around(hdmiCecClient);
 
   /**
    * Tests that the device sends all the messages that should be sent on startup. It also ensures
@@ -67,26 +80,27 @@
 
     /* Make sure device is playback only. Not applicable to playback/audio combinations */
     String deviceTypeCsv = device.executeShellCommand("getprop ro.hdmi.device_type").trim();
-    assumeTrue(deviceTypeCsv.equals(CecDevice.PLAYBACK_1.getDeviceType()));
+    assumeTrue(deviceTypeCsv.equals(LogicalAddress.PLAYBACK_1.getDeviceType()));
 
     device.executeShellCommand("reboot");
     device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
     /* Monitor CEC messages for 20s after reboot */
-    final List<CecMessage> messagesReceived = hdmiCecClient.getAllMessages(CecDevice.PLAYBACK_1, 20);
+    final List<CecOperand> messagesReceived =
+        hdmiCecClient.getAllMessages(LogicalAddress.PLAYBACK_1, 20);
 
     /* Predicate to apply on necessaryMessages to ensure that all necessaryMessages are received. */
-    final Predicate<CecMessage> notReceived = new Predicate<CecMessage>() {
+    final Predicate<CecOperand> notReceived = new Predicate<CecOperand>() {
       @Override
-      public boolean apply(@Nullable CecMessage cecMessage) {
-        return !messagesReceived.contains(cecMessage);
+      public boolean apply(@Nullable CecOperand cecOperand) {
+        return !messagesReceived.contains(cecOperand);
       }
     };
 
     /* Predicate to apply on messagesReceived to ensure all messages received are permissible. */
-    final Predicate<CecMessage> notAllowed = new Predicate<CecMessage>() {
+    final Predicate<CecOperand> notAllowed = new Predicate<CecOperand>() {
       @Override
-      public boolean apply(@Nullable CecMessage cecMessage) {
-        return !(permissibleMessages.contains(cecMessage) || necessaryMessages.contains(cecMessage));
+      public boolean apply(@Nullable CecOperand cecOperand) {
+        return !(permissibleMessages.contains(cecOperand) || necessaryMessages.contains(cecOperand));
       }
     };
 
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
index 0e40519..dfa84b8 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
@@ -18,29 +18,42 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
-import java.util.concurrent.TimeUnit;
-
 /** HDMI CEC test to verify system audio control commands (Section 11.2.15) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecSystemAudioControlTest extends BaseHostJUnit4Test {
-    private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+    private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+
+    public HdmiCecClientWrapper hdmiCecClient =
+        new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1, "-t", "a");
 
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this, "-t", "a");
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                PLAYBACK_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.15-10
@@ -49,12 +62,12 @@
     @Test
     public void cect_11_2_15_10_GiveSystemAudioModeStatus() throws Exception {
         ITestDevice device = getDevice();
-        // b/154737276 - Required for Yukawa
+        /* Home Key to prevent device from going to deep suspend state */
         device.executeShellCommand("input keyevent KEYCODE_HOME");
         device.executeShellCommand("input keyevent KEYCODE_SLEEP");
         device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
-        hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
-                CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+                CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
     }
 
     /**
@@ -67,19 +80,19 @@
     public void cect_11_2_15_11_VolumeUpDownUserControlPressed() throws Exception {
         ITestDevice device = getDevice();
         device.executeShellCommand("input keyevent KEYCODE_VOLUME_UP");
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
-                CecMessage.USER_CONTROL_PRESSED);
-        assertThat(hdmiCecClient.getParamsFromMessage(message))
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+                CecOperand.USER_CONTROL_PRESSED);
+        assertThat(CecMessage.getParams(message))
                 .isEqualTo(HdmiCecConstants.CEC_CONTROL_VOLUME_UP);
-        hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM, CecMessage.USER_CONTROL_RELEASED);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.USER_CONTROL_RELEASED);
 
 
         device.executeShellCommand("input keyevent KEYCODE_VOLUME_DOWN");
-        message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
-                CecMessage.USER_CONTROL_PRESSED);
-        assertThat(hdmiCecClient.getParamsFromMessage(message))
+        message = hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+                CecOperand.USER_CONTROL_PRESSED);
+        assertThat(CecMessage.getParams(message))
                 .isEqualTo(HdmiCecConstants.CEC_CONTROL_VOLUME_DOWN);
-        hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM, CecMessage.USER_CONTROL_RELEASED);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.USER_CONTROL_RELEASED);
     }
 
     /**
@@ -92,10 +105,9 @@
     public void cect_11_2_15_12_MuteUserControlPressed() throws Exception {
         ITestDevice device = getDevice();
         device.executeShellCommand("input keyevent KEYCODE_MUTE");
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
-                CecMessage.USER_CONTROL_PRESSED);
-        assertThat(hdmiCecClient.getParamsFromMessage(message))
-                .isEqualTo(HdmiCecConstants.CEC_CONTROL_MUTE);
-        hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM, CecMessage.USER_CONTROL_RELEASED);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+                CecOperand.USER_CONTROL_PRESSED);
+        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_CONTROL_MUTE);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.USER_CONTROL_RELEASED);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
index d6fb1dc..5e59a8f 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
@@ -21,16 +21,20 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.hdmicec.cts.CecClientMessage;
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -43,9 +47,18 @@
 
     private static final String PROPERTY_LOCALE = "persist.sys.locale";
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                LogicalAddress.PLAYBACK_1.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.6-1
@@ -53,7 +66,7 @@
      */
     @Test
     public void cect_11_2_6_1_Ack() throws Exception {
-        String command = CecClientMessage.POLL + " " + CecDevice.PLAYBACK_1;
+        String command = CecClientMessage.POLL + " " + LogicalAddress.PLAYBACK_1;
         String expectedOutput = "POLL sent";
         hdmiCecClient.sendConsoleMessage(command);
         if (!hdmiCecClient.checkConsoleOutput(expectedOutput)) {
@@ -68,11 +81,11 @@
      */
     @Test
     public void cect_11_2_6_2_GivePhysicalAddress() throws Exception {
-        hdmiCecClient.sendCecMessage(CecMessage.GIVE_PHYSICAL_ADDRESS);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
+        hdmiCecClient.sendCecMessage(CecOperand.GIVE_PHYSICAL_ADDRESS);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
         /* The checkExpectedOutput has already verified the first 4 nibbles of the message. We
             * have to verify the last 6 nibbles */
-        int receivedParams = hdmiCecClient.getParamsFromMessage(message);
+        int receivedParams = CecMessage.getParams(message);
         assertThat(HdmiCecConstants.PHYSICAL_ADDRESS).isEqualTo(receivedParams >> 8);
         assertThat(HdmiCecConstants.PLAYBACK_DEVICE_TYPE).isEqualTo(receivedParams & 0xFF);
     }
@@ -83,10 +96,10 @@
      */
     @Test
     public void cect_11_2_6_6_GiveCecVersion() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GET_CEC_VERSION);
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
-                                                            CecMessage.CEC_VERSION);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(CEC_VERSION_NUMBER);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GET_CEC_VERSION);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+                                                            CecOperand.CEC_VERSION);
+        assertThat(CecMessage.getParams(message)).isEqualTo(CEC_VERSION_NUMBER);
     }
 
     /**
@@ -95,11 +108,11 @@
      */
     @Test
     public void cect_11_2_6_7_GetMenuLanguage() throws Exception {
-        hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GET_MENU_LANGUAGE);
-        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.FEATURE_ABORT);
-        int abortedOpcode = hdmiCecClient.getParamsFromMessage(message,
-                CecMessage.GET_MENU_LANGUAGE.toString().length());
-        assertThat(CecMessage.getMessage(abortedOpcode)).isEqualTo(CecMessage.GET_MENU_LANGUAGE);
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GET_MENU_LANGUAGE);
+        String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.FEATURE_ABORT);
+        int abortedOpcode = CecMessage.getParams(message,
+                CecOperand.GET_MENU_LANGUAGE.toString().length());
+        assertThat(CecOperand.getOperand(abortedOpcode)).isEqualTo(CecOperand.GET_MENU_LANGUAGE);
     }
 
     private String getSystemLocale() throws Exception {
@@ -133,8 +146,8 @@
         final String language = originalLanguage.equals("spa") ? "eng" : "spa";
         final String newLanguage = originalLanguage.equals("spa") ? "en" : "es";
         try {
-            hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                    CecMessage.SET_MENU_LANGUAGE, hdmiCecClient.convertStringToHexParams(language));
+            hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                    CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language));
             assertThat(extractLanguage(getSystemLocale())).isEqualTo(newLanguage);
         } finally {
             setSystemLocale(locale);
@@ -152,8 +165,8 @@
         final String originalLanguage = extractLanguage(locale);
         final String language = "spb";
         try {
-            hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
-                    CecMessage.SET_MENU_LANGUAGE, hdmiCecClient.convertStringToHexParams(language));
+            hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+                    CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language));
             assertThat(extractLanguage(getSystemLocale())).isEqualTo(originalLanguage);
         } finally {
             setSystemLocale(locale);
@@ -172,8 +185,8 @@
         final String originalLanguage = extractLanguage(locale);
         final String language = originalLanguage.equals("spa") ? "eng" : "spa";
         try {
-            hdmiCecClient.sendCecMessage(CecDevice.RECORDER_1, CecDevice.BROADCAST,
-                    CecMessage.SET_MENU_LANGUAGE, hdmiCecClient.convertStringToHexParams(language));
+            hdmiCecClient.sendCecMessage(LogicalAddress.RECORDER_1, LogicalAddress.BROADCAST,
+                    CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language));
             assertThat(extractLanguage(getSystemLocale())).isEqualTo(originalLanguage);
         } finally {
             setSystemLocale(locale);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java
index 983b501..369542e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java
@@ -18,18 +18,19 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
-import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceState;
-import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
@@ -38,14 +39,24 @@
 /** HDMI CEC test to verify the device handles standby correctly (Section 11.2.3) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecSystemStandbyTest extends BaseHostJUnit4Test {
-    private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+    private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
 
     private static final String HDMI_CONTROL_DEVICE_AUTO_OFF =
             "hdmi_control_auto_device_off_enabled";
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                PLAYBACK_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
 
     private boolean setHdmiControlDeviceAutoOff(boolean turnOn) throws Exception {
         ITestDevice device = getDevice();
@@ -58,13 +69,13 @@
         return val.equals("1") ? true : false;
     }
 
-    private void checkDeviceAsleepAfterStandbySent(CecDevice source, CecDevice destination)
+    private void checkDeviceAsleepAfterStandbySent(LogicalAddress source, LogicalAddress destination)
             throws Exception {
         ITestDevice device = getDevice();
         try {
             device.executeShellCommand("input keyevent KEYCODE_HOME");
             TimeUnit.SECONDS.sleep(5);
-            hdmiCecClient.sendCecMessage(source, destination, CecMessage.STANDBY);
+            hdmiCecClient.sendCecMessage(source, destination, CecOperand.STANDBY);
             TimeUnit.SECONDS.sleep(5);
             String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
             assertThat(wakeState.trim()).isEqualTo("mWakefulness=Asleep");
@@ -84,19 +95,19 @@
         getDevice().waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
         try {
             TimeUnit.SECONDS.sleep(5);
-            checkDeviceAsleepAfterStandbySent(CecDevice.TV, CecDevice.BROADCAST);
+            checkDeviceAsleepAfterStandbySent(LogicalAddress.TV, LogicalAddress.BROADCAST);
             /* Wake up the TV */
-            hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
-            checkDeviceAsleepAfterStandbySent(CecDevice.RECORDER_1, CecDevice.BROADCAST);
+            hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
+            checkDeviceAsleepAfterStandbySent(LogicalAddress.RECORDER_1, LogicalAddress.BROADCAST);
             /* Wake up the TV */
-            hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
-            checkDeviceAsleepAfterStandbySent(CecDevice.AUDIO_SYSTEM, CecDevice.BROADCAST);
+            hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
+            checkDeviceAsleepAfterStandbySent(LogicalAddress.AUDIO_SYSTEM, LogicalAddress.BROADCAST);
             /* Wake up the TV */
-            hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
-            checkDeviceAsleepAfterStandbySent(CecDevice.PLAYBACK_2, CecDevice.BROADCAST);
+            hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
+            checkDeviceAsleepAfterStandbySent(LogicalAddress.PLAYBACK_2, LogicalAddress.BROADCAST);
         } finally {
             /* Wake up the TV */
-            hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
+            hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
         }
     }
 
@@ -108,11 +119,11 @@
     public void cect_11_2_3_3_HandleAddressedStandby() throws Exception {
         getDevice().executeShellCommand("reboot");
         getDevice().waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        checkDeviceAsleepAfterStandbySent(CecDevice.TV, CecDevice.PLAYBACK_1);
-        checkDeviceAsleepAfterStandbySent(CecDevice.RECORDER_1, CecDevice.PLAYBACK_1);
-        checkDeviceAsleepAfterStandbySent(CecDevice.AUDIO_SYSTEM, CecDevice.PLAYBACK_1);
-        checkDeviceAsleepAfterStandbySent(CecDevice.PLAYBACK_2, CecDevice.PLAYBACK_1);
-        checkDeviceAsleepAfterStandbySent(CecDevice.BROADCAST, CecDevice.PLAYBACK_1);
+        checkDeviceAsleepAfterStandbySent(LogicalAddress.TV, LogicalAddress.PLAYBACK_1);
+        checkDeviceAsleepAfterStandbySent(LogicalAddress.RECORDER_1, LogicalAddress.PLAYBACK_1);
+        checkDeviceAsleepAfterStandbySent(LogicalAddress.AUDIO_SYSTEM, LogicalAddress.PLAYBACK_1);
+        checkDeviceAsleepAfterStandbySent(LogicalAddress.PLAYBACK_2, LogicalAddress.PLAYBACK_1);
+        checkDeviceAsleepAfterStandbySent(LogicalAddress.BROADCAST, LogicalAddress.PLAYBACK_1);
     }
 
     /**
@@ -125,7 +136,7 @@
         boolean wasOn = setHdmiControlDeviceAutoOff(false);
         try {
             device.executeShellCommand("input keyevent KEYCODE_SLEEP");
-            hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST, CecMessage.STANDBY);
+            hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST, CecOperand.STANDBY);
             device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
         } finally {
             setHdmiControlDeviceAutoOff(wasOn);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java
index 3dc1463..edff394 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java
@@ -18,28 +18,42 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hdmicec.cts.CecDevice;
 import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /** HDMI CEC test to verify device vendor specific commands (Section 11.2.9) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecVendorCommandsTest extends BaseHostJUnit4Test {
-    private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+    private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
     private static final int INCORRECT_VENDOR_ID = 0x0;
 
+    public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
     @Rule
-    public HdmiCecClientWrapper hdmiCecClient =
-        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+    public RuleChain ruleChain =
+        RuleChain
+            .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+            .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+            .around(RequiredPropertyRule.asCsvContainsValue(
+                this,
+                LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+                PLAYBACK_DEVICE.getDeviceType()))
+            .around(hdmiCecClient);
 
     /**
      * Test 11.2.9-1
@@ -48,11 +62,14 @@
      */
     @Test
     public void cect_11_2_9_1_GiveDeviceVendorId() throws Exception {
-        for (CecDevice cecDevice : CecDevice.values()) {
-            hdmiCecClient.sendCecMessage(cecDevice, CecMessage.GIVE_DEVICE_VENDOR_ID);
-            String message = hdmiCecClient.checkExpectedOutput(CecMessage.DEVICE_VENDOR_ID);
-            assertThat(hdmiCecClient.getParamsFromMessage(message)).
-                    isNotEqualTo(INCORRECT_VENDOR_ID);
+        for (LogicalAddress logicalAddress : LogicalAddress.values()) {
+            // Skip the logical address of this device
+            if (logicalAddress == PLAYBACK_DEVICE) {
+                continue;
+            }
+            hdmiCecClient.sendCecMessage(logicalAddress, CecOperand.GIVE_DEVICE_VENDOR_ID);
+            String message = hdmiCecClient.checkExpectedOutput(CecOperand.DEVICE_VENDOR_ID);
+            assertThat(CecMessage.getParams(message)).isNotEqualTo(INCORRECT_VENDOR_ID);
         }
     }
 
@@ -66,7 +83,7 @@
         ITestDevice device = getDevice();
         device.executeShellCommand("reboot");
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String message = hdmiCecClient.checkExpectedOutput(CecMessage.DEVICE_VENDOR_ID);
-        assertThat(hdmiCecClient.getParamsFromMessage(message)).isNotEqualTo(INCORRECT_VENDOR_ID);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.DEVICE_VENDOR_ID);
+        assertThat(CecMessage.getParams(message)).isNotEqualTo(INCORRECT_VENDOR_ID);
     }
 }
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
index a820ae5..5aafdf0 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -25,4 +25,5 @@
     String getRestrictBackgroundStatus();
     void sendNotification(int notificationId, String notificationType);
     void registerNetworkCallback(in INetworkCallback cb);
+    void unregisterNetworkCallback();
 }
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
index 740ec26..2048bab 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
@@ -17,9 +17,11 @@
 package com.android.cts.net.hostside;
 
 import android.net.Network;
+import android.net.NetworkCapabilities;
 
 interface INetworkCallback {
     void onBlockedStatusChanged(in Network network, boolean blocked);
     void onAvailable(in Network network);
     void onLost(in Network network);
+    void onCapabilitiesChanged(in Network network, in NetworkCapabilities cap);
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index c8fe624..3efc6d0 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -30,6 +30,7 @@
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -188,7 +189,9 @@
         do {
             attempts++;
             count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
-            if (count >= expectedCount) {
+            assertFalse("Expected count " + expectedCount + " but actual is " + count,
+                    count > expectedCount);
+            if (count == expectedCount) {
                 break;
             }
             Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
@@ -708,6 +711,10 @@
         mServiceClient.registerNetworkCallback(cb);
     }
 
+    protected void unregisterNetworkCallback() throws Exception {
+        mServiceClient.unregisterNetworkCallback();
+    }
+
     /**
      * Registers a {@link NotificationListenerService} implementation that will execute the
      * notification actions right after the notification is sent.
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 3ee7b99..6546e26 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -100,4 +100,8 @@
     public void registerNetworkCallback(INetworkCallback cb) throws RemoteException {
         mService.registerNetworkCallback(cb);
     }
+
+    public void unregisterNetworkCallback() throws RemoteException {
+        mService.unregisterNetworkCallback();
+    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index ed397b9..f3cd8a9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,18 +16,23 @@
 
 package com.android.cts.net.hostside;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
 import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
 import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.net.Network;
+import android.net.NetworkCapabilities;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Objects;
@@ -35,15 +40,18 @@
 import java.util.concurrent.TimeUnit;
 
 public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
-
     private Network mNetwork;
     private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
+    @Rule
+    public final MeterednessConfigurationRule mMeterednessConfiguration
+            = new MeterednessConfigurationRule();
 
     enum CallbackState {
         NONE,
         AVAILABLE,
         LOST,
-        BLOCKED_STATUS
+        BLOCKED_STATUS,
+        CAPABILITIES
     }
 
     private static class CallbackInfo {
@@ -75,7 +83,7 @@
     }
 
     private class TestNetworkCallback extends INetworkCallback.Stub {
-        private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
+        private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
 
         private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
 
@@ -117,12 +125,18 @@
             setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
         }
 
-        public void expectLostCallback(Network expectedNetwork) {
-            expectCallback(CallbackState.LOST, expectedNetwork, null);
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+            setLastCallback(CallbackState.CAPABILITIES, network, cap);
         }
 
-        public void expectAvailableCallback(Network expectedNetwork) {
-            expectCallback(CallbackState.AVAILABLE, expectedNetwork, null);
+        public Network expectAvailableCallbackAndGetNetwork() {
+            final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+            if (cb.state != CallbackState.AVAILABLE) {
+                fail("Network is not available. Instead obtained the following callback :"
+                        + cb);
+            }
+            return cb.network;
         }
 
         public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
@@ -130,15 +144,28 @@
                     expectBlocked);
         }
 
-        void assertNoCallback() {
-            CallbackInfo cb = null;
-            try {
-                cb = mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                // Expected.
-            }
-            if (cb != null) {
-                assertNull("Unexpected callback: " + cb, cb);
+        public void waitBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
+            final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
+            do {
+                final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
+                if (cb.state == CallbackState.BLOCKED_STATUS) {
+                    assertEquals(expectBlocked, cb.arg);
+                    return;
+                }
+            } while (System.currentTimeMillis() <= deadline);
+            fail("Didn't receive onBlockedStatusChanged()");
+        }
+
+        public void expectCapabilitiesCallback(Network expectedNetwork, boolean hasCapability,
+                int capability) {
+            final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+            final NetworkCapabilities cap = (NetworkCapabilities) cb.arg;
+            assertEquals(expectedNetwork, cb.network);
+            assertEquals(CallbackState.CAPABILITIES, cb.state);
+            if (hasCapability != cap.hasCapability(capability)) {
+                fail("NetworkCapabilities callback "
+                        + (hasCapability ? "missing expected" : "has unexpected")
+                        + " capability. " + cb);
             }
         }
     }
@@ -147,13 +174,28 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mNetwork = mCm.getActiveNetwork();
-
         registerBroadcastReceiver();
 
         removeRestrictBackgroundWhitelist(mUid);
         removeRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(0);
+
+        // Initial state
+        setBatterySaverMode(false);
+        setRestrictBackground(false);
+
+        // Make wifi a metered network.
+        mMeterednessConfiguration.configureNetworkMeteredness(true);
+
+        // Register callback
+        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+        // Once the wifi is marked as metered, the wifi will reconnect. Wait for onAvailable()
+        // callback to ensure wifi is connected before the test and store the default network.
+        mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork();
+        // Check that the network is metered.
+        mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, false /* hasCapability */,
+                NET_CAPABILITY_NOT_METERED);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
     }
 
     @After
@@ -162,102 +204,80 @@
 
         setRestrictBackground(false);
         setBatterySaverMode(false);
+        unregisterNetworkCallback();
     }
 
     @RequiredProperties({DATA_SAVER_MODE})
     @Test
     public void testOnBlockedStatusChanged_dataSaver() throws Exception {
-        // Initial state
-        setBatterySaverMode(false);
-        setRestrictBackground(false);
-
-        final MeterednessConfigurationRule meterednessConfiguration
-                = new MeterednessConfigurationRule();
-        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
-            // Register callback
-            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-            mTestNetworkCallback.expectAvailableCallback(mNetwork);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
             // Enable restrict background
             setRestrictBackground(true);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
 
             // Add to whitelist
             addRestrictBackgroundWhitelist(mUid);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
 
             // Remove from whitelist
             removeRestrictBackgroundWhitelist(mUid);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
 
         // Set to non-metered network
-        meterednessConfiguration.configureNetworkMeteredness(false);
+        mMeterednessConfiguration.configureNetworkMeteredness(false);
+        mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+                NET_CAPABILITY_NOT_METERED);
         try {
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
 
             // Disable restrict background, should not trigger callback
             setRestrictBackground(false);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.assertNoCallback();
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
     @RequiredProperties({BATTERY_SAVER_MODE})
     @Test
     public void testOnBlockedStatusChanged_powerSaver() throws Exception {
-        // Set initial state.
-        setBatterySaverMode(false);
-        setRestrictBackground(false);
-
-        final MeterednessConfigurationRule meterednessConfiguration
-                = new MeterednessConfigurationRule();
-        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
-            // Register callback
-            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-            mTestNetworkCallback.expectAvailableCallback(mNetwork);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
             // Enable Power Saver
             setBatterySaverMode(true);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
 
             // Disable Power Saver
             setBatterySaverMode(false);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
 
         // Set to non-metered network
-        meterednessConfiguration.configureNetworkMeteredness(false);
+        mMeterednessConfiguration.configureNetworkMeteredness(false);
+        mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+                NET_CAPABILITY_NOT_METERED);
         try {
-            mTestNetworkCallback.assertNoCallback();
-
             // Enable Power Saver
             setBatterySaverMode(true);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
 
             // Disable Power Saver
             setBatterySaverMode(false);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
index ec536af..590e17e 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -127,6 +127,16 @@
                         unregisterNetworkCallback();
                     }
                 }
+
+                @Override
+                public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+                    try {
+                        cb.onCapabilitiesChanged(network, cap);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Cannot send onCapabilitiesChanged: " + e);
+                        unregisterNetworkCallback();
+                    }
+                }
             };
             mCm.registerNetworkCallback(makeWifiNetworkRequest(), mNetworkCallback);
             try {
@@ -135,17 +145,21 @@
                 unregisterNetworkCallback();
             }
         }
-      };
 
-    private void unregisterNetworkCallback() {
-        Log.d(TAG, "unregistering network callback");
-        mCm.unregisterNetworkCallback(mNetworkCallback);
-        mNetworkCallback = null;
-    }
+        @Override
+        public void unregisterNetworkCallback() {
+            Log.d(TAG, "unregistering network callback");
+            if (mNetworkCallback != null) {
+                mCm.unregisterNetworkCallback(mNetworkCallback);
+                mNetworkCallback = null;
+            }
+        }
+      };
 
     private NetworkRequest makeWifiNetworkRequest() {
         return new NetworkRequest.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                 .build();
     }
 
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.bp
new file mode 100644
index 0000000..3f6188a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "CVE-2016-2482",
+    defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+    srcs: ["poc.cpp"],
+    include_dirs: ["frameworks/native/include/media/openmax"],
+    shared_libs: [
+        "libnativehelper",
+        "liblog",
+        "libstagefright",
+        "libbinder",
+        "libutils",
+        "libmedia",
+        "libmedia_omx",
+        "libstagefright_foundation",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk
deleted file mode 100644
index b6cdfb0..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-2482
-LOCAL_SRC_FILES := poc.cpp
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
-                    $(TOP)/frameworks/native/include/media/openmax \
-
-LOCAL_SHARED_LIBRARIES := libnativehelper \
-                          liblog \
-                          libstagefright \
-                          libbinder \
-                          libutils \
-                          libmedia \
-                          libmedia_omx \
-                          libstagefright_foundation
-
-LOCAL_COMPATIBILITY_SUITE := cts sts vts10
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-LOCAL_ARM_MODE := arm
-LOCAL_CPPFLAGS += -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.bp
new file mode 100644
index 0000000..2a9b893
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 The Android Open Source Project
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "CVE-2016-3747",
+    defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+    srcs: ["poc.cpp"],
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+        "frameworks/native/include/media/openmax",
+    ],
+    shared_libs: [
+        "libstagefright",
+        "libbinder",
+        "libmedia",
+        "libmedia_omx",
+        "liblog",
+        "libutils",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk
deleted file mode 100644
index c4ff5f1..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-3747
-LOCAL_SRC_FILES := poc.cpp
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
-                    $(TOP)/frameworks/av/media/libstagefright \
-                    $(TOP)/frameworks/native/include/media/openmax
-
-LOCAL_SHARED_LIBRARIES := \
-        libstagefright \
-        libbinder \
-        libmedia \
-        libmedia_omx \
-        liblog \
-        libutils
-
-LOCAL_COMPATIBILITY_SUITE := cts vts10 sts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.bp
new file mode 100644
index 0000000..2a49719
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+    name: "CVE-2016-5862",
+    defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+    srcs: ["poc.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk
deleted file mode 100644
index 75a087e..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-5862
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_COMPATIBILITY_SUITE := cts sts vts10
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.bp
new file mode 100644
index 0000000..b0f0e6c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+    name: "CVE-2016-5867",
+    defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+    srcs: ["poc.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk
deleted file mode 100644
index 4186ccc..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-5867
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_COMPATIBILITY_SUITE := cts sts vts10
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/shortcuts/hostside/Android.bp b/hostsidetests/shortcuts/hostside/Android.bp
index 646953e..3613975 100644
--- a/hostsidetests/shortcuts/hostside/Android.bp
+++ b/hostsidetests/shortcuts/hostside/Android.bp
@@ -27,4 +27,7 @@
         "vts10",
         "general-tests",
     ],
+    required: [
+        "CtsBackupHostTestCases",
+    ],
 }
diff --git a/hostsidetests/statsd/AndroidTest.xml b/hostsidetests/statsd/AndroidTest.xml
index 2da1662..4598f33 100644
--- a/hostsidetests/statsd/AndroidTest.xml
+++ b/hostsidetests/statsd/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Statsd host test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="statsd" />
+    <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index c076eb6..32b6f01 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -1073,8 +1073,9 @@
         // Start test app.
         try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
                 "action.show_notification")) {
-            Thread.sleep(WAIT_TIME_SHORT);
             // Trigger a pull and wait for new pull before killing the process.
+            Thread.sleep(WAIT_TIME_LONG);
+            // Trigger new pull.
             setAppBreadcrumbPredicate();
             Thread.sleep(WAIT_TIME_LONG);
         }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
index bf599d3f..adc24db 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
@@ -78,7 +78,7 @@
         final String result = getDevice().installPackage(
                 buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), false, true);
 
-        Thread.sleep(WAIT_TIME_SHORT);
+        Thread.sleep(WAIT_TIME_LONG);
 
         ConfigMetricsReportList reports = getReportList();
         assertTrue(reports.getReportsCount() > 0);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
index bca2d47..09598b6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
@@ -51,6 +51,7 @@
     @Override
     protected void tearDown() throws Exception {
         plugInUsb();
+        super.tearDown();
     }
 
     /*
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index cfa0a37..9f15d3a 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -391,7 +391,7 @@
 
         List<Atom> statsdData = getGaugeMetricDataList();
         assertTrue(statsdData.size() > 0);
-        assertTrue(statsdData.get(0).getProcStatsPkgProc().getProcStatsSection().getAvailablePagesList().size() > 0);
+        assertTrue(statsdData.get(0).getProcStatsPkgProc().getProcStatsSection().getPackageStatsList().size() > 0);
 
         List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
 
diff --git a/hostsidetests/theme/assets/R/140dpi.zip b/hostsidetests/theme/assets/S/140dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/140dpi.zip
rename to hostsidetests/theme/assets/S/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/180dpi.zip b/hostsidetests/theme/assets/S/180dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/180dpi.zip
rename to hostsidetests/theme/assets/S/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/200dpi.zip b/hostsidetests/theme/assets/S/200dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/200dpi.zip
rename to hostsidetests/theme/assets/S/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/220dpi.zip b/hostsidetests/theme/assets/S/220dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/220dpi.zip
rename to hostsidetests/theme/assets/S/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/260dpi.zip b/hostsidetests/theme/assets/S/260dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/260dpi.zip
rename to hostsidetests/theme/assets/S/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/280dpi.zip b/hostsidetests/theme/assets/S/280dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/280dpi.zip
rename to hostsidetests/theme/assets/S/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/300dpi.zip b/hostsidetests/theme/assets/S/300dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/300dpi.zip
rename to hostsidetests/theme/assets/S/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/340dpi.zip b/hostsidetests/theme/assets/S/340dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/340dpi.zip
rename to hostsidetests/theme/assets/S/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/360dpi.zip b/hostsidetests/theme/assets/S/360dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/360dpi.zip
rename to hostsidetests/theme/assets/S/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/400dpi.zip b/hostsidetests/theme/assets/S/400dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/400dpi.zip
rename to hostsidetests/theme/assets/S/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/420dpi.zip b/hostsidetests/theme/assets/S/420dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/420dpi.zip
rename to hostsidetests/theme/assets/S/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/440dpi.zip b/hostsidetests/theme/assets/S/440dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/440dpi.zip
rename to hostsidetests/theme/assets/S/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/560dpi.zip b/hostsidetests/theme/assets/S/560dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/560dpi.zip
rename to hostsidetests/theme/assets/S/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/hdpi.zip b/hostsidetests/theme/assets/S/hdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/hdpi.zip
rename to hostsidetests/theme/assets/S/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/ldpi.zip b/hostsidetests/theme/assets/S/ldpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/ldpi.zip
rename to hostsidetests/theme/assets/S/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/mdpi.zip b/hostsidetests/theme/assets/S/mdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/mdpi.zip
rename to hostsidetests/theme/assets/S/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/tvdpi.zip b/hostsidetests/theme/assets/S/tvdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/tvdpi.zip
rename to hostsidetests/theme/assets/S/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xhdpi.zip b/hostsidetests/theme/assets/S/xhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/xhdpi.zip
rename to hostsidetests/theme/assets/S/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxhdpi.zip b/hostsidetests/theme/assets/S/xxhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/xxhdpi.zip
rename to hostsidetests/theme/assets/S/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxxhdpi.zip b/hostsidetests/theme/assets/S/xxxhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/xxxhdpi.zip
rename to hostsidetests/theme/assets/S/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/ui/Android.mk b/hostsidetests/ui/Android.mk
index 5450454..61bb6d3 100644
--- a/hostsidetests/ui/Android.mk
+++ b/hostsidetests/ui/Android.mk
@@ -24,13 +24,8 @@
 
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
 
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-$(COMPATIBILITY_TESTCASES_OUT_cts)/CtsUiHostTestCases.jar : $(COMPATIBILITY_TESTCASES_OUT_cts)/com.replica.replicaisland.apk
-
-LOCAL_CTS_TEST_PACKAGE := android.ui.cts
-
 # *Not* tagged as a cts test artifact intentionally: b/109660132
+#$(COMPATIBILITY_TESTCASES_OUT_cts)/CtsUiHostTestCases.jar : $(COMPATIBILITY_TESTCASES_OUT_cts)/com.replica.replicaisland.apk
 LOCAL_COMPATIBILITY_SUITE := general-tests
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
index 1db254a..0da168b 100644
--- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
+++ b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
@@ -40,6 +40,7 @@
 /**
  * Host side CTS tests verifying userspace reboot functionality.
  */
+@RequiresDevice
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class UserspaceRebootHostTest extends BaseHostJUnit4Test  {
 
@@ -176,6 +177,8 @@
             runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
                     "testVerifyCeStorageUnlocked");
             runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
+                    "testVerifyReceivedLockedBootCompletedBroadcast", Duration.ofMinutes(3));
+            runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
                     "testVerifyReceivedBootCompletedBroadcast", Duration.ofMinutes(6));
         } finally {
             getDevice().executeShellV2Command("cmd lock_settings clear --old 1543");
@@ -207,6 +210,8 @@
             runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
                     "testVerifyCeStorageUnlocked");
             runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
+                    "testVerifyReceivedLockedBootCompletedBroadcast", Duration.ofMinutes(3));
+            runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
                     "testVerifyReceivedBootCompletedBroadcast", Duration.ofMinutes(6));
         } finally {
             getDevice().executeShellV2Command("cmd lock_settings clear --old 1543");
@@ -252,7 +257,7 @@
             getDevice().setProperty("init.userspace_reboot.sigterm.timeoutmillis", "10");
             rebootUserspaceAndWaitForBootComplete();
             assertUserspaceRebootFailed();
-            assertLastBootReasonIs("userspace_failed,shutdown_aborted");
+            assertLastBootReasonIs("userspace_failed,shutdown_aborted,sigkill");
         } finally {
             getDevice().setProperty("init.userspace_reboot.sigkill.timeoutmillis", sigkillTimeout);
             getDevice().setProperty("init.userspace_reboot.sigterm.timeoutmillis", sigtermTimeout);
diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml b/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml
index b541348..97ffde6 100644
--- a/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml
+++ b/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml
@@ -27,14 +27,18 @@
             </intent-filter>
         </activity>
         <uses-library android:name="android.test.runner" />
-        <receiver android:name=".BasicUserspaceRebootTest$BootReceiver">
+        <receiver android:name=".BasicUserspaceRebootTest$BootReceiver"
+                  android:exported="true"
+                  android:directBootAware="true">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
+                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
             </intent-filter>
         </receiver>
         <provider android:name=".BasicUserspaceRebootTest$Provider"
                   android:authorities="com.android.cts.userspacereboot.basic"
-                  android:exported="true">
+                  android:exported="true"
+                  android:directBootAware="true">
         </provider>
     </application>
 
diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java b/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java
index 694d978..c41f221 100644
--- a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java
+++ b/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java
@@ -95,7 +95,8 @@
     }
 
     /**
-     * Receiver of {@link Intent.ACTION_BOOT_COMPLETED} broadcast.
+     * Receiver of {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} and
+     * {@link Intent.ACTION_BOOT_COMPLETED} broadcasts.
      */
     public static class BootReceiver extends BroadcastReceiver {
 
@@ -114,7 +115,8 @@
     }
 
     /**
-     * Returns whenever {@link Intent.ACTION_BOOT_COMPLETED} broadcast was received.
+     * Returns whenever {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} and
+     * {@link Intent.ACTION_BOOT_COMPLETED} broadcast were received.
      */
     public static class Provider extends ContentProvider {
 
@@ -132,9 +134,14 @@
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                 String sortOrder) {
             Context de = getContext().createDeviceProtectedStorageContext();
-            File file = new File(de.getFilesDir(), Intent.ACTION_BOOT_COMPLETED.toLowerCase());
-            MatrixCursor cursor = new MatrixCursor(new String[]{"exists"});
-            cursor.addRow(new Object[] { file.exists() ? 1 : 0});
+            File locked_boot_completed = new File(
+                    de.getFilesDir(), Intent.ACTION_LOCKED_BOOT_COMPLETED.toLowerCase());
+            File boot_completed = new File(
+                    de.getFilesDir(), Intent.ACTION_BOOT_COMPLETED.toLowerCase());
+            MatrixCursor cursor = new MatrixCursor(
+                    new String[]{ "locked_boot_completed", "boot_completed"});
+            cursor.addRow(new Object[] {
+                    locked_boot_completed.exists() ? 1 : 0, boot_completed.exists() ? 1 : 0 });
             return cursor;
         }
 
diff --git a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java b/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java
index 28603d0..4a512ce 100644
--- a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java
+++ b/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java
@@ -25,6 +25,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.UserManager;
+import android.util.Log;
 
 import com.android.compatibility.common.util.TestUtils;
 
@@ -48,6 +49,7 @@
     private static final String FILE_NAME = "secret.txt";
     private static final String SECRET_MESSAGE = "wow, much secret";
 
+    private static final Duration LOCKED_BOOT_TIMEOUT = Duration.ofMinutes(3);
     private static final Duration BOOT_TIMEOUT = Duration.ofMinutes(6);
 
     private final Context mCeContext = getInstrumentation().getContext();
@@ -81,14 +83,26 @@
     }
 
     /**
+     * Tests that {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} broadcast was sent.
+     */
+    @Test
+    public void testVerifyReceivedLockedBootCompletedBroadcast() throws Exception {
+        waitForBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED, LOCKED_BOOT_TIMEOUT);
+    }
+
+    /**
      * Tests that {@link Intent.ACTION_BOOT_COMPLETED} broadcast was sent.
      */
     @Test
     public void testVerifyReceivedBootCompletedBroadcast() throws Exception {
+        waitForBroadcast(Intent.ACTION_BOOT_COMPLETED, BOOT_TIMEOUT);
+    }
+
+    private void waitForBroadcast(String intent, Duration timeout) throws Exception {
         TestUtils.waitUntil(
-                "Didn't receive broadcast " + Intent.ACTION_BOOT_COMPLETED + " in " + BOOT_TIMEOUT,
-                (int) BOOT_TIMEOUT.getSeconds(),
-                () -> queryBroadcast(Intent.ACTION_BOOT_COMPLETED));
+                "Didn't receive broadcast " + intent + " in " + timeout,
+                (int) timeout.getSeconds(),
+                () -> queryBroadcast(intent));
     }
 
     private boolean queryBroadcast(String intent) {
@@ -98,8 +112,14 @@
         if (cursor == null) {
             return false;
         }
-        cursor.moveToFirst();
-        int index = cursor.getColumnIndex("exists");
+        if (!cursor.moveToFirst()) {
+            Log.w(TAG, "Broadcast: " + intent + " cursor is empty");
+            return false;
+        }
+        String column = intent.equals(Intent.ACTION_LOCKED_BOOT_COMPLETED)
+                ? "locked_boot_completed"
+                : "boot_completed";
+        int index = cursor.getColumnIndex(column);
         return cursor.getInt(index) == 1;
     }
 }
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 5adfac8..f7873ab 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -76,8 +76,8 @@
         PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, PACKAGE_NAME_APP3
     };
 
-    private static final int WAIT_TIME = 2000;
-    private static final int WAITFOR_MSEC = 5000;
+    private static final int WAIT_TIME = 10000;
+    private static final int WAITFOR_MSEC = 10000;
     // A secondary test activity from another APK.
     static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
     static final String SIMPLE_SERVICE = ".SimpleService";
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 3d84d388..cfc93be 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -51,6 +51,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.HashMap;
 
 import org.junit.Test;
 
@@ -224,6 +225,9 @@
         updatePreviewSurface(maxPreviewSize);
 
         createImageReader(maxYuvSize, ImageFormat.YUV_420_888, MAX_IMAGES_TO_PREPARE, imageListener);
+        HashMap<Size, Long> yuvMinFrameDurations =
+                mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
+        Long readerMinFrameDuration = yuvMinFrameDurations.get(maxYuvSize);
 
         List<Surface> outputSurfaces = new ArrayList<Surface>();
         outputSurfaces.add(mPreviewSurface);
@@ -327,14 +331,16 @@
                         cameraId,
                         frameDurationStats.first / 1e6, preparedFrameDurationStats.second / 1e6),
                 (preparedFrameDurationStats.second <=
-                        frameDurationStats.first * (1 + PREPARE_PEAK_RATE_BOUNDS)));
+                        Math.max(frameDurationStats.first, readerMinFrameDuration) *
+                        (1 + PREPARE_PEAK_RATE_BOUNDS)));
             mCollector.expectTrue(
                 String.format("Camera %s: Preview average frame interval affected by use of new " +
                         "stream: preview avg frame duration: %f ms, with new stream: %f ms",
                         cameraId,
                         frameDurationStats.first / 1e6, preparedFrameDurationStats.first / 1e6),
                 (preparedFrameDurationStats.first <=
-                        frameDurationStats.first * (1 + PREPARE_FRAME_RATE_BOUNDS)));
+                        Math.max(frameDurationStats.first, readerMinFrameDuration) *
+                        (1 + PREPARE_FRAME_RATE_BOUNDS)));
         }
     }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index bc84c83..f74aa6d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -31,6 +31,8 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -45,15 +47,10 @@
 import android.server.wm.TestJournalProvider.TestJournalContainer;
 import android.server.wm.settings.SettingsSession;
 
-import androidx.annotation.IntDef;
-import androidx.test.filters.FlakyTest;
-
 import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.Test;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.List;
 
@@ -66,22 +63,6 @@
 
     private static final float EXPECTED_FONT_SIZE_SP = 10.0f;
 
-    /** Verifies if the count of configuration changes is expected. */
-    private static final int TEST_MODE_CONFIGURATION_CHANGE = 1;
-    /** Verifies if the count of relaunch is expected. */
-    private static final int TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE = 2;
-    /** Verifies if sizes match. */
-    private static final int TEST_MODE_RESIZE = 3;
-
-    /** Test mode that defines which lifecycle callback is verified in a particular test */
-    @IntDef(flag = true, value = {
-            TEST_MODE_CONFIGURATION_CHANGE,
-            TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE,
-            TEST_MODE_RESIZE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface TestMode {}
-
     @Test
     public void testRotation90Relaunch() throws Exception{
         assumeTrue("Skipping test: no rotation support", supportsRotation());
@@ -118,105 +99,95 @@
         testRotation(NO_RELAUNCH_ACTIVITY, 2, 0, 0);
     }
 
-    @Test
-    @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
-    public void testRotation180RelaunchWithCutout() throws Exception {
-        assumeTrue("Skipping test: no rotation support", supportsRotation());
-        assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
-
-        testRotation180WithCutout(TEST_ACTIVITY, TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE);
-    }
-
-    @Test
-    @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
-    public void testRotation180NoRelaunchWithCutout() throws Exception {
-        assumeTrue("Skipping test: no rotation support", supportsRotation());
-        assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
-
-        testRotation180WithCutout(NO_RELAUNCH_ACTIVITY, TEST_MODE_CONFIGURATION_CHANGE);
-    }
-
     /**
      * Test activity configuration changes for devices with cutout(s). Landscape and
      * reverse-landscape rotations should result in same screen space available for apps.
      */
     @Test
-    @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
-    public void testConfigChangeWhenRotatingWithCutout() throws Exception {
+    public void testRotation180RelaunchWithCutout() throws Exception {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
         assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
 
-        testRotation180WithCutout(TEST_ACTIVITY, TEST_MODE_RESIZE);
+        testRotation180WithCutout(TEST_ACTIVITY, false /* canHandleConfigChange */);
     }
 
-    private void testRotation180WithCutout(ComponentName activityName, @TestMode int testMode)
-            throws Exception {
+    @Test
+    public void testRotation180NoRelaunchWithCutout() throws Exception {
+        assumeTrue("Skipping test: no rotation support", supportsRotation());
+        assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
+
+        testRotation180WithCutout(NO_RELAUNCH_ACTIVITY, true /* canHandleConfigChange */);
+    }
+
+    private void testRotation180WithCutout(ComponentName activityName,
+            boolean canHandleConfigChange) throws Exception {
         launchActivity(activityName);
         mAmWmState.computeState(activityName);
 
         try(final RotationSession rotationSession = new RotationSession()) {
-            final StateCount count1 = getStateCountForRotation(activityName, rotationSession,
-                    ROTATION_0 /* before */, ROTATION_180 /* after */);
-            final StateCount count2 = getStateCountForRotation(activityName, rotationSession,
-                    ROTATION_90 /* before */, ROTATION_270 /* after */);
+            final ActivityLifecycleCounts count1 = getLifecycleCountsForRotation(activityName,
+                    rotationSession, ROTATION_0 /* before */, ROTATION_180 /* after */,
+                    canHandleConfigChange);
+            final int configChangeCount1 = count1
+                    .getCount(ActivityCallback.ON_CONFIGURATION_CHANGED);
+            final int relaunchCount1 = count1.getCount(ActivityCallback.ON_CREATE);
 
-            final int configChange = count1.mConfigChangeCount + count2.mConfigChangeCount;
-            final int relaunch = count1.mRelaunchCount + count2.mRelaunchCount;
-            // There should at least one 180 rotation without resize.
-            final boolean sameSize = !count1.mResize || !count2.mResize;
+            final ActivityLifecycleCounts count2 = getLifecycleCountsForRotation(activityName,
+                    rotationSession, ROTATION_90 /* before */, ROTATION_270 /* after */,
+                    canHandleConfigChange);
+            final int configChangeCount2 = count2
+                    .getCount(ActivityCallback.ON_CONFIGURATION_CHANGED);
+            final int relaunchCount2 = count2.getCount(ActivityCallback.ON_CREATE);
 
-            switch(testMode) {
-                case TEST_MODE_CONFIGURATION_CHANGE: {
-                    assertTrue("There must be at most one 180 degree rotation that results in the"
-                            + " same configuration on device with cutout", configChange <= 1);
-                    assertEquals("There must be no relaunch during test", 0, relaunch);
-                    break;
-                }
-                case TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE: {
-                    // If the size change does not cross the threshold, the activity will receive
-                    // onConfigurationChanged instead of relaunching.
-                    assertTrue("There must be at most one 180 degree rotation that results in"
-                            + " relaunch or a configuration change on device with cutout",
-                            relaunch + configChange <= 1);
-                    break;
-                }
-                case TEST_MODE_RESIZE: {
-                    assertTrue("A device with cutout should have the same available screen space"
-                            + " in landscape and reverse-landscape", sameSize);
-                    break;
-                }
-                default: {
-                    fail("unrecognized test mode: " + testMode);
-                }
+            final int configChange = configChangeCount1 + configChangeCount2;
+            final int relaunch = relaunchCount1 + relaunchCount2;
+            if (canHandleConfigChange) {
+                assertWithMessage("There must be at most one 180 degree rotation that results "
+                        + "in the same configuration.").that(configChange).isLessThan(2);
+                assertEquals("There must be no relaunch during test", 0, relaunch);
+                return;
             }
+
+            // If the size change does not cross the threshold, the activity will receive
+            // onConfigurationChanged instead of relaunching.
+            assertWithMessage("There must be at most one 180 degree rotation that results in "
+                    + "relaunch or a configuration change.").that(relaunch + configChange)
+                    .isLessThan(2);
+
+            final boolean resize1 = configChangeCount1 + relaunchCount1 > 0;
+            final boolean resize2 = configChangeCount2 + relaunchCount2 > 0;
+            // There should at least one 180 rotation without resize.
+            final boolean sameSize = !resize1 || !resize2;
+
+            assertTrue("A device with cutout should have the same available screen space"
+                    + " in landscape and reverse-landscape", sameSize);
         }
     }
 
-    private StateCount getStateCountForRotation(ComponentName activityName, RotationSession session,
-            int before, int after) throws Exception {
+    private ActivityLifecycleCounts getLifecycleCountsForRotation(ComponentName activityName,
+            RotationSession session, int before, int after, boolean canHandleConfigChange)  {
+        final int currentRotation = mAmWmState.getWmState().getRotation();
+        // The test verifies the events from "before" rotation to "after" rotation. So when
+        // preparing "before" rotation, the changes should be consumed to avoid being mixed into
+        // the result to verify.
+        final boolean is90DegreeDelta = Math.abs(currentRotation - before) % 2 != 0;
+        if (is90DegreeDelta) {
+            separateTestJournal();
+        }
         session.set(before);
+        if (is90DegreeDelta) {
+            // Consume the changes of "before" rotation to make sure the activity is in a stable
+            // state to apply "after" rotation.
+            final ActivityCallback expectedCallback = canHandleConfigChange
+                    ? ActivityCallback.ON_CONFIGURATION_CHANGED
+                    : ActivityCallback.ON_CREATE;
+            mAmWmState.waitFor("activity rotated with 90 degree delta",
+                    () -> new ActivityLifecycleCounts(activityName).getCount(expectedCallback) > 0);
+        }
         separateTestJournal();
         session.set(after);
         mAmWmState.computeState(activityName);
-        final ActivityLifecycleCounts counter = new ActivityLifecycleCounts(activityName);
-
-        int configChangeCount = counter.getCount(ActivityCallback.ON_CONFIGURATION_CHANGED);
-        int relaunchCount = counter.getCount(ActivityCallback.ON_CREATE);
-        boolean resize = getLastReportedSizesForActivity(activityName) != null;
-
-        return new StateCount(configChangeCount, relaunchCount, resize);
-    }
-
-    private final static class StateCount {
-        final int mConfigChangeCount;
-        final int mRelaunchCount;
-        final boolean mResize;
-
-        StateCount(int configChangeCount, int relaunchCount, boolean resize) {
-            mConfigChangeCount = configChangeCount;
-            mRelaunchCount = relaunchCount;
-            mResize = resize;
-        }
+        return new ActivityLifecycleCounts(activityName);
     }
 
     @Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index dbbc682..f07519a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -960,6 +960,7 @@
 
     @Test
     public void testSetRequestedOrientationWhilePinned() throws Exception {
+        assumeTrue("Skipping test: no rotation support", supportsRotation());
         // Launch the PiP activity fixed as portrait, and enter picture-in-picture
         launchActivity(PIP_ACTIVITY,
                 EXTRA_PIP_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT),
diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml
index 3414847..4d6d9d9 100644
--- a/tests/media/DynamicConfig.xml
+++ b/tests/media/DynamicConfig.xml
@@ -1,6 +1,6 @@
 <dynamicConfig>
     <entry key="media_files_url">
-      <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases.zip</value>
+      <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.1.zip</value>
     </entry>
 </dynamicConfig>
 
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index d5eb85b..8dace62 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -341,8 +341,6 @@
             CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
             CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
             CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
-            CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
-                      isPass);
             CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
             CHECK_ERR(
                     loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
@@ -462,8 +460,6 @@
         CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
         CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
         CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
-        CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
-                  isPass);
         CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
         if (!isPass) continue;
 
@@ -483,8 +479,6 @@
         CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
         CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
         CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
-        CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
-                  isPass);
         CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
         if (validateFormat) {
             if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
@@ -611,8 +605,6 @@
                 CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
                 CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
                 CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
-                CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log,
-                          "input cnt != output cnt", isPass);
                 CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
                 CHECK_ERR(loopCounter == 0 && mIsAudio &&
                           (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
diff --git a/tests/media/jni/NativeCodecTestBase.h b/tests/media/jni/NativeCodecTestBase.h
index 65f4111e..b730fb0 100644
--- a/tests/media/jni/NativeCodecTestBase.h
+++ b/tests/media/jni/NativeCodecTestBase.h
@@ -92,7 +92,12 @@
     uint32_t adler32(const uint8_t* input, int offset, int len);
 
   public:
-    void saveInPTS(int64_t pts) { inpPtsArray.push_back(pts); }
+    void saveInPTS(int64_t pts) {
+        // Add only Unique timeStamp, discarding any duplicate frame / non-display frame
+        if(0 == std::count(inpPtsArray.begin(), inpPtsArray.end(), pts)) {
+            inpPtsArray.push_back(pts);
+        }
+    }
     void saveOutPTS(int64_t pts) { outPtsArray.push_back(pts); }
     bool isPtsStrictlyIncreasing(int64_t lastPts);
     bool isOutPtsListIdenticalToInpPtsList(bool requireSorting);
diff --git a/tests/media/jni/NativeMediaCommon.cpp b/tests/media/jni/NativeMediaCommon.cpp
index 0debeac..23d9804 100644
--- a/tests/media/jni/NativeMediaCommon.cpp
+++ b/tests/media/jni/NativeMediaCommon.cpp
@@ -48,11 +48,6 @@
 bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat) {
     const char* mime;
     AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &mime);
-    /* TODO(b/154177490) */
-    if ((strcmp(mime, AMEDIA_MIMETYPE_VIDEO_VP9) == 0) ||
-        (strcmp(mime, AMEDIA_MIMETYPE_VIDEO_AV1) == 0)) {
-        return true;
-    }
     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 9e759bc..56f2ba8 100644
--- a/tests/media/jni/NativeMuxerTest.cpp
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -66,14 +66,14 @@
 
     bool combineMedias(AMediaMuxer* muxer, MuxerNativeTestHelper* that, const int* repeater);
 
-    bool equals(MuxerNativeTestHelper* that);
+    bool isSubsetOf(MuxerNativeTestHelper* that);
 
     void offsetTimeStamp(int trackID, long tsOffset, int sampleOffset);
 
   private:
     void splitMediaToMuxerParameters();
 
-    static const int STTS_TOLERANCE = 100;
+    static const int STTS_TOLERANCE_US = 100;
     const char* mSrcPath;
     const char* mMime;
     int mTrackCount;
@@ -263,7 +263,7 @@
 
 // returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
 // stream are present in ref media stream
-bool MuxerNativeTestHelper::equals(MuxerNativeTestHelper* that) {
+bool MuxerNativeTestHelper::isSubsetOf(MuxerNativeTestHelper* that) {
     if (this == that) return true;
     if (that == nullptr) return false;
 
@@ -279,31 +279,28 @@
             if (thisMime != nullptr && thatMime != nullptr && !strcmp(thisMime, thatMime)) {
                 if (!isCSDIdentical(thisFormat, thatFormat)) continue;
                 if (mBufferInfo[i].size() == that->mBufferInfo[j].size()) {
-                    int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
-                    for (int k = 0; k < mBufferInfo[i].size(); k++) {
+                    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];
                         AMediaCodecBufferInfo* thatInfo = that->mBufferInfo[j][k];
                         if (thisInfo->flags != thatInfo->flags) {
-                            flagsDiff++;
+                            break;
                         }
                         if (thisInfo->size != thatInfo->size) {
-                            sizeDiff++;
+                            break;
                         } else if (memcmp(mBuffer + thisInfo->offset,
                                           that->mBuffer + thatInfo->offset, thisInfo->size)) {
-                            buffDiff++;
+                            break;
                         }
                         if (abs(thisInfo->presentationTimeUs - thatInfo->presentationTimeUs) >
-                            STTS_TOLERANCE) {
-                            tsDiff++;
+                            tolerance) {
+                            break;
                         }
                     }
-                    if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0 && buffDiff == 0)
-                        break;
-                    else {
-                        ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff "
-                              "%d, buffDiff %d", thisMime, (int)mBufferInfo[i].size(), flagsDiff,
-                              sizeDiff, tsDiff, buffDiff);
-                    }
+                    if (k == mBufferInfo[i].size()) break;
                 }
             }
         }
@@ -524,7 +521,7 @@
             fclose(rfp);
             if (muxStatus) {
                 auto* refInfo = new MuxerNativeTestHelper(crefPath);
-                if (!mediaInfoA->equals(refInfo) || !mediaInfoB->equals(refInfo)) {
+                if (!mediaInfoA->isSubsetOf(refInfo) || !mediaInfoB->isSubsetOf(refInfo)) {
                     isPass = false;
                     ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing src A and src B "
                           "failed", csrcPathA, csrcPathB, jformat);
@@ -540,7 +537,7 @@
                             fclose(ofp);
                             if (status) {
                                 auto* dstInfo = new MuxerNativeTestHelper(cdstPath);
-                                if (!dstInfo->equals(refInfo)) {
+                                if (!dstInfo->isSubsetOf(refInfo)) {
                                     isPass = false;
                                     ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing "
                                           "src A: %d, src B: %d failed", csrcPathA, csrcPathB,
@@ -614,7 +611,7 @@
                 AMediaMuxer_delete(muxer);
                 fclose(ofp);
                 auto* outInfo = new MuxerNativeTestHelper(cdstPath);
-                isPass = mediaInfo->equals(outInfo);
+                isPass = mediaInfo->isSubsetOf(outInfo);
                 if (!isPass) {
                     ALOGE("Validation failed after adding timestamp offset to track %d", trackID);
                 }
@@ -668,7 +665,7 @@
                 fclose(ofp);
                 if (muxStatus) {
                     auto* outInfo = new MuxerNativeTestHelper(cdstPath, cmime);
-                    result = mediaInfo->equals(outInfo);
+                    result = mediaInfo->isSubsetOf(outInfo);
                     delete outInfo;
                 }
                 if ((muxStatus && !result) ||
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java
new file mode 100644
index 0000000..e57b3e7
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.mediav2.cts;
+
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class CodecDecoderExtTest extends CodecDecoderTestBase {
+    private static final String LOG_TAG = CodecDecoderExtTest.class.getSimpleName();
+
+    private final String mRefFile;
+
+    public CodecDecoderExtTest(String mime, String testFile, String refFile) {
+        super(mime, testFile);
+        mRefFile = refFile;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0})")
+    public static Collection<Object[]> input() {
+        return Arrays.asList(new Object[][]{
+                {MediaFormat.MIMETYPE_VIDEO_VP9,
+                        //show and no-show frames are sent as separate inputs
+                        "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
+                        //show and no-show frames are sent as one input
+                        "bbb_340x280_768kbps_30fps_vp9.webm"},
+                {MediaFormat.MIMETYPE_VIDEO_VP9,
+                        //show and no-show frames are sent as separate inputs
+                        "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm",
+                        //show and no-show frames are sent as one input
+                        "bbb_520x390_1mbps_30fps_vp9.webm"},
+        });
+    }
+
+    /**
+     * Test decodes and compares decoded output of two files.
+     */
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testDecodeAndValidate() throws IOException, InterruptedException {
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+        if (listOfDecoders.isEmpty()) {
+            fail("no suitable codecs found for mime: " + mMime);
+        }
+        final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+        for (String decoder : listOfDecoders) {
+            decodeToMemory(mTestFile, decoder, 0, mode, Integer.MAX_VALUE);
+            OutputManager test = mOutputBuff;
+            String log = String.format("codec: %s, test file: %s, ref file: %s:: ", decoder,
+                    mTestFile, mRefFile);
+            assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+            assertTrue(log + "no input sent", 0 != mInputCount);
+            assertTrue(log + "output received", 0 != mOutputCount);
+            if (mIsAudio) {
+                assertTrue("reference output pts is not strictly increasing",
+                        test.isPtsStrictlyIncreasing(mPrevOutputPts));
+            } else {
+                assertTrue("input pts list and output pts list are not identical",
+                        test.isOutPtsListIdenticalToInpPtsList(false));
+            }
+            decodeToMemory(mRefFile, decoder, 0, mode, Integer.MAX_VALUE);
+            OutputManager ref = mOutputBuff;
+            assertTrue(log + "decoder outputs are not identical", ref.equals(test));
+        }
+    }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
index 5d1bc41..58d83c9 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -21,7 +21,6 @@
 import android.media.MediaFormat;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.Pair;
 import android.view.Surface;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
@@ -36,7 +35,6 @@
 import org.junit.runners.Parameterized;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -44,31 +42,19 @@
 import java.util.List;
 import java.util.Set;
 
-import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 @RunWith(Parameterized.class)
-public class CodecDecoderSurfaceTest extends CodecTestBase {
+public class CodecDecoderSurfaceTest extends CodecDecoderTestBase {
     private static final String LOG_TAG = CodecDecoderSurfaceTest.class.getSimpleName();
 
-    private final String mMime;
-    private final String mTestFile;
     private final String mReconfigFile;
-
-    private final ArrayList<ByteBuffer> mCsdBuffers;
-    private int mCurrCsdIdx;
-
-    private MediaExtractor mExtractor;
     private SurfaceView mSurfaceView;
 
     public CodecDecoderSurfaceTest(String mime, String testFile, String reconfigFile) {
-        mMime = mime;
-        mTestFile = testFile;
+        super(mime, testFile);
         mReconfigFile = reconfigFile;
-        mCsdBuffers = new ArrayList<>();
-        mAsyncHandle = new CodecAsyncHandler();
-        mIsAudio = mMime.startsWith("audio/");
     }
 
     private void setScreenParams(int width, int height, boolean noStretch) {
@@ -92,85 +78,6 @@
         mActivityRule.getActivity().runOnUiThread(() -> mSurfaceView.setLayoutParams(lp));
     }
 
-    private MediaFormat setUpSource(String srcFile) throws IOException {
-        mExtractor = new MediaExtractor();
-        mExtractor.setDataSource(mInpPrefix + srcFile);
-        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
-            MediaFormat format = mExtractor.getTrackFormat(trackID);
-            if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
-                mExtractor.selectTrack(trackID);
-                if (!mIsAudio) {
-                    // COLOR_FormatYUV420Flexible by default should be supported by all components
-                    // This call shouldn't effect configure() call for any codec
-                    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
-                }
-                return format;
-            }
-        }
-        fail("No track with mime: " + mMime + " found in file: " + srcFile);
-        return null;
-    }
-
-    private void enqueueCodecConfig(int bufferIndex) {
-        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-        ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
-        inputBuffer.put((ByteBuffer) csdBuffer.rewind());
-        mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
-                MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
-        }
-    }
-
-    private void queueCodecConfig() throws InterruptedException {
-        if (mIsCodecInAsyncMode) {
-            for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
-                 mCurrCsdIdx++) {
-                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
-                if (element != null) {
-                    enqueueCodecConfig(element.first);
-                }
-            }
-        } else {
-            for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
-                enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
-            }
-        }
-    }
-
-    void enqueueInput(int bufferIndex) {
-        if (mExtractor.getSampleSize() < 0) {
-            enqueueEOS(bufferIndex);
-        } else {
-            ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-            mExtractor.readSampleData(inputBuffer, 0);
-            int size = (int) mExtractor.getSampleSize();
-            long pts = mExtractor.getSampleTime();
-            int extractorFlags = mExtractor.getSampleFlags();
-            int codecFlags = 0;
-            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
-                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
-            }
-            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
-                codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
-            }
-            if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
-                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
-                mSawInputEOS = true;
-            }
-            if (ENABLE_LOGS) {
-                Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
-                        " flags: " + codecFlags);
-            }
-            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
-            if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
-                    MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
-                mOutputBuff.saveInPTS(pts);
-                mInputCount++;
-            }
-        }
-    }
-
     void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
         if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
             mSawOutputEOS = true;
@@ -261,6 +168,9 @@
                         "bbb_520x390_1mbps_30fps_vp8.webm"},
                 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm",
                         "bbb_520x390_1mbps_30fps_vp9.webm"},
+                {MediaFormat.MIMETYPE_VIDEO_VP9,
+                        "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
+                        "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm"},
                 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4",
                         "bbb_520x390_1mbps_30fps_av1.mp4"},
         });
@@ -309,8 +219,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " +
-                        mInputCount, mInputCount == mOutputCount);
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
             }
             mCodec.release();
@@ -389,8 +297,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
-                        + mInputCount, mInputCount == mOutputCount);
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
 
                 /* test flush in eos state */
@@ -407,8 +313,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
-                        + mInputCount, mInputCount == mOutputCount);
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
             }
             mCodec.release();
@@ -481,10 +385,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
 
                 /* test reconfigure codec at eos state */
@@ -501,10 +401,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
                 mExtractor.release();
 
@@ -526,10 +422,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", configRef.equals(test));
                 mExtractor.release();
             }
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index ca05d0e..1daf63f 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -16,14 +16,11 @@
 
 package android.mediav2.cts;
 
-import android.media.Image;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.os.PersistableBundle;
 import android.util.Log;
-import android.util.Pair;
 import android.view.Surface;
 
 import androidx.test.filters.LargeTest;
@@ -48,7 +45,6 @@
 import java.util.List;
 import java.util.Set;
 
-import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -62,30 +58,19 @@
  * of these tests are not to cover CDD requirements but to test components and their plugins
  */
 @RunWith(Parameterized.class)
-public class CodecDecoderTest extends CodecTestBase {
+public class CodecDecoderTest extends CodecDecoderTestBase {
     private static final String LOG_TAG = CodecDecoderTest.class.getSimpleName();
 
-    private final String mMime;
-    private final String mTestFile;
     private final String mRefFile;
     private final String mReconfigFile;
     private final float mRmsError;
 
-    private ArrayList<ByteBuffer> mCsdBuffers;
-    private int mCurrCsdIdx;
-
-    private MediaExtractor mExtractor;
-
     public CodecDecoderTest(String mime, String testFile, String refFile, String reconfigFile,
             float rmsError) {
-        mMime = mime;
-        mTestFile = testFile;
+        super(mime, testFile);
         mRefFile = refFile;
         mReconfigFile = reconfigFile;
         mRmsError = rmsError;
-        mAsyncHandle = new CodecAsyncHandler();
-        mCsdBuffers = new ArrayList<>();
-        mIsAudio = mMime.startsWith("audio/");
     }
 
     private short[] setUpAudioReference() throws IOException {
@@ -106,164 +91,6 @@
         return refData;
     }
 
-    private MediaFormat setUpSource(String srcFile) throws IOException {
-        mExtractor = new MediaExtractor();
-        mExtractor.setDataSource(mInpPrefix + srcFile);
-        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
-            MediaFormat format = mExtractor.getTrackFormat(trackID);
-            if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
-                mExtractor.selectTrack(trackID);
-                if (!mIsAudio) {
-                    // COLOR_FormatYUV420Flexible by default should be supported by all components
-                    // This call shouldn't effect configure() call for any codec
-                    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
-                }
-                return format;
-            }
-        }
-        fail("No track with mime: " + mMime + " found in file: " + srcFile);
-        return null;
-    }
-
-    private boolean hasCSD(MediaFormat format) {
-        return format.containsKey("csd-0");
-    }
-
-    private void enqueueCodecConfig(int bufferIndex) {
-        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-        ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
-        inputBuffer.put((ByteBuffer) csdBuffer.rewind());
-        mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
-                MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
-        }
-    }
-
-    void enqueueInput(int bufferIndex) {
-        if (mExtractor.getSampleSize() < 0) {
-            enqueueEOS(bufferIndex);
-        } else {
-            ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-            mExtractor.readSampleData(inputBuffer, 0);
-            int size = (int) mExtractor.getSampleSize();
-            long pts = mExtractor.getSampleTime();
-            int extractorFlags = mExtractor.getSampleFlags();
-            int codecFlags = 0;
-            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
-                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
-            }
-            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
-                codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
-            }
-            if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
-                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
-                mSawInputEOS = true;
-            }
-            if (ENABLE_LOGS) {
-                Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
-                        " flags: " + codecFlags);
-            }
-            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
-            if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
-                    MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
-                mOutputBuff.saveInPTS(pts);
-                mInputCount++;
-            }
-        }
-    }
-
-    private void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
-        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-        inputBuffer.put((ByteBuffer) buffer.rewind());
-        int flags = 0;
-        if ((info.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
-            flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
-        }
-        if ((info.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
-            flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
-        }
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "input: id: " + bufferIndex + " flags: " + info.flags + " size: " +
-                    info.size + " timestamp: " + info.presentationTimeUs);
-        }
-        mCodec.queueInputBuffer(bufferIndex, info.offset, info.size, info.presentationTimeUs,
-                flags);
-        if (info.size > 0 && ((flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
-                ((flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
-            mOutputBuff.saveInPTS(info.presentationTimeUs);
-            mInputCount++;
-        }
-    }
-
-    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
-        if (info.size > 0 && mSaveToMem) {
-            if (mIsAudio) {
-                ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
-                mOutputBuff.saveToMemory(buf, info);
-            } else {
-                // tests both getOutputImage and getOutputBuffer. Can do time division
-                // multiplexing but lets allow it for now
-                Image img = mCodec.getOutputImage(bufferIndex);
-                assertTrue(img != null);
-                mOutputBuff.checksum(img);
-
-                ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
-                mOutputBuff.checksum(buf, info.size);
-            }
-        }
-        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-            mSawOutputEOS = true;
-        }
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
-                    info.size + " timestamp: " + info.presentationTimeUs);
-        }
-        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
-            mOutputBuff.saveOutPTS(info.presentationTimeUs);
-            mOutputCount++;
-        }
-        mCodec.releaseOutputBuffer(bufferIndex, false);
-    }
-
-    private void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
-            throws InterruptedException {
-        int frameCount = 0;
-        if (mIsCodecInAsyncMode) {
-            // output processing after queuing EOS is done in waitForAllOutputs()
-            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
-                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
-                if (element != null) {
-                    int bufferID = element.first;
-                    MediaCodec.BufferInfo info = element.second;
-                    if (info != null) {
-                        dequeueOutput(bufferID, info);
-                    } else {
-                        enqueueInput(bufferID, buffer, list.get(frameCount));
-                        frameCount++;
-                    }
-                }
-            }
-        } else {
-            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
-            // output processing after queuing EOS is done in waitForAllOutputs()
-            while (!mSawInputEOS && frameCount < list.size()) {
-                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
-                if (outputBufferId >= 0) {
-                    dequeueOutput(outputBufferId, outInfo);
-                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                    mOutFormat = mCodec.getOutputFormat();
-                    mSignalledOutFormatChanged = true;
-                }
-                int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
-                if (inputBufferId != -1) {
-                    enqueueInput(inputBufferId, buffer, list.get(frameCount));
-                    frameCount++;
-                }
-            }
-        }
-    }
-
     private ArrayList<MediaCodec.BufferInfo> createSubFrames(ByteBuffer buffer, int sfCount) {
         int size = (int) mExtractor.getSampleSize();
         if (size < 0) return null;
@@ -290,48 +117,6 @@
         return list;
     }
 
-    private void queueCodecConfig() throws InterruptedException {
-        if (mIsCodecInAsyncMode) {
-            for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
-                 mCurrCsdIdx++) {
-                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
-                if (element != null) {
-                    enqueueCodecConfig(element.first);
-                }
-            }
-        } else {
-            for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
-                enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
-            }
-        }
-    }
-
-    private void decodeToMemory(String file, String decoder, long pts, int mode, int frameLimit)
-            throws IOException, InterruptedException {
-        mSaveToMem = true;
-        mOutputBuff = new OutputManager();
-        mCodec = MediaCodec.createByCodecName(decoder);
-        MediaFormat format = setUpSource(file);
-        configureCodec(format, false, true, false);
-        mCodec.start();
-        mExtractor.seekTo(pts, mode);
-        doWork(frameLimit);
-        queueEOS();
-        waitForAllOutputs();
-        mCodec.stop();
-        mCodec.release();
-        mExtractor.release();
-        mSaveToMem = false;
-    }
-
-    @Override
-    PersistableBundle validateMetrics(String decoder, MediaFormat format) {
-        PersistableBundle metrics = super.validateMetrics(decoder, format);
-        assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
-        assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 0);
-        return metrics;
-    }
-
     @Parameterized.Parameters(name = "{index}({0})")
     public static Collection<Object[]> input() {
         Set<String> list = new HashSet<>();
@@ -417,6 +202,9 @@
                         "bbb_520x390_1mbps_30fps_vp8.webm", -1.0f},
                 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm", null,
                         "bbb_520x390_1mbps_30fps_vp9.webm", -1.0f},
+                {MediaFormat.MIMETYPE_VIDEO_VP9,
+                        "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm", null,
+                        "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm", -1.0f},
                 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4", null,
                         "bbb_520x390_1mbps_30fps_av1.mp4", -1.0f},
         });
@@ -481,10 +269,6 @@
                     assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                     assertTrue(log + "no input sent", 0 != mInputCount);
                     assertTrue(log + "output received", 0 != mOutputCount);
-                    if (!mIsAudio) {
-                        assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                                " / " + mInputCount, mInputCount == mOutputCount);
-                    }
                     if (loopCounter != 0) {
                         assertTrue(log + "decoder output is flaky", ref.equals(test));
                     } else {
@@ -620,10 +404,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
 
                 /* test flush in eos state */
@@ -640,10 +420,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
                 if (validateFormat) {
                     assertTrue(log + "not received format change",
@@ -767,10 +543,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
                 if (validateFormat) {
                     assertTrue(log + "not received format change",
@@ -796,10 +568,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", ref.equals(test));
                 if (validateFormat) {
                     assertTrue(log + "not received format change",
@@ -836,10 +604,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is flaky", configRef.equals(test));
                 if (validateFormat) {
                     assertTrue(log + "not received format change",
@@ -980,11 +744,6 @@
                         assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                         assertTrue(log + "no input sent", 0 != mInputCount);
                         assertTrue(log + "output received", 0 != mOutputCount);
-                        if (!mIsAudio) {
-                            assertTrue(
-                                    log + "input count != output count, act/exp: " + mOutputCount +
-                                            " / " + mInputCount, mInputCount == mOutputCount);
-                        }
                         if (loopCounter != 0) {
                             assertTrue(log + "decoder output is flaky", ref.equals(test));
                         } else {
@@ -1079,10 +838,6 @@
                 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
                 assertTrue(log + "no input sent", 0 != mInputCount);
                 assertTrue(log + "output received", 0 != mOutputCount);
-                if (!mIsAudio) {
-                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
-                            " / " + mInputCount, mInputCount == mOutputCount);
-                }
                 assertTrue(log + "decoder output is not consistent with ref", ref.equals(test));
             }
             mCodec.release();
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index d6caa62..180e063 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -338,15 +338,21 @@
     }
 
     private void queueEOS() throws InterruptedException {
-        if (!mSawDecInputEOS) {
-            if (mIsCodecInAsyncMode) {
-                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getInput();
+        if (mIsCodecInAsyncMode) {
+            while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
                 if (element != null) {
-                    enqueueDecoderEOS(element.first);
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        dequeueDecoderOutput(bufferID, info);
+                    } else {
+                        enqueueDecoderEOS(element.first);
+                    }
                 }
-            } else {
-                enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
             }
+        } else if (!mSawDecInputEOS) {
+            enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
         }
         if (mIsCodecInAsyncMode) {
             while (!hasSeenError() && !mSawDecOutputEOS) {
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
index 6a4c7d9..79d8f20 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
@@ -60,53 +60,27 @@
  * of these tests are not to cover CDD requirements but to test components and their plugins
  */
 @RunWith(Parameterized.class)
-public class CodecEncoderTest extends CodecTestBase {
+public class CodecEncoderTest extends CodecEncoderTestBase {
     private static final String LOG_TAG = CodecEncoderTest.class.getSimpleName();
-    // files are in WorkDir.getMediaDirString();
-    private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw";
-    private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv";
-    private final int INP_FRM_WIDTH = 352;
-    private final int INP_FRM_HEIGHT = 288;
-
-    private final String mMime;
     private final int[] mBitrates;
     private final int[] mEncParamList1;
     private final int[] mEncParamList2;
-    private final String mInputFile;
     private ArrayList<MediaFormat> mFormats;
-    private byte[] mInputData;
-    private int mNumBytesSubmitted;
-    private long mInputOffsetPts;
     private int mNumSyncFramesReceived;
     private ArrayList<Integer> mSyncFramesPos;
 
-    private int mWidth, mHeight;
-    private int mChannels;
-    private int mSampleRate;
-    private int mFrameRate;
-    private int mMaxBFrames;
-
     public CodecEncoderTest(String mime, int[] bitrates, int[] encoderInfo1, int[] encoderInfo2) {
-        mMime = mime;
-        mFrameRate = 30;
-        if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
-        else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
-        mMaxBFrames = 0;
+        super(mime);
         mBitrates = bitrates;
         mEncParamList1 = encoderInfo1;
         mEncParamList2 = encoderInfo2;
         mFormats = new ArrayList<>();
         mSyncFramesPos = new ArrayList<>();
-        mAsyncHandle = new CodecAsyncHandler();
-        mIsAudio = mMime.startsWith("audio/");
-        mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile;
     }
 
     @Override
     void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
         super.resetContext(isAsync, signalEOSWithLastFrame);
-        mNumBytesSubmitted = 0;
-        mInputOffsetPts = 0;
         mNumSyncFramesReceived = 0;
         mSyncFramesPos.clear();
     }
@@ -114,184 +88,16 @@
     @Override
     void flushCodec() {
         super.flushCodec();
-        if (mIsAudio) {
-            mInputOffsetPts =
-                    (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate);
-        } else {
-            mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate;
-        }
-        mPrevOutputPts = mInputOffsetPts - 1;
-        mNumBytesSubmitted = 0;
         mNumSyncFramesReceived = 0;
         mSyncFramesPos.clear();
     }
 
-    private void setUpSource(String srcFile) throws IOException {
-        String inpPath = mInpPrefix + srcFile;
-        try (FileInputStream fInp = new FileInputStream(inpPath)) {
-            int size = (int) new File(inpPath).length();
-            mInputData = new byte[size];
-            fInp.read(mInputData, 0, size);
-        }
-    }
-
-    void fillImage(Image image) {
-        Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
-        int imageWidth = image.getWidth();
-        int imageHeight = image.getHeight();
-        Image.Plane[] planes = image.getPlanes();
-        int offset = mNumBytesSubmitted;
-        for (int i = 0; i < planes.length; ++i) {
-            ByteBuffer buf = planes[i].getBuffer();
-            int width = imageWidth;
-            int height = imageHeight;
-            int tileWidth = INP_FRM_WIDTH;
-            int tileHeight = INP_FRM_HEIGHT;
-            int rowStride = planes[i].getRowStride();
-            int pixelStride = planes[i].getPixelStride();
-            if (i != 0) {
-                width = imageWidth / 2;
-                height = imageHeight / 2;
-                tileWidth = INP_FRM_WIDTH / 2;
-                tileHeight = INP_FRM_HEIGHT / 2;
-            }
-            if (pixelStride == 1) {
-                if (width == rowStride && width == tileWidth && height == tileHeight) {
-                    buf.put(mInputData, offset, width * height);
-                } else {
-                    for (int z = 0; z < height; z += tileHeight) {
-                        int rowsToCopy = Math.min(height - z, tileHeight);
-                        for (int y = 0; y < rowsToCopy; y++) {
-                            for (int x = 0; x < width; x += tileWidth) {
-                                int colsToCopy = Math.min(width - x, tileWidth);
-                                buf.position((z + y) * rowStride + x);
-                                buf.put(mInputData, offset + y * tileWidth, colsToCopy);
-                            }
-                        }
-                    }
-                }
-            } else {
-                // do it pixel-by-pixel
-                for (int z = 0; z < height; z += tileHeight) {
-                    int rowsToCopy = Math.min(height - z, tileHeight);
-                    for (int y = 0; y < rowsToCopy; y++) {
-                        int lineOffset = (z + y) * rowStride;
-                        for (int x = 0; x < width; x += tileWidth) {
-                            int colsToCopy = Math.min(width - x, tileWidth);
-                            for (int w = 0; w < colsToCopy; w++) {
-                                buf.position(lineOffset + (x + w) * pixelStride);
-                                buf.put(mInputData[offset + y * tileWidth + w]);
-                            }
-                        }
-                    }
-                }
-            }
-            offset += tileWidth * tileHeight;
-        }
-    }
-
-    void fillByteBuffer(ByteBuffer inputBuffer) {
-        int offset = 0, frmOffset = mNumBytesSubmitted;
-        for (int plane = 0; plane < 3; plane++) {
-            int width = mWidth;
-            int height = mHeight;
-            int tileWidth = INP_FRM_WIDTH;
-            int tileHeight = INP_FRM_HEIGHT;
-            if (plane != 0) {
-                width = mWidth / 2;
-                height = mHeight / 2;
-                tileWidth = INP_FRM_WIDTH / 2;
-                tileHeight = INP_FRM_HEIGHT / 2;
-            }
-            for (int k = 0; k < height; k += tileHeight) {
-                int rowsToCopy = Math.min(height - k, tileHeight);
-                for (int j = 0; j < rowsToCopy; j++) {
-                    for (int i = 0; i < width; i += tileWidth) {
-                        int colsToCopy = Math.min(width - i, tileWidth);
-                        inputBuffer.position(offset + (k + j) * width + i);
-                        inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
-                    }
-                }
-            }
-            offset += width * height;
-            frmOffset += tileWidth * tileHeight;
-        }
-    }
-
-    void enqueueInput(int bufferIndex) {
-        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-        if (mNumBytesSubmitted >= mInputData.length) {
-            enqueueEOS(bufferIndex);
-        } else {
-            int size;
-            int flags = 0;
-            long pts = mInputOffsetPts;
-            if (mIsAudio) {
-                pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
-                size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
-                inputBuffer.put(mInputData, mNumBytesSubmitted, size);
-                if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
-                    flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
-                    mSawInputEOS = true;
-                }
-                mNumBytesSubmitted += size;
-            } else {
-                pts += mInputCount * 1000000L / mFrameRate;
-                size = mWidth * mHeight * 3 / 2;
-                int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
-                if (mNumBytesSubmitted + frmSize > mInputData.length) {
-                    fail("received partial frame to encode");
-                } else {
-                    Image img = mCodec.getInputImage(bufferIndex);
-                    if (img != null) {
-                        fillImage(img);
-                    } else {
-                        if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
-                            inputBuffer.put(mInputData, mNumBytesSubmitted, size);
-                        } else {
-                            fillByteBuffer(inputBuffer);
-                        }
-                    }
-                }
-                if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
-                    flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
-                    mSawInputEOS = true;
-                }
-                mNumBytesSubmitted += frmSize;
-            }
-            if (ENABLE_LOGS) {
-                Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
-                        " flags: " + flags);
-            }
-            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
-            mOutputBuff.saveInPTS(pts);
-            mInputCount++;
-        }
-    }
-
     void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
-                    info.size + " timestamp: " + info.presentationTimeUs);
-        }
-        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-            mSawOutputEOS = true;
-        }
-        if (info.size > 0) {
-            if (mSaveToMem) {
-                ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
-                mOutputBuff.saveToMemory(buf, info);
-            }
-            if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
-                mOutputBuff.saveOutPTS(info.presentationTimeUs);
-                mOutputCount++;
-            }
-            if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
                 mNumSyncFramesReceived += 1;
                 mSyncFramesPos.add(mOutputCount);
             }
-        }
-        mCodec.releaseOutputBuffer(bufferIndex, false);
+        super.dequeueOutput(bufferIndex, info);
     }
 
     private void encodeToMemory(String file, String encoder, int frameLimit, MediaFormat format)
@@ -340,14 +146,6 @@
         return colorFormat;
     }
 
-    @Override
-    PersistableBundle validateMetrics(String codec, MediaFormat format) {
-        PersistableBundle metrics = super.validateMetrics(codec, format);
-        assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
-        assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1);
-        return metrics;
-    }
-
     private void forceSyncFrame() {
         final Bundle syncFrame = new Bundle();
         syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index 3e09a8d..1086981 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -22,6 +22,7 @@
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
+import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.os.Build;
 import android.os.PersistableBundle;
@@ -32,6 +33,10 @@
 import androidx.annotation.NonNull;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import org.junit.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -47,6 +52,7 @@
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.zip.CRC32;
 
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -212,7 +218,10 @@
     }
 
     void saveInPTS(long pts) {
-        inpPtsList.add(pts);
+        // Add only Unique timeStamp, discarding any duplicate frame / non-display frame
+        if (!inpPtsList.contains(pts)) {
+            inpPtsList.add(pts);
+        }
     }
 
     void saveOutPTS(long pts) {
@@ -680,15 +689,21 @@
     }
 
     void queueEOS() throws InterruptedException {
-        if (!mSawInputEOS) {
-            if (mIsCodecInAsyncMode) {
-                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+        if (mIsCodecInAsyncMode) {
+            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
                 if (element != null) {
-                    enqueueEOS(element.first);
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        dequeueOutput(bufferID, info);
+                    } else {
+                        enqueueEOS(element.first);
+                    }
                 }
-            } else {
-                enqueueEOS(mCodec.dequeueInputBuffer(-1));
             }
+        } else if (!mSawInputEOS) {
+            enqueueEOS(mCodec.dequeueInputBuffer(-1));
         }
     }
 
@@ -810,3 +825,454 @@
         return metrics;
     }
 }
+
+class CodecDecoderTestBase extends CodecTestBase {
+    private static final String LOG_TAG = CodecDecoderTestBase.class.getSimpleName();
+
+    String mMime;
+    String mTestFile;
+
+    ArrayList<ByteBuffer> mCsdBuffers;
+    private int mCurrCsdIdx;
+
+    MediaExtractor mExtractor;
+
+    CodecDecoderTestBase(String mime, String testFile) {
+        mMime = mime;
+        mTestFile = testFile;
+        mAsyncHandle = new CodecAsyncHandler();
+        mCsdBuffers = new ArrayList<>();
+        mIsAudio = mMime.startsWith("audio/");
+    }
+
+    MediaFormat setUpSource(String srcFile) throws IOException {
+        mExtractor = new MediaExtractor();
+        mExtractor.setDataSource(mInpPrefix + srcFile);
+        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+            MediaFormat format = mExtractor.getTrackFormat(trackID);
+            if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
+                mExtractor.selectTrack(trackID);
+                if (!mIsAudio) {
+                    // COLOR_FormatYUV420Flexible by default should be supported by all components
+                    // This call shouldn't effect configure() call for any codec
+                    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
+                }
+                return format;
+            }
+        }
+        fail("No track with mime: " + mMime + " found in file: " + srcFile);
+        return null;
+    }
+
+    boolean hasCSD(MediaFormat format) {
+        return format.containsKey("csd-0");
+    }
+
+    void enqueueCodecConfig(int bufferIndex) {
+        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+        ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
+        inputBuffer.put((ByteBuffer) csdBuffer.rewind());
+        mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
+                MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
+        }
+    }
+
+    void enqueueInput(int bufferIndex) {
+        if (mExtractor.getSampleSize() < 0) {
+            enqueueEOS(bufferIndex);
+        } else {
+            ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+            mExtractor.readSampleData(inputBuffer, 0);
+            int size = (int) mExtractor.getSampleSize();
+            long pts = mExtractor.getSampleTime();
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+            }
+            if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                mSawInputEOS = true;
+            }
+            if (ENABLE_LOGS) {
+                Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+                        " flags: " + codecFlags);
+            }
+            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+            if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
+                    MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
+                mOutputBuff.saveInPTS(pts);
+                mInputCount++;
+            }
+        }
+    }
+
+    void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
+        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+        inputBuffer.put((ByteBuffer) buffer.rewind());
+        int flags = 0;
+        if ((info.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+            flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+        }
+        if ((info.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+            flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+        }
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "input: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+                    info.size + " timestamp: " + info.presentationTimeUs);
+        }
+        mCodec.queueInputBuffer(bufferIndex, info.offset, info.size, info.presentationTimeUs,
+                flags);
+        if (info.size > 0 && ((flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
+                ((flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
+            mOutputBuff.saveInPTS(info.presentationTimeUs);
+            mInputCount++;
+        }
+    }
+
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if (info.size > 0 && mSaveToMem) {
+            if (mIsAudio) {
+                ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+                mOutputBuff.saveToMemory(buf, info);
+            } else {
+                // tests both getOutputImage and getOutputBuffer. Can do time division
+                // multiplexing but lets allow it for now
+                Image img = mCodec.getOutputImage(bufferIndex);
+                assertTrue(img != null);
+                mOutputBuff.checksum(img);
+
+                ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+                mOutputBuff.checksum(buf, info.size);
+            }
+        }
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+                    info.size + " timestamp: " + info.presentationTimeUs);
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputBuff.saveOutPTS(info.presentationTimeUs);
+            mOutputCount++;
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, false);
+    }
+
+    void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
+            throws InterruptedException {
+        int frameCount = 0;
+        if (mIsCodecInAsyncMode) {
+            // output processing after queuing EOS is done in waitForAllOutputs()
+            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        dequeueOutput(bufferID, info);
+                    } else {
+                        enqueueInput(bufferID, buffer, list.get(frameCount));
+                        frameCount++;
+                    }
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            // output processing after queuing EOS is done in waitForAllOutputs()
+            while (!mSawInputEOS && frameCount < list.size()) {
+                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueOutput(outputBufferId, outInfo);
+                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mOutFormat = mCodec.getOutputFormat();
+                    mSignalledOutFormatChanged = true;
+                }
+                int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                if (inputBufferId != -1) {
+                    enqueueInput(inputBufferId, buffer, list.get(frameCount));
+                    frameCount++;
+                }
+            }
+        }
+    }
+
+    void queueCodecConfig() throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
+                 mCurrCsdIdx++) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+                if (element != null) {
+                    enqueueCodecConfig(element.first);
+                }
+            }
+        } else {
+            for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
+                enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
+            }
+        }
+    }
+
+    void decodeToMemory(String file, String decoder, long pts, int mode, int frameLimit)
+            throws IOException, InterruptedException {
+        mSaveToMem = true;
+        mOutputBuff = new OutputManager();
+        mCodec = MediaCodec.createByCodecName(decoder);
+        MediaFormat format = setUpSource(file);
+        configureCodec(format, false, true, false);
+        mCodec.start();
+        mExtractor.seekTo(pts, mode);
+        doWork(frameLimit);
+        queueEOS();
+        waitForAllOutputs();
+        mCodec.stop();
+        mCodec.release();
+        mExtractor.release();
+        mSaveToMem = false;
+    }
+
+    @Override
+    PersistableBundle validateMetrics(String decoder, MediaFormat format) {
+        PersistableBundle metrics = super.validateMetrics(decoder, format);
+        assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
+        assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 0);
+        return metrics;
+    }
+}
+
+class CodecEncoderTestBase extends CodecTestBase {
+    private static final String LOG_TAG = CodecEncoderTestBase.class.getSimpleName();
+
+    // files are in WorkDir.getMediaDirString();
+    private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw";
+    private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv";
+    private final int INP_FRM_WIDTH = 352;
+    private final int INP_FRM_HEIGHT = 288;
+
+    final String mMime;
+    final String mInputFile;
+    byte[] mInputData;
+    int mNumBytesSubmitted;
+    long mInputOffsetPts;
+
+    int mWidth, mHeight;
+    int mFrameRate;
+    int mMaxBFrames;
+    int mChannels;
+    int mSampleRate;
+
+    CodecEncoderTestBase(String mime) {
+        mMime = mime;
+        mWidth = INP_FRM_WIDTH;
+        mHeight = INP_FRM_HEIGHT;
+        mChannels = 1;
+        mSampleRate = 8000;
+        mFrameRate = 30;
+        mMaxBFrames = 0;
+        if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
+        else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
+        mAsyncHandle = new CodecAsyncHandler();
+        mIsAudio = mMime.startsWith("audio/");
+        mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile;
+    }
+
+    @Override
+    void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
+        super.resetContext(isAsync, signalEOSWithLastFrame);
+        mNumBytesSubmitted = 0;
+        mInputOffsetPts = 0;
+    }
+
+    @Override
+    void flushCodec() {
+        super.flushCodec();
+        if (mIsAudio) {
+            mInputOffsetPts =
+                    (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate);
+        } else {
+            mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate;
+        }
+        mPrevOutputPts = mInputOffsetPts - 1;
+        mNumBytesSubmitted = 0;
+    }
+
+    void setUpSource(String srcFile) throws IOException {
+        String inpPath = mInpPrefix + srcFile;
+        try (FileInputStream fInp = new FileInputStream(inpPath)) {
+            int size = (int) new File(inpPath).length();
+            mInputData = new byte[size];
+            fInp.read(mInputData, 0, size);
+        }
+    }
+
+    void fillImage(Image image) {
+        Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+        int imageWidth = image.getWidth();
+        int imageHeight = image.getHeight();
+        Image.Plane[] planes = image.getPlanes();
+        int offset = mNumBytesSubmitted;
+        for (int i = 0; i < planes.length; ++i) {
+            ByteBuffer buf = planes[i].getBuffer();
+            int width = imageWidth;
+            int height = imageHeight;
+            int tileWidth = INP_FRM_WIDTH;
+            int tileHeight = INP_FRM_HEIGHT;
+            int rowStride = planes[i].getRowStride();
+            int pixelStride = planes[i].getPixelStride();
+            if (i != 0) {
+                width = imageWidth / 2;
+                height = imageHeight / 2;
+                tileWidth = INP_FRM_WIDTH / 2;
+                tileHeight = INP_FRM_HEIGHT / 2;
+            }
+            if (pixelStride == 1) {
+                if (width == rowStride && width == tileWidth && height == tileHeight) {
+                    buf.put(mInputData, offset, width * height);
+                } else {
+                    for (int z = 0; z < height; z += tileHeight) {
+                        int rowsToCopy = Math.min(height - z, tileHeight);
+                        for (int y = 0; y < rowsToCopy; y++) {
+                            for (int x = 0; x < width; x += tileWidth) {
+                                int colsToCopy = Math.min(width - x, tileWidth);
+                                buf.position((z + y) * rowStride + x);
+                                buf.put(mInputData, offset + y * tileWidth, colsToCopy);
+                            }
+                        }
+                    }
+                }
+            } else {
+                // do it pixel-by-pixel
+                for (int z = 0; z < height; z += tileHeight) {
+                    int rowsToCopy = Math.min(height - z, tileHeight);
+                    for (int y = 0; y < rowsToCopy; y++) {
+                        int lineOffset = (z + y) * rowStride;
+                        for (int x = 0; x < width; x += tileWidth) {
+                            int colsToCopy = Math.min(width - x, tileWidth);
+                            for (int w = 0; w < colsToCopy; w++) {
+                                buf.position(lineOffset + (x + w) * pixelStride);
+                                buf.put(mInputData[offset + y * tileWidth + w]);
+                            }
+                        }
+                    }
+                }
+            }
+            offset += tileWidth * tileHeight;
+        }
+    }
+
+    void fillByteBuffer(ByteBuffer inputBuffer) {
+        int offset = 0, frmOffset = mNumBytesSubmitted;
+        for (int plane = 0; plane < 3; plane++) {
+            int width = mWidth;
+            int height = mHeight;
+            int tileWidth = INP_FRM_WIDTH;
+            int tileHeight = INP_FRM_HEIGHT;
+            if (plane != 0) {
+                width = mWidth / 2;
+                height = mHeight / 2;
+                tileWidth = INP_FRM_WIDTH / 2;
+                tileHeight = INP_FRM_HEIGHT / 2;
+            }
+            for (int k = 0; k < height; k += tileHeight) {
+                int rowsToCopy = Math.min(height - k, tileHeight);
+                for (int j = 0; j < rowsToCopy; j++) {
+                    for (int i = 0; i < width; i += tileWidth) {
+                        int colsToCopy = Math.min(width - i, tileWidth);
+                        inputBuffer.position(offset + (k + j) * width + i);
+                        inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
+                    }
+                }
+            }
+            offset += width * height;
+            frmOffset += tileWidth * tileHeight;
+        }
+    }
+
+    void enqueueInput(int bufferIndex) {
+        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+        if (mNumBytesSubmitted >= mInputData.length) {
+            enqueueEOS(bufferIndex);
+        } else {
+            int size;
+            int flags = 0;
+            long pts = mInputOffsetPts;
+            if (mIsAudio) {
+                pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
+                size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
+                inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+                if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
+                    flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                    mSawInputEOS = true;
+                }
+                mNumBytesSubmitted += size;
+            } else {
+                pts += mInputCount * 1000000L / mFrameRate;
+                size = mWidth * mHeight * 3 / 2;
+                int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
+                if (mNumBytesSubmitted + frmSize > mInputData.length) {
+                    fail("received partial frame to encode");
+                } else {
+                    Image img = mCodec.getInputImage(bufferIndex);
+                    if (img != null) {
+                        fillImage(img);
+                    } else {
+                        if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
+                            inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+                        } else {
+                            fillByteBuffer(inputBuffer);
+                        }
+                    }
+                }
+                if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
+                    flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                    mSawInputEOS = true;
+                }
+                mNumBytesSubmitted += frmSize;
+            }
+            if (ENABLE_LOGS) {
+                Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+                        " flags: " + flags);
+            }
+            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
+            mOutputBuff.saveInPTS(pts);
+            mInputCount++;
+        }
+    }
+
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+                    info.size + " timestamp: " + info.presentationTimeUs);
+        }
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (info.size > 0) {
+            if (mSaveToMem) {
+                ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+                mOutputBuff.saveToMemory(buf, info);
+            }
+            if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mOutputBuff.saveOutPTS(info.presentationTimeUs);
+                mOutputCount++;
+            }
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, false);
+    }
+
+    @Override
+    PersistableBundle validateMetrics(String codec, MediaFormat format) {
+        PersistableBundle metrics = super.validateMetrics(codec, format);
+        assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
+        assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1);
+        return metrics;
+    }
+}
+
+
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index 9438dcb..bbfa5aa 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -18,9 +18,7 @@
 
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
 import android.media.MediaFormat;
-import android.os.Build;
 import android.util.Log;
 import android.util.Pair;
 
@@ -39,46 +37,36 @@
 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 /**
  * Validate profile and level configuration for listed encoder components
  */
 @RunWith(Parameterized.class)
-public class EncoderProfileLevelTest extends CodecTestBase {
+public class EncoderProfileLevelTest extends CodecEncoderTestBase {
     private static final String LOG_TAG = EncoderProfileLevelTest.class.getSimpleName();
     private static final HashMap<String, int[]> mProfileMap = new HashMap<>();
     private static final HashMap<String, Pair<int[], Integer>> mProfileLevelCdd = new HashMap<>();
 
-    private final String mMime;
     private MediaFormat mConfigFormat;
-    private int mNumBytesSubmitted;
-
-    private int mHeight;
-    private int mWidth;
-    private int mChannels;
-    private int mRate;
 
     public EncoderProfileLevelTest(String mime, int bitrate, int encoderInfo1, int encoderInfo2,
             int frameRate) {
-        mMime = mime;
-        mAsyncHandle = new CodecAsyncHandler();
+        super(mime);
         mConfigFormat = new MediaFormat();
-        mIsAudio = mMime.startsWith("audio/");
         mConfigFormat.setString(MediaFormat.KEY_MIME, mMime);
         mConfigFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
         if (mIsAudio) {
-            mRate = encoderInfo1;
+            mSampleRate = encoderInfo1;
             mChannels = encoderInfo2;
-            mConfigFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mRate);
+            mConfigFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
             mConfigFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, mChannels);
         } else {
             mWidth = encoderInfo1;
             mHeight = encoderInfo2;
-            mRate = frameRate;
+            mFrameRate = frameRate;
             mConfigFormat.setInteger(MediaFormat.KEY_WIDTH, mWidth);
             mConfigFormat.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
-            mConfigFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mRate);
+            mConfigFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
             mConfigFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
             mConfigFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                     MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
@@ -204,41 +192,7 @@
                 {MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 176, 144, 20},
                 {MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 480, 360, 20},
         });
-
-        ArrayList<String> mimes = new ArrayList<>();
-        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
-        for (MediaCodecInfo codecInfo : codecInfos) {
-            if (!codecInfo.isEncoder()) continue;
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
-            String[] types = codecInfo.getSupportedTypes();
-            for (String type : types) {
-                if (!mimes.contains(type)) mimes.add(type);
-            }
-        }
-        for (String mime : cddRequiredMimeList) {
-            if (!mimes.contains(mime)) {
-                fail("media codec encoder list doesn't contain " + mime +
-                        " as required by cdd");
-            }
-        }
-        final List<Object[]> argsList = new ArrayList<>();
-        for (String mime : mimes) {
-            boolean miss = true;
-            for (int i = 0; i < exhaustiveArgsList.size(); i++) {
-                if (mime.equals(exhaustiveArgsList.get(i)[0])) {
-                    argsList.add(exhaustiveArgsList.get(i));
-                    miss = false;
-                }
-            }
-            if (miss) {
-                if (cddRequiredMimeList.contains(mime)) {
-                    fail("no test vectors available for " + mime);
-                }
-                Log.w(LOG_TAG, "no test vectors available for " + mime);
-            }
-        }
-        return argsList;
+        return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, true);
     }
 
     static {
@@ -653,45 +607,6 @@
     }
 
     @Override
-    void enqueueInput(int bufferIndex) {
-        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
-        int size;
-        int flags = 0;
-        long pts;
-        if (mIsAudio) {
-            pts = mNumBytesSubmitted * 1000000L / (2 * mChannels * mRate);
-            size = inputBuffer.capacity();
-            byte[] data = new byte[size];
-            inputBuffer.put(data);
-            mNumBytesSubmitted += size;
-        } else {
-            pts = mInputCount * 1000000L / mRate;
-            size = mWidth * mHeight * 3 / 2;
-            byte[] data = new byte[size];
-            inputBuffer.put(data);
-            mNumBytesSubmitted += size;
-        }
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
-                    " flags: " + flags);
-        }
-        mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
-        mInputCount++;
-    }
-
-    @Override
-    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
-        if (ENABLE_LOGS) {
-            Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
-                    info.size + " timestamp: " + info.presentationTimeUs);
-        }
-        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-            mSawOutputEOS = true;
-        }
-        mCodec.releaseOutputBuffer(bufferIndex, false);
-    }
-
-    @Override
     boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
         if (!super.isFormatSimilar(inpFormat, outFormat)) {
             Log.e(LOG_TAG, "Basic channel-rate/resolution comparisons failed");
@@ -778,6 +693,7 @@
         boolean[] boolStates = {true, false};
         MediaFormat format = mConfigFormat;
         mOutputBuff = new OutputManager();
+        setUpSource(mInputFile);
         int supportedCddCount = listOfEncoders.size() * (cddSupportedMime ? profileCdd.length : 1);
         for (String encoder : listOfEncoders) {
             mCodec = MediaCodec.createByCodecName(encoder);
diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java
index a5c0eb9..a0aaef1 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java
@@ -197,23 +197,19 @@
 
     static boolean isCSDIdentical(MediaFormat refFormat, MediaFormat testFormat) {
         String mime = refFormat.getString(MediaFormat.KEY_MIME);
-        /* TODO(b/154177490) */
-        if (mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9) ||
-                mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1)) {
-            return true;
-        }
         for (int i = 0; ; i++) {
             String csdKey = "csd-" + i;
             boolean refHasCSD = refFormat.containsKey(csdKey);
             boolean testHasCSD = testFormat.containsKey(csdKey);
             if (refHasCSD != testHasCSD) {
                 if (ENABLE_LOGS) {
-                    Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + "test fmt has CSD: " +
+                    Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + " test fmt has CSD: " +
                             testHasCSD);
                 }
                 return false;
             }
             if (refHasCSD) {
+                Log.v(LOG_TAG, mime + " has " + csdKey);
                 ByteBuffer r = refFormat.getByteBuffer(csdKey);
                 ByteBuffer t = testFormat.getByteBuffer(csdKey);
                 if (!r.equals(t)) {
@@ -327,7 +323,7 @@
                     }
                     if (!testBuffer.equals(refBuffer)) {
                         if (ENABLE_LOGS) {
-                            Log.d(LOG_TAG, "Mime: " + refMime + "sample data is not identical");
+                            Log.d(LOG_TAG, "Mime: " + refMime + " sample data is not identical");
                         }
                         areTracksIdentical = false;
                         break;
@@ -626,20 +622,16 @@
 
         @Parameterized.Parameters(name = "{index}({0})")
         public static Collection<Object[]> input() {
-            /* TODO: add ts files for MPEG2, AVC and AAC. These parameters are used by seek tests
-                 as well. Would it make sense to add ts files to this list */
-            /* TODO: add .flac, .midi, .wav, .aac-adts to the list */
+            /* TODO(b/157108639) - add missing test files */
             return Arrays.asList(new Object[][]{
                     {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
                             "bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4",
                             "bbb_cif_768kbps_30fps_mpeg2.mkv",}},
                     {MediaFormat.MIMETYPE_VIDEO_H263, new String[]{
                             "bbb_cif_768kbps_30fps_h263.mp4",
-                            "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",
-                            "bbb_cif_768kbps_30fps_h263_stereo_48kHz_192kbps_flac.mkv",}},
+                            "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",}},
                     {MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{
                             "bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4",
-                            "bbb_cif_768kbps_30fps_mpeg4.mkv",
                             "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp",}},
                     {MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{
                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4",
@@ -872,10 +864,6 @@
         public void testExtract() throws IOException {
             assumeTrue(shouldRunTest(mMime));
             assertTrue(mSrcFiles.length > 1);
-            assumeTrue("TODO(b/146925481)", mMime == MediaFormat.MIMETYPE_VIDEO_VP8 ||
-                    mMime == MediaFormat.MIMETYPE_VIDEO_VP9 ||
-                    mMime == MediaFormat.MIMETYPE_VIDEO_AV1 ||
-                    mMime == MediaFormat.MIMETYPE_AUDIO_FLAC);
             MediaExtractor refExtractor = new MediaExtractor();
             refExtractor.setDataSource(mInpPrefix + mSrcFiles[0]);
             boolean isOk = true;
@@ -964,8 +952,6 @@
         @Test
         public void testSeekToZero() throws IOException {
             assumeTrue(shouldRunTest(mMime));
-            assumeTrue("TODO(b/146925481)", mMime != MediaFormat.MIMETYPE_AUDIO_MPEG &&
-                    mMime != MediaFormat.MIMETYPE_AUDIO_AAC);
             boolean isOk = true;
             for (String srcFile : mSrcFiles) {
                 MediaExtractor extractor = new MediaExtractor();
@@ -1047,10 +1033,6 @@
         public void testExtractNative() {
             assumeTrue(shouldRunTest(mMime));
             assertTrue(mSrcFiles.length > 1);
-            assumeTrue("TODO(b/146925481)", mMime == MediaFormat.MIMETYPE_VIDEO_VP8 ||
-                    mMime == MediaFormat.MIMETYPE_VIDEO_VP9 ||
-                    mMime == MediaFormat.MIMETYPE_VIDEO_AV1 ||
-                    mMime == MediaFormat.MIMETYPE_AUDIO_FLAC);
             boolean isOk = true;
             for (int i = 1; i < mSrcFiles.length; i++) {
                 if (!nativeTestExtract(mInpPrefix + mSrcFiles[0], mInpPrefix + mSrcFiles[i],
@@ -1103,8 +1085,6 @@
         @Test
         public void testSeekToZeroNative() {
             assumeTrue(shouldRunTest(mMime));
-            assumeTrue("TODO(b/146925481)", mMime != MediaFormat.MIMETYPE_AUDIO_MPEG &&
-                    mMime != MediaFormat.MIMETYPE_AUDIO_AAC);
             boolean isOk = true;
             for (String srcFile : mSrcFiles) {
                 if (!nativeTestSeekToZero(mInpPrefix + srcFile, mMime)) {
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index d954f01..7311dc9 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -57,7 +57,9 @@
 class MuxerTestHelper {
     private static final String LOG_TAG = MuxerTestHelper.class.getSimpleName();
     private static final boolean ENABLE_LOGS = false;
-    static final int STTS_TOLERANCE = 100;
+    // Stts values within 0.1ms(100us) difference are fudged to save too
+    // many stts entries in MPEG4Writer.
+    static final int STTS_TOLERANCE_US = 100;
     private String mSrcPath;
     private String mMime;
     private int mTrackCount;
@@ -253,10 +255,9 @@
         }
     }
 
-    @Override
     // returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
     // stream are present in ref media stream
-    public boolean equals(Object o) {
+    boolean isSubsetOf(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         MuxerTestHelper that = (MuxerTestHelper) o;
@@ -273,43 +274,39 @@
                 if (thisMime != null && thisMime.equals(thatMime)) {
                     if (!ExtractorTest.isCSDIdentical(thisFormat, thatFormat)) continue;
                     if (mBufferInfo.get(i).size() == that.mBufferInfo.get(j).size()) {
-                        int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
-                        for (int k = 0; k < mBufferInfo.get(i).size(); k++) {
+                        long tolerance = thisMime.startsWith("video/") ? STTS_TOLERANCE_US : 0;
+                        // TODO(b/157008437) - muxed file pts is +1us of target pts
+                        tolerance += 1; // rounding error
+                        int k = 0;
+                        for (; k < mBufferInfo.get(i).size(); k++) {
                             MediaCodec.BufferInfo thisInfo = mBufferInfo.get(i).get(k);
                             MediaCodec.BufferInfo thatInfo = that.mBufferInfo.get(j).get(k);
                             if (thisInfo.flags != thatInfo.flags) {
-                                flagsDiff++;
+                                break;
                             }
                             if (thisInfo.size != thatInfo.size) {
-                                sizeDiff++;
+                                break;
                             } else {
                                 mBuff.position(thisInfo.offset);
                                 mBuff.get(refBuffer, 0, thisInfo.size);
                                 that.mBuff.position(thatInfo.offset);
                                 that.mBuff.get(testBuffer, 0, thatInfo.size);
-                                for (int count = 0; count < thisInfo.size; count++) {
+                                int count = 0;
+                                for (; count < thisInfo.size; count++) {
                                     if (refBuffer[count] != testBuffer[count]) {
-                                        buffDiff++;
                                         break;
                                     }
                                 }
+                                if (count != thisInfo.size) break;
                             }
                             if (Math.abs(
                                     thisInfo.presentationTimeUs - thatInfo.presentationTimeUs) >
-                                    STTS_TOLERANCE) {
-                                tsDiff++;
+                                    tolerance) {
+                                break;
                             }
                         }
-                        if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0 || buffDiff != 0) {
-                            if (ENABLE_LOGS) {
-                                Log.d(LOG_TAG, "For track: " + thisMime +
-                                        " Total Samples: " + mBufferInfo.get(i).size() +
-                                        " flagsDiff: " + flagsDiff +
-                                        " sizeDiff: " + sizeDiff +
-                                        " tsDiff: " + tsDiff +
-                                        " buffDiff: " + buffDiff);
-                            }
-                        } else break;
+                        // all samples are identical. successful match found. move to next track
+                        if (k == mBufferInfo.get(i).size()) break;
                     } else {
                         if (ENABLE_LOGS) {
                             Log.d(LOG_TAG, "Mime matched but sample count different." +
@@ -742,7 +739,7 @@
             try {
                 mediaInfoA.combineMedias(muxer, mediaInfoB, new int[]{1, 1});
                 refInfo = new MuxerTestHelper(mRefPath);
-                if (!mediaInfoA.equals(refInfo) || !mediaInfoB.equals(refInfo)) {
+                if (!mediaInfoA.isSubsetOf(refInfo) || !mediaInfoB.isSubsetOf(refInfo)) {
                     fail(msg + "error ! muxing src A and src B failed");
                 }
             } catch (Exception e) {
@@ -761,7 +758,7 @@
                 try {
                     mediaInfoA.combineMedias(muxer, mediaInfoB, numTrack);
                     MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
-                    if (!outInfo.equals(refInfo)) {
+                    if (!outInfo.isSubsetOf(refInfo)) {
                         fail(msg + " error ! muxing src A: " + numTrack[0] + " src B: " +
                                 numTrack[1] + "failed");
                     }
@@ -848,15 +845,11 @@
         public void testOffsetPresentationTime() throws IOException {
             final int OFFSET_TS = 111000;
             Assume.assumeTrue(shouldRunTest(mOutFormat));
-            Assume.assumeTrue("TODO(b/148978457)",
-                    mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-            Assume.assumeTrue("TODO(b/148978457)",
-                    mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
             Assume.assumeTrue("TODO(b/146423022)",
                     mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
             Assume.assumeTrue("TODO(b/146421018)",
                     mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG);
-            assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE);
+            assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE_US);
             MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath);
             for (int trackID = 0; trackID < mediaInfo.getTrackCount(); trackID++) {
                 for (int i = 0; i < mOffsetIndices.length; i++) {
@@ -866,7 +859,7 @@
                 mediaInfo.muxMedia(muxer);
                 muxer.release();
                 MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
-                if (!outInfo.equals(mediaInfo)) {
+                if (!outInfo.isSubsetOf(mediaInfo)) {
                     String msg = String.format(
                             "testOffsetPresentationTime: inp: %s, fmt: %d, trackID %d", mSrcFile,
                             mOutFormat, trackID);
@@ -881,10 +874,6 @@
         @Test
         public void testOffsetPresentationTimeNative() {
             Assume.assumeTrue(shouldRunTest(mOutFormat));
-            Assume.assumeTrue("TODO(b/148978457)",
-                    mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-            Assume.assumeTrue("TODO(b/148978457)",
-                    mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
             Assume.assumeTrue("TODO(b/146423022)",
                     mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
             Assume.assumeTrue("TODO(b/146421018)",
@@ -996,8 +985,6 @@
         public void testSimpleMux() throws IOException {
             Assume.assumeTrue("TODO(b/146421018)",
                     !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
-            Assume.assumeTrue("TODO(b/146923287)",
-                    !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
             MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, mMime);
             assertEquals("error! unexpected track count", 1, mediaInfo.getTrackCount());
             for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; format++) {
@@ -1010,7 +997,7 @@
                 try {
                     mediaInfo.muxMedia(muxer);
                     MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
-                    if (!mediaInfo.equals(outInfo)) {
+                    if (!mediaInfo.isSubsetOf(outInfo)) {
                         fail(msg + "error! output != clone(input)");
                     }
                 } catch (Exception e) {
@@ -1027,8 +1014,6 @@
         public void testSimpleMuxNative() {
             Assume.assumeTrue("TODO(b/146421018)",
                     !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
-            Assume.assumeTrue("TODO(b/146923287)",
-                    !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
             assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
         }
     }
diff --git a/tests/signature/api-check/OWNERS b/tests/signature/api-check/OWNERS
index 9cedf986..c8305d1 100644
--- a/tests/signature/api-check/OWNERS
+++ b/tests/signature/api-check/OWNERS
@@ -1,8 +1,7 @@
 # Bug component: 24949
 platform-compat-eng+reviews@google.com
 andreionea@google.com
-atrost@google.com
 mathewi@google.com
 ngeoffray@google.com
 paulduffin@google.com
-satayev@google.com
\ No newline at end of file
+satayev@google.com
diff --git a/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS
index f9ba708..66ea541 100644
--- a/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS
@@ -4,7 +4,6 @@
 platform-compat-eng+reviews@google.com
 
 andreionea@google.com
-atrost@google.com
 mathewi@google.com
 ngeoffray@google.com
 satayev@google.com
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index 6044820..9a08bcf 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -437,7 +437,12 @@
     @AppModeFull
     public void testAppSummary() throws Exception {
         for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
-            if (!shouldTestThisNetworkType(i, MINUTE/2)) {
+            // Use tolerance value that large enough to make sure stats of at
+            // least one bucket is included. However, this is possible that
+            // the test will see data of different app but with the same UID
+            // that created before testing.
+            // TODO: Consider query stats before testing and use the difference to verify.
+            if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
                 continue;
             }
             setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash
index 425104b..44d6213 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash
@@ -1 +1 @@
-d755ae773aaabd1c48d22b823e29501ee387aff1
+8e163a1b4a6f366aa0c00b6da7fc13a970ee55d8
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl
index 887cbe0..75e7a41 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl
@@ -60,6 +60,7 @@
   void renameFoo(inout test_package.Foo foo, String name);
   void renameBar(inout test_package.Foo foo, String name);
   int getF(in test_package.Foo foo);
+  String RepeatStringNullableLater(String repeated);
   const int kZero = 0;
   const int kOne = 1;
   const int kOnes = -1;
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash
index fce7d03..42915a7 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash
@@ -1 +1 @@
-3589e207cd708912b7551fb40b7fb263ef3e81b4
+dc04ee811daf7720da5a3bcf35847f7e0cea4521
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl
index c489e56..bcda3c7 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl
@@ -60,6 +60,7 @@
   void renameFoo(inout test_package.Foo foo, String name);
   void renameBar(inout test_package.Foo foo, String name);
   int getF(in test_package.Foo foo);
+  @nullable String RepeatStringNullableLater(@nullable String repeated);
   int NewMethodThatReturns10();
   const int kZero = 0;
   const int kOne = 1;
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
index f890351..effb098 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
@@ -77,6 +77,7 @@
   void renameFoo(inout test_package.Foo foo, String name);
   void renameBar(inout test_package.Foo foo, String name);
   int getF(in test_package.Foo foo);
+  @nullable String RepeatStringNullableLater(@nullable String repeated);
   int NewMethodThatReturns10();
   const int kZero = 0;
   const int kOne = 1;
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
index 0e332cd..89ea368 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
@@ -409,6 +409,22 @@
     *_aidl_return = in_value;
     return ::ndk::ScopedAStatus(AStatus_newOk());
   }
+
+#ifdef USING_VERSION_1
+  ::ndk::ScopedAStatus RepeatStringNullableLater(const std::string& in_value,
+                                                 std::string* _aidl_return) override {
+    *_aidl_return = in_value;
+    return ::ndk::ScopedAStatus(AStatus_newOk());
+  }
+#else
+  ::ndk::ScopedAStatus RepeatStringNullableLater(
+      const std::optional<std::string>& in_value,
+      std::optional<std::string>* _aidl_return) override {
+    *_aidl_return = in_value;
+    return ::ndk::ScopedAStatus(AStatus_newOk());
+  }
+#endif
+
 #ifndef USING_VERSION_1
   // All methods added from now on should be within this macro
   ::ndk::ScopedAStatus NewMethodThatReturns10(int32_t* _aidl_return) override {
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 2df3c72..158f1de 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
@@ -792,6 +792,36 @@
   }
 }
 
+TEST_P(NdkBinderTest_Aidl, RepeatStringNullableLater) {
+  std::optional<std::string> res;
+
+  std::string name;
+  EXPECT_OK(iface->GetName(&name));
+
+  // Java considers every type to be nullable, but this is okay, since it will
+  // pass back NullPointerException to the client if it does not handle a null
+  // type, similar to how a C++ server would refuse to unparcel a null
+  // non-nullable type. Of course, this is not ideal, but the problem runs very
+  // deep.
+  const bool supports_nullable = !GetParam().shouldBeOld || name == "Java";
+  if (supports_nullable) {
+    EXPECT_OK(iface->RepeatStringNullableLater(std::nullopt, &res));
+    EXPECT_EQ(std::nullopt, res);
+  } else {
+    ndk::ScopedAStatus status = iface->RepeatStringNullableLater(std::nullopt, &res);
+    ASSERT_EQ(STATUS_UNEXPECTED_NULL, AStatus_getStatus(status.get()));
+  }
+
+  EXPECT_OK(iface->RepeatStringNullableLater("", &res));
+  EXPECT_EQ("", res);
+
+  EXPECT_OK(iface->RepeatStringNullableLater("a", &res));
+  EXPECT_EQ("a", res);
+
+  EXPECT_OK(iface->RepeatStringNullableLater("say what?", &res));
+  EXPECT_EQ("say what?", res);
+}
+
 TEST_P(NdkBinderTest_Aidl, GetInterfaceVersion) {
   int32_t res;
   EXPECT_OK(iface->getInterfaceVersion(&res));
@@ -808,7 +838,7 @@
   EXPECT_OK(iface->getInterfaceHash(&res));
   if (GetParam().shouldBeOld) {
     // aidl_api/libbinder_ndk_test_interface/1/.hash
-    EXPECT_EQ("d755ae773aaabd1c48d22b823e29501ee387aff1", res);
+    EXPECT_EQ("8e163a1b4a6f366aa0c00b6da7fc13a970ee55d8", res);
   } else {
     EXPECT_EQ("notfrozen", res);
   }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
index 8166e87..c8908ab 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
@@ -118,6 +118,9 @@
     void renameBar(inout Foo foo, String name);
     int getF(in Foo foo);
 
+    // Method which is not nullable in version 1, but is nullable in version 2
+    @nullable String RepeatStringNullableLater(@nullable String repeated);
+
     // Methods that do not exist in version 1
     int NewMethodThatReturns10();
 }
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 814e1e4..b8b9364 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
@@ -658,4 +658,19 @@
         mInterface.renameBar(foo, "MYBAR");
         assertEquals("MYBAR", foo.d.a);
     }
+
+    @Test
+    public void testRepeatStringNullableLater() throws RemoteException {
+        // see notes in native NdkBinderTest_Aidl RepeatStringNullableLater
+        boolean handlesNull = !mShouldBeOld || mExpectedName == "JAVA";
+        try {
+            assertEquals(null, mInterface.RepeatStringNullableLater(null));
+            assertTrue("should reach here if null is handled", handlesNull);
+        } catch (NullPointerException e) {
+            assertFalse("should reach here if null isn't handled", handlesNull);
+        }
+        assertEquals("", mInterface.RepeatStringNullableLater(""));
+        assertEquals("a", mInterface.RepeatStringNullableLater("a"));
+        assertEquals("foo", mInterface.RepeatStringNullableLater("foo"));
+    }
 }
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java b/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
index 7b60483..b0ab502 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
@@ -371,11 +371,6 @@
   }
 
   @Override
-  public int NewMethodThatReturns10() {
-    return 10;
-  }
-
-  @Override
   public Foo repeatFoo(Foo inFoo) {
     return inFoo;
   }
@@ -398,4 +393,13 @@
     return foo.f;
   }
 
+  @Override
+  public String RepeatStringNullableLater(String in_value) {
+    return in_value;
+  }
+
+  @Override
+  public int NewMethodThatReturns10() {
+    return 10;
+  }
 }
diff --git a/tests/tests/bluetooth/Android.bp b/tests/tests/bluetooth/Android.bp
index 13e9742..13ee58e 100644
--- a/tests/tests/bluetooth/Android.bp
+++ b/tests/tests/bluetooth/Android.bp
@@ -15,7 +15,10 @@
 android_test {
     name: "CtsBluetoothTestCases",
     defaults: ["cts_defaults"],
-    static_libs: ["ctstestrunner-axt"],
+    static_libs: [
+        "ctstestrunner-axt",
+        "bluetooth-test-util-lib",
+    ],
     libs: [
         "android.test.runner.stubs",
         "android.test.base.stubs",
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp b/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp
new file mode 100644
index 0000000..0257a41
--- /dev/null
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+java_library {
+    name: "bluetooth-test-util-lib",
+
+    static_libs: [
+        "junit",
+    ],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "current",
+}
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
new file mode 100644
index 0000000..9954f054
--- /dev/null
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
@@ -0,0 +1,161 @@
+/*
+ * 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.bluetooth.cts;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.ScanRecord;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility for controlling the Bluetooth adapter from CTS test.
+ */
+public class BTAdapterUtils {
+    private static final String TAG = "BTAdapterUtils";
+
+    // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY +
+    //                              AdapterState.BREDR_START_TIMEOUT_DELAY
+    private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000;
+    // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY +
+    //                                  AdapterState.BREDR_STOP_TIMEOUT_DELAY
+    private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000;
+
+    private static BroadcastReceiver mAdapterIntentReceiver;
+
+    private static Condition mConditionAdapterIsEnabled;
+    private static ReentrantLock mAdapterStateEnablinglock;
+
+    private static Condition mConditionAdapterIsDisabled;
+    private static ReentrantLock mAdapterStateDisablinglock;
+    private static boolean mAdapterVarsInitialized;
+
+    private static class AdapterIntentReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+                int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+                int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+                Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
+
+                if (newState == BluetoothAdapter.STATE_ON) {
+                    mAdapterStateEnablinglock.lock();
+                    try {
+                        Log.d(TAG, "Signaling to mConditionAdapterIsEnabled");
+                        mConditionAdapterIsEnabled.signal();
+                    } finally {
+                        mAdapterStateEnablinglock.unlock();
+                    }
+                } else if (newState == BluetoothAdapter.STATE_OFF) {
+                    mAdapterStateDisablinglock.lock();
+                    try {
+                        Log.d(TAG, "Signaling to mConditionAdapterIsDisabled");
+                        mConditionAdapterIsDisabled.signal();
+                    } finally {
+                        mAdapterStateDisablinglock.unlock();
+                    }
+                }
+            }
+        }
+    }
+
+    /** Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled. */
+    public static boolean enableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+        if (!mAdapterVarsInitialized) {
+            initAdapterStateVariables(context);
+        }
+
+        if (bluetoothAdapter.isEnabled()) return true;
+
+        Log.d(TAG, "Enabling bluetooth adapter");
+        bluetoothAdapter.enable();
+        mAdapterStateEnablinglock.lock();
+        try {
+            // Wait for the Adapter to be enabled
+            while (!bluetoothAdapter.isEnabled()) {
+                if (!mConditionAdapterIsEnabled.await(
+                        ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    // Timeout
+                    Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
+                    break;
+                } // else spurious wakeups
+            }
+        } catch(InterruptedException e) {
+            Log.e(TAG, "enableAdapter: interrrupted");
+        } finally {
+            mAdapterStateEnablinglock.unlock();
+        }
+        return bluetoothAdapter.isEnabled();
+    }
+
+    /** Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled. */
+    public static boolean disableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+        if (!mAdapterVarsInitialized) {
+            initAdapterStateVariables(context);
+        }
+
+        if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
+
+        Log.d(TAG, "Disabling bluetooth adapter");
+        bluetoothAdapter.disable();
+        mAdapterStateDisablinglock.lock();
+        try {
+            // Wait for the Adapter to be disabled
+            while (bluetoothAdapter.getState() != BluetoothAdapter.STATE_OFF) {
+                if (!mConditionAdapterIsDisabled.await(
+                        ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    // Timeout
+                    Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
+                    break;
+                } // else spurious wakeups
+            }
+        } catch(InterruptedException e) {
+            Log.e(TAG, "enableAdapter: interrrupted");
+        } finally {
+            mAdapterStateDisablinglock.unlock();
+        }
+        return bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF;
+    }
+
+    // Initialize variables required for TestUtils#enableAdapter and TestUtils#disableAdapter
+    private static void initAdapterStateVariables(Context context) {
+        Log.d(TAG, "Initializing adapter state variables");
+        mAdapterIntentReceiver = new AdapterIntentReceiver();
+        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        context.registerReceiver(mAdapterIntentReceiver, filter);
+
+        mAdapterStateEnablinglock = new ReentrantLock();
+        mConditionAdapterIsEnabled = mAdapterStateEnablinglock.newCondition();
+        mAdapterStateDisablinglock = new ReentrantLock();
+        mConditionAdapterIsDisabled = mAdapterStateDisablinglock.newCondition();
+
+        mAdapterVarsInitialized = true;
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
index 8616a6d..2eab364 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
@@ -19,30 +19,47 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothServerSocket;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 import java.io.IOException;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * Very basic test, just of the static methods of {@link
  * BluetoothAdapter}.
  */
 public class BasicAdapterTest extends AndroidTestCase {
-    private static final int DISABLE_TIMEOUT = 8000;  // ms timeout for BT disable
-    private static final int ENABLE_TIMEOUT = 10000;  // ms timeout for BT enable
-    private static final int POLL_TIME = 400;         // ms to poll BT state
-    private static final int CHECK_WAIT_TIME = 1000;  // ms to wait before enable/disable
+    private static final String TAG = "BasicAdapterTest";
+    private static final int SET_NAME_TIMEOUT = 5000; // ms timeout for setting adapter name
 
     private boolean mHasBluetooth;
+    private ReentrantLock mAdapterNameChangedlock;
+    private Condition mConditionAdapterNameChanged;
+    private boolean mIsAdapterNameChanged;
 
     public void setUp() throws Exception {
         super.setUp();
 
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mContext.registerReceiver(mAdapterNameChangeReceiver, filter);
+
         mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_BLUETOOTH);
+        mAdapterNameChangedlock = new ReentrantLock();
+        mConditionAdapterNameChanged = mAdapterNameChangedlock.newCondition();
+        mIsAdapterNameChanged = false;
     }
 
     public void test_getDefaultAdapter() {
@@ -124,8 +141,8 @@
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 
         for (int i=0; i<5; i++) {
-            disable(adapter);
-            enable(adapter);
+            assertTrue(BTAdapterUtils.disableAdapter(adapter, mContext));
+            assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
         }
     }
 
@@ -135,21 +152,34 @@
             return;
         }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        enable(adapter);
+        assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
 
         assertTrue(BluetoothAdapter.checkBluetoothAddress(adapter.getAddress()));
     }
 
-    public void test_getName() {
+    public void test_setName_getName() {
         if (!mHasBluetooth) {
             // Skip the test if bluetooth is not present.
             return;
         }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        enable(adapter);
+        assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
 
         String name = adapter.getName();
         assertNotNull(name);
+
+        // Check renaming the adapter
+        String genericName = "Generic Device 1";
+        assertTrue(adapter.setName(genericName));
+        assertTrue(waitForAdapterNameChange());
+        mIsAdapterNameChanged = false;
+        assertEquals(genericName, adapter.getName());
+
+        // Check setting adapter back to original name
+        assertTrue(adapter.setName(name));
+        assertTrue(waitForAdapterNameChange());
+        mIsAdapterNameChanged = false;
+        assertEquals(name, adapter.getName());
     }
 
     public void test_getBondedDevices() {
@@ -158,7 +188,7 @@
             return;
         }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        enable(adapter);
+        assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
 
         Set<BluetoothDevice> devices = adapter.getBondedDevices();
         assertNotNull(devices);
@@ -174,7 +204,7 @@
         }
         // getRemoteDevice() should work even with Bluetooth disabled
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        disable(adapter);
+        assertTrue(BTAdapterUtils.disableAdapter(adapter, mContext));
 
         // test bad addresses
         try {
@@ -210,7 +240,7 @@
             return;
         }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        enable(adapter);
+        assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
 
         BluetoothServerSocket socket = adapter.listenUsingRfcommWithServiceRecord(
                 "test", UUID.randomUUID());
@@ -218,75 +248,45 @@
         socket.close();
     }
 
-    /** Helper to turn BT off.
-     * This method will either fail on an assert, or return with BT turned off.
-     * Behavior of getState() and isEnabled() are validated along the way.
-     */
-    private void disable(BluetoothAdapter adapter) {
-        sleep(CHECK_WAIT_TIME);
-        if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
-            assertFalse(adapter.isEnabled());
-            return;
-        }
-
-        assertEquals(BluetoothAdapter.STATE_ON, adapter.getState());
-        assertTrue(adapter.isEnabled());
-        adapter.disable();
-        boolean turnOff = false;
-        for (int i=0; i<DISABLE_TIMEOUT/POLL_TIME; i++) {
-            sleep(POLL_TIME);
-            int state = adapter.getState();
-            switch (state) {
-            case BluetoothAdapter.STATE_OFF:
-                assertFalse(adapter.isEnabled());
-                return;
-            default:
-                if (state != BluetoothAdapter.STATE_ON || turnOff) {
-                    assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
-                    turnOff = true;
-                }
-                break;
-            }
-        }
-        fail("disable() timeout");
-    }
-
-    /** Helper to turn BT on.
-     * This method will either fail on an assert, or return with BT turned on.
-     * Behavior of getState() and isEnabled() are validated along the way.
-     */
-    private void enable(BluetoothAdapter adapter) {
-        sleep(CHECK_WAIT_TIME);
-        if (adapter.getState() == BluetoothAdapter.STATE_ON) {
-            assertTrue(adapter.isEnabled());
-            return;
-        }
-
-        assertEquals(BluetoothAdapter.STATE_OFF, adapter.getState());
-        assertFalse(adapter.isEnabled());
-        adapter.enable();
-        boolean turnOn = false;
-        for (int i=0; i<ENABLE_TIMEOUT/POLL_TIME; i++) {
-            sleep(POLL_TIME);
-            int state = adapter.getState();
-            switch (state) {
-            case BluetoothAdapter.STATE_ON:
-                assertTrue(adapter.isEnabled());
-                return;
-            default:
-                if (state != BluetoothAdapter.STATE_OFF || turnOn) {
-                    assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
-                    turnOn = true;
-                }
-                break;
-            }
-        }
-        fail("enable() timeout");
-    }
-
     private static void sleep(long t) {
         try {
             Thread.sleep(t);
         } catch (InterruptedException e) {}
     }
+
+    private boolean waitForAdapterNameChange() {
+        mAdapterNameChangedlock.lock();
+        try {
+            // Wait for the Adapter name to be changed
+            while (!mIsAdapterNameChanged) {
+                if (!mConditionAdapterNameChanged.await(
+                        SET_NAME_TIMEOUT, TimeUnit.MILLISECONDS)) {
+                    Log.e(TAG, "Timeout while waiting for adapter name change");
+                    break;
+                }
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "waitForAdapterNameChange: interrrupted");
+        } finally {
+            mAdapterNameChangedlock.unlock();
+        }
+        return mIsAdapterNameChanged;
+    }
+
+    private final BroadcastReceiver mAdapterNameChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
+                mAdapterNameChangedlock.lock();
+                mIsAdapterNameChanged = true;
+                try {
+                    mConditionAdapterNameChanged.signal();
+                } catch (IllegalMonitorStateException ex) {
+                } finally {
+                    mAdapterNameChangedlock.unlock();
+                }
+            }
+        }
+    };
 }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index b3bc692..103816e 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -64,7 +64,6 @@
     private static final int SCAN_DURATION_MILLIS = 10000;
     private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
     private static final int SCAN_STOP_TIMEOUT = 2000;
-    private static final int ADAPTER_ENABLE_TIMEOUT = 3000;
     private CountDownLatch mFlushBatchScanLatch;
 
     private BluetoothAdapter mBluetoothAdapter;
@@ -81,10 +80,7 @@
                 Context.BLUETOOTH_SERVICE);
         mBluetoothAdapter = manager.getAdapter();
         if (!mBluetoothAdapter.isEnabled()) {
-            // Note it's not reliable to listen for Adapter.ACTION_STATE_CHANGED broadcast and check
-            // bluetooth state.
-            mBluetoothAdapter.enable();
-            TestUtils.sleep(ADAPTER_ENABLE_TIMEOUT);
+            assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
         }
         mScanner = mBluetoothAdapter.getBluetoothLeScanner();
         mLocationOn = TestUtils.isLocationOn(getContext());
@@ -105,8 +101,7 @@
         if (!mLocationOn) {
             TestUtils.disableLocation(getContext());
         }
-        mBluetoothAdapter.disable();
-        TestUtils.sleep(ADAPTER_ENABLE_TIMEOUT);
+        assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
     }
 
     /**
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
index e94c3e7..4e1419a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
@@ -62,15 +62,6 @@
     private BluetoothAdapter mBluetoothAdapter;
     private BroadcastReceiver mIntentReceiver;
 
-    private BroadcastReceiver mAdapterIntentReceiver;
-
-    private Condition mConditionAdapterIsEnabled;
-    private ReentrantLock mAdapterStateEnablinglock;
-
-    private Condition mConditionAdapterIsDisabled;
-    private ReentrantLock mAdapterStateDisablinglock;
-    private boolean mAdapterOffSignalReceived;
-
     private Condition mConditionProfileIsConnected;
     private ReentrantLock mProfileConnectedlock;
     private boolean mIsProfileReady;
@@ -89,22 +80,13 @@
                 Context.BLUETOOTH_SERVICE);
         mBluetoothAdapter = manager.getAdapter();
 
-        mAdapterStateEnablinglock = new ReentrantLock();
-        mConditionAdapterIsEnabled  = mAdapterStateEnablinglock.newCondition();
-        mAdapterStateDisablinglock = new ReentrantLock();
-        mConditionAdapterIsDisabled  = mAdapterStateDisablinglock.newCondition();
-        mProfileConnectedlock = new ReentrantLock();
-        mConditionProfileIsConnected  = mProfileConnectedlock.newCondition();
-
-        mAdapterIntentReceiver = new AdapterIntentReceiver();
-        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(mAdapterIntentReceiver, filter);
-
-        if (!enableAdapter()) {
+        if (!BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext)) {
             Log.e(TAG, "Unable to enable Bluetooth Adapter!");
             assertTrue(mBluetoothAdapter.isEnabled());
         }
 
+        mProfileConnectedlock = new ReentrantLock();
+        mConditionProfileIsConnected  = mProfileConnectedlock.newCondition();
         mIsProfileReady = false;
         mService = null;
         mIsHearingAidSupported = mBluetoothAdapter.getProfileProxy(getContext(),
@@ -117,12 +99,10 @@
     public void tearDown() {
         if (!mIsBleSupported) return;
 
-        if (!disableAdapter()) {
+        if (!BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext)) {
             Log.e(TAG, "Unable to disable Bluetooth Adapter!");
             assertTrue(mBluetoothAdapter.isEnabled());
         }
-
-        mContext.unregisterReceiver(mAdapterIntentReceiver);
     }
 
     /**
@@ -244,10 +224,10 @@
         mContext.registerReceiver(mIntentReceiver, filter);
 
         Log.d(TAG, "test_getConnectionStateChangedIntent: disable adapter and wait");
-        assertTrue(disableAdapter());
+        assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
 
         Log.d(TAG, "test_getConnectionStateChangedIntent: enable adapter and wait");
-        assertTrue(enableAdapter());
+        assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
 
         int sanityCount = WAIT_FOR_INTENT_TIMEOUT_MS;
         while ((numDevices != mIntentCallbackDeviceList.size()) && (sanityCount > 0)) {
@@ -267,86 +247,6 @@
         }
     }
 
-    private class AdapterIntentReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
-                int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
-                Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
-
-                if (newState == BluetoothAdapter.STATE_ON) {
-                    mAdapterStateEnablinglock.lock();
-                    try {
-                        mConditionAdapterIsEnabled.signal();
-                    } finally {
-                        mAdapterStateEnablinglock.unlock();
-                    }
-                } else if (newState == BluetoothAdapter.STATE_OFF) {
-                    mAdapterStateDisablinglock.lock();
-                    mAdapterOffSignalReceived = true;
-                    try {
-                        mConditionAdapterIsDisabled.signal();
-                    } finally {
-                        mAdapterStateDisablinglock.unlock();
-                    }
-                }
-            }
-        }
-    }
-
-    // Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled.
-    private boolean enableAdapter() {
-        if (mBluetoothAdapter.isEnabled()) return true;
-
-        mBluetoothAdapter.enable();
-        mAdapterStateEnablinglock.lock();
-        try {
-            // Wait for the Adapter to be enabled
-            while (!mBluetoothAdapter.isEnabled()) {
-                if (!mConditionAdapterIsEnabled.await(
-                    ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    // Timeout
-                    Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
-                    break;
-                } // else spurious wakeups
-            }
-        } catch(InterruptedException e) {
-            Log.e(TAG, "enableAdapter: interrrupted");
-        } finally {
-            mAdapterStateEnablinglock.unlock();
-        }
-        if (!mBluetoothAdapter.isEnabled()) {
-            return false;
-        }
-        return true;
-    }
-
-    // Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled.
-    private boolean disableAdapter() {
-        // Note: !mBluetoothAdapter.isEnabled() is not an accurate indication that the
-        // BluetoothAdapter is OFF.
-        mBluetoothAdapter.disable();
-        mAdapterOffSignalReceived = false;
-        mAdapterStateDisablinglock.lock();
-        try {
-            // Wait for the Adapter to be disabled
-            while (!mAdapterOffSignalReceived) {
-                if (!mConditionAdapterIsDisabled.await(
-                    ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    // Timeout
-                    Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
-                    break;
-                } // else spurious wakeups
-            }
-        } catch(InterruptedException e) {
-            Log.e(TAG, "enableAdapter: interrrupted");
-        } finally {
-            mAdapterStateDisablinglock.unlock();
-        }
-        return mAdapterOffSignalReceived;
-    }
-
     private boolean waitForProfileConnect() {
         mProfileConnectedlock.lock();
         try {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
index 433a39f..b9a93a1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
@@ -26,7 +26,6 @@
 public class LeL2capSocketTest extends AndroidTestCase {
 
     private static final int NUM_ITERATIONS_FOR_REPEATED_TEST = 100;
-    private static final int ADAPTER_TOGGLE_TIMEOUT_MS = 4000;
 
     private BluetoothAdapter mAdapter = null;
 
@@ -40,12 +39,8 @@
         assertNotNull("BluetoothAdapter.getDefaultAdapter() returned null. "
                 + "Does this device have a Bluetooth adapter?", mAdapter);
         if (!mAdapter.isEnabled()) {
-            // Note: It's not reliable to listen for Adapter.ACTION_STATE_CHANGED broadcast and
-            // check bluetooth state per comments from BluetoothLeScanTest.java
-            mAdapter.enable();
-            TestUtils.sleep(ADAPTER_TOGGLE_TIMEOUT_MS);
+            assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
         }
-        assertTrue("Bluetooth failed to be enabled", mAdapter.isEnabled());
     }
 
     @Override
@@ -53,9 +48,7 @@
         if (!TestUtils.isBleSupported(getContext())) {
             return;
         }
-        mAdapter.disable();
-        TestUtils.sleep(ADAPTER_TOGGLE_TIMEOUT_MS);
-        assertFalse("Bluetooth failed to be disabled", mAdapter.isEnabled());
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
         mAdapter = null;
         super.tearDown();
     }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
index 342266f..7c4c454 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -16,8 +16,12 @@
 
 package android.bluetooth.cts;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.le.ScanRecord;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.provider.Settings;
 import android.util.Log;
@@ -27,12 +31,14 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * Utility class for Bluetooth CTS test.
  */
 class TestUtils {
-
     /**
      * Utility method to call hidden ScanRecord.parseFromBytes method.
      */
diff --git a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
index e5ff289..0085a7c 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
@@ -875,5 +875,41 @@
         store.deleteCredentialByName("test");
     }
 
+    @Test
+    public void testProvisionAcpIdNotInValidRange() throws IdentityCredentialException {
+        assumeTrue("IC HAL is not implemented", Util.isHalImplemented());
+
+        Context appContext = InstrumentationRegistry.getTargetContext();
+        IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+        WritableIdentityCredential wc =
+                store.createCredential("testAcpNotInValidRange", "org.iso.18013-5.2019.mdl");
+
+        Collection<X509Certificate> certificateChain =
+                wc.getCredentialKeyCertificateChain("SomeChallenge".getBytes());
+
+        // Profile 32 (no authentication) - invalid profile id
+        AccessControlProfile noAuthProfile =
+                new AccessControlProfile.Builder(new AccessControlProfileId(32))
+                        .setUserAuthenticationRequired(false)
+                        .build();
+
+        Collection<AccessControlProfileId> idsNoAuth = new ArrayList<AccessControlProfileId>();
+        idsNoAuth.add(new AccessControlProfileId(32));
+        String mdlNs = "org.iso.18013-5.2019";
+        PersonalizationData personalizationData =
+                new PersonalizationData.Builder()
+                        .addAccessControlProfile(noAuthProfile)
+                        .putEntry("com.example.ns", "Name", idsNoAuth, Util.cborEncodeString("Alan"))
+                        .build();
+
+        // personalize() should fail because of the invalid profile id
+        try {
+            byte[] proofOfProvisioningSignature = wc.personalize(personalizationData);
+            assertTrue(false);
+        } catch (Exception e) {
+            // This is the expected path...
+        }
+    }
 
 }
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index c04a8b4..6cd2441 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -82,6 +82,7 @@
     private final static String[] PUBLIC_ART_LIBRARIES = {
         "libicui18n.so",
         "libicuuc.so",
+        "libnativehelper.so"
     };
 
     // The grey-list.
@@ -93,7 +94,6 @@
         "libexpat.so",
         "libgui.so",
         "libmedia.so",
-        "libnativehelper.so",
         "libskia.so",
         "libssl.so",
         "libstagefright.so",
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 46592ca..4373fdc 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -68,6 +68,8 @@
 
 import com.google.common.collect.ImmutableSet;
 
+import androidx.test.filters.RequiresDevice;
+
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 
@@ -163,21 +165,26 @@
         int[] purposes = {
                 KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY, KM_PURPOSE_SIGN | KM_PURPOSE_VERIFY
         };
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        boolean[] includeValidityDatesValues = {true, false};
 
         for (int curveIndex = 0; curveIndex < curves.length; ++curveIndex) {
             for (int challengeIndex = 0; challengeIndex < challenges.length; ++challengeIndex) {
                 for (int purposeIndex = 0; purposeIndex < purposes.length; ++purposeIndex) {
-                    try {
-                        testEcAttestation(challenges[challengeIndex],
-                                true /* includeValidityDates */,
-                                curves[curveIndex], keySizes[curveIndex], purposes[purposeIndex]);
-                        testEcAttestation(challenges[challengeIndex],
-                                false /* includeValidityDates */,
-                                curves[curveIndex], keySizes[curveIndex], purposes[purposeIndex]);
-                    } catch (Throwable e) {
-                        throw new Exception(
-                                "Failed on curve " + curveIndex + " and challege " + challengeIndex,
-                                e);
+                    for (boolean includeValidityDates : includeValidityDatesValues) {
+                        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+                            try {
+                                testEcAttestation(challenges[challengeIndex], includeValidityDates,
+                                        curves[curveIndex], keySizes[curveIndex],
+                                        purposes[purposeIndex], devicePropertiesAttestation);
+                            } catch (Throwable e) {
+                                throw new Exception("Failed on curve " + curveIndex +
+                                        " challenge " + challengeIndex + " purpose " +
+                                        purposeIndex + " includeValidityDates " +
+                                        includeValidityDates + " and devicePropertiesAttestation " +
+                                        devicePropertiesAttestation, e);
+                            }
+                        }
                     }
                 }
             }
@@ -185,43 +192,50 @@
     }
 
     public void testEcAttestation_TooLargeChallenge() throws Exception {
-        try {
-            testEcAttestation(new byte[129], true /* includeValidityDates */, "secp256r1", 256,
-                    KM_PURPOSE_SIGN);
-            fail("Attestation challenges larger than 128 bytes should be rejected");
-        } catch (ProviderException e) {
-            KeyStoreException cause = (KeyStoreException) e.getCause();
-            assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            try {
+                testEcAttestation(new byte[129], true /* includeValidityDates */, "secp256r1", 256,
+                        KM_PURPOSE_SIGN, devicePropertiesAttestation);
+                fail("Attestation challenges larger than 128 bytes should be rejected");
+            } catch (ProviderException e) {
+                KeyStoreException cause = (KeyStoreException) e.getCause();
+                assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+            }
         }
     }
 
     public void testEcAttestation_NoChallenge() throws Exception {
-        String keystoreAlias = "test_key";
-        Date now = new Date();
-        Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
-        Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
-        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
-                .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
-                .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
-                .setAttestationChallenge(null)
-                .setKeyValidityStart(now)
-                .setKeyValidityForOriginationEnd(originationEnd)
-                .setKeyValidityForConsumptionEnd(consumptionEnd)
-                .build();
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            String keystoreAlias = "test_key";
+            Date now = new Date();
+            Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
+            Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+                    .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                    .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+                    .setAttestationChallenge(null)
+                    .setKeyValidityStart(now)
+                    .setKeyValidityForOriginationEnd(originationEnd)
+                    .setKeyValidityForConsumptionEnd(consumptionEnd)
+                    .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+                    .build();
 
-        generateKeyPair(KEY_ALGORITHM_EC, spec);
+            generateKeyPair(KEY_ALGORITHM_EC, spec);
 
-        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
-        keyStore.load(null);
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
 
-        try {
-            Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            assertEquals(1, certificates.length);
+            try {
+                Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+                assertEquals(1, certificates.length);
 
-            X509Certificate attestationCert = (X509Certificate) certificates[0];
-            assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
-        } finally {
-            keyStore.deleteEntry(keystoreAlias);
+                X509Certificate attestationCert = (X509Certificate) certificates[0];
+                assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+            } finally {
+                keyStore.deleteEntry(keystoreAlias);
+            }
         }
     }
 
@@ -325,14 +339,19 @@
                         SIGNATURE_PADDING_RSA_PSS,
                 },
         };
+        boolean[] devicePropertiesAttestationValues = {true, false};
 
-        for (int keySize : keySizes) {
-            for (byte[] challenge : challenges) {
-                for (int purpose : purposes) {
-                    if (isEncryptionPurpose(purpose)) {
-                        testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes);
-                    } else {
-                        testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes);
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            for (int keySize : keySizes) {
+                for (byte[] challenge : challenges) {
+                    for (int purpose : purposes) {
+                        if (isEncryptionPurpose(purpose)) {
+                            testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes,
+                                    devicePropertiesAttestation);
+                        } else {
+                            testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes,
+                                    devicePropertiesAttestation);
+                        }
                     }
                 }
             }
@@ -340,46 +359,56 @@
     }
 
     public void testRsaAttestation_TooLargeChallenge() throws Exception {
-        try {
-            testRsaAttestation(new byte[129], true /* includeValidityDates */, 512, PURPOSE_SIGN,
-                    null /* paddingModes; may be empty because we'll never test them */);
-            fail("Attestation challenges larger than 128 bytes should be rejected");
-        } catch (ProviderException e) {
-            KeyStoreException cause = (KeyStoreException) e.getCause();
-            assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            try {
+                testRsaAttestation(new byte[129], true /* includeValidityDates */, 512,
+                        PURPOSE_SIGN,
+                        null /* paddingModes; may be empty because we'll never test them */,
+                        devicePropertiesAttestation);
+                fail("Attestation challenges larger than 128 bytes should be rejected");
+            } catch(ProviderException e){
+                KeyStoreException cause = (KeyStoreException) e.getCause();
+                assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+            }
         }
     }
 
     public void testRsaAttestation_NoChallenge() throws Exception {
-        String keystoreAlias = "test_key";
-        Date now = new Date();
-        Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
-        Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
-        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
-                .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
-                .setAttestationChallenge(null)
-                .setKeyValidityStart(now)
-                .setKeyValidityForOriginationEnd(originationEnd)
-                .setKeyValidityForConsumptionEnd(consumptionEnd)
-                .build();
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            String keystoreAlias = "test_key";
+            Date now = new Date();
+            Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
+            Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+                    .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+                    .setAttestationChallenge(null)
+                    .setKeyValidityStart(now)
+                    .setKeyValidityForOriginationEnd(originationEnd)
+                    .setKeyValidityForConsumptionEnd(consumptionEnd)
+                    .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+                    .build();
 
-        generateKeyPair(KEY_ALGORITHM_RSA, spec);
+            generateKeyPair(KEY_ALGORITHM_RSA, spec);
 
-        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
-        keyStore.load(null);
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
 
-        try {
-            Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            assertEquals(1, certificates.length);
+            try {
+                Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+                assertEquals(1, certificates.length);
 
-            X509Certificate attestationCert = (X509Certificate) certificates[0];
-            assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
-        } finally {
-            keyStore.deleteEntry(keystoreAlias);
+                X509Certificate attestationCert = (X509Certificate) certificates[0];
+                assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+            } finally {
+                keyStore.deleteEntry(keystoreAlias);
+            }
         }
     }
 
     @RestrictedBuildTest
+    @RequiresDevice  // Emulators have no place to store the needed key
     public void testRsaAttestation_DeviceLocked() throws Exception {
         String keystoreAlias = "test_key";
         Date now = new Date();
@@ -417,52 +446,62 @@
     }
 
     public void testAesAttestation() throws Exception {
-        String keystoreAlias = "test_key";
-        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_ENCRYPT)
-                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
-                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
-                .setAttestationChallenge(new byte[0])
-                .build();
-        generateKey(spec, KeyProperties.KEY_ALGORITHM_AES);
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            String keystoreAlias = "test_key";
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias,
+                    PURPOSE_ENCRYPT)
+                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                    .setAttestationChallenge(new byte[0])
+                    .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+                    .build();
+            generateKey(spec, KeyProperties.KEY_ALGORITHM_AES);
 
-        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
-        keyStore.load(null);
-        try {
-            assertNull(keyStore.getCertificateChain(keystoreAlias));
-        } finally {
-            keyStore.deleteEntry(keystoreAlias);
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            try {
+                assertNull(keyStore.getCertificateChain(keystoreAlias));
+            } finally {
+                keyStore.deleteEntry(keystoreAlias);
+            }
         }
     }
 
     public void testHmacAttestation() throws Exception {
-        String keystoreAlias = "test_key";
-        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
-                .build();
+        boolean[] devicePropertiesAttestationValues = {true, false};
+        for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+            String keystoreAlias = "test_key";
+            KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+                    .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+                    .build();
 
-        generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256);
+            generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256);
 
-        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
-        keyStore.load(null);
-        try {
-            assertNull(keyStore.getCertificateChain(keystoreAlias));
-        } finally {
-            keyStore.deleteEntry(keystoreAlias);
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            try {
+                assertNull(keyStore.getCertificateChain(keystoreAlias));
+            } finally {
+                keyStore.deleteEntry(keystoreAlias);
+            }
         }
     }
 
     private void testRsaAttestations(int keySize, byte[] challenge, int purpose,
-            String[][] paddingModes) throws Exception {
+            String[][] paddingModes, boolean devicePropertiesAttestation) throws Exception {
         for (String[] paddings : paddingModes) {
             try {
                 testRsaAttestation(challenge, true /* includeValidityDates */, keySize, purpose,
-                        paddings);
+                        paddings, devicePropertiesAttestation);
                 testRsaAttestation(challenge, false /* includeValidityDates */, keySize, purpose,
-                        paddings);
+                        paddings, devicePropertiesAttestation);
             } catch (Throwable e) {
                 throw new Exception("Failed on key size " + keySize + " challenge [" +
                         new String(challenge) + "], purposes " +
-                        buildPurposeSet(purpose) + " and paddings " +
-                        ImmutableSet.copyOf(paddings),
+                        buildPurposeSet(purpose) + " paddings " +
+                        ImmutableSet.copyOf(paddings) + " and devicePropertiesAttestation "
+                        + devicePropertiesAttestation,
                         e);
             }
         }
@@ -476,9 +515,14 @@
 
     @SuppressWarnings("deprecation")
     private void testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize,
-            int purposes, String[] paddingModes) throws Exception {
-        String keystoreAlias = "test_key";
+            int purposes, String[] paddingModes, boolean devicePropertiesAttestation)
+            throws Exception {
+        Log.i(TAG, "RSA key attestation with: challenge " + Arrays.toString(challenge) +
+                " / includeValidityDates " + includeValidityDates + " / keySize " + keySize +
+                " / purposes " + purposes + " / paddingModes " + Arrays.toString(paddingModes) +
+                " / devicePropertiesAttestation " + devicePropertiesAttestation);
 
+        String keystoreAlias = "test_key";
         Date startTime = new Date();
         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
@@ -486,7 +530,8 @@
             new KeyGenParameterSpec.Builder(keystoreAlias, purposes)
                         .setKeySize(keySize)
                         .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
-                        .setAttestationChallenge(challenge);
+                        .setAttestationChallenge(challenge)
+                        .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation);
 
         if (includeValidityDates) {
             builder.setKeyValidityStart(startTime)
@@ -517,7 +562,7 @@
             checkRsaKeyDetails(attestation, keySize, purposes, ImmutableSet.copyOf(paddingModes));
             checkKeyUsage(attestationCert, purposes);
             checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
-                    attestation);
+                    devicePropertiesAttestation, attestation);
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -538,9 +583,13 @@
 
     @SuppressWarnings("deprecation")
     private void testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve,
-            int keySize, int purposes) throws Exception {
-        String keystoreAlias = "test_key";
+            int keySize, int purposes, boolean devicePropertiesAttestation) throws Exception {
+        Log.i(TAG, "EC key attestation with: challenge " + Arrays.toString(challenge) +
+                " / includeValidityDates " + includeValidityDates + " / ecCurve " + ecCurve +
+                " / keySize " + keySize + " / purposes " + purposes +
+                " / devicePropertiesAttestation " + devicePropertiesAttestation);
 
+        String keystoreAlias = "test_key";
         Date startTime = new Date();
         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
@@ -548,7 +597,8 @@
                 purposes)
                         .setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve))
                         .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
-                        .setAttestationChallenge(challenge);
+                        .setAttestationChallenge(challenge)
+                        .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation);
 
         if (includeValidityDates) {
             builder.setKeyValidityStart(startTime)
@@ -571,7 +621,7 @@
             checkEcKeyDetails(attestation, ecCurve, keySize);
             checkKeyUsage(attestationCert, purposes);
             checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
-                    attestation);
+                    devicePropertiesAttestation, attestation);
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -596,9 +646,50 @@
         }
     }
 
+    private void checkAttestationDeviceProperties(boolean devicePropertiesAttestation,
+            Attestation attestation) {
+        final AuthorizationList keyDetailsList;
+        final AuthorizationList nonKeyDetailsList;
+        if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
+            keyDetailsList = attestation.getTeeEnforced();
+            nonKeyDetailsList = attestation.getSoftwareEnforced();
+        } else {
+            keyDetailsList = attestation.getSoftwareEnforced();
+            nonKeyDetailsList = attestation.getTeeEnforced();
+        }
+
+        if (devicePropertiesAttestation) {
+            assertEquals(Build.BRAND, keyDetailsList.getBrand());
+            assertEquals(Build.DEVICE, keyDetailsList.getDevice());
+            assertEquals(Build.PRODUCT, keyDetailsList.getProduct());
+            assertEquals(Build.MANUFACTURER, keyDetailsList.getManufacturer());
+            assertEquals(Build.MODEL, keyDetailsList.getModel());
+        } else {
+            assertNull(keyDetailsList.getBrand());
+            assertNull(keyDetailsList.getDevice());
+            assertNull(keyDetailsList.getProduct());
+            assertNull(keyDetailsList.getManufacturer());
+            assertNull(keyDetailsList.getModel());
+        }
+        assertNull(nonKeyDetailsList.getBrand());
+        assertNull(nonKeyDetailsList.getDevice());
+        assertNull(nonKeyDetailsList.getProduct());
+        assertNull(nonKeyDetailsList.getManufacturer());
+        assertNull(nonKeyDetailsList.getModel());
+    }
+
+    private void checkAttestationNoUniqueIds(Attestation attestation) {
+        assertNull(attestation.getTeeEnforced().getImei());
+        assertNull(attestation.getTeeEnforced().getMeid());
+        assertNull(attestation.getTeeEnforced().getSerialNumber());
+        assertNull(attestation.getSoftwareEnforced().getImei());
+        assertNull(attestation.getSoftwareEnforced().getMeid());
+        assertNull(attestation.getSoftwareEnforced().getSerialNumber());
+    }
+
     private void checkKeyIndependentAttestationInfo(byte[] challenge, int purposes, Date startTime,
-            boolean includesValidityDates, Attestation attestation)
-            throws NoSuchAlgorithmException, NameNotFoundException {
+            boolean includesValidityDates, boolean devicePropertiesAttestation,
+            Attestation attestation) throws NoSuchAlgorithmException, NameNotFoundException {
         checkUnexpectedOids(attestation);
         checkAttestationSecurityLevelDependentParams(attestation);
         assertNotNull(attestation.getAttestationChallenge());
@@ -612,6 +703,8 @@
         checkFlags(attestation);
         checkOrigin(attestation);
         checkAttestationApplicationId(attestation);
+        checkAttestationDeviceProperties(devicePropertiesAttestation, attestation);
+        checkAttestationNoUniqueIds(attestation);
     }
 
     private void checkUnexpectedOids(Attestation attestation) {
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
index 9a6a7de..707730d 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -211,7 +211,6 @@
     public void setUp() throws Exception {
         Log.i(TAG, "++ setUp() mContext:" + mContext);
         if (!hasMidiSupport()) {
-            Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
             return; // Not supported so don't test it.
         }
         mMidiManager = (MidiManager)mContext.getSystemService(Context.MIDI_SERVICE);
@@ -250,6 +249,10 @@
     public void test_B_SendData() throws Exception {
         Log.i(TAG, "++++ test_B_SendData() this:" + System.identityHashCode(this));
 
+        if (!hasMidiSupport()) {
+            return; // Nothing to test
+        }
+
         Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
         Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
 
diff --git a/tests/tests/net/Android.bp b/tests/tests/net/Android.bp
index 76bb27e..93a6d91 100644
--- a/tests/tests/net/Android.bp
+++ b/tests/tests/net/Android.bp
@@ -47,6 +47,7 @@
         "mockwebserver",
         "junit",
         "junit-params",
+        "libnanohttpd",
         "truth-prebuilt",
     ],
 
@@ -76,6 +77,7 @@
 android_test {
     name: "CtsNetTestCasesLatestSdk",
     defaults: ["CtsNetTestCasesDefaults"],
+    jni_uses_sdk_apis: true,
     min_sdk_version: "29",
     target_sdk_version: "29",
     test_suites: [
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index baf914f..a7e2bd7 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/net/ipsec/Android.bp b/tests/tests/net/ipsec/Android.bp
index f1f120b..16bdb05 100644
--- a/tests/tests/net/ipsec/Android.bp
+++ b/tests/tests/net/ipsec/Android.bp
@@ -33,6 +33,7 @@
         "androidx.test.ext.junit",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
+        "net-tests-utils",
     ],
 
     platform_apis: true,
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java
index 6fc7cb3..c767b78 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java
@@ -46,6 +46,7 @@
 
 import com.android.internal.net.ipsec.ike.testutils.CertUtils;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -63,7 +64,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-public final class IkeSessionParamsTest extends IkeSessionParamsTestBase {
+public final class IkeSessionParamsTest extends IkeSessionTestBase {
     private static final int HARD_LIFETIME_SECONDS = (int) TimeUnit.HOURS.toSeconds(20L);
     private static final int SOFT_LIFETIME_SECONDS = (int) TimeUnit.HOURS.toSeconds(10L);
     private static final int DPD_DELAY_SECONDS = (int) TimeUnit.MINUTES.toSeconds(10L);
@@ -105,6 +106,9 @@
 
     @Before
     public void setUp() throws Exception {
+        // This address is never used except for setting up the test network
+        setUpTestNetwork(IPV4_ADDRESS_LOCAL);
+
         mServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem");
         mClientEndCert = CertUtils.createCertFromPemFile("client-a-end-cert.pem");
         mClientIntermediateCaCertOne =
@@ -114,6 +118,11 @@
         mClientPrivateKey = CertUtils.createRsaPrivateKeyFromKeyFile("client-a-private-key.key");
     }
 
+    @After
+    public void tearDown() throws Exception {
+        tearDownTestNetwork();
+    }
+
     private static EapSessionConfig.Builder createEapOnlySafeMethodsBuilder() {
         return new EapSessionConfig.Builder()
                 .setEapIdentity(EAP_IDENTITY)
@@ -131,7 +140,7 @@
      */
     private IkeSessionParams.Builder createIkeParamsBuilderMinimum() {
         return new IkeSessionParams.Builder(sContext)
-                .setNetwork(sTunNetwork)
+                .setNetwork(mTunNetwork)
                 .setServerHostname(IPV4_ADDRESS_REMOTE.getHostAddress())
                 .addSaProposal(SA_PROPOSAL)
                 .setLocalIdentification(LOCAL_ID)
@@ -145,7 +154,7 @@
      * @see #createIkeParamsBuilderMinimum
      */
     private void verifyIkeParamsMinimum(IkeSessionParams sessionParams) {
-        assertEquals(sTunNetwork, sessionParams.getNetwork());
+        assertEquals(mTunNetwork, sessionParams.getNetwork());
         assertEquals(IPV4_ADDRESS_REMOTE.getHostAddress(), sessionParams.getServerHostname());
         assertEquals(Arrays.asList(SA_PROPOSAL), sessionParams.getSaProposals());
         assertEquals(LOCAL_ID, sessionParams.getLocalIdentification());
@@ -268,7 +277,7 @@
      */
     private IkeSessionParams.Builder createIkeParamsBuilderMinimumWithoutAuth() {
         return new IkeSessionParams.Builder(sContext)
-                .setNetwork(sTunNetwork)
+                .setNetwork(mTunNetwork)
                 .setServerHostname(IPV4_ADDRESS_REMOTE.getHostAddress())
                 .addSaProposal(SA_PROPOSAL)
                 .setLocalIdentification(LOCAL_ID)
@@ -282,7 +291,7 @@
      * @see #createIkeParamsBuilderMinimumWithoutAuth
      */
     private void verifyIkeParamsMinimumWithoutAuth(IkeSessionParams sessionParams) {
-        assertEquals(sTunNetwork, sessionParams.getNetwork());
+        assertEquals(mTunNetwork, sessionParams.getNetwork());
         assertEquals(IPV4_ADDRESS_REMOTE.getHostAddress(), sessionParams.getServerHostname());
         assertEquals(Arrays.asList(SA_PROPOSAL), sessionParams.getSaProposals());
         assertEquals(LOCAL_ID, sessionParams.getLocalIdentification());
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java
deleted file mode 100644
index c3e3ba3..0000000
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.net.ipsec.ike.cts;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkAddress;
-import android.net.Network;
-import android.net.TestNetworkInterface;
-import android.net.TestNetworkManager;
-import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.AppModeFull;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
-abstract class IkeSessionParamsTestBase extends IkeTestBase {
-    // Static state to reduce setup/teardown
-    static ConnectivityManager sCM;
-    static TestNetworkManager sTNM;
-    static ParcelFileDescriptor sTunFd;
-    static TestNetworkCallback sTunNetworkCallback;
-    static Network sTunNetwork;
-
-    static Context sContext = InstrumentationRegistry.getContext();
-    static IBinder sBinder = new Binder();
-
-    // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass
-    // methods.
-    @BeforeClass
-    public static void setUpTestNetworkBeforeClass() throws Exception {
-        InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation()
-                .adoptShellPermissionIdentity();
-        sCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
-
-        TestNetworkInterface testIface =
-                sTNM.createTunInterface(
-                        new LinkAddress[] {new LinkAddress(IPV4_ADDRESS_LOCAL, IP4_PREFIX_LEN)});
-
-        sTunFd = testIface.getFileDescriptor();
-        sTunNetworkCallback =
-                TestNetworkUtils.setupAndGetTestNetwork(
-                        sCM, sTNM, testIface.getInterfaceName(), sBinder);
-        sTunNetwork = sTunNetworkCallback.getNetworkBlocking();
-    }
-
-    // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
-    // methods.
-    @AfterClass
-    public static void tearDownTestNetworkAfterClass() throws Exception {
-        sCM.unregisterNetworkCallback(sTunNetworkCallback);
-
-        sTNM.teardownTestNetwork(sTunNetwork);
-        sTunFd.close();
-
-        InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation()
-                .dropShellPermissionIdentity();
-    }
-}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
new file mode 100644
index 0000000..661457f
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.net.ipsec.ike.cts;
+
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_IPSEC_TUNNELS permission can't be granted to instant apps")
+public class IkeSessionPskTest extends IkeSessionTestBase {
+    // Test vectors for success workflow
+    private static final String SUCCESS_IKE_INIT_RESP =
+            "46B8ECA1E0D72A18B45427679F9245D421202220000000000000015022000030"
+                    + "0000002C010100040300000C0100000C800E0080030000080300000203000008"
+                    + "0200000200000008040000022800008800020000A7AA3435D088EC1A2B7C2A47"
+                    + "1FA1B85F1066C9B2006E7C353FB5B5FDBC2A88347ED2C6F5B7A265D03AE34039"
+                    + "6AAC0145CFCC93F8BDB219DDFF22A603B8856A5DC59B6FAB7F17C5660CF38670"
+                    + "8794FC72F273ADEB7A4F316519794AED6F8AB61F95DFB360FAF18C6C8CABE471"
+                    + "6E18FE215348C2E582171A57FC41146B16C4AFE429000024A634B61C0E5C90C6"
+                    + "8D8818B0955B125A9B1DF47BBD18775710792E651083105C2900001C00004004"
+                    + "406FA3C5685A16B9B72C7F2EEE9993462C619ABE2900001C00004005AF905A87"
+                    + "0A32222AA284A7070585601208A282F0290000080000402E290000100000402F"
+                    + "00020003000400050000000800004014";
+    private static final String SUCCESS_IKE_AUTH_RESP =
+            "46B8ECA1E0D72A18B45427679F9245D42E20232000000001000000EC240000D0"
+                    + "0D06D37198F3F0962DE8170D66F1A9008267F98CDD956D984BDCED2FC7FAF84A"
+                    + "A6664EF25049B46B93C9ED420488E0C172AA6635BF4011C49792EF2B88FE7190"
+                    + "E8859FEEF51724FD20C46E7B9A9C3DC4708EF7005707A18AB747C903ABCEAC5C"
+                    + "6ECF5A5FC13633DCE3844A920ED10EF202F115DBFBB5D6D2D7AB1F34EB08DE7C"
+                    + "A54DCE0A3A582753345CA2D05A0EFDB9DC61E81B2483B7D13EEE0A815D37252C"
+                    + "23D2F29E9C30658227D2BB0C9E1A481EAA80BC6BE9006BEDC13E925A755A0290"
+                    + "AEC4164D29997F52ED7DCC2E";
+    private static final String SUCCESS_CREATE_CHILD_RESP =
+            "46B8ECA1E0D72A18B45427679F9245D42E20242000000002000000CC210000B0"
+                    + "484565D4AF6546274674A8DE339E9C9584EE2326AB9260F41C4D0B6C5B02D1D"
+                    + "2E8394E3CDE3094895F2ACCABCDCA8E82960E5196E9622BD13745FC8D6A2BED"
+                    + "E561FF5D9975421BC463C959A3CBA3478256B6D278159D99B512DDF56AC1658"
+                    + "63C65A986F395FE8B1476124B91F83FD7865304EB95B22CA4DD9601DA7A2533"
+                    + "ABF4B36EB1B8CD09522F6A600032316C74E562E6756D9D49D945854E2ABDC4C"
+                    + "3AF36305353D60D40B58BE44ABF82";
+    private static final String SUCCESS_DELETE_CHILD_RESP =
+            "46B8ECA1E0D72A18B45427679F9245D42E202520000000030000004C2A000030"
+                    + "0C5CEB882DBCA65CE32F4C53909335F1365C91C555316C5E9D9FB553F7AA916"
+                    + "EF3A1D93460B7FABAF0B4B854";
+    private static final String SUCCESS_DELETE_IKE_RESP =
+            "46B8ECA1E0D72A18B45427679F9245D42E202520000000040000004C00000030"
+                    + "9352D71100777B00ABCC6BD7DBEA697827FFAAA48DF9A54D1D68161939F5DC8"
+                    + "6743A7CEB2BE34AC00095A5B8";
+
+    private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+        IkeSessionParams ikeParams =
+                new IkeSessionParams.Builder(sContext)
+                        .setNetwork(mTunNetwork)
+                        .setServerHostname(remoteAddress.getHostAddress())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+                        .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
+                        .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
+                        .setAuthPsk(IKE_PSK)
+                        .build();
+        return new IkeSession(
+                sContext,
+                ikeParams,
+                buildTunnelModeChildSessionParams(),
+                mUserCbExecutor,
+                mIkeSessionCallback,
+                mFirstChildSessionCallback);
+    }
+
+    @Test
+    public void testIkeSessionSetupAndChildSessionSetupWithTunnelMode() throws Exception {
+        if (!hasTunnelsFeature()) return;
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
+
+        // IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
+        int expectedMsgId = 2;
+
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TUNNEL_MODE_INBOUND_TS),
+                Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
+                Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR));
+
+        IpSecTransformCallRecord firstTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord firstTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+        // Open additional Child Session
+        TestChildSessionCallback additionalChildCb = new TestChildSessionCallback();
+        ikeSession.openChildSession(buildTunnelModeChildSessionParams(), additionalChildCb);
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                true /* expectedUseEncap */,
+                SUCCESS_CREATE_CHILD_RESP);
+
+        // Verify opening additional Child Session
+        verifyChildSessionSetupBlocking(
+                additionalChildCb,
+                Arrays.asList(TUNNEL_MODE_INBOUND_TS),
+                Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
+                new ArrayList<LinkAddress>());
+        IpSecTransformCallRecord additionalTransformRecordA =
+                additionalChildCb.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord additionalTransformRecordB =
+                additionalChildCb.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(additionalTransformRecordA, additionalTransformRecordB);
+
+        // Close additional Child Session
+        ikeSession.closeChildSession(additionalChildCb);
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                true /* expectedUseEncap */,
+                SUCCESS_DELETE_CHILD_RESP);
+
+        verifyDeleteIpSecTransformPair(
+                additionalChildCb, additionalTransformRecordA, additionalTransformRecordB);
+        additionalChildCb.awaitOnClosed();
+
+        // Close IKE Session
+        ikeSession.close();
+        performCloseIkeBlocking(expectedMsgId++, SUCCESS_DELETE_IKE_RESP);
+        verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
+    }
+
+    @Test
+    public void testIkeSessionKillWithTunnelMode() throws Exception {
+        if (!hasTunnelsFeature()) return;
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
+
+        ikeSession.kill();
+        mFirstChildSessionCallback.awaitOnClosed();
+        mIkeSessionCallback.awaitOnClosed();
+    }
+
+    @Test
+    public void testIkeInitFail() throws Exception {
+        final String ikeInitFailRespHex =
+                "46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E";
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        int expectedMsgId = 0;
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                false /* expectedUseEncap */,
+                ikeInitFailRespHex);
+
+        mFirstChildSessionCallback.awaitOnClosed();
+
+        IkeException exception = mIkeSessionCallback.awaitOnClosedException();
+        assertNotNull(exception);
+        assertTrue(exception instanceof IkeProtocolException);
+        IkeProtocolException protocolException = (IkeProtocolException) exception;
+        assertEquals(ERROR_TYPE_NO_PROPOSAL_CHOSEN, protocolException.getErrorType());
+        assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
+    }
+
+    // TODO(b/155821007): Verify rekey process and handling IKE_AUTH failure
+
+    // TODO(b/155821007): Test creating transport mode Child SA
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
new file mode 100644
index 0000000..ade9813
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
@@ -0,0 +1,528 @@
+/*
+ * 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
+ *
+ * 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.net.ipsec.ike.cts;
+
+import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.LinkAddress;
+import android.net.Network;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.annotations.PolicyDirection;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.testutils.ArrayTrackRecord;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Package private base class for testing IkeSessionParams and IKE exchanges.
+ *
+ * <p>Subclasses MUST explicitly call #setUpTestNetwork and #tearDownTestNetwork to be able to use
+ * the test network
+ *
+ * <p>All IKE Sessions running in test mode will generate SPIs deterministically. That is to say
+ * each IKE Session will always generate the same IKE INIT SPI and test vectors are generated based
+ * on this deterministic IKE SPI. Each test will use different local and remote addresses to avoid
+ * the case that the next test try to allocate the same SPI before the previous test has released
+ * it, since SPI resources are not released in testing thread. Similarly, each test MUST use
+ * different Network instances to avoid sharing the same IkeSocket and hitting IKE SPI collision.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
+abstract class IkeSessionTestBase extends IkeTestBase {
+    // Package-wide common expected results that will be shared by all IKE/Child SA creation tests
+    static final String EXPECTED_REMOTE_APP_VERSION_EMPTY = "";
+    static final byte[] EXPECTED_PROTOCOL_ERROR_DATA_NONE = new byte[0];
+    static final InetAddress EXPECTED_INTERNAL_ADDR =
+            InetAddresses.parseNumericAddress("198.51.100.10");
+    static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR =
+            new LinkAddress(EXPECTED_INTERNAL_ADDR, IP4_PREFIX_LEN);
+
+    static final IkeTrafficSelector TUNNEL_MODE_INBOUND_TS =
+            new IkeTrafficSelector(
+                    MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR, EXPECTED_INTERNAL_ADDR);
+    static final IkeTrafficSelector TUNNEL_MODE_OUTBOUND_TS = DEFAULT_V4_TS;
+
+    static final long IKE_DETERMINISTIC_INITIATOR_SPI = Long.parseLong("46B8ECA1E0D72A18", 16);
+
+    // Static state to reduce setup/teardown
+    static Context sContext = InstrumentationRegistry.getContext();
+    static ConnectivityManager sCM =
+            (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+    static TestNetworkManager sTNM;
+
+    private static final int TIMEOUT_MS = 500;
+
+    // Constants to be used for providing different IP addresses for each tests
+    private static final byte IP_ADDR_LAST_BYTE_MAX = (byte) 100;
+    private static final byte[] INITIAL_AVAILABLE_IP4_ADDR_LOCAL =
+            InetAddresses.parseNumericAddress("192.0.2.1").getAddress();
+    private static final byte[] INITIAL_AVAILABLE_IP4_ADDR_REMOTE =
+            InetAddresses.parseNumericAddress("198.51.100.1").getAddress();
+    private static final byte[] NEXT_AVAILABLE_IP4_ADDR_LOCAL = INITIAL_AVAILABLE_IP4_ADDR_LOCAL;
+    private static final byte[] NEXT_AVAILABLE_IP4_ADDR_REMOTE = INITIAL_AVAILABLE_IP4_ADDR_REMOTE;
+
+    ParcelFileDescriptor mTunFd;
+    TestNetworkCallback mTunNetworkCallback;
+    Network mTunNetwork;
+    IkeTunUtils mTunUtils;
+
+    InetAddress mLocalAddress;
+    InetAddress mRemoteAddress;
+
+    Executor mUserCbExecutor;
+    TestIkeSessionCallback mIkeSessionCallback;
+    TestChildSessionCallback mFirstChildSessionCallback;
+
+    // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass
+    // methods.
+    @BeforeClass
+    public static void setUpPermissionBeforeClass() throws Exception {
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+        sTNM = sContext.getSystemService(TestNetworkManager.class);
+
+        // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
+        // a standard permission is insufficient. So we shell out the appop, to give us the
+        // right appop permissions.
+        setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
+    }
+
+    // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
+    // methods.
+    @AfterClass
+    public static void tearDownPermissionAfterClass() throws Exception {
+        setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
+
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mLocalAddress = getNextAvailableIpv4AddressLocal();
+        mRemoteAddress = getNextAvailableIpv4AddressRemote();
+        setUpTestNetwork(mLocalAddress);
+
+        mUserCbExecutor = Executors.newSingleThreadExecutor();
+        mIkeSessionCallback = new TestIkeSessionCallback();
+        mFirstChildSessionCallback = new TestChildSessionCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        tearDownTestNetwork();
+    }
+
+    void setUpTestNetwork(InetAddress localAddr) throws Exception {
+        int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP4_PREFIX_LEN;
+
+        TestNetworkInterface testIface =
+                sTNM.createTunInterface(new LinkAddress[] {new LinkAddress(localAddr, prefixLen)});
+
+        mTunFd = testIface.getFileDescriptor();
+        mTunNetworkCallback =
+                TestNetworkUtils.setupAndGetTestNetwork(
+                        sCM, sTNM, testIface.getInterfaceName(), new Binder());
+        mTunNetwork = mTunNetworkCallback.getNetworkBlocking();
+        mTunUtils = new IkeTunUtils(mTunFd);
+    }
+
+    void tearDownTestNetwork() throws Exception {
+        sCM.unregisterNetworkCallback(mTunNetworkCallback);
+
+        sTNM.teardownTestNetwork(mTunNetwork);
+        mTunFd.close();
+    }
+
+    private static void setAppOp(int appop, boolean allow) {
+        String opName = AppOpsManager.opToName(appop);
+        for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
+            String cmd =
+                    String.format(
+                            "appops set %s %s %s",
+                            pkg, // Package name
+                            opName, // Appop
+                            (allow ? "allow" : "deny")); // Action
+
+            SystemUtil.runShellCommand(cmd);
+        }
+    }
+
+    Inet4Address getNextAvailableIpv4AddressLocal() throws Exception {
+        return (Inet4Address)
+                getNextAvailableAddress(
+                        NEXT_AVAILABLE_IP4_ADDR_LOCAL,
+                        INITIAL_AVAILABLE_IP4_ADDR_LOCAL,
+                        false /* isIp6 */);
+    }
+
+    Inet4Address getNextAvailableIpv4AddressRemote() throws Exception {
+        return (Inet4Address)
+                getNextAvailableAddress(
+                        NEXT_AVAILABLE_IP4_ADDR_REMOTE,
+                        INITIAL_AVAILABLE_IP4_ADDR_REMOTE,
+                        false /* isIp6 */);
+    }
+
+    InetAddress getNextAvailableAddress(
+            byte[] nextAddressBytes, byte[] initialAddressBytes, boolean isIp6) throws Exception {
+        int addressLen = isIp6 ? IP6_ADDRESS_LEN : IP4_ADDRESS_LEN;
+
+        synchronized (nextAddressBytes) {
+            if (nextAddressBytes[addressLen - 1] == IP_ADDR_LAST_BYTE_MAX) {
+                resetNextAvailableAddress(nextAddressBytes, initialAddressBytes);
+            }
+
+            InetAddress address = InetAddress.getByAddress(nextAddressBytes);
+            nextAddressBytes[addressLen - 1]++;
+            return address;
+        }
+    }
+
+    private void resetNextAvailableAddress(byte[] nextAddressBytes, byte[] initialAddressBytes) {
+        synchronized (nextAddressBytes) {
+            System.arraycopy(
+                    nextAddressBytes, 0, initialAddressBytes, 0, initialAddressBytes.length);
+        }
+    }
+
+    TunnelModeChildSessionParams buildTunnelModeChildSessionParams() {
+        return new TunnelModeChildSessionParams.Builder()
+                .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
+                .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
+                .addInternalAddressRequest(AF_INET)
+                .addInternalAddressRequest(AF_INET6)
+                .build();
+    }
+
+    void performSetupIkeAndFirstChildBlocking(String ikeInitRespHex, String ikeAuthRespHex)
+            throws Exception {
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                0 /* expectedMsgId */,
+                false /* expectedUseEncap */,
+                ikeInitRespHex);
+
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                1 /* expectedMsgId */,
+                true /* expectedUseEncap */,
+                ikeAuthRespHex);
+    }
+
+    void performSetupIkeAndFirstChildBlocking(
+            String ikeInitRespHex, int expectedAuthReqPktCnt, String... ikeAuthRespPktHex)
+            throws Exception {
+        // TODO: Implemented in followup CL (aosp/1308675) to support awaiting multiple IKE AUTH
+        // request fragments and injecting multiple IKE AUTH response fragments
+    }
+
+    void performCloseIkeBlocking(int expectedMsgId, String deleteIkeRespHex) throws Exception {
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId,
+                true /* expectedUseEncap */,
+                deleteIkeRespHex);
+    }
+
+    /** Testing callback that allows caller to block current thread until a method get called */
+    static class TestIkeSessionCallback implements IkeSessionCallback {
+        private CompletableFuture<IkeSessionConfiguration> mFutureIkeConfig =
+                new CompletableFuture<>();
+        private CompletableFuture<Boolean> mFutureOnClosedCall = new CompletableFuture<>();
+        private CompletableFuture<IkeException> mFutureOnClosedException =
+                new CompletableFuture<>();
+
+        private int mOnErrorExceptionsCount = 0;
+        private ArrayTrackRecord<IkeProtocolException> mOnErrorExceptionsTrackRecord =
+                new ArrayTrackRecord<>();
+
+        @Override
+        public void onOpened(@NonNull IkeSessionConfiguration sessionConfiguration) {
+            mFutureIkeConfig.complete(sessionConfiguration);
+        }
+
+        @Override
+        public void onClosed() {
+            mFutureOnClosedCall.complete(true /* unused */);
+        }
+
+        @Override
+        public void onClosedExceptionally(@NonNull IkeException exception) {
+            mFutureOnClosedException.complete(exception);
+        }
+
+        @Override
+        public void onError(@NonNull IkeProtocolException exception) {
+            mOnErrorExceptionsTrackRecord.add(exception);
+        }
+
+        public IkeSessionConfiguration awaitIkeConfig() throws Exception {
+            return mFutureIkeConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+
+        public IkeException awaitOnClosedException() throws Exception {
+            return mFutureOnClosedException.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+
+        public IkeProtocolException awaitNextOnErrorException() {
+            return mOnErrorExceptionsTrackRecord.poll(
+                    (long) TIMEOUT_MS,
+                    mOnErrorExceptionsCount++,
+                    (transform) -> {
+                        return true;
+                    });
+        }
+
+        public void awaitOnClosed() throws Exception {
+            mFutureOnClosedCall.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /** Testing callback that allows caller to block current thread until a method get called */
+    static class TestChildSessionCallback implements ChildSessionCallback {
+        private CompletableFuture<ChildSessionConfiguration> mFutureChildConfig =
+                new CompletableFuture<>();
+        private CompletableFuture<Boolean> mFutureOnClosedCall = new CompletableFuture<>();
+        private CompletableFuture<IkeException> mFutureOnClosedException =
+                new CompletableFuture<>();
+
+        private int mCreatedIpSecTransformCount = 0;
+        private int mDeletedIpSecTransformCount = 0;
+        private ArrayTrackRecord<IpSecTransformCallRecord> mCreatedIpSecTransformsTrackRecord =
+                new ArrayTrackRecord<>();
+        private ArrayTrackRecord<IpSecTransformCallRecord> mDeletedIpSecTransformsTrackRecord =
+                new ArrayTrackRecord<>();
+
+        @Override
+        public void onOpened(@NonNull ChildSessionConfiguration sessionConfiguration) {
+            mFutureChildConfig.complete(sessionConfiguration);
+        }
+
+        @Override
+        public void onClosed() {
+            mFutureOnClosedCall.complete(true /* unused */);
+        }
+
+        @Override
+        public void onClosedExceptionally(@NonNull IkeException exception) {
+            mFutureOnClosedException.complete(exception);
+        }
+
+        @Override
+        public void onIpSecTransformCreated(@NonNull IpSecTransform ipSecTransform, int direction) {
+            mCreatedIpSecTransformsTrackRecord.add(
+                    new IpSecTransformCallRecord(ipSecTransform, direction));
+        }
+
+        @Override
+        public void onIpSecTransformDeleted(@NonNull IpSecTransform ipSecTransform, int direction) {
+            mDeletedIpSecTransformsTrackRecord.add(
+                    new IpSecTransformCallRecord(ipSecTransform, direction));
+        }
+
+        public ChildSessionConfiguration awaitChildConfig() throws Exception {
+            return mFutureChildConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+
+        public IkeException awaitOnClosedException() throws Exception {
+            return mFutureOnClosedException.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+
+        public IpSecTransformCallRecord awaitNextCreatedIpSecTransform() {
+            return mCreatedIpSecTransformsTrackRecord.poll(
+                    (long) TIMEOUT_MS,
+                    mCreatedIpSecTransformCount++,
+                    (transform) -> {
+                        return true;
+                    });
+        }
+
+        public IpSecTransformCallRecord awaitNextDeletedIpSecTransform() {
+            return mDeletedIpSecTransformsTrackRecord.poll(
+                    (long) TIMEOUT_MS,
+                    mDeletedIpSecTransformCount++,
+                    (transform) -> {
+                        return true;
+                    });
+        }
+
+        public void awaitOnClosed() throws Exception {
+            mFutureOnClosedCall.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /**
+     * This class represents a created or deleted IpSecTransfrom that is provided by
+     * ChildSessionCallback
+     */
+    static class IpSecTransformCallRecord {
+        public final IpSecTransform ipSecTransform;
+        public final int direction;
+
+        IpSecTransformCallRecord(IpSecTransform ipSecTransform, @PolicyDirection int direction) {
+            this.ipSecTransform = ipSecTransform;
+            this.direction = direction;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(ipSecTransform, direction);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof IpSecTransformCallRecord)) return false;
+
+            IpSecTransformCallRecord record = (IpSecTransformCallRecord) o;
+            return ipSecTransform.equals(record.ipSecTransform) && direction == record.direction;
+        }
+    }
+
+    void verifyIkeSessionSetupBlocking() throws Exception {
+        IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig();
+        assertNotNull(ikeConfig);
+        assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion());
+        assertTrue(ikeConfig.getRemoteVendorIds().isEmpty());
+        assertTrue(ikeConfig.getPcscfServers().isEmpty());
+        assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION));
+
+        IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo();
+        assertNotNull(ikeConnectInfo);
+        assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress());
+        assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress());
+        assertEquals(mTunNetwork, ikeConnectInfo.getNetwork());
+    }
+
+    void verifyChildSessionSetupBlocking(
+            TestChildSessionCallback childCallback,
+            List<IkeTrafficSelector> expectedInboundTs,
+            List<IkeTrafficSelector> expectedOutboundTs,
+            List<LinkAddress> expectedInternalAddresses)
+            throws Exception {
+        ChildSessionConfiguration childConfig = childCallback.awaitChildConfig();
+        assertNotNull(childConfig);
+        assertEquals(expectedInboundTs, childConfig.getInboundTrafficSelectors());
+        assertEquals(expectedOutboundTs, childConfig.getOutboundTrafficSelectors());
+        assertEquals(expectedInternalAddresses, childConfig.getInternalAddresses());
+        assertTrue(childConfig.getInternalSubnets().isEmpty());
+        assertTrue(childConfig.getInternalDnsServers().isEmpty());
+        assertTrue(childConfig.getInternalDhcpServers().isEmpty());
+    }
+
+    void verifyCloseIkeAndChildBlocking(
+            IpSecTransformCallRecord expectedTransformRecordA,
+            IpSecTransformCallRecord expectedTransformRecordB)
+            throws Exception {
+        verifyDeleteIpSecTransformPair(
+                mFirstChildSessionCallback, expectedTransformRecordA, expectedTransformRecordB);
+        mFirstChildSessionCallback.awaitOnClosed();
+        mIkeSessionCallback.awaitOnClosed();
+    }
+
+    static void verifyCreateIpSecTransformPair(
+            IpSecTransformCallRecord transformRecordA, IpSecTransformCallRecord transformRecordB) {
+        IpSecTransform transformA = transformRecordA.ipSecTransform;
+        IpSecTransform transformB = transformRecordB.ipSecTransform;
+
+        assertNotNull(transformA);
+        assertNotNull(transformB);
+
+        Set<Integer> expectedDirections = new HashSet<>();
+        expectedDirections.add(IpSecManager.DIRECTION_IN);
+        expectedDirections.add(IpSecManager.DIRECTION_OUT);
+
+        Set<Integer> resultDirections = new HashSet<>();
+        resultDirections.add(transformRecordA.direction);
+        resultDirections.add(transformRecordB.direction);
+
+        assertEquals(expectedDirections, resultDirections);
+    }
+
+    static void verifyDeleteIpSecTransformPair(
+            TestChildSessionCallback childCb,
+            IpSecTransformCallRecord expectedTransformRecordA,
+            IpSecTransformCallRecord expectedTransformRecordB) {
+        Set<IpSecTransformCallRecord> expectedTransforms = new HashSet<>();
+        expectedTransforms.add(expectedTransformRecordA);
+        expectedTransforms.add(expectedTransformRecordB);
+
+        Set<IpSecTransformCallRecord> resultTransforms = new HashSet<>();
+        resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
+        resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
+
+        assertEquals(expectedTransforms, resultTransforms);
+    }
+
+    /** Package private method to check if device has IPsec tunnels feature */
+    static boolean hasTunnelsFeature() {
+        return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
+    }
+
+    // TODO(b/148689509): Verify IKE Session setup using EAP and digital-signature-based auth
+
+    // TODO(b/148689509): Verify hostname based creation
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
index bc2bec6..f07c710 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
@@ -31,13 +31,15 @@
 
 /** Shared parameters and util methods for testing different components of IKE */
 abstract class IkeTestBase {
-    private static final int MIN_PORT = 0;
-    private static final int MAX_PORT = 65535;
+    static final int MIN_PORT = 0;
+    static final int MAX_PORT = 65535;
     private static final int INBOUND_TS_START_PORT = MIN_PORT;
     private static final int INBOUND_TS_END_PORT = 65520;
     private static final int OUTBOUND_TS_START_PORT = 16;
     private static final int OUTBOUND_TS_END_PORT = MAX_PORT;
 
+    static final int IP4_ADDRESS_LEN = 4;
+    static final int IP6_ADDRESS_LEN = 16;
     static final int IP4_PREFIX_LEN = 32;
     static final int IP6_PREFIX_LEN = 64;
 
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
new file mode 100644
index 0000000..2bff63a
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
@@ -0,0 +1,242 @@
+/*
+ * 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.net.ipsec.ike.cts;
+
+import static android.net.ipsec.ike.cts.PacketUtils.BytePayload;
+import static android.net.ipsec.ike.cts.PacketUtils.IP4_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.Ip4Header;
+import static android.net.ipsec.ike.cts.PacketUtils.Ip6Header;
+import static android.net.ipsec.ike.cts.PacketUtils.IpHeader;
+import static android.net.ipsec.ike.cts.PacketUtils.Payload;
+import static android.net.ipsec.ike.cts.PacketUtils.UDP_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.UdpHeader;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+
+import static org.junit.Assert.fail;
+
+import android.os.ParcelFileDescriptor;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+public class IkeTunUtils extends TunUtils {
+    private static final int PORT_LEN = 2;
+
+    private static final int NON_ESP_MARKER_LEN = 4;
+    private static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
+
+    private static final int IKE_HEADER_LEN = 28;
+    private static final int IKE_INIT_SPI_OFFSET = 0;
+    private static final int IKE_IS_RESP_BYTE_OFFSET = 19;
+    private static final int IKE_MSG_ID_OFFSET = 20;
+
+    public IkeTunUtils(ParcelFileDescriptor tunFd) {
+        super(tunFd);
+    }
+
+    /**
+     * Await the expected IKE request and inject an IKE response.
+     *
+     * @param ikeRespDataHex IKE response hex without IP/UDP headers or NON ESP MARKER.
+     */
+    public byte[] awaitReqAndInjectResp(
+            long expectedInitIkeSpi,
+            int expectedMsgId,
+            boolean expectedUseEncap,
+            String ikeRespDataHex)
+            throws Exception {
+        byte[] request =
+                awaitIkePacket(
+                        (pkt) -> {
+                            return isExpectedIkePkt(
+                                    pkt,
+                                    expectedInitIkeSpi,
+                                    expectedMsgId,
+                                    false /* expectedResp */,
+                                    expectedUseEncap);
+                        });
+
+        // Build response header by flipping address and port
+        InetAddress srcAddr = getAddress(request, false /* shouldGetSource */);
+        InetAddress dstAddr = getAddress(request, true /* shouldGetSource */);
+        int srcPort = getPort(request, false /* shouldGetSource */);
+        int dstPort = getPort(request, true /* shouldGetSource */);
+
+        byte[] response =
+                buildIkePacket(
+                        srcAddr,
+                        dstAddr,
+                        srcPort,
+                        dstPort,
+                        expectedUseEncap,
+                        hexStringToByteArray(ikeRespDataHex));
+        injectPacket(response);
+        return request;
+    }
+
+    // TODO: Implemented in followup CL (aosp/1308675) to support awaiting multiple
+    // request fragments and injecting multiple  response fragments
+
+    private byte[] awaitIkePacket(Predicate<byte[]> pktVerifier) throws Exception {
+        long endTime = System.currentTimeMillis() + TIMEOUT;
+        int startIndex = 0;
+        synchronized (mPackets) {
+            while (System.currentTimeMillis() < endTime) {
+                byte[] ikePkt = getFirstMatchingPacket(pktVerifier, startIndex);
+                if (ikePkt != null) {
+                    return ikePkt; // We've found the packet we're looking for.
+                }
+
+                startIndex = mPackets.size();
+
+                // Try to prevent waiting too long. If waitTimeout <= 0, we've already hit timeout
+                long waitTimeout = endTime - System.currentTimeMillis();
+                if (waitTimeout > 0) {
+                    mPackets.wait(waitTimeout);
+                }
+            }
+
+            fail("No matching packet found");
+        }
+
+        throw new IllegalStateException(
+                "Hit an impossible case where fail() didn't throw an exception");
+    }
+
+    private static boolean isExpectedIkePkt(
+            byte[] pkt,
+            long expectedInitIkeSpi,
+            int expectedMsgId,
+            boolean expectedResp,
+            boolean expectedUseEncap) {
+        int ipProtocolOffset = 0;
+        int ikeOffset = 0;
+        if (isIpv6(pkt)) {
+            // IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap.
+            ipProtocolOffset = IP6_PROTO_OFFSET;
+            ikeOffset = IP6_HDRLEN + UDP_HDRLEN;
+        } else {
+            // Use default IPv4 header length (assuming no options)
+            ipProtocolOffset = IP4_PROTO_OFFSET;
+            ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
+
+            if (expectedUseEncap) {
+                if (hasNonEspMarker(pkt)) {
+                    ikeOffset += NON_ESP_MARKER_LEN;
+                } else {
+                    return false;
+                }
+            }
+        }
+
+        return pkt[ipProtocolOffset] == IPPROTO_UDP
+                && isExpectedSpiAndMsgId(
+                        pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId, expectedResp);
+    }
+
+    private static boolean hasNonEspMarker(byte[] pkt) {
+        ByteBuffer buffer = ByteBuffer.wrap(pkt);
+        int ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
+        if (buffer.remaining() < ikeOffset) return false;
+
+        buffer.get(new byte[ikeOffset]); // Skip IP and UDP header
+        byte[] nonEspMarker = new byte[NON_ESP_MARKER_LEN];
+        if (buffer.remaining() < NON_ESP_MARKER_LEN) return false;
+
+        buffer.get(nonEspMarker);
+        return Arrays.equals(NON_ESP_MARKER, nonEspMarker);
+    }
+
+    private static boolean isExpectedSpiAndMsgId(
+            byte[] pkt,
+            int ikeOffset,
+            long expectedInitIkeSpi,
+            int expectedMsgId,
+            boolean expectedResp) {
+        if (pkt.length <= ikeOffset + IKE_HEADER_LEN) return false;
+
+        ByteBuffer buffer = ByteBuffer.wrap(pkt);
+        buffer.get(new byte[ikeOffset]); // Skip IP, UDP header (and NON_ESP_MARKER)
+
+        // Check message ID.
+        buffer.get(new byte[IKE_MSG_ID_OFFSET]);
+        int msgId = buffer.getInt();
+        return expectedMsgId == msgId;
+
+        // TODO: Check SPI and packet direction
+    }
+
+    private static InetAddress getAddress(byte[] pkt, boolean shouldGetSource) throws Exception {
+        int ipLen = isIpv6(pkt) ? IP6_ADDR_LEN : IP4_ADDR_LEN;
+        int srcIpOffset = isIpv6(pkt) ? IP6_ADDR_OFFSET : IP4_ADDR_OFFSET;
+        int ipOffset = shouldGetSource ? srcIpOffset : srcIpOffset + ipLen;
+
+        ByteBuffer buffer = ByteBuffer.wrap(pkt);
+        buffer.get(new byte[ipOffset]);
+        byte[] ipAddrBytes = new byte[ipLen];
+        buffer.get(ipAddrBytes);
+        return InetAddress.getByAddress(ipAddrBytes);
+    }
+
+    private static int getPort(byte[] pkt, boolean shouldGetSource) {
+        ByteBuffer buffer = ByteBuffer.wrap(pkt);
+        int srcPortOffset = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN;
+        int portOffset = shouldGetSource ? srcPortOffset : srcPortOffset + PORT_LEN;
+
+        buffer.get(new byte[portOffset]);
+        return Short.toUnsignedInt(buffer.getShort());
+    }
+
+    private static byte[] buildIkePacket(
+            InetAddress srcAddr,
+            InetAddress dstAddr,
+            int srcPort,
+            int dstPort,
+            boolean useEncap,
+            byte[] ikePacket)
+            throws Exception {
+        if (useEncap) {
+            ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length);
+            buffer.put(NON_ESP_MARKER);
+            buffer.put(ikePacket);
+            ikePacket = buffer.array();
+        }
+
+        UdpHeader udpPkt = new UdpHeader(srcPort, dstPort, new BytePayload(ikePacket));
+        IpHeader ipPkt = getIpHeader(udpPkt.getProtocolId(), srcAddr, dstAddr, udpPkt);
+        return ipPkt.getPacketBytes();
+    }
+
+    private static IpHeader getIpHeader(
+            int protocol, InetAddress src, InetAddress dst, Payload payload) {
+        if ((src instanceof Inet6Address) != (dst instanceof Inet6Address)) {
+            throw new IllegalArgumentException("Invalid src/dst address combination");
+        }
+
+        if (src instanceof Inet6Address) {
+            return new Ip6Header(protocol, (Inet6Address) src, (Inet6Address) dst, payload);
+        } else {
+            return new Ip4Header(protocol, (Inet4Address) src, (Inet4Address) dst, payload);
+        }
+    }
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
index 71450ea..cb1d826 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
@@ -47,18 +47,18 @@
     private static final String TAG = TunUtils.class.getSimpleName();
 
     private static final int DATA_BUFFER_LEN = 4096;
-    private static final int TIMEOUT = 100;
+    static final int TIMEOUT = 100;
 
-    private static final int IP4_PROTO_OFFSET = 9;
-    private static final int IP6_PROTO_OFFSET = 6;
+    static final int IP4_PROTO_OFFSET = 9;
+    static final int IP6_PROTO_OFFSET = 6;
 
-    private static final int IP4_ADDR_OFFSET = 12;
-    private static final int IP4_ADDR_LEN = 4;
-    private static final int IP6_ADDR_OFFSET = 8;
-    private static final int IP6_ADDR_LEN = 16;
+    static final int IP4_ADDR_OFFSET = 12;
+    static final int IP4_ADDR_LEN = 4;
+    static final int IP6_ADDR_OFFSET = 8;
+    static final int IP6_ADDR_LEN = 16;
 
+    final List<byte[]> mPackets = new ArrayList<>();
     private final ParcelFileDescriptor mTunFd;
-    private final List<byte[]> mPackets = new ArrayList<>();
     private final Thread mReaderThread;
 
     public TunUtils(ParcelFileDescriptor tunFd) {
@@ -107,7 +107,7 @@
         return Arrays.copyOf(inBytes, bytesRead);
     }
 
-    private byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
+    byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
         synchronized (mPackets) {
             for (int i = startIndex; i < mPackets.size(); i++) {
                 byte[] pkt = mPackets.get(i);
@@ -198,7 +198,7 @@
         }
     }
 
-    private static boolean isIpv6(byte[] pkt) {
+    static boolean isIpv6(byte[] pkt) {
         // First nibble shows IP version. 0x60 for IPv6
         return (pkt[0] & (byte) 0xF0) == (byte) 0x60;
     }
diff --git a/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt b/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
new file mode 100644
index 0000000..0816aba
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
@@ -0,0 +1,259 @@
+/*
+ * 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.net.cts
+
+import android.Manifest.permission.CONNECTIVITY_INTERNAL
+import android.Manifest.permission.NETWORK_SETTINGS
+import android.Manifest.permission.READ_DEVICE_CONFIG
+import android.Manifest.permission.WRITE_DEVICE_CONFIG
+import android.content.pm.PackageManager.FEATURE_TELEPHONY
+import android.content.pm.PackageManager.FEATURE_WIFI
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.Uri
+import android.net.cts.util.CtsNetUtils
+import android.net.wifi.WifiManager
+import android.os.Build
+import android.os.ConditionVariable
+import android.platform.test.annotations.AppModeFull
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.text.TextUtils
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.SystemUtil
+import fi.iki.elonen.NanoHTTPD
+import fi.iki.elonen.NanoHTTPD.Response.IStatus
+import fi.iki.elonen.NanoHTTPD.Response.Status
+import junit.framework.AssertionFailedError
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.runner.RunWith
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import kotlin.test.Test
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+private const val TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING = "test_captive_portal_https_url"
+private const val TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING = "test_captive_portal_http_url"
+private const val TEST_URL_EXPIRATION_TIME = "test_url_expiration_time"
+
+private const val TEST_HTTPS_URL_PATH = "https_path"
+private const val TEST_HTTP_URL_PATH = "http_path"
+private const val TEST_PORTAL_URL_PATH = "portal_path"
+
+private const val LOCALHOST_HOSTNAME = "localhost"
+
+// Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
+private const val WIFI_CONNECT_TIMEOUT_MS = 120_000L
+private const val TEST_TIMEOUT_MS = 10_000L
+
+private fun <T> CompletableFuture<T>.assertGet(timeoutMs: Long, message: String): T {
+    try {
+        return get(timeoutMs, TimeUnit.MILLISECONDS)
+    } catch (e: TimeoutException) {
+        throw AssertionFailedError(message)
+    }
+}
+
+@AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
+@RunWith(AndroidJUnit4::class)
+class CaptivePortalTest {
+    private val context: android.content.Context by lazy { getInstrumentation().context }
+    private val wm by lazy { context.getSystemService(WifiManager::class.java) }
+    private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
+    private val pm by lazy { context.packageManager }
+    private val utils by lazy { CtsNetUtils(context) }
+
+    private val server = HttpServer()
+
+    @Before
+    fun setUp() {
+        doAsShell(READ_DEVICE_CONFIG) {
+            // Verify that the test URLs are not normally set on the device, but do not fail if the
+            // test URLs are set to what this test uses (URLs on localhost), in case the test was
+            // interrupted manually and rerun.
+            assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING)
+            assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING)
+        }
+        clearTestUrls()
+        server.start()
+    }
+
+    @After
+    fun tearDown() {
+        clearTestUrls()
+        server.stop()
+    }
+
+    private fun assertEmptyOrLocalhostUrl(urlKey: String) {
+        val url = DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, urlKey)
+        assertTrue(TextUtils.isEmpty(url) || LOCALHOST_HOSTNAME == Uri.parse(url).host,
+                "$urlKey must not be set in production scenarios (current value: $url)")
+    }
+
+    private fun clearTestUrls() {
+        setHttpsUrl(null)
+        setHttpUrl(null)
+        setUrlExpiration(null)
+    }
+
+    @Test
+    fun testCaptivePortalIsNotDefaultNetwork() {
+        assumeTrue(pm.hasSystemFeature(FEATURE_TELEPHONY))
+        assumeTrue(pm.hasSystemFeature(FEATURE_WIFI))
+        utils.connectToWifi()
+        utils.connectToCell()
+
+        // Have network validation use a local server that serves a HTTPS error / HTTP redirect
+        server.addResponse(TEST_PORTAL_URL_PATH, Status.OK,
+                content = "Test captive portal content")
+        server.addResponse(TEST_HTTPS_URL_PATH, Status.INTERNAL_ERROR)
+        server.addResponse(TEST_HTTP_URL_PATH, Status.REDIRECT,
+                locationHeader = server.makeUrl(TEST_PORTAL_URL_PATH))
+        setHttpsUrl(server.makeUrl(TEST_HTTPS_URL_PATH))
+        setHttpUrl(server.makeUrl(TEST_HTTP_URL_PATH))
+        // URL expiration needs to be in the next 10 minutes
+        setUrlExpiration(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))
+
+        // Expect the portal content to be fetched at some point after detecting the portal.
+        // Some implementations may fetch the URL before startCaptivePortalApp is called.
+        val portalContentRequestCv = server.addExpectRequestCv(TEST_PORTAL_URL_PATH)
+
+        // Wait for a captive portal to be detected on the network
+        val wifiNetworkFuture = CompletableFuture<Network>()
+        val wifiCb = object : NetworkCallback() {
+            override fun onCapabilitiesChanged(
+                network: Network,
+                nc: NetworkCapabilities
+            ) {
+                if (nc.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
+                    wifiNetworkFuture.complete(network)
+                }
+            }
+        }
+        cm.requestNetwork(NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(), wifiCb)
+
+        try {
+            reconnectWifi()
+            val network = wifiNetworkFuture.assertGet(WIFI_CONNECT_TIMEOUT_MS,
+                    "Captive portal not detected after ${WIFI_CONNECT_TIMEOUT_MS}ms")
+
+            val wifiDefaultMessage = "Wifi should not be the default network when a captive " +
+                    "portal was detected and another network (mobile data) can provide internet " +
+                    "access."
+            assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
+
+            val startPortalAppPermission =
+                    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) CONNECTIVITY_INTERNAL
+                    else NETWORK_SETTINGS
+            doAsShell(startPortalAppPermission) { cm.startCaptivePortalApp(network) }
+            assertTrue(portalContentRequestCv.block(TEST_TIMEOUT_MS), "The captive portal login " +
+                    "page was still not fetched ${TEST_TIMEOUT_MS}ms after startCaptivePortalApp.")
+
+            assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
+        } finally {
+            cm.unregisterNetworkCallback(wifiCb)
+            server.stop()
+            // disconnectFromCell should be called after connectToCell
+            utils.disconnectFromCell()
+        }
+
+        clearTestUrls()
+        reconnectWifi()
+    }
+
+    private fun setHttpsUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING, url)
+    private fun setHttpUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING, url)
+    private fun setUrlExpiration(timestamp: Long?) = setConfig(TEST_URL_EXPIRATION_TIME,
+            timestamp?.toString())
+
+    private fun setConfig(configKey: String, value: String?) {
+        doAsShell(WRITE_DEVICE_CONFIG) {
+            DeviceConfig.setProperty(
+                    NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
+        }
+    }
+
+    private fun doAsShell(vararg permissions: String, action: () -> Unit) {
+        // Wrap the below call to allow for more kotlin-like syntax
+        SystemUtil.runWithShellPermissionIdentity(action, permissions)
+    }
+
+    private fun reconnectWifi() {
+        doAsShell(NETWORK_SETTINGS) {
+            assertTrue(wm.disconnect())
+            assertTrue(wm.reconnect())
+        }
+    }
+
+    /**
+     * A minimal HTTP server running on localhost (loopback), on a random available port.
+     */
+    private class HttpServer : NanoHTTPD("localhost", 0 /* auto-select the port */) {
+        // Map of URL path -> HTTP response code
+        private val responses = HashMap<String, Response>()
+
+        // Map of path -> CV to open as soon as a request to the path is received
+        private val waitForRequestCv = HashMap<String, ConditionVariable>()
+
+        /**
+         * Create a URL string that, when fetched, will hit this server with the given URL [path].
+         */
+        fun makeUrl(path: String): String {
+            return Uri.Builder()
+                    .scheme("http")
+                    .encodedAuthority("localhost:$listeningPort")
+                    .query(path)
+                    .build()
+                    .toString()
+        }
+
+        fun addResponse(
+            path: String,
+            statusCode: IStatus,
+            locationHeader: String? = null,
+            content: String = ""
+        ) {
+            val response = newFixedLengthResponse(statusCode, "text/plain", content)
+            locationHeader?.let { response.addHeader("Location", it) }
+            responses[path] = response
+        }
+
+        /**
+         * Create a [ConditionVariable] that will open when a request to [path] is received.
+         */
+        fun addExpectRequestCv(path: String): ConditionVariable {
+            return ConditionVariable().apply { waitForRequestCv[path] = this }
+        }
+
+        override fun serve(session: IHTTPSession): Response {
+            waitForRequestCv[session.queryParameterString]?.open()
+            return responses[session.queryParameterString]
+                    // Default response is a 404
+                    ?: super.serve(session)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 1ee08ff..d498ed9 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,13 +16,17 @@
 
 package android.net.cts;
 
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.content.pm.PackageManager.FEATURE_ETHERNET;
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
-import static android.content.pm.PackageManager.FEATURE_WIFI;
 import static android.content.pm.PackageManager.FEATURE_USB_HOST;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
 import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
@@ -36,6 +40,16 @@
 import static android.system.OsConstants.AF_UNSPEC;
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.annotation.NonNull;
 import android.app.Instrumentation;
@@ -45,6 +59,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
@@ -59,10 +74,12 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
+import android.net.NetworkUtils;
 import android.net.SocketKeepalive;
 import android.net.cts.util.CtsNetUtils;
 import android.net.util.KeepaliveUtils;
 import android.net.wifi.WifiManager;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Looper;
 import android.os.MessageQueue;
@@ -71,15 +88,22 @@
 import android.os.VintfRuntimeInfo;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
-import android.test.AndroidTestCase;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.ArrayUtils;
 
 import libcore.io.Streams;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
@@ -105,7 +129,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class ConnectivityManagerTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityManagerTest {
 
     private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
 
@@ -117,7 +142,10 @@
     private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500;
     private static final int MAX_KEEPALIVE_RETRY_COUNT = 3;
     private static final int MIN_KEEPALIVE_INTERVAL = 10;
-    private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
+
+    // Changing meteredness on wifi involves reconnecting, which can take several seconds (involves
+    // re-associating, DHCP...)
+    private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 30_000;
     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
     private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
     // device could have only one interface: data, wifi.
@@ -141,22 +169,19 @@
     private PackageManager mPackageManager;
     private final HashMap<Integer, NetworkConfig> mNetworks =
             new HashMap<Integer, NetworkConfig>();
-    boolean mWifiConnectAttempted;
+    boolean mWifiWasDisabled;
     private UiAutomation mUiAutomation;
     private CtsNetUtils mCtsNetUtils;
-    private boolean mShellPermissionIdentityAdopted;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        Looper.prepare();
-        mContext = getContext();
+    @Before
+    public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getContext();
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mPackageManager = mContext.getPackageManager();
         mCtsNetUtils = new CtsNetUtils(mContext);
-        mWifiConnectAttempted = false;
+        mWifiWasDisabled = false;
 
         // Get com.android.internal.R.array.networkAttributes
         int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
@@ -173,20 +198,17 @@
             } catch (Exception e) {}
         }
         mUiAutomation = mInstrumentation.getUiAutomation();
-        mShellPermissionIdentityAdopted = false;
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         // Return WiFi to its original disabled state after tests that explicitly connect.
-        if (mWifiConnectAttempted) {
+        if (mWifiWasDisabled) {
             mCtsNetUtils.disconnectFromWifi(null);
         }
         if (mCtsNetUtils.cellConnectAttempted()) {
             mCtsNetUtils.disconnectFromCell();
         }
-        dropShellPermissionIdentity();
-        super.tearDown();
     }
 
     /**
@@ -195,13 +217,12 @@
      * automatically in tearDown().
      */
     private Network ensureWifiConnected() {
-        if (mWifiManager.isWifiEnabled()) {
-            return mCtsNetUtils.getWifiNetwork();
-        }
-        mWifiConnectAttempted = true;
+        mWifiWasDisabled = !mWifiManager.isWifiEnabled();
+        // Even if wifi is enabled, the network may not be connected or ready yet
         return mCtsNetUtils.connectToWifi();
     }
 
+    @Test
     public void testIsNetworkTypeValid() {
         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
@@ -231,12 +252,14 @@
 
     }
 
+    @Test
     public void testSetNetworkPreference() {
         // getNetworkPreference() and setNetworkPreference() are both deprecated so they do
         // not preform any action.  Verify they are at least still callable.
         mCm.setNetworkPreference(mCm.getNetworkPreference());
     }
 
+    @Test
     public void testGetActiveNetworkInfo() {
         NetworkInfo ni = mCm.getActiveNetworkInfo();
 
@@ -245,6 +268,7 @@
         assertTrue(ni.getState() == State.CONNECTED);
     }
 
+    @Test
     public void testGetActiveNetwork() {
         Network network = mCm.getActiveNetwork();
         assertNotNull("You must have an active network connection to complete CTS", network);
@@ -257,6 +281,7 @@
         assertTrue(ni.getState() == State.CONNECTED);
     }
 
+    @Test
     public void testGetNetworkInfo() {
         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
             if (shouldBeSupported(type)) {
@@ -275,6 +300,7 @@
         }
     }
 
+    @Test
     public void testGetAllNetworkInfo() {
         NetworkInfo[] ni = mCm.getAllNetworkInfo();
         assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
@@ -298,6 +324,7 @@
      * and that they are made from different IP addresses.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testOpenConnection() throws Exception {
         boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
@@ -377,6 +404,7 @@
         } catch (UnsupportedOperationException expected) {}
     }
 
+    @Test
     public void testStartUsingNetworkFeature() {
 
         final String invalidateFeature = "invalidateFeature";
@@ -406,6 +434,7 @@
                (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
     }
 
+    @Test
     public void testIsNetworkSupported() {
         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
             boolean supported = mCm.isNetworkSupported(type);
@@ -417,12 +446,14 @@
         }
     }
 
+    @Test
     public void testRequestRouteToHost() {
         for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
             assertRequestRouteToHostUnsupported(type, HOST_ADDRESS);
         }
     }
 
+    @Test
     public void testTest() {
         mCm.getBackgroundDataSetting();
     }
@@ -443,6 +474,7 @@
      * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testRegisterNetworkCallback() {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -484,6 +516,7 @@
      * of a {@code NetworkCallback}.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testRegisterNetworkCallback_withPendingIntent() {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -529,6 +562,7 @@
      * see if we get a callback for an INTERNET request.
      */
     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
+    @Test
     public void testRequestNetworkCallback() {
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.requestNetwork(new NetworkRequest.Builder()
@@ -552,6 +586,7 @@
      * fail. Use WIFI and switch Wi-Fi off.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testRequestNetworkCallback_onUnavailable() {
         final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
         if (previousWifiEnabledState) {
@@ -589,6 +624,7 @@
 
     /** Verify restricted networks cannot be requested. */
     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
+    @Test
     public void testRestrictedNetworks() {
         // Verify we can request unrestricted networks:
         NetworkRequest request = new NetworkRequest.Builder()
@@ -710,6 +746,7 @@
      * for metered and unmetered networks.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testGetMultipathPreference() throws Exception {
         final ContentResolver resolver = mContext.getContentResolver();
         ensureWifiConnected();
@@ -878,18 +915,6 @@
                 keepalivesPerTransport, nc);
     }
 
-    private void adoptShellPermissionIdentity() {
-        mUiAutomation.adoptShellPermissionIdentity();
-        mShellPermissionIdentityAdopted = true;
-    }
-
-    private void dropShellPermissionIdentity() {
-        if (mShellPermissionIdentityAdopted) {
-            mUiAutomation.dropShellPermissionIdentity();
-            mShellPermissionIdentityAdopted = false;
-        }
-    }
-
     private static boolean isTcpKeepaliveSupportedByKernel() {
         final String kVersionString = VintfRuntimeInfo.getKernelVersion();
         return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
@@ -924,6 +949,7 @@
      * Verifies that version string compare logic returns expected result for various cases.
      * Note that only major and minor number are compared.
      */
+    @Test
     public void testMajorMinorVersionCompare() {
         assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
         assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
@@ -943,6 +969,7 @@
      * keepalives is set to 0.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testKeepaliveWifiUnsupported() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testKeepaliveUnsupported cannot execute unless device"
@@ -952,32 +979,36 @@
 
         final Network network = ensureWifiConnected();
         if (getSupportedKeepalivesForNet(network) != 0) return;
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
-        adoptShellPermissionIdentity();
-
-        assertEquals(0, createConcurrentSocketKeepalives(network, 1, 0));
-        assertEquals(0, createConcurrentSocketKeepalives(network, 0, 1));
-
-        dropShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> {
+            assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 1, 0));
+            assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
+        });
     }
 
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testCreateTcpKeepalive() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi");
             return;
         }
 
-        adoptShellPermissionIdentity();
-
         final Network network = ensureWifiConnected();
         if (getSupportedKeepalivesForNet(network) == 0) return;
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
+
         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
         // NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive
         // needs to be supported except if the kernel doesn't support it.
         if (!isTcpKeepaliveSupportedByKernel()) {
             // Sanity check to ensure the callback result is expected.
-            assertEquals(0, createConcurrentSocketKeepalives(network, 0, 1));
+            runWithShellPermissionIdentity(() -> {
+                assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
+            });
             Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel "
                     + VintfRuntimeInfo.getKernelVersion());
             return;
@@ -991,6 +1022,8 @@
             // Should able to start keep alive offload when socket is idle.
             final Executor executor = mContext.getMainExecutor();
             final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+
+            mUiAutomation.adoptShellPermissionIdentity();
             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
                 sk.start(MIN_KEEPALIVE_INTERVAL);
                 callback.expectStarted();
@@ -1012,6 +1045,8 @@
                 // Stop.
                 sk.stop();
                 callback.expectStopped();
+            } finally {
+                mUiAutomation.dropShellPermissionIdentity();
             }
 
             // Ensure socket is still connected.
@@ -1040,9 +1075,12 @@
 
             // Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue
             // that has not been read.
+            mUiAutomation.adoptShellPermissionIdentity();
             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
                 sk.start(MIN_KEEPALIVE_INTERVAL);
                 callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
+            } finally {
+                mUiAutomation.dropShellPermissionIdentity();
             }
         }
     }
@@ -1087,7 +1125,7 @@
     }
 
     private @NonNull ArrayList<SocketKeepalive> createConcurrentNattSocketKeepalives(
-            @NonNull Network network, int requestCount,
+            @NonNull Network network, @NonNull InetAddress srcAddr, int requestCount,
             @NonNull TestSocketKeepaliveCallback callback)  throws Exception {
 
         final Executor executor = mContext.getMainExecutor();
@@ -1095,7 +1133,6 @@
         // Initialize a real NaT-T socket.
         final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
         final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket();
-        final InetAddress srcAddr = getFirstV4Address(network);
         final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET);
         assertNotNull(srcAddr);
         assertNotNull(dstAddr);
@@ -1136,11 +1173,12 @@
      * @return the total number of keepalives created.
      */
     private int createConcurrentSocketKeepalives(
-            @NonNull Network network, int nattCount, int tcpCount) throws Exception {
+            @NonNull Network network, @NonNull InetAddress srcAddr, int nattCount, int tcpCount)
+            throws Exception {
         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
         final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
 
-        kalist.addAll(createConcurrentNattSocketKeepalives(network, nattCount, callback));
+        kalist.addAll(createConcurrentNattSocketKeepalives(network, srcAddr, nattCount, callback));
         kalist.addAll(createConcurrentTcpSocketKeepalives(network, tcpCount, callback));
 
         final int ret = kalist.size();
@@ -1160,6 +1198,7 @@
      * get leaked after iterations.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testSocketKeepaliveLimitWifi() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device"
@@ -1172,33 +1211,39 @@
         if (supported == 0) {
             return;
         }
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
-        adoptShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> {
+            // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
+            assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
 
-        // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
-        assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
-
-        // Verifies that Nat-T keepalives can be established.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported + 1, 0));
-        // Verifies that keepalives don't get leaked in second round.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported, 0));
+            // Verifies that Nat-T keepalives can be established.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
+                    supported + 1, 0));
+            // Verifies that keepalives don't get leaked in second round.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
+                    0));
+        });
 
         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
         // NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel.
-        if (isTcpKeepaliveSupportedByKernel()) {
-            assertEquals(supported, createConcurrentSocketKeepalives(network, 0, supported + 1));
+        if (!isTcpKeepaliveSupportedByKernel()) return;
+
+        runWithShellPermissionIdentity(() -> {
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
+                    supported + 1));
 
             // Verifies that different types can be established at the same time.
-            assertEquals(supported, createConcurrentSocketKeepalives(network,
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
                     supported / 2, supported - supported / 2));
 
             // Verifies that keepalives don't get leaked in second round.
-            assertEquals(supported, createConcurrentSocketKeepalives(network, 0, supported));
-            assertEquals(supported, createConcurrentSocketKeepalives(network,
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
+                    supported));
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
                     supported / 2, supported - supported / 2));
-        }
-
-        dropShellPermissionIdentity();
+        });
     }
 
     /**
@@ -1206,6 +1251,7 @@
      * don't get leaked after iterations.
      */
     @AppModeFull(reason = "Cannot request network in instant app mode")
+    @Test
     public void testSocketKeepaliveLimitTelephony() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
             Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
@@ -1222,18 +1268,19 @@
 
         final Network network = mCtsNetUtils.connectToCell();
         final int supported = getSupportedKeepalivesForNet(network);
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
-        adoptShellPermissionIdentity();
-
-        // Verifies that the supported keepalive slots meet minimum requirement.
-        assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
-
-        // Verifies that Nat-T keepalives can be established.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported + 1, 0));
-        // Verifies that keepalives don't get leaked in second round.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported, 0));
-
-        dropShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> {
+            // Verifies that the supported keepalive slots meet minimum requirement.
+            assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
+            // Verifies that Nat-T keepalives can be established.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
+                    supported + 1, 0));
+            // Verifies that keepalives don't get leaked in second round.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
+                    0));
+        });
     }
 
     private int getIntResourceForName(@NonNull String resName) {
@@ -1246,6 +1293,7 @@
      * Verifies that the keepalive slots are limited as customized for unprivileged requests.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testSocketKeepaliveUnprivileged() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device"
@@ -1258,6 +1306,8 @@
         if (supported == 0) {
             return;
         }
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
         // Resource ID might be shifted on devices that compiled with different symbols.
         // Thus, resolve ID at runtime is needed.
@@ -1273,11 +1323,46 @@
         final int expectedUnprivileged =
                 Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots);
         assertEquals(expectedUnprivileged,
-                createConcurrentSocketKeepalives(network, supported + 1, 0));
+                createConcurrentSocketKeepalives(network, srcAddr, supported + 1, 0));
     }
 
     private static void assertGreaterOrEqual(long greater, long lesser) {
         assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
                 greater >= lesser);
     }
+
+    /**
+     * Verifies that apps are not allowed to access restricted networks even if they declare the
+     * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
+     * See. b/144679405.
+     */
+    @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
+    public void testRestrictedNetworkPermission() throws Exception {
+        // Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
+        final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
+                GET_PERMISSIONS);
+        final int index = ArrayUtils.indexOf(
+                app.requestedPermissions, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+        assertTrue(index >= 0);
+        assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
+
+        // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
+        // not have netd system permission to call this function.
+        final Network wifiNetwork = ensureWifiConnected();
+        assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
+
+        // Ensure that this package cannot bind to any restricted network that's currently
+        // connected.
+        Network[] networks = mCm.getAllNetworks();
+        for (Network network : networks) {
+            NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+            if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+                try {
+                    network.bindSocket(new Socket());
+                    fail("Bind to restricted network " + network + " unexpectedly succeeded");
+                } catch (IOException expected) {}
+            }
+        }
+    }
 }
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index 1cc49f9..28753ff 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -86,7 +86,6 @@
     static final int CANCEL_RETRY_TIMES = 5;
     static final int QUERY_TIMES = 10;
     static final int NXDOMAIN = 3;
-    static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
 
     private ContentResolver mCR;
     private ConnectivityManager mCM;
@@ -107,32 +106,15 @@
         mExecutorInline = (Runnable r) -> r.run();
         mCR = getContext().getContentResolver();
         mCtsNetUtils = new CtsNetUtils(getContext());
-        storePrivateDnsSetting();
+        mCtsNetUtils.storePrivateDnsSetting();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        restorePrivateDnsSetting();
+        mCtsNetUtils.restorePrivateDnsSetting();
         super.tearDown();
     }
 
-    private void storePrivateDnsSetting() {
-        // Store private DNS setting
-        mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
-        mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
-    }
-
-    private void restorePrivateDnsSetting() throws InterruptedException {
-        // restore private DNS setting
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
-        if ("hostname".equals(mOldMode)) {
-            Settings.Global.putString(
-                mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
-            mCtsNetUtils.awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
-                    mCM.getActiveNetwork(), mOldDnsSpecifier, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
-        }
-    }
-
     private static String byteArrayToHexString(byte[] bytes) {
         char[] hexChars = new char[bytes.length * 2];
         for (int i = 0; i < bytes.length; ++i) {
@@ -416,16 +398,13 @@
         final String msg = "RawQuery " + TEST_NX_DOMAIN + " with private DNS";
         // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
         // b/144521720
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
-        Settings.Global.putString(mCR,
-                Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+        mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
         for (Network network :  getTestableNetworks()) {
             final Network networkForPrivateDns =
                     (network != null) ? network : mCM.getActiveNetwork();
             assertNotNull("Can't find network to await private DNS on", networkForPrivateDns);
             mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
-                    networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER,
-                    PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
+                    networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER, true);
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
             mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
                     executor, null, callback);
@@ -688,9 +667,7 @@
         final Network[] testNetworks = getTestableNetworks();
 
         // Set an invalid private DNS server
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
-        Settings.Global.putString(mCR,
-                Settings.Global.PRIVATE_DNS_SPECIFIER, INVALID_PRIVATE_DNS_SERVER);
+        mCtsNetUtils.setPrivateDnsStrictMode(INVALID_PRIVATE_DNS_SERVER);
         final String msg = "Test PrivateDnsBypass " + TEST_DOMAIN;
         for (Network network : testNetworks) {
             // This test cannot be ran with null network because we need to explicitly pass a
@@ -699,7 +676,7 @@
 
             // wait for private DNS setting propagating
             mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
-                    network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, false);
+                    network, INVALID_PRIVATE_DNS_SERVER, false);
 
             final CountDownLatch latch = new CountDownLatch(1);
             final DnsResolver.Callback<List<InetAddress>> errorCallback =
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
index f123187..985e313 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -41,7 +41,6 @@
 
     private static final String TAG = "MultinetworkNativeApiTest";
     static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
-    static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
 
     /**
      * @return 0 on success
@@ -69,7 +68,7 @@
         mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         mCR = getContext().getContentResolver();
         mCtsNetUtils = new CtsNetUtils(getContext());
-        storePrivateDnsSetting();
+        mCtsNetUtils.storePrivateDnsSetting();
     }
 
     @Override
@@ -77,18 +76,6 @@
         super.tearDown();
     }
 
-    private void storePrivateDnsSetting() {
-        // Store private DNS setting
-        mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
-        mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
-    }
-
-    private void restorePrivateDnsSetting() {
-        // restore private DNS setting
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
-    }
-
     private Network[] getTestableNetworks() {
         final ArrayList<Network> testableNetworks = new ArrayList<Network>();
         for (Network network : mCM.getAllNetworks()) {
@@ -239,17 +226,15 @@
         // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
         // b/144521720
         try {
-            Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
-            Settings.Global.putString(mCR,
-                    Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+            mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
             for (Network network : getTestableNetworks()) {
               // Wait for private DNS setting to propagate.
               mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
-                        network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
+                        network, GOOGLE_PRIVATE_DNS_SERVER, true);
               runResNnxDomainCheck(network.getNetworkHandle());
             }
         } finally {
-            restorePrivateDnsSetting();
+            mCtsNetUtils.restorePrivateDnsSetting();
         }
     }
 }
diff --git a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
index 5e92b41..d118c8a 100644
--- a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -33,11 +34,12 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
-import android.net.TelephonyNetworkSpecifier;
+import android.net.UidRange;
 import android.net.wifi.WifiNetworkSpecifier;
 import android.os.Build;
-import android.os.Process;
 import android.os.PatternMatcher;
+import android.os.Process;
+import android.util.ArraySet;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -59,6 +61,20 @@
     private static final String TEST_PACKAGE_NAME = "test.package.name";
     private static final MacAddress ARBITRARY_ADDRESS = MacAddress.fromString("3:5:8:12:9:2");
 
+    private class LocalNetworkSpecifier extends NetworkSpecifier {
+        private final int mId;
+
+        LocalNetworkSpecifier(int id) {
+            mId = id;
+        }
+
+        @Override
+        public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+            return other instanceof LocalNetworkSpecifier
+                && mId == ((LocalNetworkSpecifier) other).mId;
+        }
+    }
+
     @Test
     public void testCapabilities() {
         assertTrue(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build()
@@ -71,6 +87,16 @@
         verifyNoCapabilities(nr);
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testTemporarilyNotMeteredCapability() {
+        assertTrue(new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+                .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        assertFalse(new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+                .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+    }
+
     private void verifyNoCapabilities(NetworkRequest nr) {
         // NetworkCapabilities.mNetworkCapabilities is defined as type long
         final int MAX_POSSIBLE_CAPABILITY = Long.SIZE;
@@ -129,54 +155,108 @@
     @Test
     @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCanBeSatisfiedBy() {
-        final TelephonyNetworkSpecifier specifier1 = new TelephonyNetworkSpecifier.Builder()
-                .setSubscriptionId(1234 /* subId */)
-                .build();
-        final TelephonyNetworkSpecifier specifier2 = new TelephonyNetworkSpecifier.Builder()
-                .setSubscriptionId(5678 /* subId */)
-                .build();
-        final NetworkCapabilities cap = new NetworkCapabilities()
+        final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
+        final LocalNetworkSpecifier specifier2 = new LocalNetworkSpecifier(5678 /* id */);
+
+        final NetworkCapabilities capCellularMmsInternet = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_MMS)
                 .addCapability(NET_CAPABILITY_INTERNET);
-        final NetworkCapabilities capDualTransport = new NetworkCapabilities(cap)
-                .addTransportType(TRANSPORT_VPN);
-        final NetworkCapabilities capWithSpecifier1 =
-                new NetworkCapabilities(cap).setNetworkSpecifier(specifier1);
-        final NetworkCapabilities capDiffTransportWithSpecifier1 = new NetworkCapabilities()
+        final NetworkCapabilities capCellularVpnMmsInternet =
+                new NetworkCapabilities(capCellularMmsInternet).addTransportType(TRANSPORT_VPN);
+        final NetworkCapabilities capCellularMmsInternetSpecifier1 =
+                new NetworkCapabilities(capCellularMmsInternet).setNetworkSpecifier(specifier1);
+        final NetworkCapabilities capVpnInternetSpecifier1 = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addTransportType(TRANSPORT_VPN)
                 .setNetworkSpecifier(specifier1);
+        final NetworkCapabilities capCellularMmsInternetMatchallspecifier =
+                new NetworkCapabilities(capCellularMmsInternet)
+                    .setNetworkSpecifier(new MatchAllNetworkSpecifier());
+        final NetworkCapabilities capCellularMmsInternetSpecifier2 =
+                new NetworkCapabilities(capCellularMmsInternet).setNetworkSpecifier(specifier2);
 
-        final NetworkRequest requestWithSpecifier1 = new NetworkRequest.Builder()
+        final NetworkRequest requestCellularInternetSpecifier1 = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .setNetworkSpecifier(specifier1)
                 .build();
-        assertFalse(requestWithSpecifier1.canBeSatisfiedBy(null));
-        assertFalse(requestWithSpecifier1.canBeSatisfiedBy(new NetworkCapabilities()));
-        assertTrue(requestWithSpecifier1.canBeSatisfiedBy(new NetworkCapabilities(cap)
-                .setNetworkSpecifier(new MatchAllNetworkSpecifier())));
-        assertTrue(requestWithSpecifier1.canBeSatisfiedBy(cap));
-        assertTrue(requestWithSpecifier1.canBeSatisfiedBy(capWithSpecifier1));
-        assertTrue(requestWithSpecifier1.canBeSatisfiedBy(capDualTransport));
-        assertFalse(requestWithSpecifier1.canBeSatisfiedBy(
-                new NetworkCapabilities(cap).setNetworkSpecifier(specifier2)));
+        assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(null));
+        assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(new NetworkCapabilities()));
+        assertTrue(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+                capCellularMmsInternetMatchallspecifier));
+        assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(capCellularMmsInternet));
+        assertTrue(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+                capCellularMmsInternetSpecifier1));
+        assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(capCellularVpnMmsInternet));
+        assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+                capCellularMmsInternetSpecifier2));
 
-        final NetworkRequest request = new NetworkRequest.Builder()
+        final NetworkRequest requestCellularInternet = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .build();
-        assertTrue(request.canBeSatisfiedBy(cap));
-        assertTrue(request.canBeSatisfiedBy(capWithSpecifier1));
-        assertTrue(request.canBeSatisfiedBy(
-                new NetworkCapabilities(cap).setNetworkSpecifier(specifier2)));
-        assertFalse(request.canBeSatisfiedBy(capDiffTransportWithSpecifier1));
-        assertTrue(request.canBeSatisfiedBy(capDualTransport));
+        assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternet));
+        assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier1));
+        assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier2));
+        assertFalse(requestCellularInternet.canBeSatisfiedBy(capVpnInternetSpecifier1));
+        assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularVpnMmsInternet));
+    }
 
-        assertEquals(requestWithSpecifier1.canBeSatisfiedBy(capWithSpecifier1),
-                new NetworkCapabilities(capWithSpecifier1)
-                    .satisfiedByNetworkCapabilities(capWithSpecifier1));
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testInvariantInCanBeSatisfiedBy() {
+        // Test invariant that result of NetworkRequest.canBeSatisfiedBy() should be the same with
+        // NetworkCapabilities.satisfiedByNetworkCapabilities().
+        final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
+        final int uid = Process.myUid();
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        final NetworkRequest requestCombination = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .setLinkUpstreamBandwidthKbps(1000)
+                .setNetworkSpecifier(specifier1)
+                .setSignalStrength(-123)
+                .setUids(ranges).build();
+        final NetworkCapabilities capCell = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        assertCorrectlySatisfies(false, requestCombination, capCell);
+
+        final NetworkCapabilities capCellInternet = new NetworkCapabilities.Builder(capCell)
+                .addCapability(NET_CAPABILITY_INTERNET).build();
+        assertCorrectlySatisfies(false, requestCombination, capCellInternet);
+
+        final NetworkCapabilities capCellInternetBW =
+                new NetworkCapabilities.Builder(capCellInternet)
+                    .setLinkUpstreamBandwidthKbps(1024).build();
+        assertCorrectlySatisfies(false, requestCombination, capCellInternetBW);
+
+        final NetworkCapabilities capCellInternetBWSpecifier1 =
+                new NetworkCapabilities.Builder(capCellInternetBW)
+                    .setNetworkSpecifier(specifier1).build();
+        assertCorrectlySatisfies(false, requestCombination, capCellInternetBWSpecifier1);
+
+        final NetworkCapabilities capCellInternetBWSpecifier1Signal =
+                new NetworkCapabilities.Builder(capCellInternetBWSpecifier1)
+                    .setSignalStrength(-123).build();
+        assertCorrectlySatisfies(true, requestCombination,
+                capCellInternetBWSpecifier1Signal);
+
+        final NetworkCapabilities capCellInternetBWSpecifier1SignalUid =
+                new NetworkCapabilities.Builder(capCellInternetBWSpecifier1Signal)
+                    .setOwnerUid(uid)
+                    .setAdministratorUids(new int [] {uid}).build();
+        assertCorrectlySatisfies(true, requestCombination,
+                capCellInternetBWSpecifier1SignalUid);
+    }
+
+    private void assertCorrectlySatisfies(boolean expect, NetworkRequest request,
+            NetworkCapabilities nc) {
+        assertEquals(expect, request.canBeSatisfiedBy(nc));
+        assertEquals(
+                request.canBeSatisfiedBy(nc),
+                request.networkCapabilities.satisfiedByNetworkCapabilities(nc));
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
index 6214f89..f39b184 100644
--- a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -16,6 +16,8 @@
 
 package android.net.cts.util;
 
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
@@ -27,6 +29,7 @@
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
@@ -38,6 +41,7 @@
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
 import android.net.wifi.WifiManager;
+import android.provider.Settings;
 import android.system.Os;
 import android.system.OsConstants;
 import android.util.Log;
@@ -58,6 +62,7 @@
     private static final int SOCKET_TIMEOUT_MS = 2000;
     private static final int PRIVATE_DNS_PROBE_MS = 1_000;
 
+    public static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
     public static final int HTTP_PORT = 80;
     public static final String TEST_HOST = "connectivitycheck.gstatic.com";
     public static final String HTTP_REQUEST =
@@ -68,15 +73,19 @@
     public static final String NETWORK_CALLBACK_ACTION =
             "ConnectivityManagerTest.NetworkCallbackAction";
 
-    private Context mContext;
-    private ConnectivityManager mCm;
-    private WifiManager mWifiManager;
+    private final Context mContext;
+    private final ConnectivityManager mCm;
+    private final ContentResolver mCR;
+    private final WifiManager mWifiManager;
     private TestNetworkCallback mCellNetworkCallback;
+    private String mOldPrivateDnsMode;
+    private String mOldPrivateDnsSpecifier;
 
     public CtsNetUtils(Context context) {
         mContext = context;
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mCR = context.getContentResolver();
     }
 
     // Toggle WiFi twice, leaving it in the state it started in
@@ -107,6 +116,8 @@
         boolean connected = false;
         try {
             SystemUtil.runShellCommand("svc wifi enable");
+            SystemUtil.runWithShellPermissionIdentity(() -> mWifiManager.reconnect(),
+                    NETWORK_SETTINGS);
             // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
             wifiNetwork = callback.waitForAvailable();
             assertNotNull(wifiNetwork);
@@ -246,9 +257,51 @@
         return s;
     }
 
+    public void storePrivateDnsSetting() {
+        // Store private DNS setting
+        mOldPrivateDnsMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
+        mOldPrivateDnsSpecifier = Settings.Global.getString(mCR,
+                Settings.Global.PRIVATE_DNS_SPECIFIER);
+        // It's possible that there is no private DNS default value in Settings.
+        // Give it a proper default mode which is opportunistic mode.
+        if (mOldPrivateDnsMode == null) {
+            mOldPrivateDnsSpecifier = "";
+            mOldPrivateDnsMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+            Settings.Global.putString(mCR,
+                    Settings.Global.PRIVATE_DNS_SPECIFIER, mOldPrivateDnsSpecifier);
+            Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldPrivateDnsMode);
+        }
+    }
+
+    public void restorePrivateDnsSetting() throws InterruptedException {
+        if (mOldPrivateDnsMode == null || mOldPrivateDnsSpecifier == null) {
+            return;
+        }
+        // restore private DNS setting
+        if ("hostname".equals(mOldPrivateDnsMode)) {
+            setPrivateDnsStrictMode(mOldPrivateDnsSpecifier);
+            awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
+                    mCm.getActiveNetwork(),
+                    mOldPrivateDnsSpecifier, true);
+        } else {
+            Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldPrivateDnsMode);
+        }
+    }
+
+    public void setPrivateDnsStrictMode(String server) {
+        // To reduce flake rate, set PRIVATE_DNS_SPECIFIER before PRIVATE_DNS_MODE. This ensures
+        // that if the previous private DNS mode was not "hostname", the system only sees one
+        // EVENT_PRIVATE_DNS_SETTINGS_CHANGED event instead of two.
+        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, server);
+        final String mode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
+        // If current private DNS mode is "hostname", we only need to set PRIVATE_DNS_SPECIFIER.
+        if (!"hostname".equals(mode)) {
+            Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
+        }
+    }
+
     public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
-            @NonNull String server, int timeoutMs,
-            boolean requiresValidatedServers) throws InterruptedException {
+            @NonNull String server, boolean requiresValidatedServers) throws InterruptedException {
         CountDownLatch latch = new CountDownLatch(1);
         NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
         NetworkCallback callback = new NetworkCallback() {
@@ -263,7 +316,7 @@
             }
         };
         mCm.registerNetworkCallback(request, callback);
-        assertTrue(msg, latch.await(timeoutMs, TimeUnit.MILLISECONDS));
+        assertTrue(msg, latch.await(PRIVATE_DNS_SETTING_TIMEOUT_MS, TimeUnit.MILLISECONDS));
         mCm.unregisterNetworkCallback(callback);
         // Wait some time for NetworkMonitor's private DNS probe to complete. If we do not do
         // this, then the test could complete before the NetworkMonitor private DNS probe
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
index 37f90e9..ef2a0c1 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -113,15 +113,6 @@
 
         String extensions = mActivity.getExtensionsString();
 
-        final String es30RequiredList[] = {
-            "OES_EGL_image_external_essl3"
-        };
-
-        for (int i = 0; i < es30RequiredList.length; ++i) {
-            assertTrue("OpenGL ES version 3.0+ is missing extension " + es30RequiredList[i],
-                    hasExtension(extensions, es30RequiredList[i]));
-        }
-
         if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) < 1)
             return;
 
diff --git a/tests/tests/os/assets/platform_versions.txt b/tests/tests/os/assets/platform_versions.txt
index 331bae0..3762249 100644
--- a/tests/tests/os/assets/platform_versions.txt
+++ b/tests/tests/os/assets/platform_versions.txt
@@ -1 +1 @@
-R
+S
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 00cb97d..ca0f84a 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -223,6 +223,8 @@
         Pattern.compile("^([0-9A-Za-z._-]+)$");
     private static final Pattern SERIAL_NUMBER_PATTERN =
         Pattern.compile("^([0-9A-Za-z]{6,20})$");
+    private static final Pattern SKU_PATTERN =
+        Pattern.compile("^([0-9A-Za-z.,_-]+)$");
     private static final Pattern TAGS_PATTERN =
         Pattern.compile("^([0-9A-Za-z.,_-]+)$");
     private static final Pattern TYPE_PATTERN =
@@ -254,6 +256,8 @@
 
         assertTrue(SERIAL_NUMBER_PATTERN.matcher(Build.SERIAL).matches());
 
+        assertTrue(SKU_PATTERN.matcher(Build.SKU).matches());
+
         assertTrue(TAGS_PATTERN.matcher(Build.TAGS).matches());
 
         // No format requirements stated in CDD for Build.TIME
diff --git a/tests/tests/provider/src/android/provider/cts/SearchIndexableResourceTest.java b/tests/tests/provider/src/android/provider/cts/SearchIndexableResourceTest.java
new file mode 100644
index 0000000..7a7a954
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/SearchIndexableResourceTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.provider.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.provider.SearchIndexableResource;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SearchIndexableResourceTest {
+
+    private static final int RANK = 3;
+    private static final int XML_RES_ID = 4;
+    private static final String CLASS_NAME = "testClassName";
+    private static final int ICON_RES_ID = 5;
+
+    @Test
+    public void testConstructor() {
+        SearchIndexableResource resource = new SearchIndexableResource(RANK, XML_RES_ID, CLASS_NAME,
+                ICON_RES_ID);
+
+        assertEquals(RANK, resource.rank);
+        assertEquals(XML_RES_ID, resource.xmlResId);
+        assertEquals(CLASS_NAME, resource.className);
+        assertEquals(ICON_RES_ID, resource.iconResId);
+    }
+}
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
index 93fc4d9..f798bb8 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
@@ -86,6 +86,7 @@
             "/sdcard/WindowInsetsBehaviorTests";
     private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
     private static final String ARGUMENT_KEY_FORCE_ENABLE = "force_enable_gesture_navigation";
+    private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
     private static final int STEPS = 10;
 
     // The minimum value of the system gesture exclusion limit is 200 dp. The value here should be
@@ -703,6 +704,8 @@
     public void swipeOutsideLimit_systemUiVisible_allEventsCanceled() throws Throwable {
         assumeTrue(hasSystemGestureFeature());
 
+        assumeGestureNavigationMode();
+
         final int swipeCount = 1;
         final boolean insideLimit = false;
         testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_VISIBLE);
@@ -779,6 +782,14 @@
         assumeTrue("Gesture navigation required.", insets[0].left > 0);
     }
 
+    private void assumeGestureNavigationMode() {
+        // TODO: b/153032202 consider the CTS on GSI case.
+        Resources res = mTargetContext.getResources();
+        int naviMode = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer", "android");
+
+        assumeTrue("Gesture navigation required", naviMode == 2);
+    }
+
     /**
      * Set system UI visibility and wait for it is applied by the system.
      *
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 5505c2c7..12c0fe2 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -505,11 +505,15 @@
         placeAndVerifyCall(null);
     }
 
+    void placeAndVerifyCallByRedirection(boolean wasCancelled) {
+        placeAndVerifyCallByRedirection(null, wasCancelled);
+    }
+
     /**
      *  Puts Telecom in a state where there is an active call provided by the
      *  {@link CtsConnectionService} which can be tested.
      */
-    void placeAndVerifyCallByRedirection(boolean wasCancelled) {
+    void placeAndVerifyCallByRedirection(Bundle extras, boolean wasCancelled) {
         int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount();
         int currentConnections = getNumberOfConnections();
         // We expect a new connection if it wasn't cancelled.
@@ -517,7 +521,7 @@
             currentConnections++;
             currentCallCount++;
         }
-        placeAndVerifyCall(null, VideoProfile.STATE_AUDIO_ONLY, currentConnections,
+        placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY, currentConnections,
                 currentCallCount);
         // Ensure the new outgoing call broadcast fired for the outgoing call.
         assertOutgoingCallBroadcastReceived(true);
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
index 2fcedf5..dd0a5cf 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
@@ -27,6 +27,7 @@
 import android.content.ServiceConnection;
 
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -58,7 +59,18 @@
 
     private static final Uri SAMPLE_HANDLE = Uri.fromParts(PhoneAccount.SCHEME_TEL, "0001112222",
             null);
-
+    private static final Uri SAMPLE_HANDLE_WITH_POST_DIAL = new Uri.Builder()
+            .scheme(PhoneAccount.SCHEME_TEL)
+            .encodedOpaquePart("6505551212,1234567890")
+            .build();
+    private static final Uri SAMPLE_REDIRECT_HANDLE = new Uri.Builder()
+            .scheme(PhoneAccount.SCHEME_TEL)
+            .encodedOpaquePart("6505551213")
+            .build();
+    private static final Uri SAMPLE_REDIRECT_HANDLE_WITH_POST_DIAL = new Uri.Builder()
+            .scheme(PhoneAccount.SCHEME_TEL)
+            .encodedOpaquePart("6505551213,1234567890")
+            .build();
     private static final int ASYNC_TIMEOUT = 10000;
     private RoleManager mRoleManager;
     private Handler mHandler;
@@ -120,6 +132,31 @@
         assertTrue(Call.STATE_DISCONNECTED != mCall.getState());
     }
 
+    /**
+     * Verifies that post-dial digits will be re-added to a number after redirection.
+     * @throws Exception
+     */
+    public void testRedirectedCallWithPostDialDigits() throws Exception {
+        if (!shouldTestTelecom(mContext)) {
+            return;
+        }
+        mCallRedirectionServiceController.setRedirectCall(SAMPLE_REDIRECT_HANDLE, null, false);
+        Bundle extras = new Bundle();
+        extras.putParcelable(TestUtils.EXTRA_PHONE_NUMBER, SAMPLE_HANDLE_WITH_POST_DIAL);
+        placeAndVerifyCallByRedirection(extras, false /* cancelledByCallRedirection */);
+        mInCallService = mInCallCallbacks.getService();
+        assertCallGatewayConstructed(mInCallService.getLastCall(), true);
+        mCall = mInCallService.getLastCall();
+        assertEquals(SAMPLE_REDIRECT_HANDLE_WITH_POST_DIAL,
+                mCall.getDetails().getGatewayInfo().getGatewayAddress());
+        assertEquals(SAMPLE_HANDLE_WITH_POST_DIAL,
+                mCall.getDetails().getGatewayInfo().getOriginalAddress());
+        assertEquals(SAMPLE_HANDLE_WITH_POST_DIAL,
+                mCall.getDetails().getHandle());
+        assertEquals(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, mCall.getDetails().getAccountHandle());
+        assertTrue(Call.STATE_DISCONNECTED != mCall.getState());
+    }
+
     public void testRedirectedCallWithRedirectedPhoneAccount()
             throws Exception {
         if (!shouldTestTelecom(mContext)) {
diff --git a/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java b/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java
index 30447b8..8a186c9 100644
--- a/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java
+++ b/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java
@@ -67,6 +67,7 @@
     public static final String METHOD_INITIALIZE = "initialize";
     public static final String METHOD_REQUEST_UPDATE_FILE_SERVICES =
             "requestUpdateFileServices";
+    public static final String METHOD_ADD_SERVICE_ANNOUNCEMENT = "addServiceAnnouncementFile";
     public static final String METHOD_SET_TEMP_FILE_ROOT = "setTempFileRootDirectory";
     public static final String METHOD_RESET_DOWNLOAD_KNOWLEDGE = "resetDownloadKnowledge";
     public static final String METHOD_GET_DOWNLOAD_STATUS = "getDownloadStatus";
@@ -82,6 +83,7 @@
     public static final String ARGUMENT_DOWNLOAD_REQUEST = "downloadRequest";
     public static final String ARGUMENT_FILE_INFO = "fileInfo";
     public static final String ARGUMENT_RESULT_CODE = "resultCode";
+    public static final String ARGUMENT_SERVICE_ANNOUNCEMENT_FILE = "serviceAnnouncementFile";
 
     public static final String CONTROL_INTERFACE_ACTION =
             "android.telephony.cts.embmstestapp.ACTION_CONTROL_MIDDLEWARE";
@@ -256,6 +258,16 @@
         }
 
         @Override
+        public int addServiceAnnouncementFile(int subscriptionId, byte[] announcementFile) {
+            Bundle b = new Bundle();
+            b.putString(METHOD_NAME, METHOD_ADD_SERVICE_ANNOUNCEMENT);
+            b.putInt(ARGUMENT_SUBSCRIPTION_ID, subscriptionId);
+            b.putByteArray(ARGUMENT_SERVICE_ANNOUNCEMENT_FILE, announcementFile);
+            mReceivedCalls.add(b);
+            return MbmsErrors.SUCCESS;
+        }
+
+        @Override
         public int cancelDownload(DownloadRequest request) {
             Bundle b = new Bundle();
             b.putString(METHOD_NAME, METHOD_CANCEL_DOWNLOAD);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CdmaSmsCbProgramDataTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CdmaSmsCbProgramDataTest.java
new file mode 100644
index 0000000..9aee14d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CdmaSmsCbProgramDataTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.cts;
+
+import static android.telephony.cdma.CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT;
+import static android.telephony.cdma.CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+import static android.telephony.cdma.CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES;
+
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
+import com.android.internal.telephony.cdma.sms.BearerData;
+
+import org.junit.Test;
+
+public class CdmaSmsCbProgramDataTest {
+
+    private static final int OPERATION = OPERATION_CLEAR_CATEGORIES;
+    private static final int CATEGORY = CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+    private static final int LANGUAGE = BearerData.LANGUAGE_ENGLISH;
+    private static final int MAX_MESSAGES = 10;
+    private static final int ALERT_OPTION = ALERT_OPTION_NO_ALERT;
+    private static final String CATEGORY_NAME = "category_name";
+
+    @Test
+    public void testCdmaSmsCbProgramDataConstructorAndGetters() {
+        CdmaSmsCbProgramData data = new CdmaSmsCbProgramData(
+                OPERATION,
+                CATEGORY,
+                LANGUAGE,
+                MAX_MESSAGES,
+                ALERT_OPTION,
+                CATEGORY_NAME
+        );
+
+        assertEquals(OPERATION, data.getOperation());
+        assertEquals(CATEGORY, data.getCategory());
+        assertEquals(LANGUAGE, data.getLanguage());
+        assertEquals(MAX_MESSAGES, data.getMaxMessages());
+        assertEquals(ALERT_OPTION, data.getAlertOption());
+        assertEquals(CATEGORY_NAME, data.getCategoryName());
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastServiceTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastServiceTest.java
new file mode 100644
index 0000000..bef1e8d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastServiceTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.cts;
+
+import static org.junit.Assert.assertNull;
+
+import android.os.Bundle;
+import android.telephony.CellBroadcastService;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+public class CellBroadcastServiceTest {
+
+    @Test
+    public void testConstructor() {
+        CellBroadcastService testCellBroadcastService = new CellBroadcastService() {
+            @Override
+            public void onGsmCellBroadcastSms(int slotIndex, byte[] message) {
+                // do nothing
+            }
+
+            @Override
+            public void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+                    int serviceCategory) {
+                // do nothing
+            }
+
+            @Override
+            public void onCdmaScpMessage(int slotIndex, List<CdmaSmsCbProgramData> smsCbProgramData,
+                    String originatingAddress, Consumer<Bundle> callback) {
+                // do nothing
+            }
+
+            @Override
+            public CharSequence getCellBroadcastAreaInfo(int slotIndex) {
+                return null;
+            }
+        };
+
+        assertNull(testCellBroadcastService.getCellBroadcastAreaInfo(0));
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index 41438fc..2ed4a07 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -382,30 +382,32 @@
     private void verifyCellIdentityCdma(CellIdentityCdma cdma, boolean isRegistered) {
         int networkId = cdma.getNetworkId();
         assertTrue("getNetworkId() out of range [0,65535], networkId=" + networkId,
-                networkId == Integer.MAX_VALUE || (networkId >= 0 && networkId <= NETWORK_ID));
+                networkId == CellInfo.UNAVAILABLE || (networkId >= 0 && networkId <= NETWORK_ID));
 
         int systemId = cdma.getSystemId();
         assertTrue("getSystemId() out of range [0,32767], systemId=" + systemId,
-                systemId == Integer.MAX_VALUE || (systemId >= 0 && systemId <= SYSTEM_ID));
+                systemId == CellInfo.UNAVAILABLE || (systemId >= 0 && systemId <= SYSTEM_ID));
 
         int basestationId = cdma.getBasestationId();
         assertTrue("getBasestationId() out of range [0,65535], basestationId=" + basestationId,
-                basestationId == Integer.MAX_VALUE
+                basestationId == CellInfo.UNAVAILABLE
                         || (basestationId >= 0 && basestationId <= BASESTATION_ID));
 
         int longitude = cdma.getLongitude();
         assertTrue("getLongitude() out of range [-2592000,2592000], longitude=" + longitude,
-                longitude == Integer.MAX_VALUE
+                longitude == CellInfo.UNAVAILABLE
                         || (longitude >= -LONGITUDE && longitude <= LONGITUDE));
 
         int latitude = cdma.getLatitude();
         assertTrue("getLatitude() out of range [-1296000,1296000], latitude=" + latitude,
-                latitude == Integer.MAX_VALUE || (latitude >= -LATITUDE && latitude <= LATITUDE));
+                latitude == CellInfo.UNAVAILABLE
+                        || (latitude >= -LATITUDE && latitude <= LATITUDE));
 
         if (isRegistered) {
-            assertTrue("SID is required for registered cells", systemId != Integer.MAX_VALUE);
-            assertTrue("NID is required for registered cells", networkId != Integer.MAX_VALUE);
-            assertTrue("BSID is required for registered cells", basestationId != Integer.MAX_VALUE);
+            assertTrue("SID is required for registered cells", systemId != CellInfo.UNAVAILABLE);
+            assertTrue("NID is required for registered cells", networkId != CellInfo.UNAVAILABLE);
+            assertTrue("BSID is required for registered cells",
+                    basestationId != CellInfo.UNAVAILABLE);
         }
     }
 
@@ -447,7 +449,7 @@
 
         int evdoSnr = cdma.getEvdoSnr();
         assertTrue("getEvdoSnr() out of range [0,8], evdoSnr=" + evdoSnr,
-                (evdoSnr == Integer.MAX_VALUE) || (evdoSnr >= 0 && evdoSnr <= 8));
+                (evdoSnr == CellInfo.UNAVAILABLE) || (evdoSnr >= 0 && evdoSnr <= 8));
     }
 
     private void verifyCellSignalStrengthCdmaParcel(CellSignalStrengthCdma cdma) {
@@ -461,9 +463,9 @@
 
     private static void verifyPlmnInfo(String mccStr, String mncStr, int mcc, int mnc) {
         // If either int value is invalid, all values must be invalid
-        if (mcc == Integer.MAX_VALUE) {
+        if (mcc == CellInfo.UNAVAILABLE) {
             assertTrue("MNC and MNC must always be reported together.",
-                    mnc == Integer.MAX_VALUE && mccStr == null && mncStr == null);
+                    mnc == CellInfo.UNAVAILABLE && mccStr == null && mncStr == null);
             return;
         }
 
@@ -552,7 +554,7 @@
         if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
             int[] bands = nr.getBands();
             for (int band: bands) {
-                assertTrue("getBand out of range [1, 95] or [257, 261]",
+                assertTrue("getBand out of range [1, 95] or [257, 261], band = " + band,
                         (band >= BAND_FR1_MIN_NR && band <= BAND_FR1_MAX_NR)
                         || (band >= BAND_FR2_MIN_NR && band <= BAND_FR2_MAX_NR));
             }
@@ -560,7 +562,7 @@
 
         // If the cell is reported as registered, then all the logical cell info must be reported
         if (isRegistered) {
-            assertTrue("TAC is required for registered cells", tac != Integer.MAX_VALUE);
+            assertTrue("TAC is required for registered cells", tac != CellInfo.UNAVAILABLE);
             assertTrue("MCC is required for registered cells", nr.getMccString() != null);
             assertTrue("MNC is required for registered cells", nr.getMncString() != null);
         }
@@ -576,17 +578,18 @@
 
         assertTrue("getCsiRsrp() out of range [-140, -44] | Integer.MAX_INTEGER, csiRsrp = "
                         + csiRsrp, -140 <= csiRsrp && csiRsrp <= -44
-                || csiRsrp == Integer.MAX_VALUE);
+                || csiRsrp == CellInfo.UNAVAILABLE);
         assertTrue("getCsiRsrq() out of range [-20, -3] | Integer.MAX_INTEGER, csiRsrq = "
-                + csiRsrq, -20 <= csiRsrq && csiRsrq <= -3 || csiRsrq == Integer.MAX_VALUE);
+                + csiRsrq, -20 <= csiRsrq && csiRsrq <= -3 || csiRsrq == CellInfo.UNAVAILABLE);
         assertTrue("getCsiSinr() out of range [-23, 40] | Integer.MAX_INTEGER, csiSinr = "
-                + csiSinr, -23 <= csiSinr && csiSinr <= 40 || csiSinr == Integer.MAX_VALUE);
+                + csiSinr, -23 <= csiSinr && csiSinr <= 40 || csiSinr == CellInfo.UNAVAILABLE);
         assertTrue("getSsRsrp() out of range [-140, -44] | Integer.MAX_INTEGER, ssRsrp = "
-                        + ssRsrp, -140 <= ssRsrp && ssRsrp <= -44 || ssRsrp == Integer.MAX_VALUE);
+                        + ssRsrp, -140 <= ssRsrp && ssRsrp <= -44
+                || ssRsrp == CellInfo.UNAVAILABLE);
         assertTrue("getSsRsrq() out of range [-20, -3] | Integer.MAX_INTEGER, ssRsrq = "
-                + ssRsrq, -20 <= ssRsrq && ssRsrq <= -3 || ssRsrq == Integer.MAX_VALUE);
+                + ssRsrq, -20 <= ssRsrq && ssRsrq <= -3 || ssRsrq == CellInfo.UNAVAILABLE);
         assertTrue("getSsSinr() out of range [-23, 40] | Integer.MAX_INTEGER, ssSinr = "
-                + ssSinr, -23 <= ssSinr && ssSinr <= 40 || ssSinr == Integer.MAX_VALUE);
+                + ssSinr, -23 <= ssSinr && ssSinr <= 40 || ssSinr == CellInfo.UNAVAILABLE);
     }
 
     private void verifyCellInfoLteParcelandHashcode(CellInfoLte lte) {
@@ -605,23 +608,23 @@
         // Cell identity ranges from 0 to 268435456.
         int ci = lte.getCi();
         assertTrue("getCi() out of range [0,268435456], ci=" + ci,
-                (ci == Integer.MAX_VALUE) || (ci >= 0 && ci <= CI));
+                (ci == CellInfo.UNAVAILABLE) || (ci >= 0 && ci <= CI));
 
         // Verify LTE physical cell id information.
         // Only physical cell id is available for LTE neighbor.
         int pci = lte.getPci();
         // Physical cell id should be within [0, 503].
         assertTrue("getPci() out of range [0, 503], pci=" + pci,
-                (pci== Integer.MAX_VALUE) || (pci >= 0 && pci <= PCI));
+                (pci == CellInfo.UNAVAILABLE) || (pci >= 0 && pci <= PCI));
 
         // Tracking area code ranges from 0 to 65535.
         int tac = lte.getTac();
         assertTrue("getTac() out of range [0,65535], tac=" + tac,
-                (tac == Integer.MAX_VALUE) || (tac >= 0 && tac <= TAC));
+                (tac == CellInfo.UNAVAILABLE) || (tac >= 0 && tac <= TAC));
 
         int bw = lte.getBandwidth();
         assertTrue("getBandwidth out of range [1400, 20000] | Integer.Max_Value, bw=",
-                bw == Integer.MAX_VALUE || bw >= BANDWIDTH_LOW && bw <= BANDWIDTH_HIGH);
+                bw == CellInfo.UNAVAILABLE || bw >= BANDWIDTH_LOW && bw <= BANDWIDTH_HIGH);
 
         int earfcn = lte.getEarfcn();
         // Reference 3GPP 36.101 Table 5.7.3-1
@@ -630,7 +633,7 @@
         // out of the range of the narrowest 1.4Mhz deployment.
         int minEarfcn = 7;
         int maxEarfcn = EARFCN_MAX - 7;
-        if (bw != Integer.MAX_VALUE) {
+        if (bw != CellInfo.UNAVAILABLE) {
             // The number of channels used by a cell is equal to the cell bandwidth divided
             // by the channel raster (bandwidth of a channel). The center channel is the channel
             // the n/2-th channel where n is the number of channels, and since it is the center
@@ -644,12 +647,12 @@
         }
         assertTrue(
                 "getEarfcn() out of range [" + minEarfcn + "," + maxEarfcn + "], earfcn=" + earfcn,
-                earfcn == Integer.MAX_VALUE || (earfcn >= minEarfcn && earfcn <= maxEarfcn));
+                earfcn == CellInfo.UNAVAILABLE || (earfcn >= minEarfcn && earfcn <= maxEarfcn));
 
         if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
             int[] bands = lte.getBands();
             for (int band: bands) {
-                assertTrue("getBand out of range [1, 88]",
+                assertTrue("getBand out of range [1, 88], band = " + band,
                         band >= BAND_MIN_LTE && band <= BAND_MAX_LTE);
             }
         }
@@ -666,17 +669,27 @@
 
         verifyCsgInfo(lte.getClosedSubscriberGroupInfo());
 
+        verifyCellIdentityLteLocationSanitation(lte);
+
         // If the cell is reported as registered, then all the logical cell info must be reported
         if (isRegistered) {
-            assertTrue("TAC is required for registered cells", tac != Integer.MAX_VALUE);
-            assertTrue("CID is required for registered cells", ci != Integer.MAX_VALUE);
+            assertTrue("TAC is required for registered cells", tac != CellInfo.UNAVAILABLE);
+            assertTrue("CID is required for registered cells", ci != CellInfo.UNAVAILABLE);
             assertTrue("MCC is required for registered cells",
-                    lte.getMccString() != null || lte.getMcc() != Integer.MAX_VALUE);
+                    lte.getMccString() != null || lte.getMcc() != CellInfo.UNAVAILABLE);
             assertTrue("MNC is required for registered cells",
-                    lte.getMncString() != null || lte.getMnc() != Integer.MAX_VALUE);
+                    lte.getMncString() != null || lte.getMnc() != CellInfo.UNAVAILABLE);
         }
     }
 
+    private void verifyCellIdentityLteLocationSanitation(CellIdentityLte lte) {
+        CellIdentityLte sanitized = lte.sanitizeLocationInfo();
+        assertEquals(CellInfo.UNAVAILABLE, sanitized.getCi());
+        assertEquals(CellInfo.UNAVAILABLE, sanitized.getEarfcn());
+        assertEquals(CellInfo.UNAVAILABLE, sanitized.getPci());
+        assertEquals(CellInfo.UNAVAILABLE, sanitized.getTac());
+    }
+
     private void verifyCellIdentityLteParcel(CellIdentityLte lte) {
         Parcel p = Parcel.obtain();
         lte.writeToParcel(p, 0);
@@ -689,32 +702,32 @@
     private void verifyCellSignalStrengthLte(CellSignalStrengthLte cellSignalStrengthLte) {
         verifyRssiDbm(cellSignalStrengthLte.getDbm());
 
-        //Integer.MAX_VALUE indicates an unavailable field
+        //ICellInfo.UNAVAILABLE indicates an unavailable field
         int rsrp = cellSignalStrengthLte.getRsrp();
         // RSRP is being treated as RSSI in LTE (they are similar but not quite right)
         // so reusing the constants here.
         assertTrue("getRsrp() out of range, rsrp=" + rsrp, rsrp >= MIN_RSRP && rsrp <= MAX_RSRP);
 
         int rsrq = cellSignalStrengthLte.getRsrq();
-        assertTrue("getRsrq() out of range | Integer.MAX_VALUE, rsrq=" + rsrq,
-            rsrq == Integer.MAX_VALUE || (rsrq >= MIN_RSRQ && rsrq <= MAX_RSRQ));
+        assertTrue("getRsrq() out of range | CellInfo.UNAVAILABLE, rsrq=" + rsrq,
+                rsrq == CellInfo.UNAVAILABLE || (rsrq >= MIN_RSRQ && rsrq <= MAX_RSRQ));
 
         int rssi = cellSignalStrengthLte.getRssi();
-        assertTrue("getRssi() out of range [-113, -51] or Integer.MAX_VALUE if unknown, rssi="
+        assertTrue("getRssi() out of range [-113, -51] or CellInfo.UNAVAILABLE if unknown, rssi="
                 + rssi, rssi == CellInfo.UNAVAILABLE
                 || (rssi >= MIN_LTE_RSSI && rssi <= MAX_LTE_RSSI));
 
         int rssnr = cellSignalStrengthLte.getRssnr();
-        assertTrue("getRssnr() out of range | Integer.MAX_VALUE, rssnr=" + rssnr,
-            rssnr == Integer.MAX_VALUE || (rssnr >= MIN_RSSNR && rssnr <= MAX_RSSNR));
+        assertTrue("getRssnr() out of range | CellInfo.UNAVAILABLE, rssnr=" + rssnr,
+                rssnr == CellInfo.UNAVAILABLE || (rssnr >= MIN_RSSNR && rssnr <= MAX_RSSNR));
 
         int cqi = cellSignalStrengthLte.getCqi();
-        assertTrue("getCqi() out of range | Integer.MAX_VALUE, cqi=" + cqi,
-            cqi == Integer.MAX_VALUE || (cqi >= MIN_CQI && cqi <= MAX_CQI));
+        assertTrue("getCqi() out of range | CellInfo.UNAVAILABLE, cqi=" + cqi,
+                cqi == CellInfo.UNAVAILABLE || (cqi >= MIN_CQI && cqi <= MAX_CQI));
 
         int ta = cellSignalStrengthLte.getTimingAdvance();
-        assertTrue("getTimingAdvance() invalid [0-1282] | Integer.MAX_VALUE, ta=" + ta,
-                ta == Integer.MAX_VALUE || (ta >= 0 && ta <=1282));
+        assertTrue("getTimingAdvance() invalid [0-1282] | CellInfo.UNAVAILABLE, ta=" + ta,
+                ta == CellInfo.UNAVAILABLE || (ta >= 0 && ta <= 1282));
 
         int level = cellSignalStrengthLte.getLevel();
         assertTrue("getLevel() out of range [0,4], level=" + level, level >= 0 && level <= 4);
@@ -725,7 +738,8 @@
 
         int timingAdvance = cellSignalStrengthLte.getTimingAdvance();
         assertTrue("getTimingAdvance() out of range [0,1282], timingAdvance=" + timingAdvance,
-                timingAdvance == Integer.MAX_VALUE || (timingAdvance >= 0 && timingAdvance <= 1282));
+                timingAdvance == CellInfo.UNAVAILABLE
+                        || (timingAdvance >= 0 && timingAdvance <= 1282));
 
         if (mRadioHalVersion >= RADIO_HAL_VERSION_1_2) {
             assertTrue("RSRP Must be valid for LTE",
@@ -767,17 +781,17 @@
 
         int lac = wcdma.getLac();
         assertTrue("getLac() out of range [0, 65535], lac=" + lac,
-                (lac >= 0 && lac <= LAC) || lac == Integer.MAX_VALUE);
+                (lac >= 0 && lac <= LAC) || lac == CellInfo.UNAVAILABLE);
 
         int cid = wcdma.getCid();
         assertTrue("getCid() out of range [0, 268435455], cid=" + cid,
-                (cid >= 0 && cid <= CID_UMTS) || cid == Integer.MAX_VALUE);
+                (cid >= 0 && cid <= CID_UMTS) || cid == CellInfo.UNAVAILABLE);
 
         // Verify wcdma primary scrambling code information.
         // Primary scrambling code should be within [0, 511].
         int psc = wcdma.getPsc();
         assertTrue("getPsc() out of range [0, 511], psc=" + psc,
-                (psc >= 0 && psc <= PSC) || psc == Integer.MAX_VALUE);
+                (psc >= 0 && psc <= PSC) || psc == CellInfo.UNAVAILABLE);
 
         String mobileNetworkOperator = wcdma.getMobileNetworkOperator();
         assertTrue("getMobileNetworkOperator() out of range [0, 999999], mobileNetworkOperator="
@@ -800,12 +814,12 @@
 
         // If the cell is reported as registered, then all the logical cell info must be reported
         if (isRegistered) {
-            assertTrue("LAC is required for registered cells", lac != Integer.MAX_VALUE);
-            assertTrue("CID is required for registered cells", cid != Integer.MAX_VALUE);
+            assertTrue("LAC is required for registered cells", lac != CellInfo.UNAVAILABLE);
+            assertTrue("CID is required for registered cells", cid != CellInfo.UNAVAILABLE);
             assertTrue("MCC is required for registered cells",
-                    wcdma.getMccString() != null || wcdma.getMcc() != Integer.MAX_VALUE);
+                    wcdma.getMccString() != null || wcdma.getMcc() != CellInfo.UNAVAILABLE);
             assertTrue("MNC is required for registered cells",
-                    wcdma.getMncString() != null || wcdma.getMnc() != Integer.MAX_VALUE);
+                    wcdma.getMncString() != null || wcdma.getMnc() != CellInfo.UNAVAILABLE);
         }
     }
 
@@ -884,15 +898,15 @@
         // Local area code and cellid should be with [0, 65535].
         int lac = gsm.getLac();
         assertTrue("getLac() out of range [0, 65535], lac=" + lac,
-                lac == Integer.MAX_VALUE || (lac >= 0 && lac <= LAC));
+                lac == CellInfo.UNAVAILABLE || (lac >= 0 && lac <= LAC));
         int cid = gsm.getCid();
         assertTrue("getCid() out range [0, 65535], cid=" + cid,
-                cid== Integer.MAX_VALUE || (cid >= 0 && cid <= CID_GSM));
+                cid == CellInfo.UNAVAILABLE || (cid >= 0 && cid <= CID_GSM));
 
         int arfcn = gsm.getArfcn();
         // Reference 3GPP 45.005 Table 2-2
         assertTrue("getArfcn() out of range [0,1024], arfcn=" + arfcn,
-                arfcn == Integer.MAX_VALUE || (arfcn >= 0 && arfcn <= ARFCN));
+                arfcn == CellInfo.UNAVAILABLE || (arfcn >= 0 && arfcn <= ARFCN));
 
         String mobileNetworkOperator = gsm.getMobileNetworkOperator();
         assertTrue("getMobileNetworkOperator() out of range [0, 999999], mobileNetworkOperator="
@@ -910,12 +924,12 @@
 
         // If the cell is reported as registered, then all the logical cell info must be reported
         if (isRegistered) {
-            assertTrue("LAC is required for registered cells", lac != Integer.MAX_VALUE);
-            assertTrue("CID is required for registered cells", cid != Integer.MAX_VALUE);
+            assertTrue("LAC is required for registered cells", lac != CellInfo.UNAVAILABLE);
+            assertTrue("CID is required for registered cells", cid != CellInfo.UNAVAILABLE);
             assertTrue("MCC is required for registered cells",
-                    gsm.getMccString() != null || gsm.getMcc() != Integer.MAX_VALUE);
+                    gsm.getMccString() != null || gsm.getMcc() != CellInfo.UNAVAILABLE);
             assertTrue("MNC is required for registered cells",
-                    gsm.getMncString() != null || gsm.getMnc() != Integer.MAX_VALUE);
+                    gsm.getMncString() != null || gsm.getMnc() != CellInfo.UNAVAILABLE);
         }
     }
 
@@ -935,8 +949,8 @@
         assertTrue("getLevel() out of range [0,4], level=" + level, level >= 0 && level <= 4);
 
         int ta = gsm.getTimingAdvance();
-        assertTrue("getTimingAdvance() out of range [0,219] | Integer.MAX_VALUE, ta=" + ta,
-                ta == Integer.MAX_VALUE || (ta >= 0 && ta <= 219));
+        assertTrue("getTimingAdvance() out of range [0,219] | CellInfo.UNAVAILABLE, ta=" + ta,
+                ta == CellInfo.UNAVAILABLE || (ta >= 0 && ta <= 219));
 
         assertEquals(gsm.getDbm(), gsm.getRssi());
 
@@ -994,11 +1008,11 @@
 
         int lac = tdscdma.getLac();
         assertTrue("getLac() out of range [0, 65535], lac=" + lac,
-                (lac >= 0 && lac <= LAC) || lac == Integer.MAX_VALUE);
+                (lac >= 0 && lac <= LAC) || lac == CellInfo.UNAVAILABLE);
 
         int cid = tdscdma.getCid();
         assertTrue("getCid() out of range [0, 268435455], cid=" + cid,
-                (cid >= 0 && cid <= CID_UMTS) || cid == Integer.MAX_VALUE);
+                (cid >= 0 && cid <= CID_UMTS) || cid == CellInfo.UNAVAILABLE);
 
         // Verify tdscdma primary scrambling code information.
         // Primary scrambling code should be within [0, 511].
@@ -1026,8 +1040,8 @@
 
         // If the cell is reported as registered, then all the logical cell info must be reported
         if (isRegistered) {
-            assertTrue("LAC is required for registered cells", lac != Integer.MAX_VALUE);
-            assertTrue("CID is required for registered cells", cid != Integer.MAX_VALUE);
+            assertTrue("LAC is required for registered cells", lac != CellInfo.UNAVAILABLE);
+            assertTrue("CID is required for registered cells", cid != CellInfo.UNAVAILABLE);
             assertTrue("MCC is required for registered cells", tdscdma.getMccString() != null);
             assertTrue("MNC is required for registered cells", tdscdma.getMncString() != null);
         }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/DataCallResponseTest.java b/tests/tests/telephony/current/src/android/telephony/cts/DataCallResponseTest.java
new file mode 100644
index 0000000..1ec1c81
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/DataCallResponseTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.os.Parcel;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+
+import org.junit.Test;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.List;
+
+public class DataCallResponseTest {
+    private static final int CAUSE = 0;
+    private static final int RETRY = -1;
+    private static final int ID = 1;
+    private static final int LINK_STATUS = 2;
+    private static final int PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
+    private static final String IF_NAME = "IF_NAME";
+    private static final List<LinkAddress> ADDRESSES = Arrays.asList(
+            new LinkAddress(InetAddresses.parseNumericAddress("99.88.77.66"), 0));
+    private static final List<InetAddress> DNSES =
+            Arrays.asList(InetAddresses.parseNumericAddress("55.66.77.88"));
+    private static final List<InetAddress> GATEWAYS =
+            Arrays.asList(InetAddresses.parseNumericAddress("11.22.33.44"));
+    private static final List<InetAddress> PCSCFS =
+            Arrays.asList(InetAddresses.parseNumericAddress("22.33.44.55"));
+    private static final int MTU_V4 = 1440;
+    private static final int MTU_V6 = 1400;
+
+    @Test
+    public void testConstructorAndGetters() {
+        DataCallResponse response = new DataCallResponse.Builder()
+                .setCause(CAUSE)
+                .setSuggestedRetryTime(RETRY)
+                .setId(ID)
+                .setLinkStatus(LINK_STATUS)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setInterfaceName(IF_NAME)
+                .setAddresses(ADDRESSES)
+                .setDnsAddresses(DNSES)
+                .setGatewayAddresses(GATEWAYS)
+                .setPcscfAddresses(PCSCFS)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .build();
+
+        assertThat(response.getCause()).isEqualTo(CAUSE);
+        assertThat(response.getSuggestedRetryTime()).isEqualTo(RETRY);
+        assertThat(response.getId()).isEqualTo(ID);
+        assertThat(response.getLinkStatus()).isEqualTo(LINK_STATUS);
+        assertThat(response.getProtocolType()).isEqualTo(PROTOCOL_TYPE);
+        assertThat(response.getInterfaceName()).isEqualTo(IF_NAME);
+        assertThat(response.getAddresses()).isEqualTo(ADDRESSES);
+        assertThat(response.getDnsAddresses()).isEqualTo(DNSES);
+        assertThat(response.getGatewayAddresses()).isEqualTo(GATEWAYS);
+        assertThat(response.getPcscfAddresses()).isEqualTo(PCSCFS);
+        assertThat(response.getMtuV4()).isEqualTo(MTU_V4);
+        assertThat(response.getMtuV6()).isEqualTo(MTU_V6);
+    }
+
+    @Test
+    public void testEquals() {
+        DataCallResponse response = new DataCallResponse.Builder()
+                .setCause(CAUSE)
+                .setSuggestedRetryTime(RETRY)
+                .setId(ID)
+                .setLinkStatus(LINK_STATUS)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setInterfaceName(IF_NAME)
+                .setAddresses(ADDRESSES)
+                .setDnsAddresses(DNSES)
+                .setGatewayAddresses(GATEWAYS)
+                .setPcscfAddresses(PCSCFS)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .build();
+
+        DataCallResponse equalsResponse = new DataCallResponse.Builder()
+                .setCause(CAUSE)
+                .setSuggestedRetryTime(RETRY)
+                .setId(ID)
+                .setLinkStatus(LINK_STATUS)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setInterfaceName(IF_NAME)
+                .setAddresses(ADDRESSES)
+                .setDnsAddresses(DNSES)
+                .setGatewayAddresses(GATEWAYS)
+                .setPcscfAddresses(PCSCFS)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .build();
+
+        assertThat(response).isEqualTo(equalsResponse);
+    }
+
+    @Test
+    public void testNotEquals() {
+        DataCallResponse response = new DataCallResponse.Builder()
+                .setCause(CAUSE)
+                .setSuggestedRetryTime(RETRY)
+                .setId(ID)
+                .setLinkStatus(LINK_STATUS)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setInterfaceName(IF_NAME)
+                .setAddresses(ADDRESSES)
+                .setDnsAddresses(DNSES)
+                .setGatewayAddresses(GATEWAYS)
+                .setPcscfAddresses(PCSCFS)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .build();
+
+        DataCallResponse notEqualsResponse = new DataCallResponse.Builder()
+                .setCause(1)
+                .setSuggestedRetryTime(-1)
+                .setId(1)
+                .setLinkStatus(3)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setInterfaceName(IF_NAME)
+                .setAddresses(ADDRESSES)
+                .setDnsAddresses(DNSES)
+                .setGatewayAddresses(GATEWAYS)
+                .setPcscfAddresses(PCSCFS)
+                .setMtuV4(1441)
+                .setMtuV6(1440)
+                .build();
+
+        assertThat(response).isNotEqualTo(notEqualsResponse);
+        assertThat(response).isNotEqualTo(null);
+        assertThat(response).isNotEqualTo(new String[1]);
+    }
+
+    @Test
+    public void testParcel() {
+        DataCallResponse response = new DataCallResponse.Builder()
+                .setCause(CAUSE)
+                .setSuggestedRetryTime(RETRY)
+                .setId(ID)
+                .setLinkStatus(LINK_STATUS)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setInterfaceName(IF_NAME)
+                .setAddresses(ADDRESSES)
+                .setDnsAddresses(DNSES)
+                .setGatewayAddresses(GATEWAYS)
+                .setPcscfAddresses(PCSCFS)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .build();
+
+        Parcel stateParcel = Parcel.obtain();
+        response.writeToParcel(stateParcel, 0);
+        stateParcel.setDataPosition(0);
+
+        DataCallResponse parcelResponse = DataCallResponse.CREATOR.createFromParcel(stateParcel);
+        assertThat(response).isEqualTo(parcelResponse);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java b/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
new file mode 100644
index 0000000..5740272
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataProfile;
+
+import org.junit.Test;
+
+public class DataProfileTest {
+    private static final int PROFILE_ID = 1;
+    private static final String APN = "FAKE_APN";
+    private static final int PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
+    private static final int AUTH_TYPE = ApnSetting.AUTH_TYPE_NONE;
+    private static final String USER_NAME = "USER_NAME";
+    private static final String PASSWORD = "PASSWORD";
+    private static final int TYPE = DataProfile.TYPE_3GPP2;
+    private static final boolean IS_ENABLED = true;
+    private static final int APN_BITMASK = 1;
+    private static final int ROAMING_PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
+    private static final int BEARER_BITMASK = 14;
+    private static final int MTU_V4 = 1440;
+    private static final int MTU_V6 = 1400;
+    private static final boolean IS_PREFERRED = true;
+    private static final boolean IS_PERSISTENT = true;
+
+    @Test
+    public void testConstructorAndGetters() {
+        DataProfile profile = new DataProfile.Builder()
+                .setProfileId(PROFILE_ID)
+                .setApn(APN)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setAuthType(AUTH_TYPE)
+                .setUserName(USER_NAME)
+                .setPassword(PASSWORD)
+                .setType(TYPE)
+                .enable(IS_ENABLED)
+                .setSupportedApnTypesBitmask(APN_BITMASK)
+                .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+                .setBearerBitmask(BEARER_BITMASK)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .setPreferred(IS_PREFERRED)
+                .setPersistent(IS_PERSISTENT)
+                .build();
+
+        assertThat(profile.getProfileId()).isEqualTo(PROFILE_ID);
+        assertThat(profile.getApn()).isEqualTo(APN);
+        assertThat(profile.getProtocolType()).isEqualTo(PROTOCOL_TYPE);
+        assertThat(profile.getAuthType()).isEqualTo(AUTH_TYPE);
+        assertThat(profile.getUserName()).isEqualTo(USER_NAME);
+        assertThat(profile.getPassword()).isEqualTo(PASSWORD);
+        assertThat(profile.getType()).isEqualTo(TYPE);
+        assertThat(profile.isEnabled()).isEqualTo(IS_ENABLED);
+        assertThat(profile.getSupportedApnTypesBitmask()).isEqualTo(APN_BITMASK);
+        assertThat(profile.getRoamingProtocolType()).isEqualTo(ROAMING_PROTOCOL_TYPE);
+        assertThat(profile.getBearerBitmask()).isEqualTo(BEARER_BITMASK);
+        assertThat(profile.getMtuV4()).isEqualTo(MTU_V4);
+        assertThat(profile.getMtuV6()).isEqualTo(MTU_V6);
+        assertThat(profile.isPreferred()).isEqualTo(IS_PREFERRED);
+        assertThat(profile.isPersistent()).isEqualTo(IS_PERSISTENT);
+    }
+
+    @Test
+    public void testEquals() {
+        DataProfile profile = new DataProfile.Builder()
+                .setProfileId(PROFILE_ID)
+                .setApn(APN)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setAuthType(AUTH_TYPE)
+                .setUserName(USER_NAME)
+                .setPassword(PASSWORD)
+                .setType(TYPE)
+                .enable(IS_ENABLED)
+                .setSupportedApnTypesBitmask(APN_BITMASK)
+                .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+                .setBearerBitmask(BEARER_BITMASK)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .setPreferred(IS_PREFERRED)
+                .setPersistent(IS_PERSISTENT)
+                .build();
+
+        DataProfile equalsProfile = new DataProfile.Builder()
+                .setProfileId(PROFILE_ID)
+                .setApn(APN)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setAuthType(AUTH_TYPE)
+                .setUserName(USER_NAME)
+                .setPassword(PASSWORD)
+                .setType(TYPE)
+                .enable(IS_ENABLED)
+                .setSupportedApnTypesBitmask(APN_BITMASK)
+                .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+                .setBearerBitmask(BEARER_BITMASK)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .setPreferred(IS_PREFERRED)
+                .setPersistent(IS_PERSISTENT)
+                .build();
+
+        assertThat(profile).isEqualTo(equalsProfile);
+    }
+
+    @Test
+    public void testNotEquals() {
+        DataProfile profile = new DataProfile.Builder()
+                .setProfileId(PROFILE_ID)
+                .setApn(APN)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setAuthType(AUTH_TYPE)
+                .setUserName(USER_NAME)
+                .setPassword(PASSWORD)
+                .setType(TYPE)
+                .enable(IS_ENABLED)
+                .setSupportedApnTypesBitmask(APN_BITMASK)
+                .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+                .setBearerBitmask(BEARER_BITMASK)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .setPreferred(IS_PREFERRED)
+                .setPersistent(IS_PERSISTENT)
+                .build();
+
+        DataProfile notEqualsProfile = new DataProfile.Builder()
+                .setProfileId(0)
+                .setApn(APN)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setAuthType(AUTH_TYPE)
+                .setUserName(USER_NAME)
+                .setPassword(PASSWORD)
+                .setType(TYPE)
+                .enable(false)
+                .setSupportedApnTypesBitmask(2)
+                .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+                .setBearerBitmask(3)
+                .setMtuV4(1441)
+                .setMtuV6(1401)
+                .setPreferred(false)
+                .setPersistent(false)
+                .build();
+
+        assertThat(profile).isNotEqualTo(notEqualsProfile);
+        assertThat(profile).isNotEqualTo(null);
+        assertThat(profile).isNotEqualTo(new String[1]);
+    }
+
+    @Test
+    public void testParcel() {
+        DataProfile profile = new DataProfile.Builder()
+                .setProfileId(PROFILE_ID)
+                .setApn(APN)
+                .setProtocolType(PROTOCOL_TYPE)
+                .setAuthType(AUTH_TYPE)
+                .setUserName(USER_NAME)
+                .setPassword(PASSWORD)
+                .setType(TYPE)
+                .enable(IS_ENABLED)
+                .setSupportedApnTypesBitmask(APN_BITMASK)
+                .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+                .setBearerBitmask(BEARER_BITMASK)
+                .setMtuV4(MTU_V4)
+                .setMtuV6(MTU_V6)
+                .setPreferred(IS_PREFERRED)
+                .setPersistent(IS_PERSISTENT)
+                .build();
+
+        Parcel stateParcel = Parcel.obtain();
+        profile.writeToParcel(stateParcel, 0);
+        stateParcel.setDataPosition(0);
+
+        DataProfile parcelProfile = DataProfile.CREATOR.createFromParcel(stateParcel);
+        assertThat(profile).isEqualTo(parcelProfile);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
index 864cbac..782eeba 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -43,6 +43,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SmsManager;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
@@ -89,6 +90,7 @@
     private boolean mOnBarringInfoChangedCalled;
     private boolean mSecurityExceptionThrown;
     private boolean mOnRegistrationFailedCalled;
+    private boolean mOnTelephonyDisplayInfoChanged;
     @RadioPowerState private int mRadioPowerState;
     @SimActivationState private int mVoiceActivationState;
     private BarringInfo mBarringInfo;
@@ -693,6 +695,37 @@
     }
 
     @Test
+    public void testOnDisplayInfoChanged() throws Exception {
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+            return;
+        }
+        assertThat(mOnTelephonyDisplayInfoChanged).isFalse();
+
+        mHandler.post(() -> {
+            mListener = new PhoneStateListener() {
+                @Override
+                public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) {
+                    synchronized (mLock) {
+                        mOnTelephonyDisplayInfoChanged = true;
+                        mLock.notify();
+                    }
+                }
+            };
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    (tm) -> tm.listen(mListener,
+                            PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED));
+        });
+
+        synchronized (mLock) {
+            if (!mOnTelephonyDisplayInfoChanged) {
+                mLock.wait(WAIT_TIME);
+            }
+        }
+        assertTrue(mOnTelephonyDisplayInfoChanged);
+    }
+
+    @Test
     public void testOnCallForwardingIndicatorChanged() throws Throwable {
         if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
             Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsCbCmasInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbCmasInfoTest.java
new file mode 100644
index 0000000..062f690
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbCmasInfoTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.cts;
+
+import static android.telephony.SmsCbCmasInfo.CMAS_CATEGORY_SAFETY;
+import static android.telephony.SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY;
+import static android.telephony.SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
+import static android.telephony.SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE;
+import static android.telephony.SmsCbCmasInfo.CMAS_SEVERITY_SEVERE;
+import static android.telephony.SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE;
+
+import android.telephony.SmsCbCmasInfo;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SmsCbCmasInfoTest {
+
+    private static final int MESSAGE_CLASS = CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
+    private static final int CATEGORY = CMAS_CATEGORY_SAFETY;
+    private static final int RESPONSE_TYPE = CMAS_RESPONSE_TYPE_EVACUATE;
+    private static final int SEVERITY = CMAS_SEVERITY_SEVERE;
+    private static final int URGENCY = CMAS_URGENCY_IMMEDIATE;
+    private static final int CERTAINTY = CMAS_CERTAINTY_LIKELY;
+
+    @Test
+    public void testSmsCbCmasInfo() {
+        SmsCbCmasInfo info = new SmsCbCmasInfo(
+                MESSAGE_CLASS,
+                CATEGORY,
+                RESPONSE_TYPE,
+                SEVERITY,
+                URGENCY,
+                CERTAINTY
+        );
+
+        Assert.assertEquals(MESSAGE_CLASS, info.getMessageClass());
+        Assert.assertEquals(CATEGORY, info.getCategory());
+        Assert.assertEquals(RESPONSE_TYPE, info.getResponseType());
+        Assert.assertEquals(SEVERITY, info.getSeverity());
+        Assert.assertEquals(URGENCY, info.getUrgency());
+        Assert.assertEquals(CERTAINTY, info.getCertainty());
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java
index b157ab0..e038fef 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java
@@ -49,6 +49,7 @@
     private static final int TEST_SERIAL = 1234;
     private static final String TEST_PLMN = "111222";
     private static final SmsCbLocation TEST_LOCATION = new SmsCbLocation(TEST_PLMN, -1, -1);
+    private static final int TEST_DCS = 0;
     private static final int TEST_SERVICE_CATEGORY = 4097;
     private static final String TEST_LANGUAGE = "en";
     private static final String TEST_BODY = "test body";
@@ -79,9 +80,9 @@
             }
         });
         mSmsCbMessage = new SmsCbMessage(TEST_MESSAGE_FORMAT, TEST_GEO_SCOPE, TEST_SERIAL,
-                TEST_LOCATION, TEST_SERVICE_CATEGORY, TEST_LANGUAGE, 0, TEST_BODY, TEST_PRIORITY,
-                TEST_ETWS_INFO, null, TEST_MAX_WAIT_TIME, TEST_GEOS, TEST_RECEIVED_TIME,
-                TEST_SLOT, TEST_SUB_ID);
+                TEST_LOCATION, TEST_SERVICE_CATEGORY, TEST_LANGUAGE, TEST_DCS, TEST_BODY,
+                TEST_PRIORITY, TEST_ETWS_INFO, null, TEST_MAX_WAIT_TIME, TEST_GEOS,
+                TEST_RECEIVED_TIME, TEST_SLOT, TEST_SUB_ID);
     }
 
     @Test
@@ -156,6 +157,11 @@
     }
 
     @Test
+    public void testDataCodingScheme() {
+        assertEquals(TEST_DCS, mSmsCbMessage.getDataCodingScheme());
+    }
+
+    @Test
     public void testCmasInfo() {
         final SmsCbCmasInfo info =
                 new SmsCbCmasInfo(SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST,
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index fd46eef..d697aab 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -57,8 +57,10 @@
 import android.os.SystemClock;
 import android.provider.Telephony;
 import android.telephony.SmsManager;
+import android.telephony.SmsCbMessage;
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaSmsCbProgramData;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -660,6 +662,36 @@
         }
     }
 
+    @Test
+    public void testDisableCellBroadcastRange() {
+        try {
+            int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
+            executeWithShellPermissionIdentity(() -> {
+                getSmsManager().disableCellBroadcastRange(
+                        CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+                        CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+                        ranType);
+            });
+        } catch (Exception e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testEnableCellBroadcastRange() {
+        try {
+            int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
+            executeWithShellPermissionIdentity(() -> {
+                getSmsManager().enableCellBroadcastRange(
+                        CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+                        CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+                        ranType);
+            });
+        } catch (Exception e) {
+            // expected
+        }
+    }
+
     protected ArrayList<String> divideMessage(String text) {
         return getSmsManager().divideMessage(text);
     }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
index 7c8c9d46..cf7bfef 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
@@ -26,7 +26,9 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
 import org.junit.Before;
@@ -343,6 +345,45 @@
         assertEquals(2, result[0]);
     }
 
+    @Test
+    public void testGetSmsPdu() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        SmsMessage.SubmitPdu smsPdu;
+        String scAddress = null;
+        String destinationAddress = null;
+        String message = null;
+
+        // Null message, null destination
+        smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                SmsManager.STATUS_ON_ICC_READ,
+                scAddress, destinationAddress, message, System.currentTimeMillis());
+        assertNull(smsPdu);
+
+        message = "This is a test message";
+
+        // Non-null message, null destination
+        smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                SmsManager.STATUS_ON_ICC_READ,
+                scAddress, destinationAddress, message, System.currentTimeMillis());
+        assertNull(smsPdu);
+
+        if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+            // TODO: temp workaround, OCTET encoding for EMS not properly supported
+            return;
+        }
+
+        scAddress = "1650253000";
+        destinationAddress = "18004664411";
+        message = "This is a test message";
+        smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                SmsManager.STATUS_ON_ICC_READ,
+                scAddress, destinationAddress, message, System.currentTimeMillis());
+        assertNotNull(smsPdu);
+    }
+
     private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
             'A', 'B', 'C', 'D', 'E', 'F' };
 
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 72ac137..7cd5a53 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -18,13 +18,14 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -206,12 +207,16 @@
         if (!isSupported()) return;
 
         List<SubscriptionInfo> subList = mSm.getActiveSubscriptionInfoList();
+        int[] idList = mSm.getActiveSubscriptionIdList();
         // Assert when there is no sim card present or detected
         assertNotNull("Active subscriber required", subList);
+        assertNotNull("Active subscriber required", idList);
         assertFalse("Active subscriber required", subList.isEmpty());
+        assertNotEquals("Active subscriber required", 0, idList.length);
         for (int i = 0; i < subList.size(); i++) {
             assertTrue(subList.get(i).getSubscriptionId() >= 0);
             assertTrue(subList.get(i).getSimSlotIndex() >= 0);
+            assertTrue(ArrayUtils.contains(idList, subList.get(i).getSubscriptionId()));
             if (i >= 1) {
                 assertTrue(subList.get(i - 1).getSimSlotIndex()
                         <= subList.get(i).getSimSlotIndex());
@@ -336,12 +341,13 @@
         mSm.setSubscriptionPlans(mSubId, Arrays.asList(buildValidSubscriptionPlan()));
 
         // Cellular is metered by default
-        assertFalse(cm.getNetworkCapabilities(net).hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertFalse(cm.getNetworkCapabilities(net).hasCapability(
+                NET_CAPABILITY_TEMPORARILY_NOT_METERED));
 
-        // Override should make it go unmetered
+        // Override should make it go temporarily unmetered
         {
             final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
-                return caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+                return caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
             });
             mSm.setSubscriptionOverrideUnmetered(mSubId, true, 0);
             assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -350,7 +356,7 @@
         // Clearing override should make it go metered
         {
             final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
-                return !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+                return !caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
             });
             mSm.setSubscriptionOverrideUnmetered(mSubId, false, 0);
             assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -371,7 +377,8 @@
         mSm.setSubscriptionPlans(mSubId, Arrays.asList(buildValidSubscriptionPlan()));
 
         // Cellular is metered by default
-        assertFalse(cm.getNetworkCapabilities(net).hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertFalse(cm.getNetworkCapabilities(net).hasCapability(
+                NET_CAPABILITY_TEMPORARILY_NOT_METERED));
 
         SubscriptionPlan unmeteredPlan = SubscriptionPlan.Builder
                 .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
@@ -384,7 +391,7 @@
         // Unmetered plan should make it go unmetered
         {
             final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
-                return caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+                return caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
             });
             mSm.setSubscriptionPlans(mSubId, Arrays.asList(unmeteredPlan));
             assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -393,7 +400,7 @@
         // Metered plan should make it go metered
         {
             final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
-                return !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+                return !caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
             });
             mSm.setSubscriptionPlans(mSubId, Arrays.asList(buildValidSubscriptionPlan()));
             assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -485,6 +492,20 @@
     }
 
     @Test
+    public void testSubscriptionPlanResetNetworkTypes() {
+        SubscriptionPlan plan = SubscriptionPlan.Builder
+                .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
+                        Period.ofMonths(1))
+                .setTitle("CTS")
+                .setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_LTE})
+                .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+                .setDataUsage(500_000_000, System.currentTimeMillis())
+                .resetNetworkTypes()
+                .build();
+        assertEquals(plan, buildValidSubscriptionPlan());
+    }
+
+    @Test
     public void testSubscriptionGrouping() throws Exception {
         if (!isSupported()) return;
 
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 5d8219c..acf5ec7 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -37,16 +37,21 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
 import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -524,6 +529,7 @@
         mTelephonyManager.getCarrierConfig();
         mTelephonyManager.isVoiceCapable();
         mTelephonyManager.isSmsCapable();
+        mTelephonyManager.isLteCdmaEvdoGsmWcdmaEnabled();
         ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
                 (tm) -> tm.isDataConnectionAllowed());
         ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
@@ -1137,6 +1143,73 @@
     }
 
     @Test
+    public void testResetSettings() throws Exception {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
+            return;
+        }
+
+        UserManager userManager = getContext().getSystemService(UserManager.class);
+
+        boolean canChangeMobileNetworkSettings = userManager != null
+                && !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+        assertTrue("Primary user must be able to configure mobile networks to pass this test",
+                canChangeMobileNetworkSettings);
+
+        //First check permissions are correct
+        try {
+            mTelephonyManager.resetSettings();
+            fail("TelephonyManager#resetSettings requires the"
+                    + " android.Manifest.permission.NETWORK_SETTINGS permission");
+        } catch (SecurityException e) {
+            //expected
+        }
+        // and then do a reset to move data to default.
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    TelephonyManager::resetSettings, "android.permission.NETWORK_SETTINGS");
+        } catch (SecurityException e) {
+            fail("TelephonyManager#resetSettings requires the"
+                    + " android.Manifest.permission.NETWORK_SETTINGS permission");
+        }
+
+        LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(2);
+        final ContentObserver mobileDataChangeObserver = new ContentObserver(
+                new Handler(Looper.getMainLooper())) {
+            @Override
+            public void onChange(boolean selfChange) {
+                queue.offer(isDataEnabled());
+            }
+        };
+
+        getContext().getContentResolver().registerContentObserver(
+                getObservableDataEnabledUri(mTestSub), /* notifyForDescendants= */ false,
+                mobileDataChangeObserver);
+        boolean defaultDataSetting = isDataEnabled();
+
+        // set data to not the default!
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                tm -> tm.setDataEnabled(!defaultDataSetting));
+        Boolean dataChangedResult = queue.poll(TOLERANCE, TimeUnit.MILLISECONDS);
+        assertNotNull("Data setting was not changed", dataChangedResult);
+        assertEquals("Data enable change didn't work", !defaultDataSetting,
+                dataChangedResult);
+
+        // and then do a reset to move data to default again.
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    TelephonyManager::resetSettings, "android.permission.NETWORK_SETTINGS");
+        } catch (SecurityException e) {
+            fail("TelephonyManager#resetSettings requires the"
+                    + " android.Manifest.permission.NETWORK_SETTINGS permission");
+        }
+        dataChangedResult = queue.poll(TOLERANCE, TimeUnit.MILLISECONDS);
+        assertNotNull("Data setting was not changed", dataChangedResult);
+        assertEquals("resetSettings did not reset default data", defaultDataSetting,
+                dataChangedResult);
+    }
+
+    @Test
     public void testGetServiceState() throws InterruptedException {
         if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
             Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
@@ -1999,7 +2072,7 @@
             return;
         }
         if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
-            fail("This test requires two SIM cards.");
+            return;
         }
         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
                 (tm) -> tm.setPreferredOpportunisticDataSubscription(
@@ -2135,7 +2208,7 @@
             return;
         }
         if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
-            fail("This test requires two SIM cards.");
+            return;
         }
 
         List<SubscriptionInfo> subscriptionInfoList =
@@ -2276,6 +2349,28 @@
     }
 
     @Test
+    public void testIsIccLockEnabled() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        // verify SecurityException
+        try {
+            mTelephonyManager.isIccLockEnabled();
+            fail("testIsIccLockEnabled: Expected SecurityException on isIccLockEnabled");
+        } catch (SecurityException se) {
+            // expected
+        }
+
+        // test with permission
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.isIccLockEnabled());
+        } catch (SecurityException se) {
+            fail("testIsIccLockEnabled: SecurityException not expected");
+        }
+    }
+
+    @Test
     public void testIsDataEnabledForApn() {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             return;
@@ -2298,6 +2393,29 @@
     }
 
     @Test
+    public void testIsTetheringApnRequired() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        // verify SecurityException
+        try {
+            mTelephonyManager.isTetheringApnRequired();
+            fail("testIsTetheringApnRequired: Expected SecurityException on "
+                    + "isTetheringApnRequired");
+        } catch (SecurityException se) {
+            // expected
+        }
+
+        // test with permission
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.isTetheringApnRequired());
+        } catch (SecurityException se) {
+            fail("testIsIccLockEnabled: SecurityException not expected");
+        }
+    }
+
+    @Test
     public void testGetCarrierInfoForImsiEncryption() {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             return;
@@ -2480,6 +2598,68 @@
         }
     }
 
+    @Test
+    public void testOpportunisticNetworkState() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                tm -> tm.isOpportunisticNetworkEnabled());
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                tm -> tm.setOpportunisticNetworkState(true));
+        assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                tm -> tm.isOpportunisticNetworkEnabled()));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                tm -> tm.setOpportunisticNetworkState(false));
+        assertFalse(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                tm -> tm.isOpportunisticNetworkEnabled()));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                tm -> tm.setOpportunisticNetworkState(isEnabled));
+    }
+
+    @Test
+    public void testGetSimApplicationState() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        int simApplicationState = mTelephonyManager.getSimApplicationState();
+        assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED,
+                TelephonyManager.SIM_STATE_PUK_REQUIRED,
+                TelephonyManager.SIM_STATE_NETWORK_LOCKED,
+                TelephonyManager.SIM_STATE_NOT_READY,
+                TelephonyManager.SIM_STATE_PERM_DISABLED,
+                TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState));
+    }
+
+    @Test
+    public void testGetSimCardState() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        int simCardState = mTelephonyManager.getSimCardState();
+        assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+                TelephonyManager.SIM_STATE_ABSENT,
+                TelephonyManager.SIM_STATE_CARD_IO_ERROR,
+                TelephonyManager.SIM_STATE_CARD_RESTRICTED,
+                TelephonyManager.SIM_STATE_PRESENT).contains(simCardState));
+    }
+
+
+    private boolean isDataEnabled() {
+        return ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                TelephonyManager::isDataEnabled);
+    }
+
+    private Uri getObservableDataEnabledUri(int subId) {
+        Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
+        if (mTelephonyManager.getActiveModemCount() != 1) {
+            uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId);
+        }
+        return uri;
+    }
+
     /**
      * Validate Emergency Number address that only contains the dialable character.
      *
diff --git a/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java b/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java
index ecb11ee..d6fd098 100644
--- a/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java
@@ -29,11 +29,12 @@
 import android.telephony.mbms.FileServiceInfo;
 import android.telephony.mbms.MbmsErrors;
 
+import org.junit.Test;
+
 import java.io.File;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import org.junit.Test;
 
 public class MbmsDownloadSessionTest extends MbmsDownloadTestBase {
 
@@ -90,6 +91,15 @@
     }
 
     @Test
+    public void testAddServiceAnnouncementFile() throws Exception {
+        byte[] sampleAnnouncementFile = "<xml></xml>".getBytes();
+        mDownloadSession.addServiceAnnouncementFile(sampleAnnouncementFile);
+        List<Bundle> addServiceAnnouncementCalls =
+                getMiddlewareCalls(CtsDownloadService.METHOD_ADD_SERVICE_ANNOUNCEMENT);
+        assertEquals(1, addServiceAnnouncementCalls.size());
+    }
+
+    @Test
     public void testSetTempFileDirectory() throws Exception {
         String tempFileDirName = "CTSTestDir";
         File tempFileRootDirectory = new File(mContext.getFilesDir(), tempFileDirName);
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
index feda07b..a9097bb 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
@@ -27,6 +27,7 @@
 import android.content.IntentFilter;
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.DownloadableSubscription;
+import android.telephony.euicc.EuiccCardManager;
 import android.telephony.euicc.EuiccInfo;
 import android.telephony.euicc.EuiccManager;
 
@@ -51,6 +52,7 @@
     private static final String ACTION_DOWNLOAD_SUBSCRIPTION = "cts_download_subscription";
     private static final String ACTION_DELETE_SUBSCRIPTION = "cts_delete_subscription";
     private static final String ACTION_SWITCH_TO_SUBSCRIPTION = "cts_switch_to_subscription";
+    private static final String ACTION_ERASE_SUBSCRIPTIONS = "cts_erase_subscriptions";
     private static final String ACTION_START_TEST_RESOLUTION_ACTIVITY =
             "cts_start_test_resolution_activity";
     private static final String ACTIVATION_CODE = "1$LOCALHOST$04386-AGYFT-A74Y8-3F815";
@@ -60,6 +62,7 @@
                     ACTION_DOWNLOAD_SUBSCRIPTION,
                     ACTION_DELETE_SUBSCRIPTION,
                     ACTION_SWITCH_TO_SUBSCRIPTION,
+                    ACTION_ERASE_SUBSCRIPTIONS,
                     ACTION_START_TEST_RESOLUTION_ACTIVITY,
             };
 
@@ -205,6 +208,37 @@
     }
 
     @Test
+    public void testEraseSubscriptions() {
+        // test disabled state only for now
+        if (mEuiccManager.isEnabled()) {
+            return;
+        }
+
+        // set up CountDownLatch and receiver
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        mCallbackReceiver = new CallbackReceiver(countDownLatch);
+        getContext()
+                .registerReceiver(
+                        mCallbackReceiver, new IntentFilter(ACTION_ERASE_SUBSCRIPTIONS));
+
+        // call eraseSubscriptions()
+        PendingIntent callbackIntent = createCallbackIntent(ACTION_ERASE_SUBSCRIPTIONS);
+        mEuiccManager.eraseSubscriptions(EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
+                callbackIntent);
+
+        // wait for callback
+        try {
+            countDownLatch.await(CALLBACK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail(e.toString());
+        }
+
+        // verify correct result code is received
+        assertEquals(
+                EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, mCallbackReceiver.getResultCode());
+    }
+
+    @Test
     public void testStartResolutionActivity() {
         // set up CountDownLatch and receiver
         CountDownLatch countDownLatch = new CountDownLatch(1);
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 5e28767..c665116 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -1672,6 +1672,45 @@
         overrideCarrierConfig(null);
     }
 
+    @Test
+    public void testProvisioningManagerRcsProvisioningCaps() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        triggerFrameworkConnectToCarrierImsService();
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true);
+        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
+        overrideCarrierConfig(bundle);
+
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(sTestSub);
+
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            automan.adoptShellPermissionIdentity();
+            boolean provisioningStatus = provisioningManager.getRcsProvisioningStatusForCapability(
+                    RCS_CAP_PRESENCE);
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    !provisioningStatus);
+            // Make sure the change in provisioning status is correctly returned.
+            assertEquals(!provisioningStatus,
+                    provisioningManager.getRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE));
+            // TODO: Enhance test to make sure the provisioning change is also sent to the
+            // ImsService
+
+            // set back to current status
+            provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+                    provisioningStatus);
+        } finally {
+            automan.dropShellPermissionIdentity();
+        }
+
+        overrideCarrierConfig(null);
+    }
+
     private void verifyIntKey(ProvisioningManager pm,
             LinkedBlockingQueue<Pair<Integer, Integer>> intQueue, int key, int value)
             throws Exception {
@@ -1700,6 +1739,10 @@
                 TestImsService.LATCH_CREATE_MMTEL));
         assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
                 TestImsService.LATCH_MMTEL_READY));
+        int serviceSlot = sServiceConnector.getCarrierService().getMmTelFeature().getSlotIndex();
+        assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+                        + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature",
+                sTestSlot, serviceSlot);
         // Wait until ImsSmsDispatcher connects and calls onReady.
         assertTrue(sServiceConnector.getCarrierService().getMmTelFeature().getSmsImplementation()
                 .waitForOnReadyLatch());
@@ -1729,6 +1772,10 @@
         // Make sure the RcsFeature was created in the test service.
         assertNotNull("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
                 + "called!", sServiceConnector.getCarrierService().getRcsFeature());
+        int serviceSlot = sServiceConnector.getCarrierService().getRcsFeature().getSlotIndex();
+        assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+                        + "assigned slot (" + serviceSlot + "+ for the associated RcsFeature",
+                sTestSlot, serviceSlot);
     }
 
     private void triggerFrameworkConnectToCarrierImsService() throws Exception {
@@ -1744,6 +1791,33 @@
                 .waitForLatchCountdown(TestImsService.LATCH_MMTEL_READY));
         assertNotNull("ImsService created, but ImsService#createMmTelFeature was not called!",
                 sServiceConnector.getCarrierService().getMmTelFeature());
+        int serviceSlot = sServiceConnector.getCarrierService().getMmTelFeature().getSlotIndex();
+        assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+                        + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature",
+                sTestSlot, serviceSlot);
+    }
+
+    // Waiting for ImsRcsManager to become public before implementing RegistrationManager,
+    // Duplicate these methods for now.
+    private void verifyRegistrationState(ImsRcsManager regManager, int expectedState)
+            throws Exception {
+        LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
+        assertTrue(ImsUtils.retryUntilTrue(() -> {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(regManager,
+                    (m) -> m.getRegistrationState(getContext().getMainExecutor(), mQueue::offer));
+            return waitForIntResult(mQueue) == expectedState;
+        }));
+    }
+
+    // Waiting for ImsRcsManager to become public before implementing RegistrationManager,
+    // Duplicate these methods for now.
+    private void verifyRegistrationTransportType(ImsRcsManager regManager,
+            int expectedTransportType) throws Exception {
+        LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(regManager,
+                (m) -> m.getRegistrationTransportType(getContext().getMainExecutor(),
+                        mQueue::offer));
+        assertEquals(expectedTransportType, waitForIntResult(mQueue));
     }
 
     private void verifyRegistrationState(RegistrationManager regManager, int expectedState)
diff --git a/tests/tests/tethering/AndroidTest.xml b/tests/tests/tethering/AndroidTest.xml
index 25051ba..d0a2bce 100644
--- a/tests/tests/tethering/AndroidTest.xml
+++ b/tests/tests/tethering/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Tethering test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
diff --git a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
index bbb9403..8665c7e 100644
--- a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -26,7 +26,6 @@
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -59,7 +58,10 @@
 import android.net.cts.util.CtsNetUtils;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.ResultReceiver;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
 import androidx.annotation.NonNull;
@@ -676,6 +678,26 @@
             mTM.requestLatestTetheringEntitlementResult(
                     TETHERING_WIFI, false, c -> c.run(), null);
         } catch (IllegalArgumentException expect) { }
+
+        // Override carrier config to ignore entitlement check.
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
+        overrideCarrierConfig(bundle);
+
+        // Verify that requestLatestTetheringEntitlementResult() can get entitlement
+        // result TETHER_ERROR_NO_ERROR due to provisioning bypassed.
+        assertEntitlementResult(listener -> mTM.requestLatestTetheringEntitlementResult(
+                TETHERING_WIFI, false, c -> c.run(), listener), TETHER_ERROR_NO_ERROR);
+
+        // Reset carrier config.
+        overrideCarrierConfig(null);
+    }
+
+    private void overrideCarrierConfig(PersistableBundle bundle) {
+        final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        final int subId = SubscriptionManager.getDefaultSubscriptionId();
+        configManager.overrideConfig(subId, bundle);
     }
 
     @Test
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
index 1d3a5ed..7a512af 100644
--- a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
@@ -38,7 +38,9 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -48,6 +50,7 @@
 import android.widget.ProgressBar;
 import android.widget.cts.util.TestUtils;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ActivityTestRule;
@@ -696,6 +699,30 @@
         assertEquals(maxHeight, mProgressBar.getMaxHeight());
     }
 
+    @Test
+    public void testAnimationNotOverwriteNewProgress() throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            mProgressBarHorizontal.setMin(0);
+            mProgressBarHorizontal.setMax(10);
+            mProgressBarHorizontal.setProgress(10, true);
+            mProgressBarHorizontal.setProgress(0, false);
+        });
+        // wait for completion of animation.
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        SystemClock.sleep(80);
+        assertEquals(0, mProgressBarHorizontal.getProgress());
+        // test level of progress drawable
+        Drawable progressDrawable = mProgressBarHorizontal.getCurrentDrawable();
+        if (progressDrawable instanceof LayerDrawable) {
+            Drawable d = ((LayerDrawable) progressDrawable)
+                    .findDrawableByLayerId(android.R.id.progress);
+            if (d != null) {
+                progressDrawable = d;
+            }
+        }
+        assertEquals(0, progressDrawable.getLevel());
+    }
+
     /*
      * Mock class for ProgressBar to test protected methods
      */