Merge "IME OnBackInvokedCallback migration CTS tests" into tm-dev
diff --git a/apps/CtsVerifier/res/layout/audio_tap2tone_activity.xml b/apps/CtsVerifier/res/layout/audio_tap2tone_activity.xml
index 65994c0..2848130 100644
--- a/apps/CtsVerifier/res/layout/audio_tap2tone_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_tap2tone_activity.xml
@@ -25,6 +25,57 @@
         android:layout_height="wrap_content"
         android:orientation="vertical">
 
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="Pro Audio:"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text=""
+                android:id="@+id/audio_t2t_pro_audio"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="Low Latency:"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text=""
+                android:id="@+id/audio_t2t_low_latency"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="Media Performance Class:"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text=""
+                android:id="@+id/audio_t2t_mpc"/>
+        </LinearLayout>
+
         <include layout="@layout/audio_java_native_api_buttons" />
 
         <LinearLayout
@@ -47,9 +98,29 @@
                 android:id="@+id/tap2tone_stopBtn" />
         </LinearLayout>
 
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:textSize="20sp"
+                android:textStyle="bold"
+                android:text="Required Maximum Latency:"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text=""
+                android:textSize="20sp"
+                android:textStyle="bold"
+                android:id="@+id/audio_t2t_required_latency"/>
+        </LinearLayout>
+
         <TextView
             android:id="@+id/tap2tone_specTxt"
-            android:text="@string/audio_tap2tone_spec"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:textSize="20sp"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ec3e787..9314c4d 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -5389,6 +5389,7 @@
     <string name="audio_general_clear_results">Clear Results</string>
 
     <string name="audio_general_test_not_run">Test Not Run</string>
+    <string name="audio_general_testnotcompleted">Test not completed.</string>
 
     <!-- Audio Loopback Latency Test -->
     <string name="audio_loopback_latency_test">Audio Loopback Latency Test</string>
@@ -5651,16 +5652,18 @@
     <string name="audio_tap2tone">Audio Tap To Tone Test</string>
     <string name="audio_tap2tone_info">This tests the latency from a screen interaction to a
         resulting tone. This time is a combination of touch screen latency and audio latency.\nThis
-        test is best conducted in a quiet room with the device laying on a table. Select
-        the Audio API to test (\"Native\" has the lowest latency) and press the \"Start\" button.
-        Use your fingernail to tap ONCE on the field below to trigger the test tone. DO NOT leave
+        test is best conducted in a quiet room with the device laying on a table.
+        \n\nTo execute test:
+        \n1. Select the Audio API to test (\"Native\" has the lowest latency) and press the \"Start\" button.
+        \n2. Use your fingernail to tap ONCE on the field below to trigger the test tone. DO NOT leave
         the finger resting on the test field. A strong "tick" sound from the fingernail striking
-        the display is necessary to register the start of the test process. After tapping ONCE,
-        wait until the results are displayed in the field above the waveform display before
-        tapping for the next test run.\nFive successful tests runs are required to determine
-        if the test as a whole succeeds.
+        the display is necessary to register the start of the test process.
+        \n3. After tapping ONCE, wait until the results are displayed in the field above the
+        waveform display before tapping for the next test run.
+        \nFive successful tests runs are required to determine if the test as a whole succeeds.
+        \n4. Press the \"Stop"\ button to conclude the test. The results will be displayed along with the passing/failing measurement and requirement.
+        \n\n(see <a href="https://source.android.com/compatibility/12/android-12-cdd#56_audio_latency">Android CDD § 5.6. Audio Latency</a>)
     </string>
-    <string name="audio_tap2tone_spec">80ms or less average latency is STRONGLY RECOMMENDED to pass.</string>
     <string name="audio_tap2tone_too_few">Not enough edges. Use fingernail.</string>
     <string name="audio_tap2tone_too_many">Too many edges. Use fingernail. Ensure there is
     no background noise.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index b4b99db..9f987a2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -40,6 +40,7 @@
 import android.widget.Switch;
 import android.widget.Toast;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -55,6 +56,8 @@
     // Flag of launch app to fetch the unfolded/folded tests in main view from AndroidManifest.xml.
     protected static boolean sInitialLaunch;
 
+    private String[] mRequestedPermissions;
+
     // Enumerates the display modes, including unfolded and folded.
     protected enum DisplayMode {
         UNFOLDED, FOLDED;
@@ -68,7 +71,7 @@
          * Coverts the mode as suffix with brackets for test name.
          *
          * @return A string containing mode with brackets for folded mode;
-         *         empty string for unfolded mode.
+         * empty string for unfolded mode.
          */
         public String asSuffix() {
             if (name().equals(FOLDED.name())) {
@@ -79,7 +82,7 @@
     }
 
     @Override
-    public void onClick (View v) {
+    public void onClick(View v) {
         handleMenuItemSelected(v.getId());
     }
 
@@ -91,10 +94,11 @@
             PackageManager pm = getPackageManager();
             PackageInfo packageInfo = pm.getPackageInfo(
                     getApplicationInfo().packageName, PackageManager.GET_PERMISSIONS);
+            mRequestedPermissions = packageInfo.requestedPermissions;
 
-            if (packageInfo.requestedPermissions != null) {
-                String[] permissionsToRequest = removeString(packageInfo.requestedPermissions,
-                                Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+            if (mRequestedPermissions != null) {
+                String[] permissionsToRequest = removeString(mRequestedPermissions,
+                        Manifest.permission.ACCESS_BACKGROUND_LOCATION);
                 permissionsToRequest = Arrays.stream(permissionsToRequest).filter(s -> {
                     try {
                         return (pm.getPermissionInfo(s, 0).getProtection() & PROTECTION_DANGEROUS)
@@ -146,10 +150,12 @@
                 // If we're sending them to settings we don't need to request background location
                 // since they can just grant in settings.
                 sendUserToSettings();
-            } else {
-                requestPermissions(new String[] {Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+            } else if (new ArrayList<>(Arrays.asList(mRequestedPermissions)).contains(
+                    Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
+                requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                         CTS_VERIFIER_BACKGROUND_LOCATION_PERMISSION_REQUEST);
             }
+            return;
         }
         if (requestCode == CTS_VERIFIER_BACKGROUND_LOCATION_PERMISSION_REQUEST) {
             if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
@@ -191,7 +197,7 @@
         displayModeSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView,
-                boolean isChecked) {
+                    boolean isChecked) {
                 if (isChecked) {
                     sCurrentDisplayMode = DisplayMode.FOLDED.toString();
                 } else {
@@ -210,20 +216,20 @@
 
     private void handleClearItemSelected() {
         new AlertDialog.Builder(this)
-            .setMessage(R.string.test_results_clear_title)
-            .setPositiveButton(R.string.test_results_clear_yes,
-                    new DialogInterface.OnClickListener() {
-                       public void onClick(DialogInterface dialog, int id) {
-                            mAdapter.clearTestResults();
-                            Toast.makeText(
-                                TestListActivity.this,
-                                R.string.test_results_cleared,
-                                Toast.LENGTH_SHORT)
-                                    .show();
-                       }
-                   })
-            .setNegativeButton(R.string.test_results_clear_cancel, null)
-            .show();
+                .setMessage(R.string.test_results_clear_title)
+                .setPositiveButton(R.string.test_results_clear_yes,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int id) {
+                                mAdapter.clearTestResults();
+                                Toast.makeText(
+                                        TestListActivity.this,
+                                        R.string.test_results_cleared,
+                                        Toast.LENGTH_SHORT)
+                                        .show();
+                            }
+                        })
+                .setNegativeButton(R.string.test_results_clear_cancel, null)
+                .show();
     }
 
     private void handleExportItemSelected() {
@@ -265,7 +271,7 @@
      */
     private String getCurrentDisplayMode() {
         String mode = getSharedPreferences(DisplayMode.class.getName(), MODE_PRIVATE)
-            .getString(DisplayMode.class.getName(), "");
+                .getString(DisplayMode.class.getName(), "");
         return mode;
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
index 9ad290c..9275553 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.verifier.audio;
 
+import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -30,6 +31,7 @@
 import com.android.cts.verifier.CtsVerifierReportLog;
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
+import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
 import com.android.cts.verifier.audio.audiolib.CircularBufferFloat;
 import com.android.cts.verifier.audio.audiolib.StatUtils;
 import com.android.cts.verifier.audio.audiolib.TapLatencyAnalyser;
@@ -104,8 +106,17 @@
     private TapLatencyAnalyser mTapLatencyAnalyser;
 
     // Stats for latency
-    // STRONGLY RECOMMENDED in CDD 5.6
-    private static final int MAX_TAP_2_TONE_LATENCY = 80;   // ms
+    private double mMaxRequiredLatency;
+
+    // REQUIRED CDD  5.6/H-1-1
+    private static final int MAX_TAP_2_TONE_LATENCY_BASIC = 500;  // ms
+    // Requirement for "R" and "S"
+    private static final int MAX_TAP_2_TONE_LATENCY_RS = 100;  // ms
+    // Requirement for "T"
+    private static final int MAX_TAP_2_TONE_LATENCY_T     = 80;   // ms
+    // Requirement for any builds declaring "ProAudio" and "LowLatency"
+    private static final int MAX_TAP_2_TONE_LATENCY_PRO     = 80;   // ms
+    private static final int MAX_TAP_2_TONE_LATENCY_LOW     = 80;   // ms
 
     // Test API (back-end) IDs
     private static final int NUM_TEST_APIS = 2;
@@ -113,8 +124,8 @@
     private static final int TEST_API_JAVA = 1;
     private int mActiveTestAPI = TEST_API_NATIVE;
 
-    private int[] mNumMeasurements = new int[NUM_TEST_APIS];    // ms
-    private int[] mLatencySumSamples = new int[NUM_TEST_APIS];  // ms
+    private int[] mNumMeasurements = new int[NUM_TEST_APIS];
+    private int[] mLatencySumSamples = new int[NUM_TEST_APIS];
     private double[] mLatencyMin = new double[NUM_TEST_APIS];   // ms
     private double[] mLatencyMax = new double[NUM_TEST_APIS];   // ms
     private double[] mLatencyAve = new double[NUM_TEST_APIS];   // ms
@@ -138,6 +149,48 @@
         super.onCreate(savedInstanceState);
 
         // Setup UI
+        String yesString = getResources().getString(R.string.audio_general_yes);
+        String noString = getResources().getString(R.string.audio_general_no);
+
+        boolean claimsProAudio = AudioSystemFlags.claimsProAudio(this);
+        boolean claimsLowLatencyAudio = AudioSystemFlags.claimsLowLatencyAudio(this);
+
+        ((TextView) findViewById(R.id.audio_t2t_pro_audio))
+                .setText(claimsProAudio ? yesString : noString);
+        ((TextView) findViewById(R.id.audio_t2t_low_latency))
+                .setText(claimsLowLatencyAudio ? yesString : noString);
+
+        String mediaPerformanceClassString;
+        if (Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.TIRAMISU) {
+            mediaPerformanceClassString = "T";
+        } else if (Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.S)  {
+            mediaPerformanceClassString = "S";
+        } else if (Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.R) {
+            mediaPerformanceClassString = "R";
+        } else {
+            mediaPerformanceClassString = "none";
+        }
+        ((TextView) findViewById(R.id.audio_t2t_mpc)).setText(mediaPerformanceClassString);
+
+        // Note: These tests need to be ordered such that we find the LOWEST allowable latency
+        mMaxRequiredLatency = MAX_TAP_2_TONE_LATENCY_BASIC;
+        if (claimsProAudio) {
+            mMaxRequiredLatency = Math.min(mMaxRequiredLatency, MAX_TAP_2_TONE_LATENCY_PRO);
+        }
+        if (claimsLowLatencyAudio) {
+            mMaxRequiredLatency = Math.min(mMaxRequiredLatency, MAX_TAP_2_TONE_LATENCY_LOW);
+        }
+        if (Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.TIRAMISU) {
+            mMaxRequiredLatency = Math.min(mMaxRequiredLatency, MAX_TAP_2_TONE_LATENCY_T);
+        }
+        if (Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.R
+                || Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.S) {
+            mMaxRequiredLatency = Math.min(mMaxRequiredLatency, MAX_TAP_2_TONE_LATENCY_RS);
+        }
+
+        ((TextView) findViewById(R.id.audio_t2t_required_latency))
+                .setText("" + mMaxRequiredLatency + "ms");
+
         mStartBtn = (Button) findViewById(R.id.tap2tone_startBtn);
         mStartBtn.setOnClickListener(this);
         mStopBtn = (Button) findViewById(R.id.tap2tone_stopBtn);
@@ -237,7 +290,7 @@
 
     private void clearResults() {
         resetStats();
-        mSpecView.setText(getResources().getString(R.string.audio_tap2tone_spec));
+        mSpecView.setText("");
         mResultsView.setText("");
         mStatsView.setText("");
     }
@@ -248,21 +301,24 @@
     }
 
     private void calculateTestPass() {
-        // 80ms is currently STRONGLY RECOMMENDED, so pass the test as long as they have run it.
         boolean testCompleted = mTestPhase >= NUM_TEST_PHASES;
-        boolean pass = mLatencyAve[mActiveTestAPI] != 0
-                && mLatencyAve[mActiveTestAPI] <= MAX_TAP_2_TONE_LATENCY;
-
-        if (testCompleted) {
-            if (pass) {
-                mSpecView.setText("Ave: " + mLatencyAve[mActiveTestAPI] + " ms <= "
-                        + MAX_TAP_2_TONE_LATENCY + " ms -- PASS");
-            } else {
-                mSpecView.setText("Ave: " + mLatencyAve[mActiveTestAPI] + " ms > "
-                        + MAX_TAP_2_TONE_LATENCY + " ms -- DOES NOT MEET STRONGLY RECOMMENDED");
-            }
+        if (!testCompleted) {
+            mSpecView.setText(getResources().getString(R.string.audio_general_testnotcompleted));
+            getPassButton().setEnabled(false);
+            return;
         }
-        getPassButton().setEnabled(testCompleted);
+
+        double averageLatency = mLatencyAve[mActiveTestAPI];
+        boolean pass = averageLatency != 0 && averageLatency <= mMaxRequiredLatency;
+
+        if (pass) {
+            mSpecView.setText("Average: " + averageLatency + " ms <= "
+                    + mMaxRequiredLatency + " ms -- PASS");
+        } else {
+            mSpecView.setText("Average: " + averageLatency + " ms > "
+                    + mMaxRequiredLatency + " ms -- FAIL");
+        }
+        getPassButton().setEnabled(pass);
     }
 
     private void recordTestStatus() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
index bcc8ce9..106c7e9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
@@ -126,11 +126,11 @@
     }
 
     private boolean shouldPerformTest(int state) {
-        if (state == STATE_VERIFY_SIZE_CALLBACK
-                && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+        if (state == STATE_VERIFY_SIZE_CALLBACK) {
+            // TODO: revert when b/228227212 is fixed (underlying cause of b/204831731)
             return false;
-        } else if (state == STATE_VERIFY_RESIZE
-                && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) {
+        } else if (state == STATE_VERIFY_RESIZE) {
+            // TODO: revert when b/228227212 is fixed (underlying cause of b/204831731)
             return false;
         } else if (state == STATE_VERIFY_COLLECTIONS
                 && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) {
@@ -139,8 +139,7 @@
                 && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
             return false;
         }
-        // TODO: revert when b/228227212 is fixed (underlying cause of b/204831731)
-        return false;
+        return true;
     }
 
     @Override
diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
index ef10a81..0c91c75 100644
--- a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
+++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
@@ -24,6 +24,6 @@
     ],
     srcs: ["src/**/*.java"],
     sdk_version: "test_current",
-    target_sdk_version: "current",
+    target_sdk_version: "33",
     min_sdk_version: "30",
 }
diff --git a/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java b/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java
index 8deeb76..12fbb3c 100644
--- a/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java
+++ b/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java
@@ -73,6 +73,7 @@
         EXCEPTION_PATTERNS.add(":: 1002");          // used by remote control
         EXCEPTION_PATTERNS.add(":: 1020");          // used by remote control
         EXCEPTION_PATTERNS.add("0.0.0.0:7275");     // used by supl
+        EXCEPTION_PATTERNS.add("0.0.0.0:68");       // DHCP server for Tethering
         // b/150186547 ports
         EXCEPTION_PATTERNS.add("192.168.17.10:48881");
         EXCEPTION_PATTERNS.add("192.168.17.10:48896");
diff --git a/hostsidetests/car/Android.bp b/hostsidetests/car/Android.bp
index caa8290..3fab74a 100644
--- a/hostsidetests/car/Android.bp
+++ b/hostsidetests/car/Android.bp
@@ -42,6 +42,7 @@
         "general-tests",
     ],
     static_libs: [
+        "car-cts-host-util",
         "cts-statsd-atom-host-test-utils",
     ],
     data: [
diff --git a/hostsidetests/car/nonrecoverable/Android.bp b/hostsidetests/car/nonrecoverable/Android.bp
new file mode 100644
index 0000000..538e0d2
--- /dev/null
+++ b/hostsidetests/car/nonrecoverable/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "CtsCarHostNonRecoverableTestCases",
+    defaults: ["cts_defaults"],
+    // Only compile source java files in this apk.
+    srcs: [
+        "src/**/*.java",
+    ],
+    libs: [
+        "cts-tradefed",
+        "tradefed",
+        "compatibility-host-util",
+        "truth-prebuilt",
+    ],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    static_libs: [
+        "car-cts-host-util",
+        "cts-statsd-atom-host-test-utils",
+    ],
+}
diff --git a/hostsidetests/car/nonrecoverable/AndroidTest.xml b/hostsidetests/car/nonrecoverable/AndroidTest.xml
new file mode 100644
index 0000000..eb03b30
--- /dev/null
+++ b/hostsidetests/car/nonrecoverable/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Car Host Non-Recoverable Tests">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="auto" />
+    <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" />
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsCarHostNonRecoverableTestCases.jar" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/hostsidetests/car/nonrecoverable/TEST_MAPPING b/hostsidetests/car/nonrecoverable/TEST_MAPPING
new file mode 100644
index 0000000..fd34970
--- /dev/null
+++ b/hostsidetests/car/nonrecoverable/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "auto-presubmit": [
+    {
+      "name": "CtsCarHostNonRecoverableTestCases"
+    }
+  ]
+}
diff --git a/hostsidetests/car/src/android/car/cts/CarServiceHelperServiceTest.java b/hostsidetests/car/nonrecoverable/src/android/car/cts/CarServiceHelperServiceTest.java
similarity index 100%
rename from hostsidetests/car/src/android/car/cts/CarServiceHelperServiceTest.java
rename to hostsidetests/car/nonrecoverable/src/android/car/cts/CarServiceHelperServiceTest.java
diff --git a/hostsidetests/car/util/Android.bp b/hostsidetests/car/util/Android.bp
new file mode 100644
index 0000000..ab1fb2e
--- /dev/null
+++ b/hostsidetests/car/util/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+    name: "car-cts-host-util",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+    libs: [
+        "tradefed",
+        "compatibility-host-util",
+    ],
+    static_libs: [
+        "cts-statsd-atom-host-test-utils",
+    ],
+}
diff --git a/hostsidetests/car/src/android/car/cts/CarHostJUnit4TestCase.java b/hostsidetests/car/util/src/android/car/cts/CarHostJUnit4TestCase.java
similarity index 100%
rename from hostsidetests/car/src/android/car/cts/CarHostJUnit4TestCase.java
rename to hostsidetests/car/util/src/android/car/cts/CarHostJUnit4TestCase.java
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 418f926..560e10f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -697,6 +697,10 @@
         final String SECURE_SETTING_CATEGORY = "secure";
         final String GLOBAL_SETTING_CATEGORY = "global";
         final File apk = mBuildHelper.getTestFile(TEST_APP_APK);
+
+        // Needed to access dpm.getPolicyExemptApps()
+        allowTestApiAccess(DEVICE_ADMIN_PKG);
+
         try {
             // Install the test and prepare the test apk.
             installAppAsUser(PACKAGE_INSTALLER_APK, mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index a4b5c71..4ba614b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -422,6 +422,15 @@
     public void testPermissionPrompts() throws Exception {
     }
 
+
+    @Override
+    @LargeTest
+    @Test
+    @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't have UI")
+    public void testPackageInstallUserRestrictions() throws Exception {
+        super.testPackageInstallUserRestrictions();
+    }
+
     @Override
     @Test
     @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0241/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0241/Android.bp
new file mode 100644
index 0000000..ac0cbd4
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0241/Android.bp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "CVE-2020-0241",
+    defaults: [
+        "cts_hostsidetests_securitybulletin_defaults",
+    ],
+    srcs: [
+        "poc.cpp",
+    ],
+    compile_multilib: "32",
+    header_libs: [
+        "libmediadrm_headers",
+    ],
+    shared_libs: [
+        "libutils",
+        "libbinder",
+        "libmedia",
+        "libmediaplayerservice",
+    ],
+    include_dirs: [
+        "frameworks/av/media/libmediaplayerservice/nuplayer/include/",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0241/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0241/poc.cpp
new file mode 100644
index 0000000..3e6a5ec
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0241/poc.cpp
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../includes/common.h"
+#include <nuplayer/NuPlayerStreamListener.h>
+#include <stdlib.h>
+
+const size_t kBufferSize = 1024;
+
+using namespace android;
+
+bool isTestInProgress = false;
+
+struct sigaction new_action, old_action;
+
+void sigabrt_handler(int signum, siginfo_t *info, void *context) {
+  if (isTestInProgress && info->si_signo == SIGABRT) {
+    (*old_action.sa_sigaction)(signum, info, context);
+    return;
+  }
+  exit(EXIT_FAILURE);
+}
+
+class StreamSource : public IStreamSource {
+public:
+  void setListener(const sp<IStreamListener> &listener
+                   __attribute__((unused))) {}
+  void setBuffers(const Vector<sp<IMemory>> &buffers __attribute__((unused))) {}
+  void onBufferAvailable(size_t index __attribute__((unused))) {}
+
+protected:
+  IBinder *onAsBinder() { return (IBinder *)malloc(kBufferSize); }
+};
+
+int main() {
+  sigemptyset(&new_action.sa_mask);
+  new_action.sa_flags = SA_SIGINFO;
+  new_action.sa_sigaction = sigabrt_handler;
+  sigaction(SIGABRT, &new_action, &old_action);
+
+  const sp<StreamSource> source = new StreamSource();
+  FAIL_CHECK(source != nullptr);
+  isTestInProgress = true;
+  sp<NuPlayer::NuPlayerStreamListener> listener =
+      new NuPlayer::NuPlayerStreamListener(source, nullptr);
+  isTestInProgress = false;
+  return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java
new file mode 100644
index 0000000..237ed83
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0241 extends SecurityTestCase {
+
+    /**
+     * b/151456667
+     * Vulnerability Behavior : SIGABRT in self
+     * Vulnerable Library     : libmediaplayerservice (As per AOSP code)
+     * Vulnerable Function    : android::NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener
+                                (As per AOSP code)
+     */
+    @AsbSecurityTest(cveBugId = 151456667)
+    @Test
+    public void testPocCVE_2020_0241() throws Exception {
+        pocPusher.only32();
+        String binaryName = "CVE-2020-0241";
+        AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+        testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+                .setBacktraceIncludes(new BacktraceFilterPattern("libmediaplayerservice",
+                        "android::NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener"));
+        String signals[] = {CrashUtils.SIGABRT};
+        testConfig.config.setSignals(signals);
+        testConfig.config.setAbortMessageIncludes(
+                AdbUtils.escapeRegexSpecialChars("Pure virtual function called"));
+        AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java
new file mode 100644
index 0000000..7487d15
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0315 extends StsExtraBusinessLogicHostTestBase  {
+    static final String TEST_PKG = "android.security.cts.CVE_2021_0315";
+    ITestDevice mDevice;
+
+    @After
+    public void tearDown() throws Exception {
+        AdbUtils.runCommandLine("input keyevent KEYCODE_BACK", mDevice);
+    }
+
+    @AsbSecurityTest(cveBugId = 169763814)
+    @Test
+    public void testPocCVE_2021_0315() throws Exception {
+        mDevice = getDevice();
+        uninstallPackage(mDevice, TEST_PKG);
+
+        /* Wake up the screen */
+        AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", mDevice);
+        AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", mDevice);
+        AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", mDevice);
+
+        installPackage("CVE-2021-0315.apk");
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testOverlayButtonPresence");
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java
new file mode 100644
index 0000000..a8256d6
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2022_20115 extends StsExtraBusinessLogicHostTestBase {
+    private static final String TEST_PKG = "android.security.cts.CVE_2022_20115";
+    private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+    private static final String TEST_APP = "CVE-2022-20115.apk";
+
+    @AsbSecurityTest(cveBugId = 210118427)
+    @Test
+    public void testPocCVE_2022_20115() throws Exception {
+        ITestDevice device = getDevice();
+        uninstallPackage(device, TEST_PKG);
+
+        /* Wake up the screen */
+        AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+        AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+        AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+        installPackage(TEST_APP);
+
+        Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testCellLocation"));
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/Android.bp
new file mode 100644
index 0000000..5fbde22
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "CVE-2021-0315",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.core",
+    ],
+    platform_apis: true,
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/AndroidManifest.xml
new file mode 100644
index 0000000..9a2afd5
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.security.cts.CVE_2021_0315"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <application android:label="CVE-2021-0315">
+        <service android:name=".PocService" />
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2021_0315" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/res/values/strings.xml
new file mode 100644
index 0000000..38f57bf
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+    <string name="accountName">abc@xyz.org</string>
+    <string name="accountType">com.sampleAccType</string>
+    <string name="activityNotFoundMsg">The activity with intent %1$s was not found</string>
+    <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+    <string name="cmdDumpsysActivity">dumpsys activity %1$s</string>
+    <string name="errorAuthResponse">Got an error in GrantCredentialsPermissionActivity
+    AccountAuthenticatorResponse with errorCode = %1$s and errorMessage = %2$s</string>
+    <string name="exShellCmdDumpsys">Got an exception while running shell cmd dumpsys activity
+    </string>
+    <string name="failMsg">Device is vulnerable to b/169763814 hence any app with
+    "SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string>
+    <string name="overlayButtonText">OverlayButton</string>
+    <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+    <string name="mResumedTrue">mResumed=true</string>
+    <string name="startServiceExMsg">The service with intent %1$s could not be started</string>
+    <string name="vulActivityNotRunningError">The %1$s is not currently running on the device
+    </string>
+    <string name="vulClsName">android.accounts.GrantCredentialsPermissionActivity</string>
+    <string name="vulPkg">android</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/src/android/security/cts/CVE_2021_0315/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/src/android/security/cts/CVE_2021_0315/DeviceTest.java
new file mode 100644
index 0000000..b5c1128
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/src/android/security/cts/CVE_2021_0315/DeviceTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0315;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.GrantCredentialsPermissionActivity;
+import android.accounts.IAccountAuthenticatorResponse;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Process;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+    Context mContext;
+    String mVulActivity = "";
+
+    private void startOverlayService() {
+        Intent intent = new Intent(mContext, PocService.class);
+        assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg),
+                Settings.canDrawOverlays(mContext));
+        try {
+            mContext.startService(intent);
+        } catch (Exception e) {
+            assumeNoException(mContext.getString(R.string.startServiceExMsg, intent), e);
+        }
+    }
+
+    public void startVulnerableActivity() {
+        Intent intent = new Intent();
+        intent.setClassName(mContext.getString(R.string.vulPkg),
+                mContext.getString(R.string.vulClsName));
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT,
+                new Account(mContext.getString(R.string.accountName),
+                        mContext.getString(R.string.accountType)));
+        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE,
+                AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE);
+        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE,
+                new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
+                    @Override
+                    public void onResult(Bundle value) {
+                    }
+
+                    @Override
+                    public void onRequestContinued() {
+                    }
+
+                    @Override
+                    public void onError(int errorCode, String errorMessage) {
+                        assumeTrue(mContext.getString(R.string.errorAuthResponse, errorCode,
+                                errorMessage), false);
+                    }
+                }));
+        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, Process.myUid());
+        PackageManager pm = mContext.getPackageManager();
+        assumeNotNull(pm);
+        ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        assumeNotNull(mContext.getString(R.string.activityNotFoundMsg, intent), ri);
+        assumeNotNull(ri.activityInfo);
+        mVulActivity = ri.activityInfo.name;
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            assumeNoException(e);
+        }
+    }
+
+    @Test
+    public void testOverlayButtonPresence() {
+        mContext = getApplicationContext();
+        assumeNotNull(mContext);
+        UiDevice device = UiDevice.getInstance(getInstrumentation());
+        assumeNotNull(device);
+
+        /* Start the overlay service */
+        startOverlayService();
+
+        /* Wait for the overlay window */
+        Pattern overlayTextPattern = Pattern.compile(mContext.getString(R.string.overlayButtonText),
+                Pattern.CASE_INSENSITIVE);
+        final int launchTimeoutMs = 20000;
+        assumeTrue(mContext.getString(R.string.overlayUiScreenError),
+                device.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs));
+
+        /* Start the vulnerable activity */
+        startVulnerableActivity();
+
+        /* Wait until an object of current activity is gone */
+        boolean overlayDisallowed =
+                device.wait(Until.gone(By.pkg(mContext.getPackageName())), launchTimeoutMs);
+
+        /* Check if the currently running activity is the vulnerable activity */
+        String activityDump = "";
+        try {
+            activityDump = device.executeShellCommand(
+                    mContext.getString(R.string.cmdDumpsysActivity, mVulActivity));
+        } catch (IOException e) {
+            assumeNoException(mContext.getString(R.string.exShellCmdDumpsys), e);
+        }
+        Pattern activityPattern = Pattern.compile(mContext.getString(R.string.mResumedTrue),
+                Pattern.CASE_INSENSITIVE);
+        assumeTrue(mContext.getString(R.string.vulActivityNotRunningError, mVulActivity),
+                activityPattern.matcher(activityDump).find());
+
+        /* Failing the test as fix is not present */
+        assertTrue(mContext.getString(R.string.failMsg, mVulActivity), overlayDisallowed);
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/src/android/security/cts/CVE_2021_0315/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/src/android/security/cts/CVE_2021_0315/PocService.java
new file mode 100644
index 0000000..319546d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0315/src/android/security/cts/CVE_2021_0315/PocService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0315;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+
+public class PocService extends Service {
+    Button mButton;
+    WindowManager mWindowManager;
+    LayoutParams mLayoutParams;
+
+    private int getScreenWidth() {
+        return Resources.getSystem().getDisplayMetrics().widthPixels;
+    }
+
+    private int getScreenHeight() {
+        return Resources.getSystem().getDisplayMetrics().heightPixels;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWindowManager = getSystemService(WindowManager.class);
+        mLayoutParams = new LayoutParams();
+        mLayoutParams.type = LayoutParams.TYPE_APPLICATION_OVERLAY;
+        mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;
+        mLayoutParams.format = PixelFormat.OPAQUE;
+        mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+        mLayoutParams.width = getScreenWidth();
+        mLayoutParams.height = getScreenHeight();
+        mLayoutParams.x = getScreenWidth() / 2;
+        mLayoutParams.y = getScreenHeight() / 2;
+
+        /* Show the floating window */
+        assumeTrue(getString(R.string.canNotDrawOverlaysMsg), Settings.canDrawOverlays(this));
+        mButton = new Button(this);
+        mButton.setText(getString(R.string.overlayButtonText));
+        mWindowManager.addView(mButton, mLayoutParams);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mWindowManager != null && mButton != null) {
+            mWindowManager.removeView(mButton);
+        }
+        super.onDestroy();
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/Android.bp
new file mode 100644
index 0000000..4cb6abe
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/Android.bp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "CVE-2022-20115",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.core",
+        "compatibility-device-util-axt",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/AndroidManifest.xml
new file mode 100644
index 0000000..5a48cbb
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.security.cts.CVE_2022_20115"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+
+    <application
+        android:allowBackup="true"
+        android:label="CVE_2022_20115"
+        android:supportsRtl="true">
+        <activity android:name="PocActivity" android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2022_20115" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/layout/activity_main.xml
new file mode 100644
index 0000000..b8f7f3f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="CVE-2022-20115"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/values/integers.xml
new file mode 100644
index 0000000..1389507
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/values/integers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<resources>
+    <integer name="PASS">0</integer>
+    <integer name="ASSUMPTION_FAILURE">1</integer>
+    <integer name="FAIL">2</integer>
+    <integer name="MAX_WAIT_TIME_MS">2000</integer>
+    <integer name="BROADCAST_WAIT_TIME_MS">100</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/values/strings.xml
new file mode 100644
index 0000000..0fd17f5
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/res/values/strings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<resources>
+    <string name="activityNotStartedException">Activity Failed to Start!</string>
+    <string name="airplaneModeEnable">enable</string>
+    <string name="airplaneModeDisable">disable</string>
+    <string name="assumptionFailure">ServiceState Object or Bundle returned NULL</string>
+    <string name="errorMessage">Device is vulnerable to b/210118427! hence any app with
+        READ_PHONE_STATE permission can access Cell Location</string>
+    <string name="failedToDisable">Failed to Disable Airplane mode!</string>
+    <string name="failedToEnable">Failed to Enable Airplane mode!</string>
+    <string name="failedToExecute">Failed to execute shell command!</string>
+    <string name="failedToOpenApp">Failed to open the app!</string>
+    <string name="getAirplaneModeStatus">settings get global airplane_mode_on</string>
+    <string name="passMessage">Test Passed.Fix is present.</string>
+    <string name="MESSAGE_KEY">MESSAGE</string>
+    <string name="RESULT_KEY">RESULT</string>
+    <string name="SHARED_PREFERENCE">CVE_2022_20115</string>
+    <string name="serviceStateIntentExtra">android.intent.extra.SERVICE_STATE</string>
+    <string name="sendBroadcast">cmd connectivity airplane-mode</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/src/android/security/cts/CVE_2022_20115/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/src/android/security/cts/CVE_2022_20115/DeviceTest.java
new file mode 100644
index 0000000..d94e577
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/src/android/security/cts/CVE_2022_20115/DeviceTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2022_20115;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.pm.PackageManager;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import com.android.compatibility.common.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+    static final int TIMEOUT_MS = 20000;
+    static final int TIMEOUT_SP = 10;
+    private UiDevice mDevice;
+    private Context mContext;
+
+    @Before
+    public void enableAirplaneMode() {
+        mContext = getApplicationContext();
+        assumeNotNull(mContext);
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        assumeNotNull(mDevice);
+        try {
+            mDevice.executeShellCommand(mContext.getResources().getString(R.string.sendBroadcast)
+                    + " " + mContext.getResources().getString(R.string.airplaneModeEnable));
+            TestUtils.waitUntil(mContext.getResources().getString(R.string.failedToEnable),
+                    TIMEOUT_SP / 2, () -> isAirplaneModeOn());
+        } catch (Exception e) {
+            assumeNoException(mContext.getResources().getString(R.string.failedToExecute), e);
+        }
+    }
+
+    boolean isAirplaneModeOn() {
+        boolean isEnabled = false;
+        try {
+            isEnabled = mDevice
+                    .executeShellCommand(
+                            mContext.getResources().getString(R.string.getAirplaneModeStatus))
+                    .trim().equals("1");
+        } catch (IOException e) {
+            assumeNoException(mContext.getResources().getString(R.string.failedToExecute), e);
+        }
+        return isEnabled;
+    }
+
+    boolean isAirplaneModeOff() {
+        boolean isDisabled = false;
+        try {
+            isDisabled = mDevice
+                    .executeShellCommand(
+                            mContext.getResources().getString(R.string.getAirplaneModeStatus))
+                    .trim().equals("0");
+        } catch (IOException e) {
+            assumeNoException(mContext.getResources().getString(R.string.failedToExecute), e);
+        }
+        return isDisabled;
+    }
+
+    @Test
+    public void testCellLocation() {
+        PackageManager packageManager = mContext.getPackageManager();
+        assumeNotNull(packageManager);
+        final Intent intent = packageManager.getLaunchIntentForPackage(mContext.getPackageName());
+        assumeNotNull(intent);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        try {
+            mContext.startActivity(intent);
+        } catch (Exception e) {
+            assumeNoException(mContext.getString(R.string.activityNotStartedException), e);
+        }
+        assumeTrue(mContext.getResources().getString(R.string.failedToOpenApp), mDevice
+                .wait(Until.hasObject(By.pkg(mContext.getPackageName()).depth(0)), TIMEOUT_MS));
+
+        /* Disable Airplane Mode */
+        try {
+            mDevice.executeShellCommand(mContext.getResources().getString(R.string.sendBroadcast)
+                    + " " + mContext.getResources().getString(R.string.airplaneModeDisable));
+            TestUtils.waitUntil(mContext.getResources().getString(R.string.failedToDisable),
+                    TIMEOUT_SP / 2, () -> isAirplaneModeOff());
+        } catch (Exception e) {
+            assumeNoException(mContext.getResources().getString(R.string.failedToExecute), e);
+        }
+
+        int result = -1;
+        String message = null;
+        SharedPreferences sh = mContext.getSharedPreferences(
+                mContext.getResources().getString(R.string.SHARED_PREFERENCE),
+                mContext.MODE_APPEND);
+        assumeNotNull(sh);
+        final Semaphore preferenceChanged = new Semaphore(0);
+        OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+            @Override
+            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+                if (key.equals(mContext.getResources().getString(R.string.RESULT_KEY))) {
+                    if (sharedPreferences.getInt(key, 0) == mContext.getResources()
+                            .getInteger(R.integer.FAIL)) {
+                        preferenceChanged.release();
+                    }
+                }
+            }
+        };
+        sh.registerOnSharedPreferenceChangeListener(listener);
+        try {
+            preferenceChanged.tryAcquire(TIMEOUT_SP, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            assumeNoException(e);
+        }
+        result = sh.getInt(mContext.getResources().getString(R.string.RESULT_KEY),
+                mContext.getResources().getInteger(R.integer.PASS));
+        message = sh.getString(mContext.getResources().getString(R.string.MESSAGE_KEY),
+                mContext.getResources().getString(R.string.passMessage));
+        assumeTrue(message,
+                result != mContext.getResources().getInteger(R.integer.ASSUMPTION_FAILURE));
+        assertNotEquals(message, result, mContext.getResources().getInteger(R.integer.FAIL));
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/src/android/security/cts/CVE_2022_20115/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/src/android/security/cts/CVE_2022_20115/PocActivity.java
new file mode 100644
index 0000000..8d38d94
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20115/src/android/security/cts/CVE_2022_20115/PocActivity.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2022_20115;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.telephony.CellIdentity;
+import android.telephony.ServiceState;
+import android.telephony.NetworkRegistrationInfo;
+import android.os.Bundle;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class PocActivity extends Activity {
+    private void setResultForSP(int result, String message) {
+        SharedPreferences sh =
+                getSharedPreferences(getString(R.string.SHARED_PREFERENCE), Context.MODE_PRIVATE);
+        SharedPreferences.Editor edit = sh.edit();
+        edit.putInt(getString(R.string.RESULT_KEY), result);
+        edit.putString(getString(R.string.MESSAGE_KEY), message);
+        edit.commit();
+    }
+
+    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            ServiceState serviceStateObj;
+            Bundle bundle = intent.getExtras();
+            if (bundle == null) {
+                setResultForSP(getResources().getInteger(R.integer.ASSUMPTION_FAILURE),
+                        getResources().getString(R.string.assumptionFailure));
+                return;
+            }
+
+            serviceStateObj = bundle.getParcelable(
+                    context.getResources().getString(R.string.serviceStateIntentExtra));
+            if (serviceStateObj == null) {
+                setResultForSP(getResources().getInteger(R.integer.ASSUMPTION_FAILURE),
+                        getResources().getString(R.string.assumptionFailure));
+                return;
+            }
+
+            List<NetworkRegistrationInfo> networkInfoList = new ArrayList<>();
+            networkInfoList = serviceStateObj.getNetworkRegistrationInfoList();
+            for (NetworkRegistrationInfo netRegInfo : networkInfoList) {
+                long startTime = System.currentTimeMillis();
+                CellIdentity cellIdentity = netRegInfo.getCellIdentity();
+                while ((cellIdentity == null) && (System.currentTimeMillis() - startTime < context
+                        .getResources().getInteger(R.integer.MAX_WAIT_TIME_MS))) {
+                    /* waiting to receive airplane mode changes broadcast */
+                    try {
+                        Thread.sleep(context.getResources()
+                                .getInteger(R.integer.BROADCAST_WAIT_TIME_MS));
+                    } catch (InterruptedException e) {
+                        assumeNoException(e);
+                    }
+                }
+
+                if (cellIdentity != null) {
+                    setResultForSP(getResources().getInteger(R.integer.FAIL),
+                            getResources().getString(R.string.errorMessage));
+                } else {
+                    setResultForSP(getResources().getInteger(R.integer.PASS),
+                            getResources().getString(R.string.passMessage));
+                }
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SERVICE_STATE);
+        PocActivity pocActivity = PocActivity.this;
+        pocActivity.registerReceiver(pocActivity.broadcastReceiver, intentFilter);
+    }
+}
diff --git a/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java b/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java
index 76f4723..b716174 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java
@@ -144,7 +144,10 @@
         mLocationTimeZoneManagerShellHelper.stop();
 
         // Reset settings and server flags as best we can.
-        mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(mOriginalAutoDetectionEnabled);
+        if (mTimeZoneDetectorShellHelper.isAutoDetectionEnabled()
+                != mOriginalAutoDetectionEnabled) {
+            mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(mOriginalAutoDetectionEnabled);
+        }
         mLocationShellHelper.setLocationEnabledForCurrentUser(mOriginalLocationEnabled);
         mDeviceConfigShellHelper.restoreDeviceConfigStateForTest(mDeviceConfigPreTestState);
 
diff --git a/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerStatsTest.java b/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerStatsTest.java
index 0be4769..e591d2d 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerStatsTest.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerStatsTest.java
@@ -142,7 +142,10 @@
         mLocationTimeZoneManagerShellHelper.stop();
 
         // Reset settings and server flags as best we can.
-        mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(mOriginalAutoDetectionEnabled);
+        if (mTimeZoneDetectorShellHelper.isAutoDetectionEnabled()
+                != mOriginalAutoDetectionEnabled) {
+            mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(mOriginalAutoDetectionEnabled);
+        }
         mLocationShellHelper.setLocationEnabledForCurrentUser(mOriginalLocationEnabled);
 
         ConfigUtils.removeConfig(getDevice());
diff --git a/tests/PhotoPicker/Android.bp b/tests/PhotoPicker/Android.bp
index 5a9fc33..45ecfe2 100644
--- a/tests/PhotoPicker/Android.bp
+++ b/tests/PhotoPicker/Android.bp
@@ -25,6 +25,7 @@
     test_suites: ["general-tests", "mts-mediaprovider", "cts"],
     sdk_version: "core_current",
     min_sdk_version: "30",
+    target_sdk_version: "33",
     libs: [
             "android.test.base",
             "android.test.runner",
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index 674ee21..201a85d 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -1954,8 +1954,13 @@
 
     @Test
     public void testGetUidProcessState_checkAccess() throws Exception {
-        PermissionUtils.grantPermission(
-                STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+        boolean hasPermissionGrantChanged = false;
+        if (!PermissionUtils.isPermissionGranted(STUB_PACKAGE_NAME,
+                android.Manifest.permission.PACKAGE_USAGE_STATS)) {
+            PermissionUtils.grantPermission(
+                    STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+            hasPermissionGrantChanged = true;
+        }
         int newUserId = UserHandle.USER_NULL;
         try {
             // Verify that calling the API doesn't trigger any exceptions.
@@ -1979,15 +1984,22 @@
             if (newUserId != UserHandle.USER_NULL) {
                 removeUser(newUserId);
             }
-            PermissionUtils.revokePermission(
-                    STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+            if (hasPermissionGrantChanged) {
+                PermissionUtils.revokePermission(
+                        STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+            }
         }
     }
 
     @Test
     public void testGetUidProcessCapabilities_checkAccess() throws Exception {
-        PermissionUtils.grantPermission(
-                STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+        boolean hasPermissionGrantChanged = false;
+        if (!PermissionUtils.isPermissionGranted(STUB_PACKAGE_NAME,
+                android.Manifest.permission.PACKAGE_USAGE_STATS)) {
+            PermissionUtils.grantPermission(
+                    STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+            hasPermissionGrantChanged = true;
+        }
         int newUserId = UserHandle.USER_NULL;
         try {
             // Verify that calling the API doesn't trigger any exceptions.
@@ -2011,8 +2023,10 @@
             if (newUserId != UserHandle.USER_NULL) {
                 removeUser(newUserId);
             }
-            PermissionUtils.revokePermission(
-                    STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+            if (hasPermissionGrantChanged) {
+                PermissionUtils.revokePermission(
+                        STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
+            }
         }
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
index 0f6ce62..9865d1b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
@@ -114,7 +114,7 @@
         mUiBot.waitForIdleSync();
 
         // Verify IME is shown
-        assertMockImeStatus(activity.getRootWindowInsets(), true);
+        assertMockImeStatus(activity, true);
     }
 
     @Test
@@ -526,6 +526,93 @@
         activity.assertAutoFilled();
     }
 
+    @Test
+    public void testCancelFillDialog_showDropdown() throws Exception {
+        // Enable feature and test service
+        enableFillDialogFeature(sContext);
+        enableService();
+
+        // Set response with a dataset > fill dialog should have two buttons
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("Dropdown Presentation"))
+                        .setDialogPresentation(createPresentation("Dialog Presentation"))
+                        .build())
+                .setDialogHeader(createPresentation("Dialog Header"))
+                .setDialogTriggerIds(ID_PASSWORD);
+        sReplier.addResponse(builder.build());
+
+        // Start activity and autofill
+        LoginActivity activity = startLoginActivity();
+        mUiBot.waitForIdleSync();
+
+        sReplier.getNextFillRequest();
+        mUiBot.waitForIdleSync();
+
+        // Click on password field to trigger fill dialog
+        mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
+        mUiBot.waitForIdleSync();
+
+        // Verify the fill dialog shown
+        mUiBot.assertFillDialogDatasets("Dialog Presentation");
+
+        // Touch outside to cancel the fill dialog, should back to dropdown UI
+        mUiBot.touchOutsideDialog();
+        mUiBot.waitForIdleSync();
+
+        mUiBot.assertDatasets("Dropdown Presentation");
+        assertMockImeStatus(activity, true);
+
+        // Set expected value, then select dataset
+        activity.expectAutoFill("dude", "sweet");
+        mUiBot.selectDataset("Dropdown Presentation");
+
+        // Check the results.
+        activity.assertAutoFilled();
+    }
+
+    @Test
+    public void testDismissedFillDialog_showIme() throws Exception {
+        // Enable feature and test service
+        enableFillDialogFeature(sContext);
+        enableService();
+
+        // Set response with a dataset
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("Dropdown Presentation"))
+                        .setDialogPresentation(createPresentation("Dialog Presentation"))
+                        .build())
+                .setDialogHeader(createPresentation("Dialog Header"))
+                .setDialogTriggerIds(ID_PASSWORD);
+        sReplier.addResponse(builder.build());
+
+        // Start activity and autofill
+        LoginActivity activity = startLoginActivity();
+        mUiBot.waitForIdleSync();
+
+        sReplier.getNextFillRequest();
+        mUiBot.waitForIdleSync();
+
+        // Click on password field to trigger fill dialog
+        mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
+        mUiBot.waitForIdleSync();
+
+        // Verify the fill dialog shown
+        mUiBot.assertFillDialogDatasets("Dialog Presentation");
+
+        // Touch "No thanks" button to dismiss the fill dialog
+        mUiBot.clickFillDialogDismiss();
+        mUiBot.waitForIdleSync();
+
+        // Verify IME is shown
+        assertMockImeStatus(activity, true);
+    }
+
     private FieldsNoPasswordActivity startNoPasswordActivity() throws Exception {
         final Intent intent = new Intent(mContext, FieldsNoPasswordActivity.class)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
index 9502925..c9d4fa6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
@@ -39,6 +39,7 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
 import android.autofillservice.cts.R;
+import android.autofillservice.cts.activities.LoginActivity;
 import android.content.AutofillOptions;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -1696,11 +1697,11 @@
     /**
      * Asserts whether mock IME is showing
      */
-    public static void assertMockImeStatus(WindowInsets rootWindowInsets,
+    public static void assertMockImeStatus(LoginActivity activity,
             boolean expectedImeShow) throws Exception {
         Timeouts.MOCK_IME_TIMEOUT.run("assertMockImeStatus(" + expectedImeShow + ")",
                 () -> {
-                    final boolean actual = isImeShowing(rootWindowInsets);
+                    final boolean actual = isImeShowing(activity.getRootWindowInsets());
                     Log.v(TAG, "assertMockImeStatus(): expected=" + expectedImeShow + ", actual="
                             + actual);
                     return actual == expectedImeShow ? "expected" : null;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java
index e51e4c9..134e0c0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java
@@ -1368,6 +1368,26 @@
         selectDataset(picker, dataset);
     }
 
+    /**
+     * Touch outside the fill dialog.
+     */
+    public void touchOutsideDialog() throws Exception {
+        Log.v(TAG, "touchOutsideDialog()");
+        final UiObject2 picker = findFillDialogPicker();
+        mDevice.click(1, picker.getVisibleBounds().top - 1);
+    }
+
+    /**
+     * click dismiss button the fill dialog.
+     */
+    public void clickFillDialogDismiss() throws Exception {
+        Log.v(TAG, "dismissedFillDialog()");
+        final UiObject2 picker = findFillDialogPicker();
+        final UiObject2 noButton =
+                picker.findObject(By.res("android", RESOURCE_ID_FILL_DIALOG_BUTTON_NO));
+        noButton.click();
+    }
+
     private UiObject2 findFillDialogPicker() throws Exception {
         return waitForObject(FILL_DIALOG_SELECTOR, UI_DATASET_PICKER_TIMEOUT);
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
index 49586c4..1ec0a11 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
@@ -82,7 +82,9 @@
 
     @After
     public void tearDown() {
-        mTaskFragmentOrganizer.unregisterOrganizer();
+        if (mTaskFragmentOrganizer != null) {
+            mTaskFragmentOrganizer.unregisterOrganizer();
+        }
     }
 
     public static IBinder getActivityToken(@NonNull Activity activity) {
diff --git a/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java b/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
index 8a5d528..0e794d0 100755
--- a/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
+++ b/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
 import android.quickaccesswallet.NoPermissionQuickAccessWalletService;
 import android.quickaccesswallet.QuickAccessWalletActivity;
@@ -69,6 +70,7 @@
  * Tests parceling of the {@link WalletCard}
  */
 @RunWith(AndroidJUnit4.class)
+@AppModeFull
 public class QuickAccessWalletClientTest {
 
     private static final String SETTING_KEY = "lockscreen_show_wallet";
diff --git a/tests/signature/api-check/shared-libs-api/Android.bp b/tests/signature/api-check/shared-libs-api/Android.bp
index afab2f3..607df48 100644
--- a/tests/signature/api-check/shared-libs-api/Android.bp
+++ b/tests/signature/api-check/shared-libs-api/Android.bp
@@ -24,6 +24,7 @@
     static_libs: [
         "cts-api-signature-test",
         "compatibility-device-util-axt",
+        "junit-params",
     ],
 }
 
@@ -36,6 +37,8 @@
     jarjar_rules: ":cts-android-test-jarjar-rules",
 
     java_resources: [
+        ":cts-current-api-gz",
+        ":cts-shared-libs-names.txt",
         ":CtsSharedLibsApiSignatureTestCases_cts-shared-libs-all-current.api",
         ":CtsSharedLibsApiSignatureTestCases_cts-shared-libs-all-previous.api",
     ],
@@ -60,12 +63,29 @@
     srcs: ["src/**/*.java"],
 }
 
+genrule {
+    name: "cts-shared-libs-names.txt",
+    srcs: [
+        "AndroidManifest.xml",
+    ],
+    out: [
+        "shared-libs-names.txt",
+    ],
+    cmd: "grep 'uses-library' $(in) | cut -f2 -d\\\" | sort > $(out)",
+}
+
 // Generates a zip file containing the current public and system API files for shared libraries.
 genrule {
     name: "CtsSharedLibsApiSignatureTestCases_cts-shared-libs-all-current.api",
     srcs: [
         ":android.net.ipsec.ike{.public.api.txt}",
         ":android.net.ipsec.ike{.system.api.txt}",
+        ":android.test.base{.public.api.txt}",
+        ":android.test.base{.system.api.txt}",
+        ":android.test.runner{.public.api.txt}",
+        ":android.test.runner{.system.api.txt}",
+        ":android.test.mock{.public.api.txt}",
+        ":android.test.mock{.system.api.txt}",
         ":com.android.future.usb.accessory{.public.api.txt}",
         ":com.android.future.usb.accessory{.system.api.txt}",
         ":com.android.libraries.tv.tvsystem{.public.api.txt}",
@@ -82,6 +102,8 @@
         ":com.android.nfc_extras{.system.api.txt}",
         ":javax.obex{.public.api.txt}",
         ":javax.obex{.system.api.txt}",
+        ":org.apache.http.legacy{.public.api.txt}",
+        ":org.apache.http.legacy{.system.api.txt}",
     ],
     tools: [
         "soong_zip",
diff --git a/tests/signature/api-check/shared-libs-api/AndroidTest.xml b/tests/signature/api-check/shared-libs-api/AndroidTest.xml
index 14a54a2..9bd8233 100644
--- a/tests/signature/api-check/shared-libs-api/AndroidTest.xml
+++ b/tests/signature/api-check/shared-libs-api/AndroidTest.xml
@@ -28,6 +28,7 @@
         <option name="package" value="android.signature.cts.api.shared_libs" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="class" value="android.signature.cts.api.SignatureMultiLibsTest" />
+        <option name="instrumentation-arg" key="base-api-files" value="current.api.gz" />
         <option name="instrumentation-arg" key="expected-api-files" value="shared-libs-all-current.api.zip" />
         <option name="instrumentation-arg" key="previous-api-files" value="shared-libs-all-previous.api.zip" />
         <option name="runtime-hint" value="30s" />
diff --git a/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java b/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
index 2bc63f7..2d0f719 100644
--- a/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
+++ b/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
@@ -17,20 +17,33 @@
 package android.signature.cts.api;
 
 import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.SharedLibraryInfo;
 import android.signature.cts.ApiComplianceChecker;
 import android.signature.cts.ApiDocumentParser;
 import android.signature.cts.JDiffClassDescription;
 import android.signature.cts.VirtualPath;
+import android.util.Log;
 import androidx.test.platform.app.InstrumentationRegistry;
-import java.util.Arrays;
+import com.google.common.base.Suppliers;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
-import org.junit.BeforeClass;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import junitparams.naming.TestCaseName;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
 import org.junit.Test;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
 
 /**
  * Verifies that any shared library provided by this device and for which this test has a
@@ -40,23 +53,103 @@
  * {@code <uses-library>} entry for every shared library that provides an API that is contained
  * within the shared-libs-all.api.zip supplied to this test.
  */
-public class SignatureMultiLibsTest extends SignatureTest {
+@RunWith(JUnitParamsRunner.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SignatureMultiLibsTest extends AbstractSignatureTest {
+
+    protected static final Supplier<String[]> EXPECTED_API_FILES =
+            getSupplierOfAMandatoryCommaSeparatedListArgument(EXPECTED_API_FILES_ARG);
+
+    protected static final Supplier<String[]> PREVIOUS_API_FILES =
+            getSupplierOfAMandatoryCommaSeparatedListArgument(PREVIOUS_API_FILES_ARG);
 
     private static final String TAG = SignatureMultiLibsTest.class.getSimpleName();
 
-    private static Set<String> libraries;
+    /**
+     * A memoized supplier of the list of shared libraries on the device.
+     */
+    protected static final Supplier<Set<String>> AVAILABLE_SHARED_LIBRARIES =
+            Suppliers.memoize(SignatureMultiLibsTest::retrieveActiveSharedLibraries)::get;
+
+    private static final String SHARED_LIBRARY_LIST_RESOURCE_NAME = "shared-libs-names.txt";
 
     /**
-     * Obtain a list of shared libraries from the device.
+     * Retrieve the names of the shared libraries that are active on the device.
+     *
+     * @return The set of shared library names.
      */
-    @BeforeClass
-    public static void retrieveListOfSharedLibrariesOnDevice() throws Exception {
+    private static Set<String> retrieveActiveSharedLibraries() {
         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        String result = runShellCommand(instrumentation, "cmd package list libraries");
-        libraries = Arrays.stream(result.split("\n")).map(line -> line.split(":")[1])
-                .peek(library -> System.out.printf("%s: Found library: %s%n",
-                        SignatureMultiLibsTest.class.getSimpleName(), library))
-                .collect(Collectors.toCollection(TreeSet::new));
+        Context context = instrumentation.getTargetContext();
+
+        List<SharedLibraryInfo> sharedLibraries =
+                context.getPackageManager().getSharedLibraries(0);
+
+        Set<String> sharedLibraryNames = new TreeSet<>();
+        for (SharedLibraryInfo sharedLibrary : sharedLibraries) {
+            String name = sharedLibrary.getName();
+            sharedLibraryNames.add(name);
+            Log.d(TAG, String.format("Found library: %s%n", name));
+        }
+
+        return sharedLibraryNames;
+    }
+
+    /**
+     * A memoized supplier of the list of shared libraries that this can test.
+     */
+    protected static final Supplier<List<String>> TESTABLE_SHARED_LIBRARIES =
+            Suppliers.memoize(SignatureMultiLibsTest::retrieveTestableSharedLibraries)::get;
+
+    /**
+     * Retrieve the names of the shared libraries that are testable by this test.
+     *
+     * @return The set of shared library names.
+     */
+    private static List<String> retrieveTestableSharedLibraries() {
+        ClassLoader classLoader = SignatureMultiLibsTest.class.getClassLoader();
+        try (InputStream is = classLoader.getResourceAsStream(SHARED_LIBRARY_LIST_RESOURCE_NAME)) {
+            if (is == null) {
+                throw new RuntimeException(
+                        "Resource " + SHARED_LIBRARY_LIST_RESOURCE_NAME + " could not be found");
+            }
+
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
+                return reader.lines()
+                        .filter(line -> !line.isEmpty())
+                        .sorted()
+                        .collect(Collectors.toList());
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Could not retrieve testable shared libraries", e);
+        }
+    }
+
+    /**
+     * Convert the list of testable shared libraries into a form suitable for parameterizing a test.
+     */
+    public Object[][] getTestableSharedLibraryParameters() {
+        List<String> libraries = TESTABLE_SHARED_LIBRARIES.get();
+        Object[][] params = new Object[libraries.size()][1];
+        for (int i = 0; i < libraries.size(); i++) {
+            String name = libraries.get(i);
+            TestableLibraryParameter param = new TestableLibraryParameter(name);
+            params[i][0] = param;
+        }
+        return params;
+    }
+
+    /**
+     * Skips the test if the supplied library is unavailable on the device.
+     *
+     * <p>If the library is unavailable then this throws an
+     * {@link org.junit.AssumptionViolatedException}.</p>
+     *
+     * @param library the name of the library that needs to be available.
+     */
+    private void skipTestIfLibraryIsUnavailable(String library) {
+        Assume.assumeTrue("Shared library " + library + " is not available on this device",
+                AVAILABLE_SHARED_LIBRARIES.get().contains(library));
     }
 
     /**
@@ -65,29 +158,45 @@
      *
      * @param apiDocumentParser the parser to use.
      * @param apiResources the list of API resource files.
+     * @param library the name of the library whose APIs should be parsed.
      * @return a stream of {@link JDiffClassDescription}.
      */
     private Stream<JDiffClassDescription> parseActiveSharedLibraryApis(
-            ApiDocumentParser apiDocumentParser, String[] apiResources) {
+            ApiDocumentParser apiDocumentParser, String[] apiResources, String library) {
+
         return retrieveApiResourcesAsStream(getClass().getClassLoader(), apiResources)
-                .filter(this::checkLibrary)
+                .filter(path -> {
+                    String apiLibraryName = getLibraryNameFromPath(path);
+                    return apiLibraryName.equals(library);
+                })
                 .flatMap(apiDocumentParser::parseAsStream);
     }
 
     /**
-     * Tests that the device's API matches the expected set defined in xml.
-     * <p/>
-     * Will check the entire API, and then report the complete list of failures
+     * Tests that each shared library's API matches its current API.
+     *
+     * <p>One test per shared library, checks the entire API, and then reports the complete list of
+     * failures.</p>
      */
     @Test
-    public void testSignature() {
+    // Parameterize this method with the set of testable shared libraries.
+    @Parameters(method = "getTestableSharedLibraryParameters")
+    // The test name is the method name followed by and _ and the shared library name, with .s
+    // replaced with _. e.g. testRuntimeCompatibilityWithCurrentApi_android_test_base.
+    @TestCaseName("{method}_{0}")
+    public void testRuntimeCompatibilityWithCurrentApi(TestableLibraryParameter parameter) {
+        String library = parameter.getName();
+        skipTestIfLibraryIsUnavailable(library);
         runWithTestResultObserver(mResultObserver -> {
             ApiComplianceChecker complianceChecker =
                     new ApiComplianceChecker(mResultObserver, mClassProvider);
 
+            // Load classes from any API files that form the base which the expected APIs extend.
+            loadBaseClasses(complianceChecker);
+
             ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
 
-            parseActiveSharedLibraryApis(apiDocumentParser, expectedApiFiles)
+            parseActiveSharedLibraryApis(apiDocumentParser, EXPECTED_API_FILES.get(), library)
                     .forEach(complianceChecker::checkSignatureCompliance);
 
             // After done parsing all expected API files, perform any deferred checks.
@@ -96,17 +205,27 @@
     }
 
     /**
-     * Tests that the device's API matches the previous APIs defined in xml.
+     * Tests that each shared library's API matches its previous APIs.
+     *
+     * <p>One test per shared library, checks the entire API, and then reports the complete list of
+     * failures.</p>
      */
     @Test
-    public void testPreviousSignatures() {
+    // Parameterize this method with the set of testable shared libraries.
+    @Parameters(method = "getTestableSharedLibraryParameters")
+    // The test name is the method name followed by and _ and the shared library name, with .s
+    // replaced with _. e.g. testRuntimeCompatibilityWithPreviousApis_android_test_base.
+    @TestCaseName("{method}_{0}")
+    public void testRuntimeCompatibilityWithPreviousApis(TestableLibraryParameter parameter) {
+        String library = parameter.getName();
+        skipTestIfLibraryIsUnavailable(library);
         runWithTestResultObserver(mResultObserver -> {
             ApiComplianceChecker complianceChecker =
                     new ApiComplianceChecker(mResultObserver, mClassProvider);
 
             ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
 
-            parseActiveSharedLibraryApis(apiDocumentParser, previousApiFiles)
+            parseActiveSharedLibraryApis(apiDocumentParser, PREVIOUS_API_FILES.get(), library)
                     .map(clazz -> clazz.setPreviousApiFlag(true))
                     .forEach(complianceChecker::checkSignatureCompliance);
 
@@ -116,25 +235,37 @@
     }
 
     /**
-     * Check to see if the supplied name is an API file for a shared library that is available on
-     * this device.
+     * Get the library name from the API file's path.
      *
      * @param path the path of the API file.
-     * @return true if the API corresponds to a shared library on the device, false otherwise.
+     * @return the library name for the API file.
      */
-    private boolean checkLibrary (VirtualPath path) {
+    private String getLibraryNameFromPath(VirtualPath path) {
         String name = path.toString();
-        String libraryName = name.substring(name.lastIndexOf('/') + 1).split("-")[0];
-        boolean matched = libraries.contains(libraryName);
-        if (matched) {
-            System.out.printf("%s: Processing API file %s, from library %s as it does match a"
-                            + " shared library on this device%n",
-                    getClass().getSimpleName(), name, libraryName);
-        } else {
-            System.out.printf("%s: Ignoring API file %s, from library %s as it does not match a"
-                    + " shared library on this device%n",
-                    getClass().getSimpleName(), name, libraryName);
+        return name.substring(name.lastIndexOf('/') + 1).split("-")[0];
+    }
+
+    /**
+     * A wrapper around a shared library name to ensure that its string representation is suitable
+     * for use in a parameterized test name, i.e. does not contain any characters that are not
+     * allowed in a test name by CTS/AndroidJUnitRunner.
+     */
+    public static class TestableLibraryParameter {
+        private final String name;
+        private final String parameter;
+
+        public TestableLibraryParameter(String name) {
+            this.name = name;
+            this.parameter = name.replace('.', '_');
         }
-        return matched;
+
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String toString() {
+            return parameter;
+        }
     }
 }
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
index f4d364b..b1dd014 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
@@ -26,24 +26,16 @@
 import android.signature.cts.JDiffClassDescription;
 import android.signature.cts.ResultObserver;
 import android.signature.cts.VirtualPath;
-import android.signature.cts.VirtualPath.LocalFilePath;
-import android.signature.cts.VirtualPath.ResourcePath;
 import android.util.Log;
-
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
-
 import com.android.compatibility.common.util.DynamicConfigDeviceSide;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import com.google.common.base.Suppliers;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.function.Predicate;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
-import java.util.zip.ZipFile;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.runner.RunWith;
 
@@ -62,8 +54,6 @@
      */
     private static final String DYNAMIC_CONFIG_NAME_OPTION = "dynamic-config-name";
 
-    private static final String TAG = "SignatureTest";
-
     private TestResultObserver mResultObserver;
 
     ClassProvider mClassProvider;
@@ -73,6 +63,11 @@
      */
     private Collection<String> expectedFailures = Collections.emptyList();
 
+    @AfterClass
+    public static void closeResourceStore() {
+        ResourceStore.close();
+    }
+
     public Instrumentation getInstrumentation() {
         return InstrumentationRegistry.getInstrumentation();
     }
@@ -176,6 +171,13 @@
         mResultObserver.onTestComplete(); // Will throw is there are failures
     }
 
+    static Supplier<String[]> getSupplierOfAnOptionalCommaSeparatedListArgument(String key) {
+        return Suppliers.memoize(() -> {
+            Bundle arguments = InstrumentationRegistry.getArguments();
+            return getCommaSeparatedListOptional(arguments, key);
+        })::get;
+    }
+
     static String[] getCommaSeparatedListOptional(Bundle instrumentationArgs, String key) {
         String argument = instrumentationArgs.getString(key);
         if (argument == null) {
@@ -184,6 +186,13 @@
         return argument.split(",");
     }
 
+    static Supplier<String[]> getSupplierOfAMandatoryCommaSeparatedListArgument(String key) {
+        return Suppliers.memoize(() -> {
+            Bundle arguments = InstrumentationRegistry.getArguments();
+            return getCommaSeparatedListRequired(arguments, key);
+        })::get;
+    }
+
     static String[] getCommaSeparatedListRequired(Bundle instrumentationArgs, String key) {
         String argument = instrumentationArgs.getString(key);
         if (argument == null) {
@@ -192,47 +201,6 @@
         return argument.split(",");
     }
 
-    private static Stream<VirtualPath> readResource(ClassLoader classLoader, String resourceName) {
-        try {
-            ResourcePath resourcePath =
-                    VirtualPath.get(classLoader, resourceName);
-            if (resourceName.endsWith(".zip")) {
-                // Extract to a temporary file and read from there.
-                Path file = extractResourceToFile(resourceName, resourcePath.newInputStream());
-                return flattenPaths(VirtualPath.get(file.toString()));
-            } else {
-                return Stream.of(resourcePath);
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private static Path extractResourceToFile(String resourceName, InputStream is) throws IOException {
-        Path tempDirectory = Files.createTempDirectory("signature");
-        Path file = tempDirectory.resolve(resourceName);
-        Log.i(TAG, "extractResourceToFile: extracting " + resourceName + " to " + file);
-        Files.copy(is, file);
-        is.close();
-        return file;
-    }
-
-    /**
-     * Given a path in the local file system (possibly of a zip file) flatten it into a stream of
-     * virtual paths.
-     */
-    private static Stream<VirtualPath> flattenPaths(LocalFilePath path) {
-        try {
-            if (path.toString().endsWith(".zip")) {
-                return getZipEntryFiles(path);
-            } else {
-                return Stream.of(path);
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     /**
      * Create a stream of {@link JDiffClassDescription} by parsing a set of API resource files.
      *
@@ -263,18 +231,6 @@
             ClassLoader classLoader,
             String[] apiResources) {
         return Stream.of(apiResources)
-                .flatMap(resourceName -> readResource(classLoader, resourceName));
-    }
-
-    /**
-     * Get the zip entries that are files.
-     *
-     * @param path the path to the zip file.
-     * @return paths to zip entries
-     */
-    private static Stream<VirtualPath> getZipEntryFiles(LocalFilePath path) throws IOException {
-        @SuppressWarnings("resource")
-        ZipFile zip = new ZipFile(path.toFile());
-        return zip.stream().map(entry -> VirtualPath.get(zip, entry));
+                .flatMap(resourceName -> ResourceStore.readResource(classLoader, resourceName));
     }
 }
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractSignatureTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractSignatureTest.java
new file mode 100644
index 0000000..6df224f
--- /dev/null
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractSignatureTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.signature.cts.api;
+
+import android.signature.cts.ApiComplianceChecker;
+import android.signature.cts.ApiDocumentParser;
+import java.util.function.Supplier;
+
+/**
+ * Base class of the tests that check accessibility of API signatures at runtime.
+ */
+public class AbstractSignatureTest extends AbstractApiTest {
+
+    private static final String TAG = AbstractSignatureTest.class.getSimpleName();
+
+    /**
+     * The name of the instrumentation option that contains the list of the current API signatures
+     * that are expected to be accessible.
+     */
+    protected static final String EXPECTED_API_FILES_ARG = "expected-api-files";
+
+    /**
+     * The name of the instrumentation option that contains the list of the previous API signatures
+     * that are expected to be accessible.
+     */
+    protected static final String PREVIOUS_API_FILES_ARG = "previous-api-files";
+
+    /**
+     * Supplier of the list of files specified in the instrumentation argument "base-api-files".
+     */
+    private static final Supplier<String[]> BASE_API_FILES =
+            getSupplierOfAnOptionalCommaSeparatedListArgument("base-api-files");
+
+    /**
+     * Load the base API files into the supplied compliance checker.
+     *
+     * <p>Base API files are not checked by the compliance checker but may be extended by classes
+     * which are checked.</p>
+     *
+     * @param complianceChecker the {@link ApiComplianceChecker} into which the base API will be
+     *                          loaded.
+     */
+    protected void loadBaseClasses(ApiComplianceChecker complianceChecker) {
+        ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
+        parseApiResourcesAsStream(apiDocumentParser, BASE_API_FILES.get())
+                .forEach(complianceChecker::addBaseClass);
+    }
+}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/ResourceStore.java b/tests/signature/api-check/src/java/android/signature/cts/api/ResourceStore.java
new file mode 100644
index 0000000..722ea63
--- /dev/null
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/ResourceStore.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.signature.cts.api;
+
+import android.signature.cts.VirtualPath;
+import android.util.Log;
+import com.google.common.base.Suppliers;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.zip.ZipFile;
+
+/**
+ * Manages local storage of resources that need to be extracted from the Jar into the local
+ * filesystem before use.
+ */
+public class ResourceStore {
+
+    private static final String TAG = ResourceStore.class.getSimpleName();
+
+    /**
+     * Supplier for the temporary directory.
+     */
+    private static final Supplier<Path> TEMPORARY_DIRECTORY = Suppliers.memoize(() -> {
+        try {
+            return Files.createTempDirectory("signature");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    })::get;
+
+    /**
+     * A map from a {@link VirtualPath} to a {@link ZipFile} for zip file resources that
+     * have been extracted from the jar into the local file system.
+     */
+    private static final Map<VirtualPath, ZipFile> extractedZipFiles = new HashMap<>();
+
+    public static Stream<VirtualPath> readResource(ClassLoader classLoader, String resourceName) {
+        try {
+            VirtualPath resourcePath = VirtualPath.get(classLoader, resourceName);
+            if (resourceName.endsWith(".zip")) {
+                // Extract to a temporary file and then read from there. If the resource has already
+                // been extracted before then reuse the previous file.
+                @SuppressWarnings("resource")
+                ZipFile zip = extractedZipFiles.computeIfAbsent(resourcePath, virtualPath -> {
+                    try {
+                        Path path = extractResourceToFile(resourceName, resourcePath);
+                        return new ZipFile(path.toFile());
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+                return zip.stream().map(entry -> VirtualPath.get(zip, entry));
+            } else {
+                return Stream.of(resourcePath);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Close any previously opened {@link ZipFile} instances.
+     */
+    public static void close() {
+        for (ZipFile zipfile: extractedZipFiles.values()) {
+            // If an error occurred when closing the ZipFile log the failure and continue.
+            try {
+                zipfile.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Could not close ZipFile " + zipfile, e);
+            }
+        }
+    }
+
+    private static Path extractResourceToFile(String resourceName, VirtualPath resourcePath)
+            throws IOException {
+        Path tempDirectory = TEMPORARY_DIRECTORY.get();
+        Path file = tempDirectory.resolve(resourceName);
+        try (InputStream is = resourcePath.newInputStream()) {
+            Log.i(TAG, "extractResourceToFile: extracting " + resourceName + " to " + file);
+            Files.copy(is, file);
+        }
+        return file;
+    }
+}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
index 1be75ca..136211f 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
@@ -23,48 +23,55 @@
 import android.signature.cts.FailureType;
 import android.signature.cts.JDiffClassDescription;
 import android.signature.cts.ReflectionHelper;
+import com.google.common.base.Suppliers;
 import java.util.Comparator;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import org.junit.Test;
 
 /**
  * Performs the signature check via a JUnit test.
  */
-public class SignatureTest extends AbstractApiTest {
+public class SignatureTest extends AbstractSignatureTest {
 
     private static final String TAG = SignatureTest.class.getSimpleName();
 
-    protected String[] expectedApiFiles;
-    protected String[] previousApiFiles;
-    protected String[] baseApiFiles;
-    private String[] unexpectedApiFiles;
+    private static final String UNEXPECTED_API_FILES_ARG = "unexpected-api-files";
+
+    private static final Supplier<String[]> EXPECTED_API_FILES =
+            getSupplierOfAnOptionalCommaSeparatedListArgument(EXPECTED_API_FILES_ARG);
+    private static final Supplier<String[]> UNEXPECTED_API_FILES =
+            getSupplierOfAnOptionalCommaSeparatedListArgument(UNEXPECTED_API_FILES_ARG);
+    private static final Supplier<String[]> PREVIOUS_API_FILES =
+            getSupplierOfAnOptionalCommaSeparatedListArgument(PREVIOUS_API_FILES_ARG);
+
+    private static final Supplier<Set<JDiffClassDescription>> UNEXPECTED_CLASSES =
+            Suppliers.memoize(SignatureTest::loadUnexpectedClasses)::get;
 
     @Override
-    protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
-        expectedApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "expected-api-files");
-        baseApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "base-api-files");
-        unexpectedApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "unexpected-api-files");
-        previousApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "previous-api-files");
+    protected void initializeFromArgs(Bundle instrumentationArgs) {
+        String[] expectedApiFiles = EXPECTED_API_FILES.get();
+        String[] unexpectedApiFiles = UNEXPECTED_API_FILES.get();
 
         if (expectedApiFiles.length + unexpectedApiFiles.length == 0) {
             throw new IllegalStateException(
-                    "Expected at least one file to be specified in"
-                            + " 'expected-api-files' or 'unexpected-api-files'");
+                    String.format("Expected at least one file to be specified in '%s' or '%s'",
+                            EXPECTED_API_FILES_ARG, UNEXPECTED_API_FILES_ARG));
         }
     }
 
     /**
-     * Tests that the device's API matches the expected set defined in xml.
-     * <p/>
-     * Will check the entire API, and then report the complete list of failures
+     * Make sure that this APK cannot access any unexpected classes.
+     *
+     * <p>The set of unexpected classes may be empty, in which case this test does nothing.</p>
      */
     @Test
-    public void testSignature() {
+    public void testCannotAccessUnexpectedClasses() {
         runWithTestResultObserver(mResultObserver -> {
-            Set<JDiffClassDescription> unexpectedClasses = loadUnexpectedClasses();
+            Set<JDiffClassDescription> unexpectedClasses = UNEXPECTED_CLASSES.get();
             for (JDiffClassDescription classDescription : unexpectedClasses) {
                 Class<?> unexpectedClass = findUnexpectedClass(classDescription, mClassProvider);
                 if (unexpectedClass != null) {
@@ -74,16 +81,52 @@
                             "Class should not be accessible to this APK");
                 }
             }
+        });
+    }
 
+    /**
+     * Tests that the device's API matches the expected set defined in xml.
+     *
+     * <p>Will check the entire API, and then report the complete list of failures</p>
+     */
+    @Test
+    public void testRuntimeCompatibilityWithCurrentApi() {
+        runWithTestResultObserver(mResultObserver -> {
             ApiComplianceChecker complianceChecker =
                     new ApiComplianceChecker(mResultObserver, mClassProvider);
 
             // Load classes from any API files that form the base which the expected APIs extend.
             loadBaseClasses(complianceChecker);
+
             // Load classes from system API files and check for signature compliance.
+            String[] expectedApiFiles = EXPECTED_API_FILES.get();
+            Set<JDiffClassDescription> unexpectedClasses = UNEXPECTED_CLASSES.get();
             checkClassesSignatureCompliance(complianceChecker, expectedApiFiles, unexpectedClasses,
                     false /* isPreviousApi */);
+
+            // After done parsing all expected API files, perform any deferred checks.
+            complianceChecker.checkDeferred();
+        });
+    }
+
+    /**
+     * Tests that the device's API matches the last few previously released api files.
+     *
+     * <p>Will check all the recently released api files, and then report the complete list of
+     * failures.</p>
+     */
+    @Test
+    public void testRuntimeCompatibilityWithPreviousApis() {
+        runWithTestResultObserver(mResultObserver -> {
+            ApiComplianceChecker complianceChecker =
+                    new ApiComplianceChecker(mResultObserver, mClassProvider);
+
+            // Load classes from any API files that form the base which the expected APIs extend.
+            loadBaseClasses(complianceChecker);
+
             // Load classes from previous API files and check for signature compliance.
+            String[] previousApiFiles = PREVIOUS_API_FILES.get();
+            Set<JDiffClassDescription> unexpectedClasses = UNEXPECTED_CLASSES.get();
             checkClassesSignatureCompliance(complianceChecker, previousApiFiles, unexpectedClasses,
                     true /* isPreviousApi */);
 
@@ -105,9 +148,10 @@
         }
     }
 
-    private Set<JDiffClassDescription> loadUnexpectedClasses() {
+    private static Set<JDiffClassDescription> loadUnexpectedClasses() {
         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
-        return parseApiResourcesAsStream(apiDocumentParser, unexpectedApiFiles)
+        return retrieveApiResourcesAsStream(SignatureTest.class.getClassLoader(), UNEXPECTED_API_FILES.get())
+                .flatMap(apiDocumentParser::parseAsStream)
                 .collect(Collectors.toCollection(SignatureTest::newSetOfClassDescriptions));
     }
 
@@ -115,12 +159,6 @@
         return new TreeSet<>(Comparator.comparing(JDiffClassDescription::getAbsoluteClassName));
     }
 
-    private void loadBaseClasses(ApiComplianceChecker complianceChecker) {
-        ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
-        parseApiResourcesAsStream(apiDocumentParser, baseApiFiles)
-                .forEach(complianceChecker::addBaseClass);
-    }
-
     private void checkClassesSignatureCompliance(ApiComplianceChecker complianceChecker,
             String[] classes, Set<JDiffClassDescription> unexpectedClasses, boolean isPreviousApi) {
         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
@@ -129,5 +167,4 @@
                 .map(clazz -> clazz.setPreviousApiFlag(isPreviousApi))
                 .forEach(complianceChecker::checkSignatureCompliance);
     }
-
 }
diff --git a/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java b/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java
index 140dd6d..51b2ff0 100644
--- a/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java
+++ b/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java
@@ -20,6 +20,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.Objects;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
@@ -55,6 +56,24 @@
 
     public abstract InputStream newInputStream() throws IOException;
 
+    /**
+     * Override as abstract to force sub-classes to implement it.
+     */
+    @Override
+    public abstract int hashCode();
+
+    /**
+     * Override as abstract to force sub-classes to implement it.
+     */
+    @Override
+    public abstract boolean equals(Object obj);
+
+    /**
+     * Override as abstract to force sub-classes to implement it.
+     */
+    @Override
+    public abstract String toString();
+
     public static class LocalFilePath extends VirtualPath {
         private final String path;
 
@@ -76,6 +95,23 @@
         }
 
         @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            LocalFilePath that = (LocalFilePath) o;
+            return path.equals(that.path);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(path);
+        }
+
+        @Override
         public String toString() {
             return path;
         }
@@ -98,6 +134,24 @@
         }
 
         @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            ZipEntryPath that = (ZipEntryPath) o;
+            return zip.getName().equals(that.zip.getName())
+                    && entry.getName().equals(that.entry.getName());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(zip.getName(), entry.getName());
+        }
+
+        @Override
         public String toString() {
             return "zip:file:" + zip.getName() + "!/" + entry.getName();
         }
@@ -119,6 +173,23 @@
         }
 
         @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            ResourcePath that = (ResourcePath) o;
+            return url.equals(that.url);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(url);
+        }
+
+        @Override
         public String toString() {
             return url.toExternalForm();
         }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
index d1e08fc..6138f38 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
@@ -17,6 +17,8 @@
 package android.bluetooth.cts;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
 import static android.bluetooth.BluetoothStatusCodes.FEATURE_SUPPORTED;
 
 import static org.junit.Assert.assertEquals;
@@ -114,7 +116,7 @@
         }
         MockitoAnnotations.initMocks(this);
         mExecutor = mContext.getMainExecutor();
-        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
+        TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT,BLUETOOTH_PRIVILEGED,BLUETOOTH_SCAN);
         mAdapter = TestUtils.getBluetoothAdapterOrDie();
         assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
 
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/app/ActivityManagerHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/app/ActivityManagerHelperTest.java
index 7c23b4d..aaba29f 100644
--- a/tests/tests/car_builtin/src/android/car/cts/builtin/app/ActivityManagerHelperTest.java
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/app/ActivityManagerHelperTest.java
@@ -44,12 +44,11 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.PollingCheck;
-import com.android.compatibility.common.util.SystemUtil;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -69,14 +68,11 @@
     private static final String GRANTED_PERMISSION_INTERACT_ACROSS_USERS =
             "android.permission.INTERACT_ACROSS_USERS";
     private static final String PERMISSION_REMOVE_TASKS = "android.permission.REMOVE_TASKS";
-    private static final String PERMISSION_GET_TASKS = "android.permission.GET_TASKS";
     private static final String PERMISSION_MANAGE_ACTIVITY_TASKS =
             "android.permission.MANAGE_ACTIVITY_TASKS";
 
     private static final String SIMPLE_APP_PACKAGE_NAME = "android.car.cts.builtin.apps.simple";
-    private static final String SIMPLE_ACTIVITY_NAME = "SimpleActivity";
-    private static final String START_SIMPLE_ACTIVITY_COMMAND = "am start -W -n "
-            + SIMPLE_APP_PACKAGE_NAME + "/." + SIMPLE_ACTIVITY_NAME;
+    private static final String SIMPLE_ACTIVITY_NAME = SIMPLE_APP_PACKAGE_NAME + ".SimpleActivity";
 
     private static final int OWNING_UID = UserHandle.ALL.getIdentifier();
     private static final int MAX_NUM_TASKS = 1_000;
@@ -100,6 +96,13 @@
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private final Context mContext = mInstrumentation.getContext();
 
+    @Before
+    public void setUp() throws Exception {
+        // Home was launched in ActivityManagerTestBase#setUp, wait until it is stable,
+        // in order not to mix the event of its TaskView Activity with the TestActivity.
+        mWmState.waitForHomeActivityVisible();
+    }
+
     @Test
     public void testCheckComponentPermission() throws Exception {
         // not requested from Manifest
@@ -183,20 +186,18 @@
     public void testProcessObserverCallback() throws Exception {
         // setup
         ProcessObserverCallbackTestImpl callbackImpl = new ProcessObserverCallbackTestImpl();
-        launchSimpleActivity();
 
         // execute
         try {
             mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
-                        PERMISSION_SET_ACTIVITY_WATCHER);
+                    PERMISSION_SET_ACTIVITY_WATCHER);  // for registerProcessObserverCallback
 
             ActivityManagerHelper.registerProcessObserverCallback(callbackImpl);
 
-            ArrayList<Integer> appTasks = getAppTasks(SIMPLE_APP_PACKAGE_NAME);
-            appTasks.forEach((taskId) -> ActivityManagerHelper.removeTask(taskId));
+            launchSimpleActivity();
 
             // assert
-            assertThat(callbackImpl.isProcessDiedObserved()).isTrue();
+            assertThat(callbackImpl.waitForForegroundActivitiesChanged()).isTrue();
         } finally {
             // teardown
             ActivityManagerHelper.unregisterProcessObserverCallback(callbackImpl);
@@ -249,45 +250,27 @@
     private static final class ProcessObserverCallbackTestImpl extends ProcessObserverCallback {
         private final CountDownLatch mLatch = new CountDownLatch(1);
 
-        private int mObservedPid = -1;
-        private int mObservedUid = -1;
-        private boolean mProcessDiedObserved;
-
+        // Use onForegroundActivitiesChanged(), because onProcessDied() can be called
+        // in very long time later even if the task was removed.
         @Override
-        public void onProcessDied(int pid, int uid) {
-            mProcessDiedObserved = true;
-            Log.d(TAG, "ProcessDied: pid " + pid + " uid " + uid);
+        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+            Log.d(TAG, "onForegroundActivitiesChanged: pid " + pid + " uid " + uid);
             mLatch.countDown();
         }
 
-        public boolean isProcessDiedObserved() throws Exception {
-            if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                throw new Exception("process died is not observed in " + TIMEOUT_MS + " ms)");
-            }
-            return mProcessDiedObserved;
+        public boolean waitForForegroundActivitiesChanged() throws Exception {
+            return mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         }
     }
 
     private void launchSimpleActivity() {
-        SystemUtil.runShellCommand(START_SIMPLE_ACTIVITY_COMMAND);
-    }
-
-    private ArrayList<Integer> getAppTasks(String pkgName) {
-        ActivityManager am = mContext.getSystemService(ActivityManager.class);
-
-        List<ActivityManager.RunningTaskInfo> runningTasks = am.getRunningTasks(MAX_NUM_TASKS);
-        ArrayList<Integer> appTasks = new ArrayList<>();
-        for (ActivityManager.RunningTaskInfo taskInfo : runningTasks) {
-            String taskPackageName = taskInfo.baseActivity.getPackageName();
-            int taskId = taskInfo.taskId;
-            Log.d(TAG, "tasks package name: " + taskPackageName);
-            if (taskPackageName.equals(pkgName)) {
-                Log.d(TAG, "getAppTask(): adding " + SIMPLE_APP_PACKAGE_NAME + " task " + taskId);
-                appTasks.add(taskId);
-            }
-        }
-
-        return appTasks;
+        ComponentName simpleActivity = new ComponentName(
+                SIMPLE_APP_PACKAGE_NAME, SIMPLE_ACTIVITY_NAME);
+        Intent intent = new Intent()
+                .setComponent(simpleActivity)
+                .addFlags(FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent, /* options = */ null);
+        waitAndAssertTopResumedActivity(simpleActivity, DEFAULT_DISPLAY, "Activity isn't resumed");
     }
 
     private <T> T launchTestActivity(Class<T> type) {
diff --git a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt
index eca3280..c961624 100644
--- a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt
+++ b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt
@@ -31,6 +31,7 @@
 import android.content.Intent
 import android.net.MacAddress
 import android.os.Parcelable
+import android.os.SystemClock.sleep
 import androidx.test.uiautomator.UiDevice
 import org.junit.Assume
 import org.junit.Assume.assumeFalse
@@ -100,7 +101,7 @@
             } else {
                 confirmationUi.clickNegativeButtonMultipleDevices()
             }
-        }
+    }
 
     protected fun test_userDismissed(
         singleDevice: Boolean = false,
@@ -118,8 +119,24 @@
         displayName: String?,
         cancelAction: () -> Unit
     ) {
+        // Give the discovery service extra time to find the first match device before
+        // pressing the negative button for singleDevice && userRejected.
+        if (singleDevice) {
+            setDiscoveryTimeout(2.seconds)
+        }
+
         sendRequestAndLaunchConfirmation(singleDevice, selfManaged, displayName)
 
+        if (singleDevice) {
+            // The discovery timeout is 2 sec, but let's wait for 3. So that we have enough
+            // time to wait until the dialog appeared.
+            sleep(3.seconds.inWholeMilliseconds)
+        }
+        // Test can stop here since there's no device found after discovery timeout.
+        assumeFalse(callback.invocations.contains(OnFailure(REASON_DISCOVERY_TIMEOUT)))
+        // Check callback invocations: There should be 0 invocation before any actions are made.
+        assertEmpty(callback.invocations)
+
         callback.assertInvokedByActions {
             cancelAction()
         }
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt
index 96fe027..cf9dde0 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt
@@ -74,6 +74,15 @@
             InstrumentationRegistry.getInstrumentation().getUiAutomation()
 
         private var backgroundThread = HandlerThread("PackageManagerShellCommandMultiUserTest")
+
+        fun skipTheInstallType(installTypeString: String): Boolean {
+            if (installTypeString == "install-incremental" &&
+                !context.packageManager.hasSystemFeature(
+                    PackageManager.FEATURE_INCREMENTAL_DELIVERY)) {
+                return true
+            }
+            return false
+        }
     }
 
     private lateinit var primaryUser: UserReference
@@ -130,6 +139,9 @@
             "install-incremental"
         ) installTypeString: String
     ) {
+        if (skipTheInstallType(installTypeString)) {
+            return
+        }
         val startTimeMillisForPrimaryUser = System.currentTimeMillis()
         installPackageAsUser(TEST_HW5, primaryUser, installTypeString)
         assertTrue(isAppInstalledForUser(TEST_APP_PACKAGE, primaryUser))
@@ -205,6 +217,9 @@
             "install-incremental"
         ) installTypeString: String
     ) {
+        if (skipTheInstallType(installTypeString)) {
+            return
+        }
         if (!backgroundThread.isAlive) {
             backgroundThread.start()
         }
@@ -264,6 +279,9 @@
             "install-incremental"
         ) installTypeString: String
     ) {
+        if (skipTheInstallType(installTypeString)) {
+            return
+        }
         installPackageAsUser(TEST_HW5, primaryUser, installTypeString)
         assertTrue(isAppInstalledForUser(TEST_APP_PACKAGE, primaryUser))
         assertFalse(isAppInstalledForUser(TEST_APP_PACKAGE, secondaryUser))
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java
index b8d3f6f..ba192c4 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java
@@ -16,8 +16,6 @@
 
 package android.hardware.input.cts.tests;
 
-import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
-
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
@@ -35,6 +33,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.os.SystemClock;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -165,9 +164,10 @@
             if (mVirtualDevice != null) {
                 mVirtualDevice.close();
             }
-            InstrumentationRegistry.getTargetContext().getSystemService(InputManager.class)
-                    .unregisterInputDeviceListener(mInputDeviceListener);
-            disassociateCompanionDevice();
+            final Context context = InstrumentationRegistry.getTargetContext();
+            context.getSystemService(InputManager.class).unregisterInputDeviceListener(
+                    mInputDeviceListener);
+            disassociateCompanionDevice(context.getPackageName());
         }
     }
 
@@ -181,13 +181,14 @@
     private void associateCompanionDevice(String packageName) {
         // Associate this package for user 0 with a zeroed-out MAC address (not used in this test)
         SystemUtil.runShellCommand(
-                String.format("cmd companiondevice associate 0 %s 00:00:00:00:00:00", packageName));
+                String.format("cmd companiondevice associate %d %s 00:00:00:00:00:00",
+                        Process.myUserHandle().getIdentifier(), packageName));
     }
 
-    private void disassociateCompanionDevice() {
-        SystemUtil.runShellCommand("cmd companiondevice disassociate 0 "
-                + InstrumentationRegistry.getTargetContext().getPackageName()
-                + " 00:00:00:00:00:00");
+    private void disassociateCompanionDevice(String packageName) {
+        SystemUtil.runShellCommand(
+                String.format("cmd companiondevice disassociate %d %s 00:00:00:00:00:00",
+                        Process.myUserHandle().getIdentifier(), packageName));
     }
 
     private void tapActivityToFocus() {
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualKeyboardTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualKeyboardTest.java
index 741116b..1492aae 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualKeyboardTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualKeyboardTest.java
@@ -63,7 +63,7 @@
                         KeyEvent.ACTION_DOWN,
                         KeyEvent.KEYCODE_A,
                         /* repeat= */ 0,
-                        /* metaState= */ KeyEvent.META_NUM_LOCK_ON,
+                        /* metaState= */ 0,
                         /* deviceId= */ 0,
                         /* scancode= */ 0,
                         /* flags= */ 0,
@@ -74,7 +74,7 @@
                         KeyEvent.ACTION_UP,
                         KeyEvent.KEYCODE_A,
                         /* repeat= */ 0,
-                        /* metaState= */ KeyEvent.META_NUM_LOCK_ON,
+                        /* metaState= */ 0,
                         /* deviceId= */ 0,
                         /* scancode= */ 0,
                         /* flags= */ 0,
diff --git a/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java b/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java
index 541c68f..fc5d459 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java
@@ -15,41 +15,25 @@
  */
 package android.media.cts;
 
-import android.app.Activity;
-import android.content.Intent;
 import android.media.cts.R;
-import android.os.Build;
+
+import android.app.Activity;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.WindowInsetsController;
 import android.view.WindowManager;
 
-import androidx.test.filters.SdkSuppress;
-
-import com.android.compatibility.common.util.ApiLevelUtil;
-
 public class MediaStubActivity extends Activity {
     private static final String TAG = "MediaStubActivity";
     private SurfaceHolder mHolder;
     private SurfaceHolder mHolder2;
 
-    public static final String INTENT_EXTRA_NO_TITLE = "NoTitle";
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R)) {
-            Intent intent = getIntent();
-            if (intent.getBooleanExtra(INTENT_EXTRA_NO_TITLE, false)) {
-                hideTitle();
-            }
-        }
         setTurnScreenOn(true);
         setShowWhenLocked(true);
 
@@ -80,26 +64,4 @@
     public SurfaceHolder getSurfaceHolder2() {
         return mHolder2;
     }
-
-    /** Note: Must be called from the thread used to create this activity. */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
-    public void hideSystemBars() {
-        var surfaceV = (SurfaceView)findViewById(R.id.surface);
-        WindowInsetsController windowInsetsController = surfaceV.getWindowInsetsController();
-        if (windowInsetsController == null) {
-            return;
-        }
-        // Configure the behavior of the hidden system bars
-        windowInsetsController.setSystemBarsBehavior(
-                WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
-        // Hide both the status bar and the navigation bar
-        windowInsetsController.hide(WindowInsets.Type.systemBars());
-    }
-
-    /** Note: Must be called before {@code setContentView}. */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
-    private void hideTitle() {
-        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
-    }
-
 }
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderPushBlankBuffersOnStopTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderPushBlankBuffersOnStopTest.java
deleted file mode 100644
index 394a779..0000000
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderPushBlankBuffersOnStopTest.java
+++ /dev/null
@@ -1,256 +0,0 @@
-package android.media.decoder.cts;
-
-import android.content.Intent;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.media.cts.MediaHeavyPresubmitTest;
-import android.media.cts.MediaStubActivity;
-import android.media.cts.Preconditions;
-import android.media.MediaCodec;
-import android.media.MediaCodecList;
-import android.media.MediaExtractor;
-import android.media.MediaFormat;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.view.Surface;
-
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.runner.screenshot.ScreenCapture;
-import androidx.test.runner.screenshot.Screenshot;
-
-import com.android.compatibility.common.util.ApiLevelUtil;
-
-import org.junit.Test;
-import org.junit.Assert;
-import org.junit.Assume;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Class that contains tests for the "Push blank buffers on stop" decoder feature.
- * <br>
- * In order to detect that a blank buffer has been pushed to the {@code Surface}that the codec works
- * on, we take a fullscreen screenshot before and after the call to {@code MediaCodec#stop}. This
- * workaround appears necessary at the time of writing because the usual APIs to extract the content
- * of a native {@code Surface} (such as {@code PixelCopy} or {@code ImageReader}) appear to fail for
- * this frame specifically.
- * <br>
- * This test class is inspired from the {@link DecoderTest} test class, but with specific setup code
- * to ensure the activity is launched in immersive mode and its title is removed.
- */
-@MediaHeavyPresubmitTest
-public class DecoderPushBlankBuffersOnStopTest {
-    private static final String TAG = "DecoderPushBlankBufferOnStopTest";
-    private static final String mInpPrefix = WorkDir.getMediaDirString();
-
-    /**
-     * Retrieve a file descriptor to a test resource from its file name.
-     * @param res  Name from a resource in the media assets
-     */
-    private static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
-            throws FileNotFoundException {
-        final String mediaDirPath = WorkDir.getMediaDirString();
-        File mediaFile = new File(mediaDirPath + res);
-        Preconditions.assertTestFileExists(mediaDirPath + res);
-        ParcelFileDescriptor parcelFD =
-                ParcelFileDescriptor.open(mediaFile, ParcelFileDescriptor.MODE_READ_ONLY);
-        return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
-    }
-
-    private static boolean isUniformlyBlank(Bitmap bitmap) {
-        final var color = new Color(); // Defaults to opaque black in sRGB
-        final int width = bitmap.getWidth();
-        final int height = bitmap.getHeight();
-        // Check a subset of pixels against the first pixel of the image.
-        // This is not strictly sufficient, but probably good enough and much more efficient.
-        for (int y = 0; y < height; y+=4) {
-            for (int x = 0; x < width; x+=4) {
-                if (color.toArgb() != bitmap.getColor(x, y).toArgb()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    private void testPushBlankBuffersOnStop(String testVideo) throws Exception {
-        // Configure the test activity to hide its title
-        final var noTitle = new Intent(ApplicationProvider.getApplicationContext(),
-                MediaStubActivity.class);
-        noTitle.putExtra(MediaStubActivity.INTENT_EXTRA_NO_TITLE, true);
-        try(ActivityScenario<MediaStubActivity> scenario = ActivityScenario.launch(noTitle)) {
-            final var surface = new AtomicReference<Surface>();
-            scenario.onActivity(activity -> {
-                        surface.set(activity.getSurfaceHolder().getSurface());
-                    });
-
-            // Setup media extraction
-            final AssetFileDescriptor fd = getAssetFileDescriptorFor(testVideo);
-            final var extractor = new MediaExtractor();
-            extractor.setDataSource(fd);
-            fd.close();
-            MediaFormat format = null;
-            int trackIndex = -1;
-            for (int i = 0; i < extractor.getTrackCount(); i++) {
-                format = extractor.getTrackFormat(i);
-                if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
-                    trackIndex = i;
-                    break;
-                }
-            }
-            Assert.assertTrue("No video track was found", trackIndex >= 0);
-            extractor.selectTrack(trackIndex);
-            // Enable PUSH_BLANK_BUFFERS_ON_STOP
-            format.setInteger(MediaFormat.KEY_PUSH_BLANK_BUFFERS_ON_STOP, 1);
-
-            // Setup video codec
-            final var mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-            final String decoderName = mcl.findDecoderForFormat(format);
-            Assume.assumeNotNull(String.format("No decoder for %s", format), format);
-            final MediaCodec decoder = MediaCodec.createByCodecName(decoderName);
-            // Boolean set from the decoding thread to signal that a frame has been decoded
-            final var displayedFrame = new AtomicBoolean(false);
-            // Lock used for thread synchronization
-            final Lock lock = new ReentrantLock();
-            // Condition that signals the decoding thread has made enough progress
-            final Condition processingDone = lock.newCondition();
-            final var cb = new MediaCodec.Callback() {
-                    /** Queue input buffers until one buffer has been decoded. */
-                    @Override
-                    public void onInputBufferAvailable(MediaCodec codec, int index) {
-                        lock.lock();
-                        try {
-                            // Stop queuing frames once a frame has been displayed
-                            if (displayedFrame.get()) {
-                                return;
-                            }
-                        } finally {
-                            lock.unlock();
-                        }
-
-                        ByteBuffer inputBuffer = codec.getInputBuffer(index);
-                        int sampleSize = extractor.readSampleData(inputBuffer,
-                                0 /* offset */);
-                        if (sampleSize < 0) {
-                            codec.queueInputBuffer(index, 0 /* offset */, 0 /* sampleSize */,
-                                    0 /* presentationTimeUs */,
-                                    MediaCodec.BUFFER_FLAG_END_OF_STREAM);
-                            return;
-                        }
-                        final long presentationTimeMs = System.currentTimeMillis();
-                        codec.queueInputBuffer(index, 0 /* offset */, sampleSize,
-                                presentationTimeMs * 1000, 0 /* flags */);
-                        extractor.advance();
-                    }
-
-                    /** Render the output buffer and signal that the processing is done. */
-                    @Override
-                    public void onOutputBufferAvailable(MediaCodec codec, int index,
-                            MediaCodec.BufferInfo info) {
-                        lock.lock();
-                        try {
-                            // Stop dequeuing frames once a frame has been displayed
-                            if (displayedFrame.get()) {
-                                return;
-                            }
-                        } finally {
-                            lock.unlock();
-                        }
-                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                            return;
-                        }
-                        codec.releaseOutputBuffer(index, true);
-                    }
-
-                    /**
-                     * Check if the error is transient. If it is, ignore it, otherwise signal end of
-                     * processing.
-                     */
-                    @Override
-                    public void onError(MediaCodec codec, MediaCodec.CodecException e) {
-                        if (e.isTransient()) {
-                            return;
-                        }
-                        lock.lock();
-                        try {
-                            processingDone.signal();
-                        } finally {
-                            lock.unlock();
-                        }
-                    }
-
-                    /** Ignore format changed events. */
-                    @Override
-                    public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { }
-                };
-            final var onFrameRenderedListener = new MediaCodec.OnFrameRenderedListener() {
-                    @Override
-                    public void onFrameRendered(MediaCodec codec, long presentationTimeUs,
-                            long nanoTime) {
-                        lock.lock();
-                        try {
-                            displayedFrame.set(true);
-                            processingDone.signal();
-                        } finally {
-                            lock.unlock();
-                        }
-                    }
-                };
-            decoder.setCallback(cb);
-            decoder.setOnFrameRenderedListener(onFrameRenderedListener, null /* handler */);
-            scenario.onActivity(activity -> activity.hideSystemBars());
-            decoder.configure(format, surface.get(), null /* MediaCrypto */, 0 /* flags */);
-            // Start playback
-            decoder.start();
-            final long startTime = System.currentTimeMillis();
-            // Wait until the codec has decoded a frame, or a timeout.
-            lock.lock();
-            try {
-                long startTimeMs = System.currentTimeMillis();
-                long timeoutMs = 1000;
-                while ((System.currentTimeMillis() < startTimeMs + timeoutMs) &&
-                        !displayedFrame.get()) {
-                    processingDone.await(timeoutMs, TimeUnit.MILLISECONDS);
-                }
-            } finally {
-                lock.unlock();
-            }
-            Assert.assertTrue("Could not render any frame.", displayedFrame.get());
-            final ScreenCapture captureBeforeStop = Screenshot.capture();
-            Assert.assertFalse("Frame is blank before stop.", isUniformlyBlank(
-                            captureBeforeStop.getBitmap()));
-            decoder.stop();
-            final ScreenCapture captureAfterStop = Screenshot.capture();
-            Assert.assertTrue("Frame is not blank after stop.", isUniformlyBlank(
-                            captureAfterStop.getBitmap()));
-            decoder.release();
-            extractor.release();
-        }
-    }
-
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
-    @Test
-    public void testPushBlankBuffersOnStopVp9() throws Exception {
-        testPushBlankBuffersOnStop(
-                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
-    }
-
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
-    @Test
-    public void testPushBlankBuffersOnStopAvc() throws Exception {
-        testPushBlankBuffersOnStop(
-                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-    }
-}
diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
index e74efca..ac1e9df 100644
--- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
+++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
@@ -18,8 +18,10 @@
 
 import android.app.AppOpsManager
 import android.app.KeyguardManager
+import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
+import android.content.res.Resources.NotFoundException
 import android.hardware.SensorPrivacyManager
 import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener
 import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE
@@ -316,6 +318,18 @@
         Assume.assumeTrue(packageManager
                 .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN))
 
+//      TODO use actual test api when it can be added
+//      Assume.assumeTrue(callWithShellPermissionIdentity { spm.requiresAuthentication() })
+        val packageContext: Context = context.createPackageContext("android", 0)
+        try {
+            Assume.assumeTrue(packageContext.resources.getBoolean(packageContext.resources
+                    .getIdentifier("config_sensorPrivacyRequiresAuthentication", "bool", "android"))
+            )
+        } catch (e: NotFoundException) {
+        // Since by default we want authentication to be required we
+        // continue the test if the OEM has removed this resource.
+        }
+
         setSensor(false)
         assertFalse(isSensorPrivacyEnabled())
         runWhileLocked {
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java
index dbf81f2..fbcee8a 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java
@@ -18,9 +18,13 @@
 
 import static androidx.test.InstrumentationRegistry.getContext;
 
+import static com.android.compatibility.common.util.PropertyUtil.getVendorApiLevel;
+
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.pm.PackageManager;
+import android.os.Build;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,6 +38,7 @@
 
     @Before
     public void setUp() {
+        assumeTrue(getVendorApiLevel() > Build.VERSION_CODES.S);
         mPackageManager = getContext().getPackageManager();
     }
 
diff --git a/tests/tests/virtualdevice/src/android/virtualdevice/cts/CreateVirtualDisplayTest.java b/tests/tests/virtualdevice/src/android/virtualdevice/cts/CreateVirtualDisplayTest.java
index dcba1f0..2228ed1 100644
--- a/tests/tests/virtualdevice/src/android/virtualdevice/cts/CreateVirtualDisplayTest.java
+++ b/tests/tests/virtualdevice/src/android/virtualdevice/cts/CreateVirtualDisplayTest.java
@@ -52,6 +52,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+
 @RunWith(AndroidJUnit4.class)
 @AppModeFull(reason = " cannot be accessed by instant apps")
 public class CreateVirtualDisplayTest {
@@ -175,4 +177,32 @@
                         /* executor= */ null,
                         mVirtualDisplayCallback));
     }
+
+    @Test
+    public void createVirtualDisplay_createAndRemoveSeveralDisplays() throws InterruptedException {
+        mVirtualDevice =
+                mVirtualDeviceManager.createVirtualDevice(
+                        mFakeAssociationRule.getAssociationInfo().getId(),
+                        DEFAULT_VIRTUAL_DEVICE_PARAMS);
+        ArrayList<VirtualDisplay> displays = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            displays.add(mVirtualDevice.createVirtualDisplay(
+                    /* width= */ 100,
+                    /* height= */ 100,
+                    /* densityDpi= */ 240,
+                    /* surface= */ null,
+                    /* flags= */ 0,
+                    Runnable::run,
+                    mVirtualDisplayCallback));
+            // TODO(b/230544802) - for now, use sleep to avoid deadlock when creating multiple
+            //  displays in quick succession
+            Thread.sleep(50);
+        }
+
+        // Releasing several displays in quick succession should not cause deadlock
+        while (!displays.isEmpty()) {
+            int index = displays.size() - 1;
+            displays.remove(index).release();
+        }
+    }
 }
diff --git a/tests/tests/voiceinteraction/TEST_MAPPING b/tests/tests/voiceinteraction/TEST_MAPPING
index 7c84b9c..b22a259 100644
--- a/tests/tests/voiceinteraction/TEST_MAPPING
+++ b/tests/tests/voiceinteraction/TEST_MAPPING
@@ -5,19 +5,6 @@
       "options": [
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        // TODO(b/225076204): Remove the following four test cases after fixing the test fail.
-        {
-          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_createDetectorTwiceQuickly_triggerSuccess"
-        },
-        {
-          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromDsp_success"
-        },
-        {
-          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromExternalSource_success"
-        },
-        {
-          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromMic_success"
         }
       ]
     }
diff --git a/tests/translation/AndroidManifest.xml b/tests/translation/AndroidManifest.xml
index aab6515..80ac551 100644
--- a/tests/translation/AndroidManifest.xml
+++ b/tests/translation/AndroidManifest.xml
@@ -22,10 +22,16 @@
     <application android:label="Translation TestCase">
         <uses-library android:name="android.test.runner"/>
 
+        <!--
+        EmptyActivity uses a transparent theme so that SimpleActivity below it can have its views
+        translated. See UiTranslationManagerTest#testTranslationAfterStartActivityOnSameTask.
+        -->
         <activity android:name=".EmptyActivity"
                   android:label="EmptyActivity"
-                  android:exported="true">
+                  android:exported="true"
+                  android:theme="@style/TransparentTheme">
         </activity>
+
         <activity android:name=".SimpleActivity"
                   android:label="SimpleActivity"
                   android:exported="true">
diff --git a/tests/translation/res/values/styles.xml b/tests/translation/res/values/styles.xml
new file mode 100644
index 0000000..3dd2eb6
--- /dev/null
+++ b/tests/translation/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <style name="TransparentTheme">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:backgroundDimEnabled">false</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/translation/src/android/translation/cts/Helper.java b/tests/translation/src/android/translation/cts/Helper.java
index 041b055..3474979 100644
--- a/tests/translation/src/android/translation/cts/Helper.java
+++ b/tests/translation/src/android/translation/cts/Helper.java
@@ -67,6 +67,8 @@
 
     public static final String CUSTOM_TRANSLATION_ID_MY_TAG = "myTag";
     public static final String LOCAL_TEST_FILES_DIR = "/sdcard/CtsTranslationTestCases";
+    public static final int TEMP_SERVICE_DURATION_MS = 30_000;
+
     private static final String LOG_TAG = "log.tag.UiTranslation";
 
     /**
@@ -77,7 +79,8 @@
     public static void setTemporaryTranslationService(String service) {
         Log.d(TAG, "Setting translation service to " + service);
         final int userId = UserHandle.myUserId();
-        runShellCommand("cmd translation set temporary-service %d %s 12000", userId, service);
+        runShellCommand("cmd translation set temporary-service %d %s %d", userId, service,
+                TEMP_SERVICE_DURATION_MS);
     }
 
     /**
@@ -97,7 +100,8 @@
     public static void setTemporaryContentCaptureService(String service) {
         Log.d(TAG, "Setting content capture service to " + service);
         final int userId = UserHandle.myUserId();
-        runShellCommand("cmd content_capture set temporary-service %d %s 12000", userId, service);
+        runShellCommand("cmd content_capture set temporary-service %d %s %d", userId, service,
+                TEMP_SERVICE_DURATION_MS);
     }
 
     /**
diff --git a/tests/translation/src/android/translation/cts/TranslationManagerTest.java b/tests/translation/src/android/translation/cts/TranslationManagerTest.java
index 67fa455..551e20ce 100644
--- a/tests/translation/src/android/translation/cts/TranslationManagerTest.java
+++ b/tests/translation/src/android/translation/cts/TranslationManagerTest.java
@@ -245,11 +245,6 @@
 
         translator.destroy();
         assertThat(translator.isDestroyed()).isTrue();
-        try {
-            mServiceWatcher.waitOnDisconnected();
-        } catch (InterruptedException e) {
-            Log.w(TAG, "Exception waiting for onDisconnected");
-        }
 
         // Wait for translation to finish
         translationLatch.await();
@@ -326,11 +321,6 @@
 
         translator.destroy();
         assertThat(translator.isDestroyed()).isTrue();
-        try {
-            mServiceWatcher.waitOnDisconnected();
-        } catch (InterruptedException e) {
-            Log.w(TAG, "Exception waiting for onDisconnected");
-        }
 
         // Wait for translation to finish
         translationLatch.await();
@@ -415,11 +405,6 @@
 
         translator.destroy();
         assertThat(translator.isDestroyed()).isTrue();
-        try {
-            mServiceWatcher.waitOnDisconnected();
-        } catch (InterruptedException e) {
-            Log.w(TAG, "Exception waiting for onDisconnected");
-        }
     }
 
     @Test
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index b9d5547..21b36ca 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -112,7 +112,7 @@
 fi
 
 # include any host-side test jars
-for j in ${CTS_ROOT}/android-cts/testcases/*.jar; do
+for j in $(find ${CTS_ROOT}/android-cts/testcases -name '*.jar'); do
     JAR_PATH=${JAR_PATH}:$j
 done