Snap for 9251420 from 61669df01b1d38acb204585f919ccd7faac4c711 to mainline-conscrypt-release

Change-Id: I3130f4f86c04ce518f8f827dcc2eb8bcad13bda2
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index b49337c..631f823 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2138,10 +2138,6 @@
         device was p2p device. Check the test case on the other device.</string>
     <string name="p2p_connection_error">
         Test failed.\n\nFailed to establish a p2p connection.</string>
-    <string name="p2p_connection_latency_error">
-        Test failed. \n\nConnection was expected to be established within %1$dms. But took=%2$dms.
-        \n\nPlease ensure that the Group Owner is ready on the other device before running
-        this test</string>
     <string name="p2p_detect_disconnection_error">
         Test failed.\n\nFailed to detect client disconnection.</string>
     <string name="p2p_disconnect_error">
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java
index f0c62c4..20ce5ac 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java
@@ -23,9 +23,7 @@
 import android.net.wifi.p2p.WifiP2pGroup;
 import android.net.wifi.p2p.WifiP2pInfo;
 import android.net.wifi.p2p.WifiP2pManager;
-import android.os.Build;
 
-import com.android.compatibility.common.util.PropertyUtil;
 import com.android.cts.verifier.R;
 
 import java.lang.reflect.Method;
@@ -189,11 +187,21 @@
      * @throws InterruptedException
      */
     protected boolean connectTest(WifiP2pConfig config) throws InterruptedException {
+        notifyTestMsg(R.string.p2p_searching_target);
+
+        /*
+         * Search target device and check its capability.
+         */
         ActionListenerTest actionListener = new ActionListenerTest();
+        mP2pMgr.discoverPeers(mChannel, actionListener);
+        if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+            mReason = mContext.getString(R.string.p2p_discover_peers_error);
+            return false;
+        }
+
         /*
          * Try to connect the target device.
          */
-        long startTime = System.currentTimeMillis();
         mP2pMgr.connect(mChannel, config, actionListener);
         if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
             mReason = mContext.getString(R.string.p2p_connect_error);
@@ -216,15 +224,6 @@
         WifiP2pGroup group = mReceiverTest.getWifiP2pGroup();
         if (group != null) {
             if (!group.isGroupOwner()) {
-                long endTime = System.currentTimeMillis();
-                long connectionLatency = endTime - startTime;
-                if (PropertyUtil.isVndkApiLevelAtLeast(Build.VERSION_CODES.TIRAMISU)
-                        && connectionLatency
-                        > MAXIMUM_EXPECTED_CONNECTION_LATENCY_WITH_CONFIG_MS) {
-                    mReason = mContext.getString(R.string.p2p_connection_latency_error,
-                            MAXIMUM_EXPECTED_CONNECTION_LATENCY_WITH_CONFIG_MS, connectionLatency);
-                    return false;
-                }
                 setTargetAddress(group.getOwner().deviceAddress);
             } else {
                 mReason = mContext.getString(R.string.p2p_connection_error);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java
index 2117419..7f608c5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java
@@ -44,7 +44,6 @@
 
     protected static final int TIMEOUT = 25000;
     protected static final int TIMEOUT_FOR_USER_ACTION = 60000;
-    protected static final int MAXIMUM_EXPECTED_CONNECTION_LATENCY_WITH_CONFIG_MS = 1500;
     protected static final int SUCCESS = 0;
 
     protected Context mContext;
diff --git a/apps/PermissionApp/Android.bp b/apps/PermissionApp/Android.bp
index 70c7d67..df62a77 100644
--- a/apps/PermissionApp/Android.bp
+++ b/apps/PermissionApp/Android.bp
@@ -30,6 +30,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
index d7a056b..cdec896 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
@@ -18,6 +18,9 @@
 
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 
+import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.FIXED;
+import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.NOT_FIXED;
+import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.SUPER_FIXED;
 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
 
 import static org.junit.Assert.fail;
@@ -150,14 +153,14 @@
      * @param permissions the set of permissions, formatted "permission_name fixed_boolean"
      */
     public void setException(String pkg, String sha256, String... permissions) {
-        HashMap<String, Boolean> permissionsMap = new HashMap<>();
+        HashMap<String, UidState.FixedState> permissionsMap = new HashMap<>();
         for (String permissionString : permissions) {
             String[] parts = permissionString.trim().split("\\s+");
             if (parts.length != 2) {
                 Log.e(LOG_TAG, "Unable to parse remote exception permission: " + permissionString);
                 return;
             }
-            permissionsMap.put(parts[0], Boolean.valueOf(parts[1]));
+            permissionsMap.put(parts[0], Boolean.valueOf(parts[1]) ? FIXED : NOT_FIXED);
         }
         mRemoteExceptions.add(new DefaultPermissionGrantException(pkg, sha256, permissionsMap));
     }
@@ -174,14 +177,14 @@
      */
     public void setExceptionWithMetadata(String company, String metadata, String pkg,
             String sha256, String... permissions) {
-        HashMap<String, Boolean> permissionsMap = new HashMap<>();
+        HashMap<String, UidState.FixedState> permissionsMap = new HashMap<>();
         for (String permissionString : permissions) {
             String[] parts = permissionString.trim().split("\\s+");
             if (parts.length != 2) {
                 Log.e(LOG_TAG, "Unable to parse remote exception permission: " + permissionString);
                 return;
             }
-            permissionsMap.put(parts[0], Boolean.valueOf(parts[1]));
+            permissionsMap.put(parts[0], Boolean.valueOf(parts[1]) ? FIXED : NOT_FIXED);
         }
         mRemoteExceptions.add(new DefaultPermissionGrantException(
                 company, metadata, pkg, sha256, permissionsMap));
@@ -378,9 +381,9 @@
         }
 
         List<String> requestedPermissions = Arrays.asList(packageInfo.requestedPermissions);
-        for (Map.Entry<String, Boolean> entry : exception.permissions.entrySet()) {
+        for (Map.Entry<String, UidState.FixedState> entry : exception.permissions.entrySet()) {
             String permission = entry.getKey();
-            Boolean fixed = entry.getValue();
+            UidState.FixedState fixed = entry.getValue();
             if (!requestedPermissions.contains(permission)) {
                 Log.w(LOG_TAG, "Permission " + permission + " not requested by: " + packageName);
                 continue;
@@ -478,7 +481,7 @@
         Context context = getInstrumentation().getTargetContext();
 
         for (PackageInfo pkg : packageInfos.values()) {
-            int targetSdk = pkg.applicationInfo.targetSandboxVersion;
+            int targetSdk = pkg.applicationInfo.targetSdkVersion;
             int uid = pkg.applicationInfo.uid;
 
             for (String permission : pkg.requestedPermissions) {
@@ -512,8 +515,8 @@
                         }
 
                         appendPackagePregrantedPerms(pkg, "split from non-dangerous permission "
-                                        + permission, false, Collections.singleton(extendedPerm),
-                                outUidStates);
+                                        + permission, NOT_FIXED,
+                                Collections.singleton(extendedPerm), outUidStates);
                     }
                 }
             }
@@ -544,7 +547,7 @@
                     }
 
                     appendPackagePregrantedPerms(pkg, "permission " + permissionToAdd
-                                    + " is granted to pre-" + targetSdk + " apps", false,
+                                    + " is granted to pre-" + targetSdk + " apps", NOT_FIXED,
                             Collections.singleton(permissionToAdd), outUidStates);
                 }
             }
@@ -553,12 +556,12 @@
 
     public static void appendPackagePregrantedPerms(PackageInfo packageInfo, String reason,
             boolean fixed, Set<String> pregrantedPerms, SparseArray<UidState> outUidStates) {
-        appendPackagePregrantedPerms(packageInfo, reason, fixed, false, pregrantedPerms,
-                outUidStates);
+        appendPackagePregrantedPerms(packageInfo, reason, fixed ? FIXED : NOT_FIXED,
+                pregrantedPerms, outUidStates);
     }
 
     public static void appendPackagePregrantedPerms(PackageInfo packageInfo, String reason,
-            boolean fixed, boolean fromRole, Set<String> pregrantedPerms,
+            UidState.FixedState fixed, Set<String> pregrantedPerms,
             SparseArray<UidState> outUidStates) {
         final int uid = packageInfo.applicationInfo.uid;
         UidState uidState = outUidStates.get(uid);
@@ -568,13 +571,7 @@
         }
         for (String requestedPermission : packageInfo.requestedPermissions) {
             if (pregrantedPerms.contains(requestedPermission)) {
-                // Role permissions are not fixed. If we're already getting a fixed pregrant, don't
-                // override with a non-fixed role pregrant
-                if (fromRole && uidState.grantedPermissions.getOrDefault(requestedPermission,
-                        false)) {
-                    continue;
-                }
-                uidState.addGrantedPermission(packageInfo.packageName, reason, requestedPermission,
+                uidState.addGrantedPermission(packageInfo, reason, requestedPermission,
                         fixed);
             }
         }
@@ -634,8 +631,9 @@
 
                 setPermissionGrantState(packageInfo.packageName, permission, false);
 
+                UidState.FixedState fixedState = uidState.grantedPermissions.valueAt(i);
                 Boolean fixed = grantAsFixedPackageNames.contains(packageInfo.packageName)
-                        || uidState.grantedPermissions.valueAt(i);
+                        || fixedState == SUPER_FIXED || fixedState == FIXED;
 
                 // Weaker grant is fine, e.g. not-fixed instead of fixed.
                 if (!fixed && packageManager.checkPermission(permission, packageInfo.packageName)
@@ -715,9 +713,9 @@
         public class GrantReason {
             public final String reason;
             public final boolean override;
-            public final Boolean fixed;
+            public final FixedState fixed;
 
-            GrantReason(String reason, boolean override, Boolean fixed) {
+            GrantReason(String reason, boolean override, FixedState fixed) {
                 this.reason = reason;
                 this.override = override;
                 this.fixed = fixed;
@@ -739,10 +737,34 @@
             }
         }
 
+        /**
+         * Enum representing if a permission's pregrant condition should be fixed and how it should
+         * interact with other pregrant conditions' fixed status.
+         */
+        public enum FixedState {
+
+            /**
+             * Permission is fixed and when merging with other pregrant conditions it won't lose
+             * fixed status and will override non-fixed status.
+             */
+            SUPER_FIXED,
+
+            /**
+             * Permission is fixed and when merging with other pregrant conditions it may lose
+             * fixed status.
+             */
+            FIXED,
+
+            /**
+             * Permission is not fixed.
+             */
+            NOT_FIXED
+        }
+
         // packageName -> permission -> [reason]
         public ArrayMap<String, ArrayMap<String, ArraySet<GrantReason>>> mGrantReasons =
                 new ArrayMap<>();
-        public ArrayMap<String, Boolean> grantedPermissions = new ArrayMap<>();
+        public ArrayMap<String, FixedState> grantedPermissions = new ArrayMap<>();
 
         public void log() {
             for (String packageName : mGrantReasons.keySet()) {
@@ -793,32 +815,26 @@
             }
         }
 
-        public void addGrantedPermission(String packageName, String reason, String permission,
-                Boolean fixed) {
-            Context context = getInstrumentation().getTargetContext();
+        public void addGrantedPermission(PackageInfo packageInfo, String reason, String permission,
+                FixedState fixed) {
+            String packageName = packageInfo.packageName;
+            int targetSdk = packageInfo.applicationInfo.targetSdkVersion;
 
             // Add permissions split off from the permission to granted
-            try {
-                PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
-                int targetSdk = info.applicationInfo.targetSdkVersion;
-
-                for (String extendedPerm : extendBySplitPermissions(permission, targetSdk)) {
-                    mergeGrantedPermission(packageName, extendedPerm.equals(permission) ? reason
-                                    : reason + " (split from " + permission + ")", extendedPerm,
-                            fixed, false);
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                // ignore
+            for (String extendedPerm : extendBySplitPermissions(permission, targetSdk)) {
+                mergeGrantedPermission(packageName, extendedPerm.equals(permission) ? reason
+                                : reason + " (split from " + permission + ")", extendedPerm,
+                        fixed, false);
             }
         }
 
         public void overrideGrantedPermission(String packageName, String reason, String permission,
-                Boolean fixed) {
+                FixedState fixed) {
             mergeGrantedPermission(packageName, reason, permission, fixed, true);
         }
 
         public void mergeGrantedPermission(String packageName, String reason, String permission,
-                Boolean fixed, boolean override) {
+                FixedState fixed, boolean override) {
             if (!mGrantReasons.containsKey(packageName)) {
                 mGrantReasons.put(packageName, new ArrayMap<>());
             }
@@ -830,18 +846,22 @@
             mGrantReasons.get(packageName).get(permission).add(new GrantReason(reason, override,
                     fixed));
 
-            Boolean oldFixed = grantedPermissions.get(permission);
+            FixedState oldFixed = grantedPermissions.get(permission);
             if (oldFixed == null) {
                 grantedPermissions.put(permission, fixed);
             } else {
-                if (override) {
-                    if (oldFixed == Boolean.FALSE && fixed == Boolean.TRUE) {
+                if (oldFixed != SUPER_FIXED && fixed == SUPER_FIXED) {
+                    Log.w(LOG_TAG, "override already granted permission " + permission + "("
+                            + fixed + ") for " + packageName);
+                    grantedPermissions.put(permission, fixed);
+                } else if (override) {
+                    if (oldFixed == NOT_FIXED && fixed == FIXED) {
                         Log.w(LOG_TAG, "override already granted permission " + permission + "("
                                 + fixed + ") for " + packageName);
                         grantedPermissions.put(permission, fixed);
                     }
                 } else {
-                    if (oldFixed == Boolean.TRUE && fixed == Boolean.FALSE) {
+                    if (oldFixed == FIXED && fixed == NOT_FIXED) {
                         Log.w(LOG_TAG, "add already granted permission " + permission + "("
                                 + fixed + ") to " + packageName);
                         grantedPermissions.put(permission, fixed);
@@ -859,20 +879,20 @@
         public String pkg;
         public String sha256;
         public boolean hasBrand; // in rare cases, brand will be specified instead of SHA256 hash
-        public Map<String, Boolean> permissions = new HashMap<>();
+        public Map<String, UidState.FixedState> permissions = new HashMap<>();
 
         public boolean hasNonBrandSha256() {
             return !sha256.isEmpty() && !hasBrand;
         }
 
         public DefaultPermissionGrantException(String pkg, String sha256,
-                Map<String, Boolean> permissions) {
+                Map<String, UidState.FixedState> permissions) {
             this(UNSET_PLACEHOLDER, UNSET_PLACEHOLDER, pkg, sha256, permissions);
         }
 
         public DefaultPermissionGrantException(String company, String metadata, String pkg,
                 String sha256,
-                Map<String, Boolean> permissions) {
+                Map<String, UidState.FixedState> permissions) {
             this.company = company;
             this.metadata = metadata;
             this.pkg = pkg;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
index 488c73f..c3b6900 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
@@ -1532,6 +1532,27 @@
         return false;
     }
 
+    /**
+     *  Function to identify if the device is a cuttlefish instance
+     */
+    public static boolean onCuttlefish() throws IOException {
+        String device = SystemProperties.get("ro.product.device", "");
+        String model = SystemProperties.get("ro.product.model", "");
+        String name = SystemProperties.get("ro.product.name", "");
+
+        // Return true for cuttlefish instances
+        if (!device.startsWith("vsoc_")) {
+            return false;
+        }
+        if (!model.startsWith("Cuttlefish ")) {
+            return false;
+        }
+        if (name.startsWith("cf_") || name.startsWith("aosp_cf_")) {
+            return true;
+        }
+        return false;
+    }
+
     /*
      *  -------------------------------------- END --------------------------------------
      */
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
index 3f42e32..a9543c2 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.StaleObjectException;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -28,6 +29,7 @@
 import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
 import android.util.TypedValue;
+import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.core.app.ApplicationProvider;
@@ -37,6 +39,8 @@
 public class UiAutomatorUtils {
     private UiAutomatorUtils() {}
 
+    private static final String LOG_TAG = "UiAutomatorUtils";
+
     /** Default swipe deadzone percentage. See {@link UiScrollable}. */
     private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1;
 
@@ -86,7 +90,15 @@
         final int minViewHeightPx = convertDpToPx(MIN_VIEW_HEIGHT_DP);
 
         while (view == null && start + timeoutMs > System.currentTimeMillis()) {
-            view = getUiDevice().wait(Until.findObject(selector), 1000);
+            try {
+                view = getUiDevice().wait(Until.findObject(selector), 1000);
+            } catch (StaleObjectException exception) {
+                // UiDevice.wait() may cause StaleObjectException if the {@link View} attached to
+                // UiObject2 is no longer in the view tree.
+                Log.v(LOG_TAG, "UiObject2 view is no longer in the view tree.", exception);
+                getUiDevice().waitForIdle();
+                continue;
+            }
 
             if (view == null || view.getVisibleBounds().height() < minViewHeightPx) {
                 final double deadZone = !(FeatureUtil.isWatch() || FeatureUtil.isTV())
diff --git a/hostsidetests/appcompat/strictjavapackages/Android.bp b/hostsidetests/appcompat/strictjavapackages/Android.bp
index 272be12..2656bbc 100644
--- a/hostsidetests/appcompat/strictjavapackages/Android.bp
+++ b/hostsidetests/appcompat/strictjavapackages/Android.bp
@@ -27,6 +27,7 @@
     ],
     static_libs: [
         "compat-classpaths-testing",
+        "modules-utils-build-testing",
         "dexlib2-no-guava-no-cli",
     ],
     // tag this module as a cts test artifact
diff --git a/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml b/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml
index e610581..f76eab4 100644
--- a/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml
+++ b/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml
@@ -20,6 +20,7 @@
           android:targetSandboxVersion="2">
 
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <application android:requestLegacyExternalStorage="true">
         <uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index 1d42357..6b1f6fc 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -237,6 +237,7 @@
                     "Landroid/app/sdksandbox/IRequestSurfacePackageCallback;",
                     "Landroid/app/sdksandbox/ISdkSandboxManager;",
                     "Landroid/app/sdksandbox/ISdkSandboxLifecycleCallback;",
+                    "Landroid/app/sdksandbox/ISdkSandboxProcessDeathCallback;",
                     "Landroid/app/sdksandbox/ISendDataCallback;",
                     "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;",
                     "Landroid/app/sdksandbox/ISdkToServiceCallback;"
@@ -737,10 +738,12 @@
                 "Lcom/android/sdksandbox/IDataReceivedCallback;",
                 "Lcom/android/sdksandbox/ILoadSdkInSandboxCallback;",
                 "Lcom/android/sdksandbox/IRequestSurfacePackageFromSdkCallback;",
+                "Lcom/android/sdksandbox/ISdkSandboxDisabledCallback;",
                 "Lcom/android/sdksandbox/ISdkSandboxManagerToSdkSandboxCallback;",
                 "Lcom/android/sdksandbox/ISdkSandboxService;",
                 "Lcom/android/sdksandbox/SandboxLatencyInfo-IA;",
-                "Lcom/android/sdksandbox/SandboxLatencyInfo;"
+                "Lcom/android/sdksandbox/SandboxLatencyInfo;",
+                "Lcom/android/sdksandbox/IUnloadSdkCallback;"
             );
 
     private static final ImmutableMap<String, ImmutableSet<String>> FULL_APK_IN_APEX_BURNDOWN =
@@ -1007,13 +1010,14 @@
 
     /**
      * Ensure that no apk-in-apex bundles classes that could be eclipsed by jars in
-     * BOOTCLASSPATH, SYSTEMSERVERCLASSPATH.
+     * BOOTCLASSPATH.
      */
     @Test
     public void testApkInApex_nonClasspathClasses() throws Exception {
         HashMultimap<String, Multimap<String, String>> perApkClasspathDuplicates =
                 HashMultimap.create();
         Arrays.stream(collectApkInApexPaths())
+                .filter(apk -> apk != null && !apk.isEmpty())
                 .parallel()
                 .forEach(apk -> {
                     File apkFile = null;
@@ -1037,9 +1041,12 @@
                                     className -> !burndownClasses.contains(className)
                                             // TODO: b/225341497
                                             && !className.equals("Landroidx/annotation/Keep;"));
+                        final Multimap<String, String> bcpOnlyDuplicates =
+                                Multimaps.filterKeys(filteredDuplicates,
+                                    sBootclasspathJars::contains);
                         if (!filteredDuplicates.isEmpty()) {
                             synchronized (perApkClasspathDuplicates) {
-                                perApkClasspathDuplicates.put(apk, filteredDuplicates);
+                                perApkClasspathDuplicates.put(apk, bcpOnlyDuplicates);
                             }
                         }
                     } catch (Exception e) {
@@ -1056,7 +1063,9 @@
      * and shared library jars.
      */
     @Test
-    public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies() {
+    public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies()
+            throws Exception {
+        assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT());
         // WARNING: Do not add more exceptions here, no androidx should be in bootclasspath.
         // See go/androidx-api-guidelines#module-naming for more details.
         final ImmutableMap<String, ImmutableSet<String>>
@@ -1079,6 +1088,7 @@
             + "bootclasspath. Please use alternatives provided by the platform instead. "
             + "See go/androidx-api-guidelines#module-naming.")
                 .that(sJarsToClasses.entries().stream()
+                        .filter(e -> e.getKey().endsWith(".jar"))
                         .filter(e -> e.getValue().startsWith("Landroidx/"))
                         .filter(e -> !isLegacyAndroidxDependency(
                             LegacyExemptAndroidxSharedLibsNamesToClasses, e.getKey(), e.getValue()))
@@ -1100,11 +1110,12 @@
                 .reduce(Stream::concat).orElseGet(Stream::empty)
                 .parallel()
                 .filter(jarPath -> {
-                    return sJarsToFiles
-                            .get(jarPath)
-                            .stream()
-                            .anyMatch(file -> file.contains(".kotlin_builtins")
-                                    || file.contains(".kotlin_module"));
+                    // Exclude shared library apks.
+                    return jarPath.endsWith(".jar")
+                            && sJarsToFiles.get(jarPath)
+                                .stream()
+                                .anyMatch(file -> file.contains(".kotlin_builtins")
+                                        || file.contains(".kotlin_module"));
                 })
                 .collect(ImmutableList.toImmutableList());
         assertThat(kotlinFiles).isEmpty();
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp
index ed22bab..d6b736d 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp
@@ -38,7 +38,9 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-documentsui",
+        "mts-mainline-infra",
+        "mts-mediaprovider",
         "sts",
     ],
     certificate: ":cts-testkey2",
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp
index b42c9ac..723d063 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp
@@ -33,7 +33,9 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-documentsui",
+        "mts-mainline-infra",
+        "mts-mediaprovider",
         "sts",
     ],
     certificate: ":cts-testkey1",
diff --git a/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp b/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp
index 8c59b53..1608365 100644
--- a/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp
+++ b/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp
@@ -28,6 +28,7 @@
     test_suites: [
         "cts",
         "general-tests",
+        "sts",
     ],
     sdk_version: "test_current",
     min_sdk_version: "19",
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index 0e2a887..d5052b9 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -238,6 +238,29 @@
 }
 
 android_test_helper_app {
+    name: "AppCloningDeviceTest",
+    manifest: "AndroidManifest.xml",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "truth-prebuilt",
+        "cts-scopedstorage-lib",
+        "modules-utils-build_system",
+    ],
+    compile_multilib: "both",
+    test_suites: [
+        "general-tests",
+        "mts-mediaprovider",
+        "cts",
+    ],
+    sdk_version: "test_current",
+    target_sdk_version: "33",
+    min_sdk_version: "29",
+    java_resources: [
+        ":CtsScopedStorageTestAppB",
+    ],
+}
+
+android_test_helper_app {
     name: "LegacyStorageTest",
     manifest: "legacy/AndroidManifest.xml",
     srcs: ["legacy/src/**/*.java"],
@@ -318,6 +341,31 @@
 }
 
 java_test_host {
+    name: "CtsAppCloningMediaProviderHostTest",
+    srcs: ["host/src/**/*.java"],
+    libs: [
+        "cts-tradefed",
+        "tradefed",
+        "testng",
+    ],
+    static_libs: [
+        "modules-utils-build-testing",
+        "compatibility-host-util",
+    ],
+    test_suites: [
+        "general-tests",
+        "mts-mediaprovider",
+        "cts",
+    ],
+    test_config: "AndroidTestAppCloning.xml",
+    per_testcase_directory: true,
+    data: [
+        ":CtsScopedStorageTestAppB",
+        ":AppCloningDeviceTest",
+    ],
+}
+
+java_test_host {
     name: "GtsPreserveLegacyStorageHostTest",
     srcs: [
         "host/src/android/scopedstorage/cts/host/PreserveLegacyStorageHostTest.java",
diff --git a/hostsidetests/scopedstorage/AndroidTestAppCloning.xml b/hostsidetests/scopedstorage/AndroidTestAppCloning.xml
new file mode 100644
index 0000000..665db66
--- /dev/null
+++ b/hostsidetests/scopedstorage/AndroidTestAppCloning.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.
+  -->
+<configuration description="External storage host test Test for App cloning use case">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <option
+        name="config-descriptor:metadata"
+        key="mainline-param"
+        value="com.google.android.mediaprovider.apex" />
+
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="class" value="android.scopedstorage.cts.host.AppCloningMediaProviderHostTest" />
+    </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.mediaprovider" />
+    </object>
+</configuration>
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningMediaProviderHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningMediaProviderHostTest.java
new file mode 100644
index 0000000..77086f0
--- /dev/null
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningMediaProviderHostTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.scopedstorage.cts.host;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class AppCloningMediaProviderHostTest extends BaseHostTestCase{
+
+    protected static final String DEVICE_TEST_APP_PACKAGE = "android.scopedstorage.cts";
+    protected static final String DEVICE_TEST_APP = "AppCloningDeviceTest.apk";
+    private static final String DEVICE_TEST_CLASS = DEVICE_TEST_APP_PACKAGE
+            + ".AppCloningDeviceTest";
+    // This app performs the File Creation and Read operations from the Device.
+    protected static final String SCOPED_STORAGE_TEST_APP_B_APK = "CtsScopedStorageTestAppB.apk";
+
+    private static final int CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS = 20000;
+    private static final long DEFAULT_INSTRUMENTATION_TIMEOUT_MS = 600_000;
+    private static final String EXTERNAL_STORAGE_PATH = "/storage/emulated/%d/";
+    private static final String CURRENT_USER_ID = "currentUserId";
+    private static final String FILE_TO_BE_CREATED = "fileToBeCreated";
+    private static final String FILE_EXPECTED_TO_BE_PRESENT = "fileExpectedToBePresent";
+    private static final String FILE_NOT_EXPECTED_TO_BE_PRESENT = "fileNotExpectedToBePresent";
+    /**
+     * Provide different name to Files being created, on each execution of the test, so that
+     * flakiness from previously existing files can be avoided.
+     */
+    private static final String NONCE = String.valueOf(System.nanoTime());
+    private static String sCloneUserId;
+
+    @BeforeClassWithInfo
+    public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
+        final ITestDevice sDevice = testInfo.getDevice();
+        assertThat(sDevice).isNotNull();
+
+        setDevice(sDevice);
+
+        assumeTrue("Device doesn't support multiple users", supportsMultipleUsers());
+        assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode());
+        assumeTrue(isAtLeastS());
+        assumeFalse("Device uses sdcardfs", usesSdcardFs());
+
+        // create clone user
+        String output = sDevice.executeShellCommand(
+                "pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE "
+                        + "testUser");
+        sCloneUserId = output.substring(output.lastIndexOf(' ') + 1).replaceAll("[^0-9]",
+                "");
+        assertThat(sCloneUserId).isNotEmpty();
+        // start clone user
+        CommandResult out = sDevice.executeShellV2Command("am start-user -w " + sCloneUserId);
+        assertThat(isSuccessful(out)).isTrue();
+
+        Integer mCloneUserIdInt = Integer.parseInt(sCloneUserId);
+        String sCloneUserStoragePath = String.format(EXTERNAL_STORAGE_PATH,
+                Integer.parseInt(sCloneUserId));
+        // Check that the clone user directories have been created
+        eventually(() -> sDevice.doesFileExist(sCloneUserStoragePath, mCloneUserIdInt),
+                CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS);
+    }
+
+    @AfterClassWithInfo
+    public static void afterClass(TestInformation testInfo) throws Exception {
+        if (!supportsMultipleUsers() || isHeadlessSystemUserMode() || !isAtLeastS()
+                || usesSdcardFs()) return;
+        testInfo.getDevice().executeShellCommand("pm remove-user " + sCloneUserId);
+    }
+
+    @Test
+    public void testGetFilesInDirectoryViaMediaProvider() throws Exception {
+        // Install the Device Test App in both the user spaces.
+        installPackage(DEVICE_TEST_APP, "--user all");
+        // Install the Scoped Storage Test App in both the user spaces.
+        installPackage(SCOPED_STORAGE_TEST_APP_B_APK, "--user all");
+
+        int currentUserId = getCurrentUserId();
+        final String fileName = "tmpFileToPush" + NONCE + ".png";
+
+        // We add the file in DCIM directory of User 0.
+        Map<String, String> ownerArgs = new HashMap<>();
+        ownerArgs.put(CURRENT_USER_ID, String.valueOf(currentUserId));
+        ownerArgs.put(FILE_TO_BE_CREATED, fileName);
+        runDeviceTestAsUserInPkgA("testInsertFilesInDirectoryViaMediaProvider",
+                currentUserId, ownerArgs);
+
+        // We add the file in DCIM directory of Cloned User.
+        final String fileNameClonedUser = "tmpFileToPushClonedUser" + NONCE + ".png";
+        Map<String, String> cloneArgs = new HashMap<>();
+        cloneArgs.put(CURRENT_USER_ID, sCloneUserId);
+        cloneArgs.put(FILE_TO_BE_CREATED, fileNameClonedUser);
+        runDeviceTestAsUserInPkgA("testInsertFilesInDirectoryViaMediaProvider",
+                Integer.parseInt(sCloneUserId), cloneArgs);
+
+        // Querying as user 0 should enlist the file(s) created by user 0 only.
+        Map<String, String> listFilesArgs = new HashMap<>();
+        listFilesArgs.put(CURRENT_USER_ID, String.valueOf(currentUserId));
+        listFilesArgs.put(FILE_EXPECTED_TO_BE_PRESENT, fileName);
+        listFilesArgs.put(FILE_NOT_EXPECTED_TO_BE_PRESENT, fileNameClonedUser);
+        runDeviceTestAsUserInPkgA("testGetFilesInDirectoryViaMediaProviderRespectsUserId",
+                currentUserId, listFilesArgs);
+
+        // Querying as cloned user should enlist the file(s) created by cloned user only.
+        listFilesArgs.put(CURRENT_USER_ID, sCloneUserId);
+        listFilesArgs.put(FILE_EXPECTED_TO_BE_PRESENT, fileNameClonedUser);
+        listFilesArgs.put(FILE_NOT_EXPECTED_TO_BE_PRESENT, fileName);
+        runDeviceTestAsUserInPkgA("testGetFilesInDirectoryViaMediaProviderRespectsUserId",
+                Integer.parseInt(sCloneUserId), listFilesArgs);
+    }
+
+    protected void runDeviceTestAsUserInPkgA(@Nonnull String testMethod, int userId,
+            @Nonnull Map<String, String> args) throws Exception {
+        DeviceTestRunOptions deviceTestRunOptions =
+                new DeviceTestRunOptions(DEVICE_TEST_APP_PACKAGE)
+                .setDevice(getDevice())
+                .setTestClassName(DEVICE_TEST_CLASS)
+                .setTestMethodName(testMethod)
+                .setMaxInstrumentationTimeoutMs(DEFAULT_INSTRUMENTATION_TIMEOUT_MS)
+                .setUserId(userId);
+        for (Map.Entry<String, String> entry : args.entrySet()) {
+            deviceTestRunOptions.addInstrumentationArg(entry.getKey(), entry.getValue());
+        }
+        assertWithMessage(testMethod + " failed").that(
+                runDeviceTests(deviceTestRunOptions)).isTrue();
+    }
+}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java
index 5587a88..6bc7328 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java
@@ -16,6 +16,8 @@
 
 package android.scopedstorage.cts.host;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.NativeDevice;
@@ -28,6 +30,11 @@
 abstract class BaseHostTestCase extends BaseHostJUnit4Test {
     private int mCurrentUserId = NativeDevice.INVALID_USER_ID;
     private static final String ERROR_MESSAGE_TAG = "[ERROR]";
+    protected static ITestDevice sDevice = null;
+
+    protected static void setDevice(ITestDevice device) {
+        sDevice = device;
+    }
 
     protected String executeShellCommand(String cmd, Object... args) throws Exception {
         return getDevice().executeShellCommand(String.format(cmd, args));
@@ -42,14 +49,14 @@
     }
 
     // TODO (b/174775905) remove after exposing the check from ITestDevice.
-    protected boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException {
-        String result = getDevice()
+    protected static boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException {
+        String result = sDevice
                 .executeShellCommand("getprop ro.fw.mu.headless_system_user").trim();
         return "true".equalsIgnoreCase(result);
     }
 
-    protected boolean isAtLeastS() throws DeviceNotAvailableException {
-        return getDevice().getApiLevel() >= 31 /* BUILD.VERSION_CODES.S */;
+    protected static boolean isAtLeastS() throws DeviceNotAvailableException {
+        return sDevice.getApiLevel() >= 31 /* BUILD.VERSION_CODES.S */;
     }
 
     protected static void eventually(ThrowingRunnable r, long timeoutMillis) {
@@ -79,7 +86,7 @@
         return mCurrentUserId;
     }
 
-    protected boolean isSuccessful(CommandResult result) {
+    protected static boolean isSuccessful(CommandResult result) {
         if (!CommandStatus.SUCCESS.equals(result.getStatus())) {
             return false;
         }
@@ -91,6 +98,22 @@
         return (stderr == null || stderr.trim().isEmpty());
     }
 
+    protected static boolean supportsMultipleUsers() throws DeviceNotAvailableException {
+        return sDevice.getMaxNumberOfUsersSupported() > 1;
+    }
+
+    protected static boolean usesSdcardFs() throws Exception {
+        CommandResult out = sDevice.executeShellV2Command("cat /proc/mounts");
+        assertThat(isSuccessful(out)).isTrue();
+        for (String line : out.getStdout().split("\n")) {
+            String[] split = line.split(" ");
+            if (split.length >= 3 && split[2].equals("sdcardfs")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void setCurrentUserId() throws Exception {
         if (mCurrentUserId != NativeDevice.INVALID_USER_ID) return;
 
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index c7887f8..7590679 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -26,6 +26,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -310,6 +311,7 @@
     }
 
     @Test
+    @Ignore("b/247099819")
     public void testClearPackageData() throws Exception {
         grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
         try {
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/AppCloningDeviceTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/AppCloningDeviceTest.java
new file mode 100644
index 0000000..6df4c8f
--- /dev/null
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/AppCloningDeviceTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.scopedstorage.cts;
+
+import static android.scopedstorage.cts.lib.TestUtils.canOpenFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.createFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.listAs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.install.lib.TestApp;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class AppCloningDeviceTest {
+
+    private static final String EMPTY_STRING = "";
+    private static final String EXTERNAL_STORAGE_DCIM_PATH = "/storage/emulated/%d/DCIM";
+
+    // An app with no permissions
+    private static final TestApp APP_B_NO_PERMS = new TestApp("TestAppB",
+            "android.scopedstorage.cts.testapp.B.noperms", 1, false,
+            "CtsScopedStorageTestAppB.apk");
+
+    @Test
+    public void testInsertFilesInDirectoryViaMediaProvider() throws Exception {
+        String dirPath = String.format(EXTERNAL_STORAGE_DCIM_PATH,
+                Integer.parseInt(getCurrentUserId()));
+        final File dir = new File(dirPath);
+        assertThat(dir.exists()).isTrue();
+        final File file = new File(dir, getFileToBeCreatedName());
+        assertThat(createFileAs(APP_B_NO_PERMS, file.getPath())).isTrue();
+        assertThat(canOpenFileAs(APP_B_NO_PERMS, file, true)).isTrue();
+        assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).contains(file.getName());
+    }
+
+    @Test
+    public void testGetFilesInDirectoryViaMediaProviderRespectsUserId() throws Exception {
+        String dirPath = String.format(EXTERNAL_STORAGE_DCIM_PATH,
+                Integer.parseInt(getCurrentUserId()));
+        final File dir = new File(dirPath);
+        assertThat(dir.exists()).isTrue();
+        final File expectedFile = new File(dir, getFileToBeExpectedName());
+        assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).contains(expectedFile.getName());
+        final File notExpectedFile = new File(dir, getFileNotToBeExpectedName());
+        assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).doesNotContain(notExpectedFile.getName());
+    }
+
+    private String getTestArgumentValueForGivenKey(String testArgumentKey) {
+        final Bundle testArguments = InstrumentationRegistry.getArguments();
+        String testArgumentValue = testArguments.getString(testArgumentKey, EMPTY_STRING);
+        return testArgumentValue;
+    }
+
+    private String getCurrentUserId() {
+        return getTestArgumentValueForGivenKey("currentUserId");
+    }
+
+    private String getFileToBeCreatedName() {
+        return getTestArgumentValueForGivenKey("fileToBeCreated");
+    }
+
+    private String getFileToBeExpectedName() {
+        return getTestArgumentValueForGivenKey("fileExpectedToBePresent");
+    }
+
+    private String getFileNotToBeExpectedName() {
+        return getTestArgumentValueForGivenKey("fileNotExpectedToBePresent");
+    }
+}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 1bb701b..253a7b8 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -1151,22 +1151,24 @@
 
             executeShellCommand("pm clear --user " + getCurrentUser() + " " + testAppPackageName);
 
-            // Wait a max of 5 seconds for the cleaning after "pm clear" command to complete.
+            // Wait a max of 10 seconds for the cleaning after "pm clear" command to complete.
             int i = 0;
-            while(i < 10 && getFileRowIdFromDatabase(fileToBeDeleted) != -1
+            while (i < 20 && getFileRowIdFromDatabase(fileToBeDeleted) != -1
                 && getFileRowIdFromDatabase(nestedFileToBeDeleted) != -1) {
                 Thread.sleep(500);
                 i++;
             }
-
-            assertThat(getFileOwnerPackageFromDatabase(fileToRemain)).isNull();
-            assertThat(getFileRowIdFromDatabase(fileToRemain)).isNotEqualTo(-1);
-
-            assertThat(getFileOwnerPackageFromDatabase(fileToBeDeleted)).isNull();
             assertThat(getFileRowIdFromDatabase(fileToBeDeleted)).isEqualTo(-1);
-
-            assertThat(getFileOwnerPackageFromDatabase(nestedFileToBeDeleted)).isNull();
             assertThat(getFileRowIdFromDatabase(nestedFileToBeDeleted)).isEqualTo(-1);
+
+            // Poll for package name to be cleared for existing files
+            i = 0;
+            while (i < 20 && getFileOwnerPackageFromDatabase(fileToRemain) != null) {
+                Thread.sleep(500);
+                i++;
+            }
+            assertThat(getFileRowIdFromDatabase(fileToRemain)).isNotEqualTo(-1);
+            assertThat(getFileOwnerPackageFromDatabase(fileToRemain)).isNull();
         } finally {
             deleteFilesAs(APP_B_NO_PERMS, fileToRemain);
             deleteFilesAs(APP_B_NO_PERMS, fileToBeDeleted);
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
index 52141c6..bea2bd8 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
@@ -47,7 +47,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Scanner;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -258,48 +257,6 @@
     }
 
     /**
-     * Enables malloc debug on a given process.
-     *
-     * @param processName the name of the process to run with libc malloc debug
-     * @param device the device to use
-     * @return true if enabling malloc debug succeeded
-     */
-    public static boolean enableLibcMallocDebug(String processName, ITestDevice device) throws Exception {
-        device.executeShellCommand("setprop libc.debug.malloc.program " + processName);
-        device.executeShellCommand("setprop libc.debug.malloc.options \"backtrace guard\"");
-        /**
-         * The pidof command is being avoided because it does not exist on versions before M, and
-         * it behaves differently between M and N.
-         * Also considered was the ps -AoPID,CMDLINE command, but ps does not support options on
-         * versions before O.
-         * The [^]] prefix is being used for the grep command to avoid the case where the output of
-         * ps includes the grep command itself.
-         */
-        String cmdOut = device.executeShellCommand("ps -A | grep '[^]]" + processName + "'");
-        /**
-         * .hasNextInt() checks if the next token can be parsed as an integer, not if any remaining
-         * token is an integer.
-         * Example command: $ ps | fgrep mediaserver
-         * Out: media     269   1     77016  24416 binder_thr 00f35142ec S /system/bin/mediaserver
-         * The second field of the output is the PID, which is needed to restart the process.
-         */
-        Scanner s = new Scanner(cmdOut).useDelimiter("\\D+");
-        if(!s.hasNextInt()) {
-            CLog.w("Could not find pid for process: " + processName);
-            return false;
-        }
-
-        String result = device.executeShellCommand("kill -9 " + s.nextInt());
-        if(!result.equals("")) {
-            CLog.w("Could not restart process: " + processName);
-            return false;
-        }
-
-        TimeUnit.SECONDS.sleep(1);
-        return true;
-    }
-
-    /**
      * Pushes and installs an apk to the selected device
      *
      * @param pathToApk a string path to apk from the /res folder
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java
index 1f5caa5..b461fae 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java
@@ -22,14 +22,14 @@
 import android.platform.test.annotations.AsbSecurityTest;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class Bug_182810085 extends StsExtraBusinessLogicHostTestBase {
+public class Bug_182810085 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.BUG_182810085";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "BUG-182810085.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java
index 75bbd0a..f0b6568 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java
@@ -23,10 +23,10 @@
 import org.junit.Before;
 import org.junit.runner.RunWith;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public final class Bug_183613671 extends StsExtraBusinessLogicHostTestBase {
+public final class Bug_183613671 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.BUG_183613671";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "BUG-183613671.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java
index adf6103..7b183b3 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java
@@ -25,10 +25,10 @@
 import org.junit.runner.RunWith;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public final class Bug_183963253 extends StsExtraBusinessLogicHostTestBase {
+public final class Bug_183963253 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.BUG_183963253";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "BUG-183963253.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java
new file mode 100644
index 0000000..0723e53
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java
@@ -0,0 +1,66 @@
+/*
+ * 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 static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class Bug_237291548 extends NonRootSecurityTestCase {
+
+    private static final String TEST_PKG = "android.security.cts.BUG_237291548";
+    private static final String TEST_CLASS = TEST_PKG + ".DeviceTest";
+    private static final String TEST_APP = "BUG-237291548.apk";
+    private static final String TEST_FAIL_INSTALL_APP = "BUG-237291548-FAIL-INSTALL.apk";
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        uninstallPackage(getDevice(), TEST_PKG);
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = 237291548)
+    public void testRunDeviceTestsPassesFull() throws Exception {
+        installPackage(TEST_APP);
+
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testExceedGroupLimit");
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testExceedMimeLengthLimit");
+    }
+
+    @Test(expected = TargetSetupError.class)
+    @AsbSecurityTest(cveBugId = 237291548)
+    public void testInvalidApkFails() throws Exception {
+        try {
+            installPackage(TEST_FAIL_INSTALL_APP);
+        } catch (TargetSetupError e) {
+            assertThat(e.getMessage(),
+                    containsString("Max limit on number of MIME Groups reached"));
+            throw e;
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java
index 3aa0474..32a1e6c 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java
@@ -23,13 +23,13 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_0015 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2020_0015 extends NonRootSecurityTestCase {
 
     @AppModeFull
     @AsbSecurityTest(cveBugId = 139017101)
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java
index 2bc254e..094eaea 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java
@@ -19,7 +19,7 @@
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_0338 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2020_0338 extends NonRootSecurityTestCase {
 
     @AppModeFull
     @AsbSecurityTest(cveBugId = 123700107)
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java
index 27e202c..63c8128 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_0448 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2020_0448 extends NonRootSecurityTestCase {
 
     static final String TEST_APP = "CVE-2020-0448.apk";
     static final String TEST_PKG = "android.security.cts.CVE_2020_0448";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java
index 4b1bc22..9df42ae 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java
@@ -22,7 +22,7 @@
 import android.platform.test.annotations.AsbSecurityTest;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -38,7 +38,7 @@
  * collected from the hostside and reported accordingly.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0305 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0305 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0305";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0305.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java
index b828d2d..1476e91 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0315 extends StsExtraBusinessLogicHostTestBase  {
+public class CVE_2021_0315 extends NonRootSecurityTestCase {
     static final String TEST_PKG = "android.security.cts.CVE_2021_0315";
 
     @After
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java
index b3b0f90..57b9a86 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0441 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0441 extends NonRootSecurityTestCase {
     static final String TEST_PKG = "android.security.cts.CVE_2021_0441";
     static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     static final String TEST_APP = "CVE-2021-0441.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java
new file mode 100644
index 0000000..45a1905
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java
@@ -0,0 +1,68 @@
+/*
+ * 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 static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0487 extends NonRootSecurityTestCase {
+    private static final String TEST_PKG = "android.security.cts.CVE_2021_0487";
+
+    /**
+     * b/174046397
+     * Vulnerable app    : CalendarProvider.apk
+     * Vulnerable module : com.android.providers.calendar
+     * Is Play managed   : No
+     */
+    @AsbSecurityTest(cveBugId = 174046397)
+    @Test
+    public void testPocCVE_2021_0487() {
+        ITestDevice device = null;
+        try {
+            device = getDevice();
+            /* 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);
+
+            /* Install the application */
+            installPackage("CVE-2021-0487.apk");
+
+            AdbUtils.runCommandLine(
+                    "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", device);
+            runDeviceTests(TEST_PKG, TEST_PKG + "." + "DeviceTest", "testOverlayButtonPresence");
+        } catch (Exception e) {
+            assumeNoException(e);
+        } finally {
+            try {
+                // return to home screen
+                AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+            } catch (Exception e) {
+                // ignore exceptions here
+            }
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java
index 30af472..7cd6360 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java
@@ -21,7 +21,7 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0523 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0523 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.cve_2021_0523";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0523.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java
index 5a7ec8d..f775822 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java
@@ -20,14 +20,14 @@
 import android.platform.test.annotations.AsbSecurityTest;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0586 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0586 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.cve_2021_0586";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0586.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java
index eb74b20..92c6435 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java
@@ -21,7 +21,7 @@
 import android.platform.test.annotations.RequiresDevice;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import java.util.regex.Pattern;
 import org.junit.Assert;
 import org.junit.Before;
@@ -33,7 +33,7 @@
 import static org.junit.Assume.assumeTrue;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0591 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0591 extends NonRootSecurityTestCase {
 
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0591";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java
index 29fd2b3..2e1ddda 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java
@@ -19,7 +19,7 @@
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0642 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0642 extends NonRootSecurityTestCase {
     static final String TEST_APP = "CVE-2021-0642.apk";
     static final String TEST_PKG = "android.security.cts.cve_2021_0642";
     static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java
index 26bba4a..15c59ef 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java
@@ -19,14 +19,14 @@
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.AsbSecurityTest;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0685 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0685 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.cve_2021_0685";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0685.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java
index bf261fd..01a3c07 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java
@@ -22,7 +22,7 @@
 import android.platform.test.annotations.AsbSecurityTest;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.log.LogUtil.CLog;
 
 import org.junit.After;
@@ -38,7 +38,7 @@
  * Test installs sample app and then tries to overwrite *.apk file
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0691 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0691 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0691";
     private static final String TEST_APP = "CVE-2021-0691.apk";
     private static final String DEVICE_TMP_DIR = "/data/local/tmp/";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java
index 2b7ad14..98deb18 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java
@@ -19,13 +19,13 @@
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.AsbSecurityTest;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0693 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0693 extends NonRootSecurityTestCase {
 
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0693";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java
index fabaf89..9225b56 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java
@@ -20,13 +20,13 @@
 import android.platform.test.annotations.AsbSecurityTest;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import org.junit.Before;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0706 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0706 extends NonRootSecurityTestCase {
 
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0706";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java
index 760c265..d4f6a45 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java
@@ -20,7 +20,7 @@
 import android.util.Log;
 import android.platform.test.annotations.AsbSecurityTest;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.log.LogUtil.CLog;
 import org.junit.After;
 import org.junit.Assert;
@@ -30,7 +30,7 @@
 import static org.junit.Assert.*;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0921 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0921 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0921";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0921.apk";
@@ -57,7 +57,7 @@
         getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
 
         //run the test
-        Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "test"));
+        runDeviceTests(TEST_PKG, TEST_CLASS, "test");
         CLog.i("testRunDeviceTest() end");
     }
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
index cbf1088..d83f26a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
@@ -23,7 +23,7 @@
 
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -31,7 +31,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0928 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0928 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0928";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0928.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java
index ecb6bdd..833b93a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java
@@ -19,13 +19,13 @@
 import android.platform.test.annotations.AsbSecurityTest;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0953 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0953 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 184046278)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java
index 95c90d4..847feef 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0954 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0954 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0954";
 
     @AsbSecurityTest(cveBugId = 143559931)
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
index 65934f2..b7b0e2b 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
@@ -21,7 +21,7 @@
 import android.platform.test.annotations.AsbSecurityTest;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0965 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_0965 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_0965";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-0965.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
index 3b12ce5..c47ebf1 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -26,7 +26,7 @@
 import org.junit.Test;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39626 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39626 extends NonRootSecurityTestCase {
     static final String TEST_APP = "CVE-2021-39626.apk";
     static final String TEST_PKG = "android.security.cts.CVE_2021_39626";
     static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java
index 444f1a5..f755142 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java
@@ -23,13 +23,13 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39692 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39692 extends NonRootSecurityTestCase {
 
     @AppModeFull
     @AsbSecurityTest(cveBugId = 209611539)
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java
index acc6a2e..63235ec 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java
@@ -23,7 +23,7 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,7 +31,7 @@
 import java.io.File;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39700 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39700 extends NonRootSecurityTestCase {
 
     /**
      * b/201645790
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java
index f8d6fe6..5e78a90 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -26,7 +26,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39701 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39701 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 212286849)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java
index cf8a688..cf5d47c 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java
@@ -21,14 +21,14 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39702 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39702 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_39702";
     private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
     private static final String TEST_APP = "CVE-2021-39702.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java
new file mode 100644
index 0000000..9aebd15
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java
@@ -0,0 +1,57 @@
+/*
+ * 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 static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39704 extends NonRootSecurityTestCase {
+
+    @AsbSecurityTest(cveBugId = 209965481)
+    @Test
+    public void testPocCVE_2021_39704() {
+        try {
+            final String testPkg = "android.security.cts.CVE_2021_39704";
+
+            ITestDevice device = getDevice();
+
+            AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+            AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+            AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+            installPackage("CVE-2021-39704.apk");
+            AdbUtils.runCommandLine(
+                    "pm revoke " + "android.security.cts.CVE_2021_39704 "
+                            + "android.permission.ACCESS_COARSE_LOCATION",
+                    device);
+
+            runDeviceTests(testPkg, testPkg + "." + "DeviceTest",
+                    "testdeleteNotificationChannelGroup");
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java
index cd8afef..ecf096f 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java
@@ -21,7 +21,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39706 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39706 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 200164168)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java
new file mode 100644
index 0000000..e40cea6
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java
@@ -0,0 +1,74 @@
+/*
+ * 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 static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39707 extends NonRootSecurityTestCase {
+
+    @AsbSecurityTest(cveBugId = 200688991)
+    @Test
+    public void testPocCVE_2021_39707() {
+        ITestDevice device = getDevice();
+        final String testPkg = "android.security.cts.CVE_2021_39707";
+        int userId = -1;
+        try {
+            // 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);
+
+            // Create restricted user
+            String commandOutput = AdbUtils.runCommandLine(
+                    "pm create-user --restricted CVE_2021_39707_RestrictedUser", device);
+
+            // Extract user id of the restricted user
+            String[] tokens = commandOutput.split("\\s+");
+            assumeTrue(tokens.length > 0);
+            assumeTrue(tokens[0].equals("Success:"));
+            userId = Integer.parseInt(tokens[tokens.length - 1]);
+
+            // Install PoC application
+            installPackage("CVE-2021-39707.apk");
+            runDeviceTests(testPkg, testPkg + ".DeviceTest", "testAppRestrictionsFragment");
+        } catch (Exception e) {
+            assumeNoException(e);
+        } finally {
+            try {
+                // Back to home screen after test
+                AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+                if (userId != -1) {
+                    // Remove restricted user
+                    AdbUtils.runCommandLine("pm remove-user " + userId, device);
+                }
+            } catch (Exception e) {
+                // ignore all exceptions
+            }
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java
index 0ae1efa..d67b4e6 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39794 extends StsExtraBusinessLogicHostTestBase  {
+public class CVE_2021_39794 extends NonRootSecurityTestCase {
 
     static final String TEST_APP = "CVE-2021-39794-test.apk";
     static final String RECEIVER_APP = "CVE-2021-39794-receiver.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java
index 4755ddb..a427e65 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39795 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39795 extends NonRootSecurityTestCase {
     private static final String TEST_PKG = "android.security.cts.CVE_2021_39795";
     private static final String DIR_PATH = "/storage/emulated/0/Android/data/CVE-2021-39795-dir";
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
index f90cae0..07fa927 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
@@ -20,14 +20,14 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39796 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39796 extends NonRootSecurityTestCase {
     static final int USER_ID = 0;
     static final String TEST_PKG = "android.security.cts.CVE_2021_39796";
     static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java
index ee835f5..1707ce9 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java
@@ -20,13 +20,13 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39797 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39797 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 209607104)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java
new file mode 100644
index 0000000..f1eaad2
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java
@@ -0,0 +1,51 @@
+/*
+ * 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 static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39808 extends NonRootSecurityTestCase {
+
+    @AsbSecurityTest(cveBugId = 209966086)
+    @Test
+    public void testPocCVE_2021_39808() {
+        try {
+            final String testPkg = "android.security.cts.CVE_2021_39808";
+
+            ITestDevice device = getDevice();
+
+            AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+            AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+            AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+            installPackage("CVE-2021-39808.apk");
+            runDeviceTests(testPkg, testPkg + "." + "DeviceTest","testService");
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
index f952082..9745336 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
@@ -21,14 +21,14 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_39810 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2021_39810 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 212610736)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java
index df8701c..ec4d197 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -26,7 +26,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20004 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20004 extends NonRootSecurityTestCase {
     final static String TEST_PKG = "android.security.cts.CVE_2022_20004_test";
     final static String PROVIDER_PKG = "android.security.cts.CVE_2022_20004_provider";
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java
index 47ea7ca..abc94f5 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20007 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20007 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 211481342)
     @Test
@@ -37,10 +37,12 @@
         final String testClass = testPkg + "." + "DeviceTest";
         final String testApp = "CVE-2022-20007.apk";
         final String testAttackerApp = "CVE-2022-20007-Attacker.apk";
+        final String testSecondApp = "CVE-2022-20007-Second.apk";
         ITestDevice device = getDevice();
         try {
             installPackage(testApp);
             installPackage(testAttackerApp);
+            installPackage(testSecondApp);
             AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
             AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
             AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java
index a8256d6..e83f090 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20115 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20115 extends NonRootSecurityTestCase {
     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";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java
index 45c6fb1..9e5e7eb 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java
@@ -18,7 +18,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -26,7 +26,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20138 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20138 extends NonRootSecurityTestCase {
     static final String TEST_APP = "CVE-2022-20138.apk";
     static final String TEST_PKG = "android.security.cts.CVE_2022_20138";
     static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java
new file mode 100644
index 0000000..3d31cee
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java
@@ -0,0 +1,71 @@
+/*
+ * 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 static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2022_20197 extends NonRootSecurityTestCase {
+    private static final String TEST_PKG = "android.security.cts.CVE_2022_20197";
+
+    @AsbSecurityTest(cveBugId = 208279300)
+    @Test
+    public void testPocCVE_2022_20197() {
+        ITestDevice device = null;
+        boolean isPolicyPresent = true;
+        boolean isHiddenApiEnabled = true;
+        String status = "";
+        try {
+            device = getDevice();
+            installPackage("CVE-2022-20197.apk");
+
+            status = AdbUtils.runCommandLine("settings get global hidden_api_policy", device);
+            if (status.toLowerCase().contains("null")) {
+                isPolicyPresent = false;
+            } else if (!status.toLowerCase().contains("1")) {
+                isHiddenApiEnabled = false;
+            }
+            if (!isPolicyPresent || !isHiddenApiEnabled) {
+                AdbUtils.runCommandLine("settings put global hidden_api_policy 1", device);
+            }
+            runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testParcel");
+        } catch (Exception e) {
+            assumeNoException(e);
+        } finally {
+            try {
+                if (!isPolicyPresent) {
+                    AdbUtils.runCommandLine("settings delete global hidden_api_policy", device);
+                } else if (!isHiddenApiEnabled) {
+                    AdbUtils.runCommandLine("settings put global hidden_api_policy " + status,
+                            device);
+                }
+            } catch (Exception e) {
+                // ignore all exceptions.
+            }
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java
index f593f20..18d4cdd 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java
@@ -21,7 +21,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20223 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20223 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 223578534)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java
index 1886a4a..59e7631 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20230 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20230 extends NonRootSecurityTestCase {
     public static final int USER_ID = 0;
     static final String TEST_APP = "CVE-2022-20230.apk";
     static final String TEST_PKG = "android.security.cts.CVE_2022_20230";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java
index de245bb..8087e69 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20347 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20347 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 228450811)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java
index 0f66dfd..df33a31 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20348 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20348 extends NonRootSecurityTestCase {
     static final String TEST_PKG = "android.security.cts.CVE_2022_20348";
     public static final String TEST_DEVICE_ADMIN_RECEIVER = ".PocDeviceAdminReceiver";
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java
index c4f5225..f8dcc48 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20349 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20349 extends NonRootSecurityTestCase {
     static final String TEST_PKG = "android.security.cts.CVE_2022_20349";
     public static final String TEST_DEVICE_ADMIN_RECEIVER = ".PocDeviceAdminReceiver";
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java
index e661b4f..12bb187 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AsbSecurityTest;
 
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2022_20353 extends StsExtraBusinessLogicHostTestBase {
+public class CVE_2022_20353 extends NonRootSecurityTestCase {
 
     @AsbSecurityTest(cveBugId = 221041256)
     @Test
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20501.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20501.java
new file mode 100644
index 0000000..067a011a
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20501.java
@@ -0,0 +1,68 @@
+/*
+ * 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 static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+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_2022_20501 extends NonRootSecurityTestCase {
+    private ITestDevice mDevice;
+
+    /**
+     * b/246933359
+     * Vulnerable app       : Telecom.apk
+     * Vulnerable module    : com.android.server.telecom
+     * Is Play managed      : No
+     */
+    @AsbSecurityTest(cveBugId = 246933359)
+    @Test
+    public void testPocCVE_2022_20501() {
+        try {
+            final String testPkg = "android.security.cts.CVE_2022_20501";
+            mDevice = getDevice();
+            installPackage("CVE-2022-20501.apk");
+
+            // Wake up the device
+            AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP && wm dismiss-keyguard",
+                    mDevice);
+
+            AdbUtils.runCommandLine(
+                    "pm grant " + testPkg + " android.permission.SYSTEM_ALERT_WINDOW", mDevice);
+            runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence");
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+
+    @After
+    public void tearDown() {
+        try {
+            AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", mDevice);
+        } catch (Exception ignored) {
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp
new file mode 100644
index 0000000..9ac80ac
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp
@@ -0,0 +1,64 @@
+// 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: "BUG-237291548",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "vts10",
+        "sts",
+    ],
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.core",
+    ],
+    sdk_version: "current",
+}
+
+android_test_helper_app {
+    name: "BUG-237291548-FAIL-INSTALL",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    manifest: ":BUG-237291548-BAD-MANIFEST",
+    test_suites: [
+        "cts",
+        "vts10",
+        "sts",
+    ],
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.core",
+    ],
+    sdk_version: "current",
+}
+
+// Modify the manifest file to include more than 500 MIME groups. The resulting
+// test apk generated using this manifest should fail package install since the
+// number of MIME groups is limited to a maximum of 500 per package.
+genrule {
+    name: "BUG-237291548-BAD-MANIFEST",
+    srcs: ["AndroidManifest.xml"],
+    out: ["BadAndroidManifest.xml"],
+    cmd: "awk '/myMimeGroup/{print;for(i=0;i<501;i++){sub(/myMimeGroup[0-9]*/,\"myMimeGroup\"i);print}}1' $(in) > $(out)",
+}
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml
new file mode 100644
index 0000000..cc692b8
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.BUG_237291548"
+    android:targetSandboxVersion="2">
+
+  <application>
+    <uses-library android:name="android.test.runner" />
+    <activity
+        android:name=".MainActivity"
+        android:exported="true">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+        <data android:mimeGroup="myMimeGroup" />
+      </intent-filter>
+    </activity>
+  </application>
+
+  <instrumentation
+      android:name="androidx.test.runner.AndroidJUnitRunner"
+      android:targetPackage="android.security.cts.BUG_237291548" />
+
+</manifest>
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java
new file mode 100644
index 0000000..e4554aa
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.BUG_237291548;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+    private static final String MIME_GROUP = "myMimeGroup";
+
+    PackageManager mPm = getApplicationContext().getPackageManager();
+
+    @Test(expected = IllegalStateException.class)
+    public void testExceedGroupLimit() {
+        Set<String> mimeTypes = mPm.getMimeGroup(MIME_GROUP);
+        assertEquals(mimeTypes.size(), 0);
+        for (int i = 0; i < 500; i++) {
+            mimeTypes.add("MIME" + i);
+            mPm.setMimeGroup(MIME_GROUP, mimeTypes);
+        }
+        mimeTypes = mPm.getMimeGroup(MIME_GROUP);
+        assertEquals(500, mimeTypes.size());
+        mimeTypes.add("ONETOMANYMIME");
+        mPm.setMimeGroup(MIME_GROUP, mimeTypes);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testExceedMimeLengthLimit() {
+        Set<String> mimeTypes = new HashSet<>();
+        mimeTypes.add(new String(new char[64]).replace("\0", "MIME"));
+        mPm.setMimeGroup(MIME_GROUP, mimeTypes);
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp
new file mode 100644
index 0000000..14d6baf
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/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-0487",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+    ],
+    sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml
new file mode 100644
index 0000000..5c5934c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.CVE_2021_0487">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <application>
+        <service android:name=".PocService" />
+    </application>
+   <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2021_0487" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml
new file mode 100644
index 0000000..de5f253
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <integer name="assumptionFailure">-1</integer>
+    <integer name="pass">0</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml
new file mode 100644
index 0000000..4d4098d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="broadcastReceiverCalendar">CalendarProviderBroadcastReceiver</string>
+    <string name="calendarClsName">%1$s.CalendarDebugActivity</string>
+    <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+    <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string>
+    <string name="dumpsysActivity">dumpsys activity %1$s</string>
+    <string name="errorMessage">Device is vulnerable to b/174046397 hence any app with
+    "SYSTEM_ALERT_WINDOW" can overlay the CalendarDebugActivity screen</string>
+    <string name="messageKey">message</string>
+    <string name="overlayButtonText">OverlayButton</string>
+    <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+    <string name="resultKey">result</string>
+    <string name="resumedTrue">mResumed=true</string>
+    <string name="sharedPreferences">CVE_2021_0487_prefs</string>
+    <string name="vulActivityNotRunningError">The CalendarDebugActivity is not currently
+    running on the device</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java
new file mode 100644
index 0000000..0a3e65d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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_0487;
+
+import static android.provider.CalendarContract.ACTION_EVENT_REMINDER;
+import static android.provider.CalendarContract.CONTENT_URI;
+
+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.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
+import android.net.Uri;
+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.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+    private static final int LAUNCH_TIMEOUT_MS = 10000;
+    private String mVulnerablePkgName = "";
+    private Context mContext = getApplicationContext();
+    Resources mResources;
+
+    private void startOverlayService() {
+        Intent intent = new Intent(mContext, PocService.class);
+        assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg),
+                Settings.canDrawOverlays(mContext));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startService(intent);
+    }
+
+    private void startVulnerableActivity() {
+        Intent intent = new Intent(ACTION_EVENT_REMINDER);
+        Intent vulnPkgNameIntent = new Intent();
+        intent.setData(CONTENT_URI);
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> ris = pm.queryBroadcastReceivers(intent, 0);
+        for (ResolveInfo ri : ris) {
+            if (ri.activityInfo.name
+                    .contains(mContext.getString(R.string.broadcastReceiverCalendar))) {
+                mVulnerablePkgName = ri.activityInfo.packageName;
+            }
+        }
+        vulnPkgNameIntent.setClassName(mVulnerablePkgName,
+                mContext.getString(R.string.calendarClsName, mVulnerablePkgName));
+        vulnPkgNameIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(vulnPkgNameIntent);
+    }
+
+    @Test
+    public void testOverlayButtonPresence() {
+        try {
+            UiDevice device = UiDevice.getInstance(getInstrumentation());
+
+            /* Go to home screen */
+            device.pressHome();
+
+            /* Start the overlay service */
+            startOverlayService();
+            mResources = mContext.getResources();
+
+            SharedPreferences sharedPrefs = mContext.getSharedPreferences(
+                    mResources.getString(R.string.sharedPreferences), Context.MODE_APPEND);
+            Semaphore preferenceChanged = new Semaphore(0);
+            OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+                @Override
+                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+                        String key) {
+                    if (key.equals(mResources.getString(R.string.resultKey))) {
+                        preferenceChanged.release();
+                    }
+                }
+            };
+            sharedPrefs.registerOnSharedPreferenceChangeListener(listener);
+
+            /* Wait for the overlay service to set some result in shared preferences */
+            assumeTrue(preferenceChanged.tryAcquire(LAUNCH_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+            int result = sharedPrefs.getInt(mResources.getString(R.string.resultKey),
+                    mResources.getInteger(R.integer.assumptionFailure));
+            String message = sharedPrefs.getString(mResources.getString(R.string.messageKey),
+                    mResources.getString(R.string.defaultSemaphoreMsg));
+            assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure));
+
+            /* Wait for the overlay window */
+            Pattern overlayTextPattern = Pattern.compile(
+                    mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE);
+            assumeTrue(mContext.getString(R.string.overlayUiScreenError),
+                    device.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS));
+
+            /* Start the vulnerable activity */
+            startVulnerableActivity();
+
+            /* Wait until the object of overlay window is gone */
+            boolean overlayDisallowed =
+                    device.wait(Until.gone(By.pkg(mContext.getPackageName())), LAUNCH_TIMEOUT_MS);
+
+            /* Check if the currently running activity is the vulnerable activity */
+            String activityDump = device.executeShellCommand(
+                    mContext.getString(R.string.dumpsysActivity, mVulnerablePkgName));
+            Pattern activityPattern = Pattern.compile(mContext.getString(R.string.resumedTrue),
+                    Pattern.CASE_INSENSITIVE);
+            assumeTrue(mContext.getString(R.string.vulActivityNotRunningError),
+                    activityPattern.matcher(activityDump).find());
+
+            /* Failing the test as fix is not present */
+            assertTrue(mContext.getString(R.string.errorMessage), overlayDisallowed);
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java
new file mode 100644
index 0000000..1a8cd0f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java
@@ -0,0 +1,114 @@
+/*
+ * 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_0487;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import androidx.annotation.IntegerRes;
+
+public class PocService extends Service {
+    private Button mButton;
+    private WindowManager mWindowManager;
+    private WindowManager.LayoutParams mLayoutParams;
+
+    int getInteger(@IntegerRes int resId) {
+        return getResources().getInteger(resId);
+    }
+
+    private static int getScreenWidth() {
+        return Resources.getSystem().getDisplayMetrics().widthPixels;
+    }
+
+    private static int getScreenHeight() {
+        return Resources.getSystem().getDisplayMetrics().heightPixels;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        try {
+            mWindowManager = getSystemService(WindowManager.class);
+            mLayoutParams = new WindowManager.LayoutParams();
+            mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+            mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.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;
+        } catch (Exception e) {
+            sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage());
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        try {
+            showFloatingWindow();
+            sendTestResult(getInteger(R.integer.pass), "");
+        } catch (Exception e) {
+            sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage());
+        }
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @Override
+    public void onDestroy() {
+        try {
+            mWindowManager.removeView(mButton);
+            super.onDestroy();
+        } catch (Exception e) {
+            // ignore the exception
+        }
+    }
+
+    private void showFloatingWindow() {
+        mButton = new Button(this);
+        mButton.setText(getString(R.string.overlayButtonText));
+        mWindowManager.addView(mButton, mLayoutParams);
+        mButton.setTag(mButton.getVisibility());
+    }
+
+    private void sendTestResult(int result, String message) {
+        try {
+            SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences),
+                    Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), result);
+            edit.putString(getString(R.string.messageKey), message);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore the exception
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java
index 167a849..3b19485 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java
@@ -11,8 +11,6 @@
 
 import java.io.File;
 
-import static org.junit.Assume.assumeNoException;
-
 public class Trigger {
     private static final String TAG = "TAG_2021_0921.Trigger";
     private Context mContext;
@@ -26,8 +24,19 @@
       return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
 
+    private static boolean isWear(Context context) {
+      PackageManager pm = context.getPackageManager();
+      return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
+    private static boolean isTv(Context context) {
+      PackageManager pm = context.getPackageManager();
+      return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION);
+    }
+
     public void accountSettings() {
         Log.d(TAG, "accountSettings() start");
+        String exitMessage = "accountSettings() end";
 
         //replaces intent.setAction(Intent.ACTION_REBOOT) in original Poc
         Intent arbitraryIntent = new Intent(mContext, TestActivity.class);
@@ -36,8 +45,19 @@
         Authenticator.mIntent = arbitraryIntent;
 
         Intent intent = new Intent();
-        String pkg = isCar(mContext) ? "com.android.car.settings" : "com.android.settings";
-        intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountSettings"));
+        String pkg = "com.android.settings";
+        if (isCar(mContext)) {
+            pkg = "com.android.car.settings";
+            intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountActivity"));
+        } else if (isTv(mContext)) {
+            pkg = "com.android.tv.settings";
+            intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountWithTypeActivity"));
+        } else if (isWear(mContext)) {
+            pkg = "com.google.android.clockwork.home";
+            intent.setComponent(new ComponentName(pkg, pkg + ".accounts.SetupAccountReceiver"));
+        } else {
+            intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountSettings"));
+        }
         intent.setAction(Intent.ACTION_RUN);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         String authTypes[] = {"android.security.cts"};
@@ -47,8 +67,9 @@
         try {
             mContext.startActivity(intent);
         } catch (ActivityNotFoundException e) {
-            assumeNoException(e);
+            // activity does not exist on this device
+            exitMessage = "accountSettings() failure: activity does not exist on this device";
         }
-        Log.d(TAG, "accountSettings() end");
+        Log.d(TAG, exitMessage);
     }
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp
new file mode 100644
index 0000000..044a5f5
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp
@@ -0,0 +1,38 @@
+/*
+ * 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-39704",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.rules",
+    ],
+    sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml
new file mode 100644
index 0000000..70b7a73
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.CVE_2021_39704">
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <application
+        android:supportsRtl="true">
+        <service
+            android:name=".PocService"
+            android:exported="true">
+        </service>
+        <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_2021_39704" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml
new file mode 100644
index 0000000..ec924a9
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <integer name="pass">2</integer>
+    <integer name="timeoutMs">5000</integer>
+    <integer name="assumptionFailure">3</integer>
+    <integer name="fail">1</integer>
+    <integer name="width">50</integer>
+    <integer name="height">50</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml
new file mode 100644
index 0000000..ab82c01
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <string name="channel">channel</string>
+    <string name="failMessage">Failed to open </string>
+    <string name="group">group</string>
+    <string name="groupId">groupId</string>
+    <string name="messageKey">messageKey</string>
+    <string name="passMessage">Passed</string>
+    <string name="resultKey">resultKey</string>
+    <string name="sharedPreference">sharedPreference</string>
+    <string name="vulnerableMessage">Vulnerable to b/209965481</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java
new file mode 100644
index 0000000..6336229
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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_39704;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeNoException;
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.Semaphore;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+    @Test
+    public void testdeleteNotificationChannelGroup() {
+        try {
+            Context context = getApplicationContext();
+            PackageManager packageManager = context.getPackageManager();
+            Intent intent = packageManager
+                    .getLaunchIntentForPackage(context.getPackageName());
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+            context.startActivity(intent);
+            SharedPreferences sh = context.getSharedPreferences(
+                    context.getString(R.string.sharedPreference),
+                    Context.MODE_APPEND);
+            final Semaphore preferenceChanged = new Semaphore(0);
+            OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+                @Override
+                public void onSharedPreferenceChanged(
+                        SharedPreferences sharedPreferences, String key) {
+                    if (key.equals(context.getString(R.string.resultKey))) {
+                        if (sharedPreferences.getInt(key, 0) == context
+                                .getResources().getInteger(R.integer.pass)) {
+                            preferenceChanged.release();
+                        }
+                    }
+                }
+            };
+            sh.registerOnSharedPreferenceChangeListener(listener);
+            preferenceChanged.tryAcquire(
+                    context.getResources().getInteger(R.integer.timeoutMs),
+                    TimeUnit.MILLISECONDS);
+
+            int result = sh.getInt(context.getString(R.string.resultKey),
+                    context.getResources().getInteger(R.integer.pass));
+            String message = sh.getString(
+                    context.getString(R.string.messageKey),
+                    context.getString(R.string.passMessage));
+            assumeTrue(message, result != context.getResources()
+                    .getInteger(R.integer.assumptionFailure));
+            assertNotEquals(message, result,
+                    context.getResources().getInteger(R.integer.fail));
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java
new file mode 100644
index 0000000..60ce757
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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_39704;
+
+import android.app.Activity;
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+//PocActitvity is required because requestPermissions needs to implemented to request location permission.
+public class PocActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        try {
+            super.onCreate(savedInstanceState);
+            if (this.checkCallingOrSelfPermission(
+                Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+                startForegroundService(new Intent(this, PocService.class));
+                this.requestPermissions(
+                        new String[] {
+                                Manifest.permission.ACCESS_COARSE_LOCATION },0);
+            }
+        } catch (Exception e) {
+            setExceptionStatus(e.toString(),
+                    getResources().getInteger(R.integer.assumptionFailure));
+        }
+    }
+
+    private void setExceptionStatus(String message, int status) {
+        try {
+            SharedPreferences sh = getSharedPreferences(
+                    getString(R.string.sharedPreference), Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), status);
+            edit.putString(getString(R.string.messageKey), message);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore exception
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java
new file mode 100644
index 0000000..23303c3
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java
@@ -0,0 +1,103 @@
+/*
+ * 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_39704;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.IBinder;
+
+//PocService is needed to build the notification when the service starts.
+public class PocService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        try {
+            exploitBug();
+            super.onCreate();
+        } catch (Exception e) {
+            setResult(getResources().getInteger(R.integer.assumptionFailure),
+                    e.getMessage());
+        }
+    }
+
+    void exploitBug() {
+        try {
+            final NotificationManager notificationManager = getSystemService(
+                    NotificationManager.class);
+            final String id = getString(R.string.channel);
+            final String groupId = getString(R.string.groupId);
+            notificationManager.createNotificationChannelGroup(
+                    new NotificationChannelGroup(groupId,
+                            getString(R.string.group)));
+            NotificationChannel notificationChannel = new NotificationChannel(
+                    id, id, NotificationManager.IMPORTANCE_HIGH);
+            notificationChannel.setGroup(groupId);
+            notificationManager.createNotificationChannel(notificationChannel);
+            Notification notification = new Notification.Builder(this, id)
+                    .setSmallIcon(createNotificationIcon()).build();
+            startForeground(1, notification);
+            setResult(getResources().getInteger(R.integer.fail),
+                    getString(R.string.vulnerableMessage));
+            notificationManager.deleteNotificationChannelGroup(groupId);
+            setResult(getResources().getInteger(R.integer.fail),
+                    getString(R.string.vulnerableMessage));
+        } catch (SecurityException e) {
+            setResult(getResources().getInteger(R.integer.pass),
+                    getString(R.string.passMessage));
+        }
+    }
+
+    private void setResult(int result, String message) {
+        try {
+            SharedPreferences sh = getSharedPreferences(
+                    getString(R.string.sharedPreference), Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), result);
+            edit.putString(getString(R.string.messageKey), message);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore exception
+        }
+    }
+
+    Icon createNotificationIcon() {
+        Resources resources = getResources();
+        Bitmap testBitmap = Bitmap.createBitmap(
+                resources.getInteger(R.integer.width),
+                resources.getInteger(R.integer.height),
+                Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(testBitmap);
+        canvas.drawColor(Color.BLUE);
+        return Icon.createWithBitmap(testBitmap);
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp
new file mode 100644
index 0000000..517619a
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/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-39707",
+    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-39707/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml
new file mode 100644
index 0000000..bfb3943
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.security.cts.CVE_2021_39707">
+    <application android:label="@string/testAppLabel">
+        <receiver android:name=".PocReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+            </intent-filter>
+        </receiver>
+        <activity android:name=".PocActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.CALL_PRIVILEGED" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="tel" />
+            </intent-filter>
+        </activity>
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2021_39707" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml
new file mode 100644
index 0000000..902f48c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml
@@ -0,0 +1,27 @@
+<?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="defaultSettingsPkgName">com.android.settings</string>
+    <string name="resTestAppIcon">%1$s:id/app_restrictions_settings</string>
+    <string name="testAppLabel">CVE-2021-39707</string>
+    <string name="testFailMsg">Device is vulnerable to b/200688991!!</string>
+    <string name="textAppContentAccess">App &amp; content access</string>
+    <string name="textRestrictedUser">CVE_2021_39707_RestrictedUser</string>
+    <string name="timedOutMsg">Timed out waiting for text/res \'%1$s\' on display</string>
+    <string name="uriData">tel:555-TEST</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java
new file mode 100644
index 0000000..db3acb0
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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_39707;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+    @Test
+    public void testAppRestrictionsFragment() {
+        try {
+            /* Start the "User Settings" window */
+            Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            Context context = getApplicationContext();
+            context.startActivity(intent);
+            String settingsPkgName =
+                    intent.resolveActivity(context.getPackageManager()).getPackageName();
+            settingsPkgName =
+                    (settingsPkgName == null) ? context.getString(R.string.defaultSettingsPkgName)
+                            : settingsPkgName;
+
+            /*
+             * Click on the text "CVE_2021_39707_RestrictedUser", the restricted user that we added
+             * before
+             */
+            final int uiTimeoutMs = 5000;
+            String textRestrictedUser = context.getString(R.string.textRestrictedUser);
+            BySelector selector = By.text(textRestrictedUser);
+            UiDevice device = UiDevice.getInstance(getInstrumentation());
+            assumeTrue(context.getString(R.string.timedOutMsg, textRestrictedUser),
+                    device.wait(Until.hasObject(selector), uiTimeoutMs));
+            device.findObject(selector).click();
+
+            /* Click on the text "App & content access" */
+            String textAppContentAccess = context.getString(R.string.textAppContentAccess);
+            selector = By.text(textAppContentAccess);
+            assumeTrue(context.getString(R.string.timedOutMsg, textAppContentAccess),
+                    device.wait(Until.hasObject(selector), uiTimeoutMs));
+            device.findObject(selector).click();
+
+            /*
+             * Click on the icon with resource name
+             * "com.android.settings:id/app_restrictions_settings" next to the test app
+             * "CVE-2021-39707"
+             */
+            UiScrollable scrollable = new UiScrollable(new UiSelector());
+            String textTestApp = context.getString(R.string.testAppLabel);
+            scrollable.scrollTextIntoView(textTestApp);
+            selector = By.text(textTestApp);
+            assumeTrue(context.getString(R.string.timedOutMsg, textTestApp),
+                    device.wait(Until.hasObject(selector), uiTimeoutMs));
+            UiObject2 parent = device.findObject(selector).getParent().getParent().getParent();
+            selector = By.res(context.getString(R.string.resTestAppIcon, settingsPkgName));
+            parent.findObject(selector).click();
+
+            /*
+             * Wait on the UI of the dialer app, test fails if the dialer app appears on the screen
+             * which indicates vulnerable behaviour
+             */
+            TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+            selector = By.pkg(telecomManager.getSystemDialerPackage());
+            assertFalse(context.getString(R.string.testFailMsg),
+                    device.wait(Until.hasObject(selector), uiTimeoutMs));
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java
new file mode 100644
index 0000000..92645c4
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java
@@ -0,0 +1,25 @@
+/*
+ * 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_39707;
+
+import android.app.Activity;
+
+// In order to detect the vulnerability, intent with action "android.intent.action.CALL_PRIVILEGED"
+// must resolve to more than 1 activity, so PocActivity is defined here with this intent to have at
+// least one activity other than the "PrivilegedCallActivity".
+public class PocActivity extends Activity {
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java
new file mode 100644
index 0000000..6d4caae
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java
@@ -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 android.security.cts.CVE_2021_39707;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+public class PocReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        try {
+            Bundle result = new Bundle();
+            Intent dialIntent = new Intent();
+            dialIntent.setData(Uri.parse(context.getString(R.string.uriData)));
+            dialIntent.setAction(Intent.ACTION_CALL_PRIVILEGED);
+            result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, dialIntent);
+            setResultExtras(result);
+        } catch (Exception e) {
+            // ignore all exceptions, in the worst case, any exception caught here indicates that
+            // setting extra intent was unsuccessful, so test will pass in the worst case.
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp
new file mode 100644
index 0000000..13a86e3
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp
@@ -0,0 +1,38 @@
+/*
+ * 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-39808",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.rules",
+    ],
+    platform_apis: true,
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml
new file mode 100644
index 0000000..0394d6c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.CVE_2021_39808">
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <application>
+        <service
+            android:name=".PocService"
+            android:exported="true">
+        </service>
+    </application>
+
+    <instrumentation
+            android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.security.cts.CVE_2021_39808" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml
new file mode 100644
index 0000000..8e7d104
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <integer name="assumptionFailure">4</integer>
+    <integer name="fail">2</integer>
+    <integer name="falseVal">-1</integer>
+    <integer name="height">50</integer>
+    <integer name="pass">3</integer>
+    <integer name="setFlag">1</integer>
+    <integer name="timeoutMs">10000</integer>
+    <integer name="value">0</integer>
+    <integer name="width">50</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml
new file mode 100644
index 0000000..f4fb741
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <string name="assumptionFailure">Assumption failure occurred</string>
+    <string name="errorNoMethodFound">No method found</string>
+    <string name="errorTargetMethodNotFound">Target method not found</string>
+    <string name="flag">flag</string>
+    <string name="functionName">createNotificationChannelGroups</string>
+    <string name="group">group</string>
+    <string name="groupId">groupId</string>
+    <string name="illegalCode">Illegal Code</string>
+    <string name="messageKey">MESSAGE</string>
+    <string name="resultKey">RESULT</string>
+    <string name="message">message</string>
+    <string name="notification">notification</string>
+    <string name="passMessage">Passed</string>
+    <string name="sharedPreference">CVE_2021_39808</string>
+    <string name="vulnerableMessage">
+    Vulnerable to b/209966086!! Foreground service ran without user notification
+    </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java
new file mode 100644
index 0000000..a32638d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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_39808;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeNoException;
+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.res.Resources;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+    @Test
+    public void testService() {
+        try {
+            Context context = getApplicationContext();
+            Intent intent = new Intent(context, PocService.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            context.startService(intent);
+            SharedPreferences sh = context.getSharedPreferences(
+                    context.getString(R.string.sharedPreference),
+                    Context.MODE_APPEND);
+            final Semaphore preferenceChanged = new Semaphore(0);
+            OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+                @Override
+                public void onSharedPreferenceChanged(
+                        SharedPreferences sharedPreferences, String key) {
+                    if (key.equals(context.getString(R.string.resultKey))) {
+                        if (sharedPreferences.getInt(key, 0) == context
+                                .getResources().getInteger(R.integer.pass)) {
+                            preferenceChanged.release();
+                        }
+                    }
+                }
+            };
+            sh.registerOnSharedPreferenceChangeListener(listener);
+
+            preferenceChanged.tryAcquire(
+                    context.getResources().getInteger(R.integer.timeoutMs),
+                    TimeUnit.MILLISECONDS);
+
+            int result = sh.getInt(context.getString(R.string.resultKey),
+                    context.getResources().getInteger(R.integer.pass));
+            String message = sh.getString(context.getString(R.string.messageKey),
+                    context.getString(R.string.passMessage));
+            assumeTrue(message, result != context.getResources()
+                    .getInteger(R.integer.assumptionFailure));
+            assertNotEquals(message, result,
+                    context.getResources().getInteger(R.integer.fail));
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java
new file mode 100644
index 0000000..73b0df4
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java
@@ -0,0 +1,137 @@
+/*
+ * 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_39808;
+
+import android.app.INotificationManager;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+
+import java.lang.reflect.Method;
+
+public class PocService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        try {
+            super.onCreate();
+            setResult(getResources().getInteger(R.integer.fail),
+                    getResources().getString(R.string.vulnerableMessage));
+            createNotificationGroup();
+        } catch (Exception e) {
+            setResult(getResources().getInteger(R.integer.assumptionFailure),
+                    e.getMessage());
+        }
+    }
+
+    void createNotificationGroup() throws Exception {
+        IBinder binder = ServiceManager
+                .getService(getResources().getString(R.string.notification));
+        int serviceId = getTransactionCode(
+                getResources().getString(R.string.functionName));
+        if (serviceId == -1) {
+            setResult(getResources().getInteger(R.integer.assumptionFailure),
+                    getString(R.string.errorNoMethodFound));
+            return;
+        } else if (serviceId == -2) {
+            setResult(getResources().getInteger(R.integer.assumptionFailure),
+                    getString(R.string.errorTargetMethodNotFound));
+            return;
+        }
+        createNotificationGroup(binder, serviceId);
+        NotificationManager notificationManager = (NotificationManager) getSystemService(
+                NOTIFICATION_SERVICE);
+        NotificationChannelGroup notificationChannelGroup = notificationManager
+                .getNotificationChannelGroup(
+                        getResources().getString(R.string.groupId));
+        if (!notificationChannelGroup.isBlocked()) {
+            setResult(getResources().getInteger(R.integer.pass),
+                    getResources().getString(R.string.passMessage));
+        }
+    }
+
+    int getTransactionCode(String methodName) {
+        int txCode = IBinder.FIRST_CALL_TRANSACTION;
+        String txName = INotificationManager.Stub
+                .getDefaultTransactionName(txCode);
+        if (txName == null) {
+            return -1;
+        }
+        while (txName != null && txCode <= IBinder.LAST_CALL_TRANSACTION) {
+            txName = INotificationManager.Stub
+                    .getDefaultTransactionName(++txCode);
+            if (txName.equals(methodName)) {
+                break;
+            }
+        }
+        if (txName == null) {
+            return -2;
+        }
+        return txCode;
+    }
+
+    void createNotificationGroup(IBinder binder, int code) throws Exception {
+        String description = binder.getInterfaceDescriptor();
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(description);
+        data.writeString(this.getPackageName());
+        data.writeInt(getResources().getInteger(R.integer.setFlag));
+        data.writeInt(getResources().getInteger(R.integer.setFlag));
+        data.writeString(NotificationChannelGroup.class.getName());
+        data.writeInt(getResources().getInteger(R.integer.setFlag));
+        data.writeByte((byte) getResources().getInteger(R.integer.setFlag));
+        data.writeString(getResources().getString(R.string.groupId));
+        TextUtils.writeToParcel(getResources().getString(R.string.group), data,
+                getResources().getInteger(R.integer.setFlag));
+        data.writeByte((byte) getResources().getInteger(R.integer.value));
+        data.writeInt(getResources().getInteger(R.integer.falseVal));
+        data.writeInt(getResources().getInteger(R.integer.setFlag));
+        boolean val = (boolean) binder.transact(code, data, reply,
+                getResources().getInteger(R.integer.value));
+        if (!val) {
+            setResult(getResources().getInteger(R.integer.assumptionFailure),
+                    getResources().getString(R.string.illegalCode));
+        }
+        reply.readException();
+    }
+
+    private void setResult(int result, String message) {
+        try {
+            SharedPreferences sh = getSharedPreferences(
+                    getString(R.string.sharedPreference), Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), result);
+            edit.putString(getString(R.string.messageKey), message);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore exception
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml
index 9f7ac84..731eac4 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml
@@ -23,7 +23,7 @@
         android:label="CVE-2022-20007-Attacker"
         android:supportsRtl="true">
         <activity
-            android:name=".PocActivity"
+            android:name=".PocAttackerActivity"
             android:exported="true"
             android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
         </activity>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java
similarity index 95%
rename from hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java
rename to hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java
index ad87ea7..988517e 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java
@@ -20,7 +20,7 @@
 import android.os.Bundle;
 import android.view.WindowManager;
 
-public class PocActivity extends Activity {
+public class PocAttackerActivity extends Activity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp
new file mode 100644
index 0000000..98d5962
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp
@@ -0,0 +1,33 @@
+/*
+ * 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-20007-Second",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml
new file mode 100644
index 0000000..7880b0f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.CVE_2022_20007_second"
+    android:sharedUserId="android.security.cts.CVE_2022_20007_shared_uid"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <application
+        android:label="CVE-2022-20007-Second"
+        android:process="android.security.cts.CVE_2022_20007"
+        android:supportsRtl="true">
+        <activity
+            android:name=".SecondPocActivity"
+            android:exported="true">
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml
new file mode 100644
index 0000000..d327e30
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml
new file mode 100644
index 0000000..e112bcd
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <integer name="fail">1</integer>
+    <integer name="pass">0</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml
new file mode 100644
index 0000000..c20d81c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="resultKey2">result2</string>
+    <string name="sharedPreferences">SharedPreferences</string>
+    <string name="testAppPackage">android.security.cts.CVE_2022_20007</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java
new file mode 100644
index 0000000..867da1c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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_20007_second;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+public class SecondPocActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        setSharedPreferenes(getResources().getInteger(R.integer.fail));
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        setSharedPreferenes(getResources().getInteger(R.integer.pass));
+    }
+
+    void setSharedPreferenes(int result) {
+        try {
+            Context testAppContext = createPackageContext(getString(R.string.testAppPackage),
+                    Context.CONTEXT_IGNORE_SECURITY);
+            SharedPreferences sh = testAppContext.getSharedPreferences(
+                    getString(R.string.sharedPreferences), Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey2), result);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore exception here
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp
index 713c0ed..0633c69 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp
@@ -32,6 +32,7 @@
     static_libs: [
         "androidx.test.core",
         "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
     ],
     sdk_version: "current",
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml
index ea78d62..c5dd6b5 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml
@@ -17,13 +17,15 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.security.cts.CVE_2022_20007"
+    android:sharedUserId="android.security.cts.CVE_2022_20007_shared_uid"
     android:versionCode="1"
     android:versionName="1.0">
     <application
         android:label="CVE-2022-20007"
+        android:process="android.security.cts.CVE_2022_20007"
         android:supportsRtl="true">
         <activity
-            android:name=".PocActivity"
+            android:name=".FirstPocActivity"
             android:exported="true">
         </activity>
         <activity
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml
index 26b15c2..bdb3775 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml
@@ -17,6 +17,9 @@
 
 <resources>
     <integer name="assumptionFailure">-1</integer>
-    <integer name="pass">0</integer>
     <integer name="fail">1</integer>
+    <integer name="pass">0</integer>
+    <integer name="permitCount">2</integer>
+    <integer name="threeActivities">3</integer>
+    <integer name="twoActivities">2</integer>
 </resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml
index 1368bc2..e9910b7 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml
@@ -17,14 +17,22 @@
 
 <resources>
     <string name="assumptionFailureMessage">
-    Assumption failure occurred.
+    Assumption failure occurred. Bounds :
     </string>
+    <string name="attackerActivity">PocAttackerActivity</string>
+    <string name="attackerPkg">android.security.cts.CVE_2022_20007_attacker</string>
+    <string name="boundsNotEqualMessage">Activity bounds are not equal</string>
+    <string name="dumpsysCmd">dumpsys activity %1$s</string>
     <string name="failMessage">
     Vulnerable to b/211481342!! Race Condition when startActivities() is invoked which can cause
-    Not-Paused Background Activity
+    Not-Paused Background Activity. Bounds :
     </string>
+    <string name="mBounds">mBounds</string>
     <string name="messageKey">message</string>
-    <string name="passMessage">Pass</string>
+    <string name="numActivities">numActivities</string>
     <string name="resultKey">result</string>
+    <string name="resultKey2">result2</string>
+    <string name="secondActivity">SecondPocActivity</string>
+    <string name="secondPocAppPkg">android.security.cts.CVE_2022_20007_second</string>
     <string name="sharedPreferences">SharedPreferences</string>
 </resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java
index 925da1c..d4828b8 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java
@@ -17,18 +17,22 @@
 package android.security.cts.CVE_2022_20007;
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static org.junit.Assert.assertNotEquals;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assume.assumeNoException;
 import static org.junit.Assume.assumeNotNull;
 import static org.junit.Assume.assumeTrue;
 
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.util.Log;
 
 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;
@@ -39,6 +43,13 @@
 @RunWith(AndroidJUnit4.class)
 public class DeviceTest {
     private Context mContext = getApplicationContext();
+    private UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
+    private boolean mIsVulnerable = true;
+    private boolean mIsVulnerable2 = true;
+    private String mFirstPocActivityBounds = "";
+    private String mSecondPocActivityBounds = "";
+    private String mPocAttackerActivityBounds = "";
+    private SharedPreferences mSharedPrefs = null;
 
     String getStringRes(int key) {
         return mContext != null ? mContext.getResources().getString(key) : null;
@@ -48,44 +59,115 @@
         return mContext != null ? mContext.getResources().getInteger(key) : null;
     }
 
-    @Test
-    public void testRaceCondition() throws Exception {
-        final long timeoutSec = 20L;
-        assumeNotNull(mContext);
+    String getBounds(String activityName) throws Exception {
+        String output =
+                mDevice.executeShellCommand(mContext.getString(R.string.dumpsysCmd, activityName));
+        output = output.substring(output.indexOf(getStringRes(R.string.mBounds)),
+                output.indexOf(")", output.indexOf(getStringRes(R.string.mBounds))) + 1);
+        return output;
+    }
+
+    void launchMainActivity(int numActivities) {
         final Intent intent = new Intent(mContext, PocMainActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
+        intent.putExtra(getStringRes(R.string.numActivities), numActivities);
+        mContext.startActivity(intent);
+    }
+
+    void checkResult(String key) {
+        int result = mSharedPrefs.getInt(key, getIntegerRes(R.integer.assumptionFailure));
+        assumeTrue(
+                getStringRes(R.string.assumptionFailureMessage) + mFirstPocActivityBounds + " "
+                        + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds,
+                result != getIntegerRes(R.integer.assumptionFailure));
+        assertFalse(
+                getStringRes(R.string.failMessage) + mFirstPocActivityBounds + " "
+                        + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds,
+                mIsVulnerable && result == getIntegerRes(R.integer.fail));
+    }
+
+    @Test
+    public void testRaceCondition() {
+        final long timeoutSec = 30L;
         try {
-            mContext.startActivity(intent);
-        } catch (ActivityNotFoundException e) {
-            assumeNoException(e);
-        }
-        SharedPreferences sharedPrefs = mContext.getSharedPreferences(
-                getStringRes(R.string.sharedPreferences), Context.MODE_APPEND);
-        assumeNotNull(sharedPrefs);
-        final Semaphore preferenceChanged = new Semaphore(0);
-        OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
-            @Override
-            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-                if (key.equals(getStringRes(R.string.resultKey))) {
-                    if (sharedPreferences.getInt(key,
-                            getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes(
-                                    R.integer.pass)) {
-                        preferenceChanged.release();
+            assumeNotNull(mContext);
+            launchMainActivity(getIntegerRes(R.integer.twoActivities));
+            mSharedPrefs = mContext.getSharedPreferences(getStringRes(R.string.sharedPreferences),
+                    Context.MODE_APPEND);
+            assumeNotNull(mSharedPrefs);
+            final Semaphore preferenceChanged = new Semaphore(0);
+            final Semaphore preferenceChanged2 = new Semaphore(0);
+            OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+                @Override
+                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+                        String key) {
+                    if (key.equals(getStringRes(R.string.resultKey))) {
+                        if (sharedPreferences.getInt(key,
+                                getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes(
+                                        R.integer.pass)) {
+                            preferenceChanged.release();
+                            mIsVulnerable = false;
+                        }
+                    } else if (key.equals(getStringRes(R.string.resultKey2))) {
+                        if (sharedPreferences.getInt(key,
+                                getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes(
+                                        R.integer.pass)) {
+                            preferenceChanged2.release();
+                            mIsVulnerable2 = false;
+                        }
                     }
                 }
-            }
-        };
-        sharedPrefs.registerOnSharedPreferenceChangeListener(listener);
-        try {
+            };
+            mSharedPrefs.registerOnSharedPreferenceChangeListener(listener);
             preferenceChanged.tryAcquire(timeoutSec, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
+
+            // Check if attacker activity is able to overlay victim activity
+            mFirstPocActivityBounds = getBounds(FirstPocActivity.class.getName());
+            String attackerActivityName = getStringRes(R.string.attackerPkg) + "/."
+                    + getStringRes(R.string.attackerActivity);
+            mPocAttackerActivityBounds = getBounds(attackerActivityName);
+            Log.e("DeviceTest", "mFirstPocActivityBounds=" + mFirstPocActivityBounds);
+            Log.e("DeviceTest", "mPocAttackerActivityBounds=" + mPocAttackerActivityBounds);
+            boolean isValidConfiguration =
+                    mFirstPocActivityBounds.equals(mPocAttackerActivityBounds);
+            if (isValidConfiguration) {
+                checkResult(getStringRes(R.string.resultKey));
+            } else {
+                // Device might have 2 task display areas. Detect vulnerability in this case.
+                mDevice.pressHome();
+                assumeTrue(mDevice.wait(Until.gone(By.pkg(mContext.getPackageName())), timeoutSec));
+                mIsVulnerable = true;
+                mIsVulnerable2 = true;
+                launchMainActivity(getIntegerRes(R.integer.threeActivities));
+                preferenceChanged.tryAcquire(getIntegerRes(R.integer.permitCount), timeoutSec,
+                        TimeUnit.SECONDS);
+                preferenceChanged2.tryAcquire(timeoutSec, TimeUnit.SECONDS);
+
+                // check if attacker activity is able to overlay any of the victim activities
+                mFirstPocActivityBounds = getBounds(FirstPocActivity.class.getName());
+                String secondActivityName = getStringRes(R.string.secondPocAppPkg) + "/."
+                        + getStringRes(R.string.secondActivity);
+                mSecondPocActivityBounds = getBounds(secondActivityName);
+                mPocAttackerActivityBounds = getBounds(attackerActivityName);
+                Log.e("DeviceTest", "mFirstPocActivityBounds=" + mFirstPocActivityBounds);
+                Log.e("DeviceTest", "mSecondPocActivityBounds=" + mSecondPocActivityBounds);
+                Log.e("DeviceTest", "mPocAttackerActivityBounds=" + mPocAttackerActivityBounds);
+                isValidConfiguration = mFirstPocActivityBounds.equals(mPocAttackerActivityBounds);
+                boolean isValidConfiguration2 =
+                        mSecondPocActivityBounds.equals(mPocAttackerActivityBounds);
+                assumeTrue(
+                        getStringRes(R.string.boundsNotEqualMessage) + mFirstPocActivityBounds + " "
+                                + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds,
+                        isValidConfiguration || isValidConfiguration2);
+
+                if (isValidConfiguration) {
+                    checkResult(getStringRes(R.string.resultKey));
+                } else {
+                    checkResult(getStringRes(R.string.resultKey2));
+                }
+            }
+        } catch (Exception e) {
             assumeNoException(e);
         }
-        int result = sharedPrefs.getInt(getStringRes(R.string.resultKey),
-                getIntegerRes(R.integer.assumptionFailure));
-        String message = sharedPrefs.getString(getStringRes(R.string.messageKey),
-                getStringRes(R.string.assumptionFailureMessage));
-        assumeTrue(message, result != getIntegerRes(R.integer.assumptionFailure));
-        assertNotEquals(message, result, getIntegerRes(R.integer.fail));
     }
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java
similarity index 67%
rename from hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java
rename to hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java
index 038335e..c89986b 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java
@@ -21,29 +21,35 @@
 import android.content.SharedPreferences;
 import android.os.Bundle;
 
-public class PocActivity extends Activity {
+public class FirstPocActivity extends Activity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
-        setSharedPreferenes(getResources().getInteger(R.integer.fail),
-                getString(R.string.failMessage));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        setSharedPreferenes(getResources().getInteger(R.integer.fail));
     }
 
     @Override
     protected void onPause() {
         super.onPause();
-        setSharedPreferenes(getResources().getInteger(R.integer.pass),
-                getString(R.string.passMessage));
+        setSharedPreferenes(getResources().getInteger(R.integer.pass));
     }
 
-    void setSharedPreferenes(int result, String message) {
-        SharedPreferences sh =
-                getSharedPreferences(getString(R.string.sharedPreferences), Context.MODE_PRIVATE);
-        SharedPreferences.Editor edit = sh.edit();
-        edit.putInt(getString(R.string.resultKey), result);
-        edit.putString(getString(R.string.messageKey), message);
-        edit.commit();
+    void setSharedPreferenes(int result) {
+        try {
+            SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences),
+                    Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), result);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore exception here
+        }
     }
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java
index 7a4e841..94de7f0 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java
@@ -17,7 +17,6 @@
 package android.security.cts.CVE_2022_20007;
 
 import android.app.Activity;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -30,30 +29,48 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
-        launchAttack();
-    }
-
-    public void launchAttack() {
-        String testPkgName = getPackageName();
-        final Intent coverIntent = new Intent();
-        coverIntent.setComponent(new ComponentName("android.security.cts.CVE_2022_20007_attacker",
-                "android.security.cts.CVE_2022_20007_attacker.PocActivity"));
-        coverIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION |
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-        final Intent victimIntent = new Intent(PocMainActivity.this, PocActivity.class);
-        victimIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-        Intent[] intents = {victimIntent, coverIntent};
         try {
-            startActivities(intents);
-        } catch (ActivityNotFoundException e) {
-            SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences),
-                    Context.MODE_PRIVATE);
-            SharedPreferences.Editor edit = sh.edit();
-            edit.putInt(getString(R.string.resultKey),
-                    getResources().getInteger(R.integer.assumptionFailure));
-            edit.putString(getString(R.string.messageKey),
-                    getString(R.string.assumptionFailureMessage));
-            edit.commit();
+            String testPkgName = getPackageName();
+            final Intent coverIntent = new Intent();
+            coverIntent.setComponent(new ComponentName(getString(R.string.attackerPkg),
+                    getString(R.string.attackerPkg) + "." + getString(R.string.attackerActivity)));
+            coverIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
+            final Intent victimIntent = new Intent(PocMainActivity.this, FirstPocActivity.class);
+            victimIntent
+                    .setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY);
+            int numActivities = getIntent().getIntExtra(getString(R.string.numActivities),
+                    /* default */ getResources().getInteger(R.integer.twoActivities));
+            if (numActivities == getResources().getInteger(R.integer.twoActivities)) {
+                Intent[] intents = {victimIntent, coverIntent};
+                startActivities(intents);
+            } else {
+                final Intent secondVictimIntent = new Intent();
+                secondVictimIntent.setComponent(new ComponentName(
+                        getString(R.string.secondPocAppPkg), getString(R.string.secondPocAppPkg)
+                                + "." + getString(R.string.secondActivity)));
+                secondVictimIntent.setFlags(
+                        Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY);
+                startActivity(victimIntent);
+
+                // wait to prevent both the victim activities from getting launched on same display
+                Thread.sleep(5000);
+                Intent[] intents2 = {secondVictimIntent, coverIntent};
+                startActivities(intents2);
+            }
+        } catch (Exception e) {
+            try {
+                SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences),
+                        Context.MODE_PRIVATE);
+                SharedPreferences.Editor edit = sh.edit();
+                edit.putInt(getString(R.string.resultKey),
+                        getResources().getInteger(R.integer.assumptionFailure));
+                edit.putString(getString(R.string.messageKey),
+                        getString(R.string.assumptionFailureMessage));
+                edit.commit();
+            } catch (Exception ex) {
+                // ignore exception here
+            }
         }
     }
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp
new file mode 100644
index 0000000..582076e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp
@@ -0,0 +1,38 @@
+/*
+ * 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-20197",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.core",
+    ],
+    platform_apis: true,
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml
new file mode 100644
index 0000000..3ea2a62
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.security.cts.CVE_2022_20197">
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2022_20197" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml
new file mode 100644
index 0000000..c9a9407
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="vulnerableMsg">Device is vulnerable to b/208279300!</string>
+    <string name="stringObj">CVE_2022_20197</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java
new file mode 100644
index 0000000..a7b5618
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.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.CVE_2022_20197;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeNoException;
+
+import android.app.PendingIntent;
+import android.content.res.Resources;
+import android.os.Parcel;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+    @Test
+    public void testParcel() {
+        try {
+            Resources resources = getApplicationContext().getResources();
+            Parcel parcel = Parcel.obtain();
+            Object cookie = (Object) resources.getString(R.string.stringObj);
+            parcel.setClassCookie(PendingIntent.class, cookie);
+            parcel.recycle();
+            Object value = parcel.getClassCookie(PendingIntent.class);
+            assertNull(resources.getString(R.string.vulnerableMsg), value);
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java
index 52f43c5..ec61aa1 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java
@@ -63,25 +63,20 @@
         return mContext.getResources().getInteger(resId);
     }
 
+    void switchBluetoothMode(String action) {
+        Intent intent = new Intent(mContext, PocActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(mContext.getString(R.string.btAction), action);
+        mContext.startActivity(intent);
+    }
+
     @Test
     public void testBluetoothDiscoverable() {
         OnSharedPreferenceChangeListener sharedPrefListener;
         SharedPreferences sharedPrefs;
         boolean btState = false;
         try {
-            BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
-
-            // Save the state of bluetooth adapter to reset after the test
-            btState = btAdapter.isEnabled();
-
-            // If bluetooth is disabled, enable it and wait for start activity to complete
             mContext = InstrumentationRegistry.getInstrumentation().getContext();
-            Intent intent = new Intent(mContext, PocActivity.class);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.putExtra(mContext.getString(R.string.btAction),
-                    BluetoothAdapter.ACTION_REQUEST_ENABLE);
-            mContext.startActivity(intent);
-
             Resources resources = mContext.getResources();
             sharedPrefs = mContext.getSharedPreferences(
                     resources.getString(R.string.sharedPreferences), Context.MODE_APPEND);
@@ -96,6 +91,25 @@
                 }
             };
             sharedPrefs.registerOnSharedPreferenceChangeListener(sharedPrefListener);
+            BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+            // Save the state of bluetooth adapter to reset after the test
+            btState = btAdapter.isEnabled();
+
+            // Disable bluetooth if already enabled in 'SCAN_MODE_CONNECTABLE_DISCOVERABLE' mode
+            if (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+                switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE);
+                assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs),
+                        TimeUnit.MILLISECONDS));
+                int result = sharedPrefs.getInt(resources.getString(R.string.resultKey),
+                        resources.getInteger(R.integer.assumptionFailure));
+                String message = sharedPrefs.getString(resources.getString(R.string.messageKey),
+                        resources.getString(R.string.defaultSemaphoreMsg));
+                assumeTrue(message, result != resources.getInteger(R.integer.assumptionFailure));
+            }
+
+            // Enable bluetooth if in disabled state
+            switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_ENABLE);
             assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs),
                     TimeUnit.MILLISECONDS));
             int result = sharedPrefs.getInt(resources.getString(R.string.resultKey),
@@ -107,6 +121,9 @@
             // Checking if bluetooth is enabled. The test requires bluetooth to be enabled
             assumeTrue(btAdapter.isEnabled());
 
+            // Checking if bluetooth mode is not set to SCAN_MODE_CONNECTABLE_DISCOVERABLE
+            assumeTrue(btAdapter.getScanMode() != btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+
             // Launch bluetooth settings which is supposed to set scan mode to
             // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is present
             UiAutomation uiautomation =
@@ -114,7 +131,7 @@
             uiautomation
                     .adoptShellPermissionIdentity(android.Manifest.permission.MODIFY_PHONE_STATE);
             String settingsPkg = getSettingsPkgName();
-            intent = new Intent();
+            Intent intent = new Intent();
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.setData(Uri.parse(mContext.getString(R.string.uri)));
             intent.setClassName(settingsPkg, settingsPkg + mContext.getString(R.string.className));
@@ -135,11 +152,7 @@
             try {
                 // Disable bluetooth if it was OFF before the test
                 if (!btState) {
-                    Intent intent = new Intent(mContext, PocActivity.class);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    intent.putExtra(mContext.getString(R.string.btAction),
-                            BluetoothAdapter.ACTION_REQUEST_DISABLE);
-                    mContext.startActivity(intent);
+                    switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE);
                     assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs),
                             TimeUnit.MILLISECONDS));
                 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/Android.bp
new file mode 100644
index 0000000..9498881
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/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-2022-20501",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+    ],
+    platform_apis: true,
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/AndroidManifest.xml
new file mode 100644
index 0000000..5789b65
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.CVE_2022_20501">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <application>
+        <service android:name=".PocService"
+            android:enabled="true" />
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2022_20501" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/res/values/strings.xml
new file mode 100644
index 0000000..2af8c0e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?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="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="dumpsysActivityException">Could not execute dumpsys activity command</string>
+    <string name="overlayButtonText">CVE_2022_20501_OverlayButton</string>
+    <string name="overlayErrorMessage">Device is vulnerable to b/246933359 hence any app with
+    "SYSTEM_ALERT_WINDOW permission can overlay the %1$s screen</string>
+    <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+    <string name="mResumedTrue">mResumed=true</string>
+    <string name="telUri">tel:6505551212</string>
+    <string name="telecomPkgDefault">com.android.server.telecom</string>
+    <string name="vulClsName">.settings.EnableAccountPreferenceActivity</string>
+    <string name="vulActivityNotRunningError">The %1$s is not currently running on the device
+    </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/DeviceTest.java
new file mode 100644
index 0000000..d6f5198
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/DeviceTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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_20501;
+
+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.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+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.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+    private Context mContext;
+
+    private void startOverlayService() {
+        Intent intent = new Intent(mContext, PocService.class);
+        assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg),
+                Settings.canDrawOverlays(mContext));
+        mContext.startService(intent);
+    }
+
+    private String getTelecomPkgName() {
+        PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
+        UiAutomation ui = getInstrumentation().getUiAutomation();
+        String name = mContext.getString(R.string.telecomPkgDefault);
+        try {
+            ui.adoptShellPermissionIdentity(android.Manifest.permission.INTERACT_ACROSS_USERS);
+            Intent intent = new Intent(Intent.ACTION_CALL);
+            intent.setData(Uri.parse(mContext.getString(R.string.telUri)));
+            ResolveInfo info = pm.resolveActivityAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY,
+                    UserHandle.USER_SYSTEM);
+            name = info.activityInfo.packageName;
+        } catch (Exception e) {
+            assumeNoException(e);
+        } finally {
+            ui.dropShellPermissionIdentity();
+        }
+        return name;
+    }
+
+    @Test
+    public void testOverlayButtonPresence() {
+        try {
+            mContext = getApplicationContext();
+            UiDevice device = UiDevice.getInstance(getInstrumentation());
+
+            // Start the overlay service
+            startOverlayService();
+
+            // Wait for the overlay window
+            Pattern overlayTextPattern = Pattern.compile(
+                    mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE);
+            final long launchTimeoutMs = 20_000L;
+            assumeTrue(mContext.getString(R.string.overlayUiScreenError),
+                    device.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs));
+
+            // Start the vulnerable activity
+            String pkg = getTelecomPkgName();
+            String cls = mContext.getString(R.string.vulClsName);
+            Intent intent = new Intent();
+            String vulActivity = pkg + cls;
+            intent.setClassName(pkg, vulActivity);
+            PackageManager pm = mContext.getPackageManager();
+            ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+            assumeNotNull(mContext.getString(R.string.activityNotFoundMsg, intent), ri);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intent);
+
+            // Wait until overlay window is gone
+            boolean overlayDisallowed =
+                    device.wait(Until.gone(By.text(overlayTextPattern)), launchTimeoutMs);
+
+            // Check if the currently running activity is the vulnerable activity
+            String activityDump = device.executeShellCommand(
+                    mContext.getString(R.string.cmdDumpsysActivity, vulActivity));
+
+            Pattern activityPattern = Pattern.compile(mContext.getString(R.string.mResumedTrue),
+                    Pattern.CASE_INSENSITIVE);
+            assumeTrue(mContext.getString(R.string.vulActivityNotRunningError, vulActivity),
+                    activityPattern.matcher(activityDump).find());
+
+            // Failing the test as overlay window is being allowed making code vulnerable
+            assertTrue(mContext.getString(R.string.overlayErrorMessage, vulActivity),
+                    overlayDisallowed);
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/PocService.java
new file mode 100644
index 0000000..8e1c5d3
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/PocService.java
@@ -0,0 +1,74 @@
+/*
+ * 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_20501;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+
+public class PocService extends Service {
+    private Button mButton;
+    private WindowManager mWindowManager;
+
+    @Override
+    public void onCreate() {
+        try {
+            super.onCreate();
+            mWindowManager = getSystemService(WindowManager.class);
+            LayoutParams layoutParams = new LayoutParams();
+            layoutParams.type = LayoutParams.TYPE_APPLICATION_OVERLAY;
+            layoutParams.flags =
+                    LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;
+            layoutParams.format = PixelFormat.OPAQUE;
+            layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+            DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
+            layoutParams.width = displayMetrics.widthPixels;
+            layoutParams.height = displayMetrics.heightPixels;
+            layoutParams.x = displayMetrics.widthPixels / 2;
+            layoutParams.y = displayMetrics.heightPixels / 2;
+
+            // Show the floating window
+            mButton = new Button(this);
+            mButton.setText(getString(R.string.overlayButtonText));
+            mWindowManager.addView(mButton, layoutParams);
+        } catch (Exception ignored) {
+            // In case of occurrence of an exception overlay won't appear on display which results in
+            // assumption failure in device test. Hence ignoring this exception here.
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        try {
+            mWindowManager.removeView(mButton);
+        } catch (Exception ignored) {
+        }
+        super.onDestroy();
+    }
+}
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java
index 42b9b5c..781be6b 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java
@@ -34,7 +34,6 @@
 import com.android.os.AtomsProto.AppCrashOccurred;
 import com.android.os.AtomsProto.AppUsageEventOccurred;
 import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.AttributionNode;
 import com.android.os.AtomsProto.AudioStateChanged;
 import com.android.os.AtomsProto.CameraStateChanged;
 import com.android.os.AtomsProto.FlashlightStateChanged;
@@ -49,6 +48,7 @@
 import com.android.os.AtomsProto.UiEventReported;
 import com.android.os.AtomsProto.VibratorStateChanged;
 import com.android.os.AtomsProto.WakelockStateChanged;
+import com.android.os.AttributionNode;
 import com.android.os.StatsLog.EventMetricData;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.log.LogUtil;
diff --git a/tests/PhotoPicker/AndroidManifest.xml b/tests/PhotoPicker/AndroidManifest.xml
index e10bf52..ca87690 100644
--- a/tests/PhotoPicker/AndroidManifest.xml
+++ b/tests/PhotoPicker/AndroidManifest.xml
@@ -19,7 +19,21 @@
           package="android.photopicker.cts">
 <application android:label="Photo Picker Device Tests">
     <uses-library android:name="android.test.runner" />
-    <activity android:name="android.photopicker.cts.GetResultActivity" />
+    <activity android:name="android.photopicker.cts.GetResultActivity"
+              android:exported="false"
+              android:enabled="true">
+        <intent-filter android:priority="999">
+            <action android:name="android.provider.action.PICK_IMAGES" />
+            <category android:name="android.intent.category.DEFAULT" />
+            <data android:mimeType="image/*" />
+            <data android:mimeType="video/*" />
+        </intent-filter>
+        <intent-filter android:priority="999">
+            <action android:name="android.provider.action.PICK_IMAGES" />
+            <category android:name="android.intent.category.DEFAULT" />
+        </intent-filter>
+    </activity>
+
 
     <provider android:name="android.photopicker.cts.cloudproviders.CloudProviderPrimary"
               android:authorities="android.photopicker.cts.cloudproviders.cloud_primary"
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java
index c7824bb..1746f4e 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java
@@ -18,13 +18,12 @@
 
 import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData;
 import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertReadOnlyAccess;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUriAndPath;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findAndClickBrowse;
-
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertReadOnlyAccess;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -33,7 +32,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.photopicker.cts.util.GetContentActivityAliasUtils;
-import android.photopicker.cts.util.PhotoPickerUiUtils;
+import android.photopicker.cts.util.UiAssertionUtils;
 import android.util.Pair;
 
 import androidx.test.uiautomator.UiObject;
@@ -42,9 +41,7 @@
 import androidx.test.uiautomator.UiSelector;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -73,26 +70,19 @@
         if (mActivity != null) {
             mActivity.finish();
         }
-    }
 
-    @BeforeClass
-    public static void setUpBeforeClass() throws Exception {
-        sDocumentsUiPackageName = getDocumentsUiPackageName();
-        sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState();
+        GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState);
     }
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
 
+        sDocumentsUiPackageName = getDocumentsUiPackageName();
+        sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState();
         clearPackageData(sDocumentsUiPackageName);
     }
 
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
-        GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState);
-    }
-
     @Test
     public void testMimeTypeFilter() throws Exception {
         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
@@ -100,7 +90,7 @@
         intent.addCategory(Intent.CATEGORY_OPENABLE);
         intent.setType("audio/*");
         mActivity.startActivityForResult(intent, REQUEST_CODE);
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
         // Should open documentsUi
         assertThatShowsDocumentsUiButtons();
 
@@ -116,7 +106,7 @@
         intent.setType("image/*");
         intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"video/*", "audio/*"});
         mActivity.startActivityForResult(intent, REQUEST_CODE);
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
         // Should open documentsUi
         assertThatShowsDocumentsUiButtons();
 
@@ -140,7 +130,7 @@
         intent.setType("image/*");
         mActivity.startActivityForResult(intent, REQUEST_CODE);
 
-        findAndClickBrowse(mDevice);
+        findAndClickBrowse(sDevice);
 
         findAndClickFilesInDocumentsUi(fileNameList);
 
@@ -166,7 +156,7 @@
         intent.setType("image/*");
         mActivity.startActivityForResult(intent, REQUEST_CODE);
 
-        findAndClickBrowse(mDevice);
+        findAndClickBrowse(sDevice);
 
         findAndClickFilesInDocumentsUi(fileNameList);
 
@@ -185,7 +175,7 @@
         mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE);
 
         // Should open Picker
-        assertThatShowsPickerUi();
+        UiAssertionUtils.assertThatShowsPickerUi();
     }
 
     @Test
@@ -207,7 +197,7 @@
         findAndClickMediaIcon();
 
         // Should open Picker
-        assertThatShowsPickerUi();
+        UiAssertionUtils.assertThatShowsPickerUi();
     }
 
     private void findAndClickMediaIcon() throws Exception {
@@ -219,30 +209,13 @@
                 new UiObject(appList).waitForExists(SHORT_TIMEOUT)).isTrue();
 
         String photoPickerAppName = "Media";
-        UiObject mediaButton = mDevice.findObject(new UiSelector().text(photoPickerAppName));
+        UiObject mediaButton = sDevice.findObject(new UiSelector().text(photoPickerAppName));
 
         assertWithMessage("Timed out waiting for " + photoPickerAppName + " app icon to appear")
                 .that(new UiScrollable(appList).scrollIntoView(mediaButton)).isTrue();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
-        clickAndWait(mDevice, mediaButton);
-    }
-
-    private void assertThatShowsPickerUi() {
-        // Assert that Search bar for DocumentsUi shows
-        // Add a short timeout wait for DocumentsUi to show
-        assertThat(new UiObject(new UiSelector().resourceIdMatches(
-                PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/bottom_sheet"))
-                .waitForExists(SHORT_TIMEOUT)).isTrue();
-
-        // Assert that "Recent files" header for DocumentsUi shows
-        assertThat(new UiObject(new UiSelector().resourceIdMatches(
-                PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/privacy_text"))
-                .exists()).isTrue();
-
-        // Assert that Documents list UiObject for DocumentsUi shows
-        assertThat(new UiObject(new UiSelector().text("Photos")).exists()).isTrue();
-        assertThat(new UiObject(new UiSelector().text("Albums")).exists()).isTrue();
+        clickAndWait(sDevice, mediaButton);
     }
 
     private void assertThatShowsDocumentsUiButtons() {
@@ -268,7 +241,7 @@
     private void findAndClickSelect() throws Exception {
         final UiObject selectButton = new UiObject(new UiSelector().resourceId(
                 sDocumentsUiPackageName + ":id/action_menu_select"));
-        clickAndWait(mDevice, selectButton);
+        clickAndWait(sDevice, selectButton);
     }
 
     private void findAndClickFileInDocumentsUi(String fileName) throws Exception {
@@ -284,7 +257,7 @@
             // Enforce to set the list mode
             // Because UiScrollable can't reach the real bottom (when WEB_LINKABLE_FILE item)
             // in grid mode when screen landscape mode
-            clickAndWait(mDevice, new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName
+            clickAndWait(sDevice, new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName
                     + ":id/sub_menu_list")));
         } catch (UiObjectNotFoundException ignored) {
             // Do nothing, already be in list mode.
@@ -306,9 +279,9 @@
                 }
             }
 
-            mDevice.swipe(/* startX= */ mDevice.getDisplayWidth() / 2,
-                    /* startY= */ mDevice.getDisplayHeight() / 2,
-                    /* endX= */ mDevice.getDisplayWidth() / 2,
+            sDevice.swipe(/* startX= */ sDevice.getDisplayWidth() / 2,
+                    /* startY= */ sDevice.getDisplayHeight() / 2,
+                    /* endX= */ sDevice.getDisplayWidth() / 2,
                     /* endY= */ 0,
                     /* steps= */ 40);
         }
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java
index 0f61dc7..6cd0a7b 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java
@@ -16,15 +16,15 @@
 
 package android.photopicker.cts;
 
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPersistedGrant;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPickerUriFormat;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -36,7 +36,9 @@
 import android.content.ClipData;
 import android.content.Intent;
 import android.net.Uri;
+import android.photopicker.cts.util.UiAssertionUtils;
 import android.provider.MediaStore;
+import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiObject;
@@ -55,6 +57,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest {
 
+    private static final String TAG = "ActionPickImagesOnlyTest";
     private List<Uri> mUriList = new ArrayList<>();
 
     @After
@@ -70,6 +73,24 @@
     }
 
     @Test
+    public void testPhotoPickerIntentDelegation() throws Exception {
+        final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+
+        for (String mimeType: new String[] {
+                null,
+                "image/*",
+                "video/*"
+        }) {
+            Log.d(TAG, "Testing Photo Picker intent delegation with MimeType " + mimeType);
+            intent.setType(mimeType);
+
+            mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE);
+
+            UiAssertionUtils.assertThatShowsPickerUi();
+        }
+    }
+
+    @Test
     public void testMultiSelect_invalidParam() throws Exception {
         final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
         intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit() + 1);
@@ -102,10 +123,10 @@
         assertThat(itemCount).isEqualTo(imageCount);
         // Select maxCount + 1 item
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        UiObject snackbarTextView = mDevice.findObject(new UiSelector().text(
+        UiObject snackbarTextView = sDevice.findObject(new UiSelector().text(
                 "Select up to 2 items"));
         assertWithMessage("Timed out while waiting for snackbar to appear").that(
                 snackbarTextView.waitForExists(SHORT_TIMEOUT)).isTrue();
@@ -113,7 +134,7 @@
         assertWithMessage("Timed out waiting for snackbar to disappear").that(
                 snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue();
 
-        clickAndWait(mDevice, findAddButton());
+        clickAndWait(sDevice, findAddButton());
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         final int count = clipData.getItemCount();
@@ -132,7 +153,7 @@
         final int itemCount = itemList.size();
         assertThat(itemCount).isEqualTo(imageCount);
         // Select 1 item
-        clickAndWait(mDevice, itemList.get(0));
+        clickAndWait(sDevice, itemList.get(0));
 
         final Uri uri = mActivity.getResult().data.getData();
         assertPickerUriFormat(uri, mContext.getUserId());
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java
index ceaf3b2..c89716f 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java
@@ -20,11 +20,11 @@
 import static android.photopicker.cts.PickerProviderMediaGenerator.MediaGenerator;
 import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvider;
 import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess;
 import static android.provider.MediaStore.PickerMediaColumns;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -374,12 +374,12 @@
         for (int i = 0; i < itemList.size(); i++) {
             final UiObject item = itemList.get(i);
             item.click();
-            mDevice.waitForIdle();
+            sDevice.waitForIdle();
         }
 
         final UiObject addButton = findAddButton();
         addButton.click();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         return mActivity.getResult().data.getClipData();
     }
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
index 79d4b2b..ca5a251 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
@@ -16,17 +16,13 @@
 
 package android.photopicker.cts;
 
-import android.Manifest;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
-import android.provider.DeviceConfig;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
-import com.android.modules.utils.build.SdkLevel;
-
 import org.junit.Before;
 
 /**
@@ -35,32 +31,31 @@
  */
 public class PhotoPickerBaseTest {
     public static int REQUEST_CODE = 42;
+    private static final Instrumentation sInstrumentation =
+            InstrumentationRegistry.getInstrumentation();
+    protected static final UiDevice sDevice = UiDevice.getInstance(sInstrumentation);
 
     protected GetResultActivity mActivity;
     protected Context mContext;
-    protected UiDevice mDevice;
 
     @Before
     public void setUp() throws Exception {
-        final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
-        mDevice = UiDevice.getInstance(inst);
-
         final String setSyncDelayCommand =
                 "device_config put storage pickerdb.default_sync_delay_ms 0";
-        mDevice.executeShellCommand(setSyncDelayCommand);
+        sDevice.executeShellCommand(setSyncDelayCommand);
 
-        mContext = inst.getContext();
+        mContext = sInstrumentation.getContext();
         final Intent intent = new Intent(mContext, GetResultActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
 
         // Wake up the device and dismiss the keyguard before the test starts
-        mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
-        mDevice.executeShellCommand("wm dismiss-keyguard");
+        sDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        sDevice.executeShellCommand("wm dismiss-keyguard");
 
-        mActivity = (GetResultActivity) inst.startActivitySync(intent);
+        mActivity = (GetResultActivity) sInstrumentation.startActivitySync(intent);
         // Wait for the UI Thread to become idle.
-        inst.waitForIdleSync();
+        sInstrumentation.waitForIdleSync();
         mActivity.clearResult();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
     }
 }
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
index 48c1ea1..e06873e 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
@@ -16,14 +16,14 @@
 
 package android.photopicker.cts;
 
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findProfileButton;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPickerUriFormat;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -90,7 +90,7 @@
         // Click the profile button to change to personal profile
         final UiObject profileButton = findProfileButton();
         profileButton.click();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         final List<UiObject> itemList = findItemList(imageCount);
         final int itemCount = itemList.size();
@@ -98,12 +98,12 @@
         for (int i = 0; i < itemCount; i++) {
             final UiObject item = itemList.get(i);
             item.click();
-            mDevice.waitForIdle();
+            sDevice.waitForIdle();
         }
 
         final UiObject addButton = findAddButton();
         addButton.click();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         final int count = clipData.getItemCount();
@@ -144,7 +144,7 @@
         // Click the profile button to change to work profile
         final UiObject profileButton = findProfileButton();
         profileButton.click();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         assertBlockedByAdminDialog(isInvokedFromWorkProfile);
     }
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java
new file mode 100644
index 0000000..bfa0462
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.photopicker.cts;
+
+import static android.os.SystemProperties.getBoolean;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.verifyActionBarExists;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.verifySettingsActivityIsVisible;
+
+import android.content.Intent;
+import android.photopicker.cts.util.PhotoPickerUiUtils;
+import android.provider.MediaStore;
+
+import androidx.test.uiautomator.UiObject;
+
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Photo Picker tests for settings page launched from the overflow menu in PhotoPickerActivity or
+ * the Settings app.
+ */
+public class PhotoPickerSettingsTest extends PhotoPickerBaseTest {
+
+    private static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+    private static final String ALLOWED_CLOUD_PROVIDERS_KEY = "allowed_cloud_providers";
+    private static final String SETTINGS_SYS_PROP =
+            "debug.photopicker.enable_settings_screen";
+
+    private static String sPreviouslyAllowedCloudProviders;
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        // Store current allowed cloud providers for reset at the end of tests.
+        sPreviouslyAllowedCloudProviders = sDevice.executeShellCommand(
+                String.format("device_config get %s %s", NAMESPACE_STORAGE_NATIVE_BOOT,
+                        ALLOWED_CLOUD_PROVIDERS_KEY));
+
+        // Enable Settings menu item in PhotoPickerActivity's overflow menu.
+        sDevice.executeShellCommand(
+                String.format("device_config put %s %s not_empty", NAMESPACE_STORAGE_NATIVE_BOOT,
+                        ALLOWED_CLOUD_PROVIDERS_KEY));
+        sDevice.executeShellCommand(String.format("setprop %s true", SETTINGS_SYS_PROP));
+        Assume.assumeTrue(getBoolean(SETTINGS_SYS_PROP, false));
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        // Reset allowed cloud providers device config.
+        if (sPreviouslyAllowedCloudProviders == null
+                || sPreviouslyAllowedCloudProviders.isBlank()) {
+            // Delete the device config since `device_config put` does not support empty values.
+            sDevice.executeShellCommand(
+                    String.format("device_config delete %s %s", NAMESPACE_STORAGE_NATIVE_BOOT,
+                            ALLOWED_CLOUD_PROVIDERS_KEY));
+        } else {
+            sDevice.executeShellCommand(
+                    String.format("device_config put %s %s %s", NAMESPACE_STORAGE_NATIVE_BOOT,
+                            ALLOWED_CLOUD_PROVIDERS_KEY, sPreviouslyAllowedCloudProviders));
+        }
+
+        // Disable Settings menu item in PhotoPickerActivity's overflow menu.
+        sDevice.executeShellCommand(String.format("setprop %s false", SETTINGS_SYS_PROP));
+        Assume.assumeFalse(getBoolean(SETTINGS_SYS_PROP, true));
+    }
+
+    @Test
+    public void testSettingsLaunchFromOverflowMenu() throws Exception {
+        // Launch PhotoPickerActivity.
+        final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+        // Click on the Settings menu item in the overflow menu.
+        final UiObject settingsMenuItem = PhotoPickerUiUtils.findSettingsOverflowMenuItem(sDevice);
+        PhotoPickerUiUtils.clickAndWait(sDevice, settingsMenuItem);
+
+        // Verify PhotoPickerSettingsActivity is launched and visible.
+        verifySettingsActivityIsVisible(sDevice);
+        verifyActionBarExists(sDevice);
+    }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
index 440e472..11d99d1 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
@@ -18,11 +18,6 @@
 
 import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData;
 import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertContainsMimeType;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
-import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createDNGVideosAndGetUris;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideosAndGetUris;
@@ -34,6 +29,11 @@
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddOrSelectButton;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertContainsMimeType;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertMimeType;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPersistedGrant;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPickerUriFormat;
+import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -52,8 +52,7 @@
 import androidx.test.uiautomator.UiSelector;
 
 import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -83,17 +82,14 @@
 
     private static int sGetContentTakeOverActivityAliasState;
 
-    @BeforeClass
-    public static void setUpBeforeClass() throws Exception {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
         sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState();
         clearPackageData(getDocumentsUiPackageName());
     }
 
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
-        GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState);
-    }
-
     @After
     public void tearDown() throws Exception {
         for (Uri uri : mUriList) {
@@ -104,6 +100,8 @@
         if (mActivity != null) {
             mActivity.finish();
         }
+
+        GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState);
     }
 
     @Test
@@ -115,7 +113,7 @@
         launchPhotoPickerForIntent(intent);
 
         final UiObject item = findItemList(itemCount).get(0);
-        clickAndWait(mDevice, item);
+        clickAndWait(sDevice, item);
 
         final Uri uri = mActivity.getResult().data.getData();
         assertPickerUriFormat(uri, mContext.getUserId());
@@ -132,14 +130,14 @@
         final Intent intent = new Intent(mAction);
         launchPhotoPickerForIntent(intent);
 
-        UiObject albumsTab = mDevice.findObject(new UiSelector().text(
+        UiObject albumsTab = sDevice.findObject(new UiSelector().text(
                 "Albums"));
-        clickAndWait(mDevice, albumsTab);
+        clickAndWait(sDevice, albumsTab);
         final UiObject album = findItemList(1).get(0);
-        clickAndWait(mDevice, album);
+        clickAndWait(sDevice, album);
 
         final UiObject item = findItemList(itemCount).get(0);
-        clickAndWait(mDevice, item);
+        clickAndWait(sDevice, item);
 
         final Uri uri = mActivity.getResult().data.getData();
         assertPickerUriFormat(uri, mContext.getUserId());
@@ -156,11 +154,11 @@
         addMultipleSelectionFlag(intent);
         launchPhotoPickerForIntent(intent);
 
-        UiObject albumsTab = mDevice.findObject(new UiSelector().text(
+        UiObject albumsTab = sDevice.findObject(new UiSelector().text(
                 "Albums"));
-        clickAndWait(mDevice, albumsTab);
+        clickAndWait(sDevice, albumsTab);
         final UiObject album = findItemList(1).get(0);
-        clickAndWait(mDevice, album);
+        clickAndWait(sDevice, album);
 
         final List<UiObject> itemList = findItemList(videoCount);
         final int itemCount = itemList.size();
@@ -168,10 +166,10 @@
         assertThat(itemCount).isEqualTo(videoCount);
 
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findViewSelectedButton());
+        clickAndWait(sDevice, findViewSelectedButton());
 
         // Wait for playback to start. This is needed in some devices where playback
         // buffering -> ready state takes around 10s.
@@ -189,11 +187,11 @@
 
         final UiObject item = findItemList(itemCount).get(0);
         item.longClick();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         final UiObject addButton = findPreviewAddOrSelectButton();
         assertThat(addButton.waitForExists(1000)).isTrue();
-        clickAndWait(mDevice, addButton);
+        clickAndWait(sDevice, addButton);
 
         final Uri uri = mActivity.getResult().data.getData();
         assertPickerUriFormat(uri, mContext.getUserId());
@@ -212,10 +210,10 @@
         final int itemCount = itemList.size();
         assertThat(itemCount).isEqualTo(imageCount);
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findAddButton());
+        clickAndWait(sDevice, findAddButton());
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         final int count = clipData.getItemCount();
@@ -243,26 +241,26 @@
         assertThat(itemCount).isEqualTo(videoCount);
 
         // Select one item from Photo grid
-        clickAndWait(mDevice, itemList.get(0));
+        clickAndWait(sDevice, itemList.get(0));
 
         // Preview the item
         UiObject item = itemList.get(1);
         item.longClick();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         final UiObject addOrSelectButton = findPreviewAddOrSelectButton();
         assertWithMessage("Timed out waiting for AddOrSelectButton to appear")
                 .that(addOrSelectButton.waitForExists(1000)).isTrue();
 
         // Select the item from Preview
-        clickAndWait(mDevice, addOrSelectButton);
+        clickAndWait(sDevice, addOrSelectButton);
 
-        mDevice.pressBack();
+        sDevice.pressBack();
 
         // Select one more item from Photo grid
-        clickAndWait(mDevice, itemList.get(2));
+        clickAndWait(sDevice, itemList.get(2));
 
-        clickAndWait(mDevice, findAddButton());
+        clickAndWait(sDevice, findAddButton());
 
         // Verify that all 3 items are returned
         final ClipData clipData = mActivity.getResult().data.getClipData();
@@ -289,10 +287,10 @@
         final int itemCount = itemList.size();
         assertThat(itemCount).isEqualTo(imageCount);
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findViewSelectedButton());
+        clickAndWait(sDevice, findViewSelectedButton());
 
         // Swipe left three times
         swipeLeftAndWait();
@@ -300,10 +298,10 @@
         swipeLeftAndWait();
 
         // Deselect one item
-        clickAndWait(mDevice, findPreviewSelectedCheckButton());
+        clickAndWait(sDevice, findPreviewSelectedCheckButton());
 
         // Return selected items
-        clickAndWait(mDevice, findPreviewAddButton());
+        clickAndWait(sDevice, findPreviewAddButton());
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         final int count = clipData.getItemCount();
@@ -350,20 +348,20 @@
 
         // Test 2: Click Mute Button
         // Click to unmute the audio
-        clickAndWait(mDevice, muteButton);
+        clickAndWait(sDevice, muteButton);
 
         waitForBinderCallsToComplete();
 
         // Check that mute button state is unmute, i.e., it shows `volume up` icon
         assertMuteButtonState(muteButton, /* isMuted */ false);
         // Click on the muteButton and check that mute button status is now 'mute'
-        clickAndWait(mDevice, muteButton);
+        clickAndWait(sDevice, muteButton);
 
         waitForBinderCallsToComplete();
 
         assertMuteButtonState(muteButton, /* isMuted */ true);
         // Click on the muteButton and check that mute button status is now unmute
-        clickAndWait(mDevice, muteButton);
+        clickAndWait(sDevice, muteButton);
 
         waitForBinderCallsToComplete();
 
@@ -371,8 +369,8 @@
 
         // Test 3: Next preview resumes mute state
         // Go back and launch preview again
-        mDevice.pressBack();
-        clickAndWait(mDevice, findViewSelectedButton());
+        sDevice.pressBack();
+        clickAndWait(sDevice, findViewSelectedButton());
 
         waitForBinderCallsToComplete();
 
@@ -408,7 +406,7 @@
 
         // Test 2: Swipe resumes mute state, with state of mute button 'volume up' / 'unmute'
         // Click muteButton again to check the next video resumes the previous video's mute state
-        clickAndWait(mDevice, muteButton);
+        clickAndWait(sDevice, muteButton);
 
         waitForBinderCallsToComplete();
 
@@ -462,12 +460,12 @@
 
         final UiObject muteButton = findMuteButton();
         // unmute the audio of video preview
-        clickAndWait(mDevice, muteButton);
+        clickAndWait(sDevice, muteButton);
 
         // Remote video preview involves binder calls
         // Wait for Binder calls to complete and device to be idle
         MediaStore.waitForIdle(mContext.getContentResolver());
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         assertMuteButtonState(muteButton, /* isMuted */ false);
 
@@ -512,7 +510,7 @@
 
         final UiObject playerView = findPlayerView();
         // Click on StyledPlayerView to make the video controls visible
-        clickAndWait(mDevice, playerView);
+        clickAndWait(sDevice, playerView);
         assertPlayerControlsVisible(playPauseButton, muteButton);
 
         // Wait for 1s and check that controls are still visible
@@ -521,7 +519,7 @@
         // Click on StyledPlayerView and check that controls are no longer visible. Don't click in
         // the center, clicking in the center may pause the video.
         playerView.clickBottomRight();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
         assertPlayerControlsHidden(playPauseButton, muteButton);
 
         // Swipe left and check that controls are not visible
@@ -529,7 +527,7 @@
         assertPlayerControlsHidden(playPauseButton, muteButton);
 
         // Click on the StyledPlayerView and check that controls appear
-        clickAndWait(mDevice, playerView);
+        clickAndWait(sDevice, playerView);
         assertPlayerControlsVisible(playPauseButton, muteButton);
 
         // Swipe left to check that controls are now visible on swipe
@@ -562,10 +560,10 @@
         final int itemCount = itemList.size();
         assertThat(itemCount).isAtLeast(videoCount);
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findAddButton());
+        clickAndWait(sDevice, findAddButton());
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         final int count = clipData.getItemCount();
@@ -608,10 +606,10 @@
         final int itemCount = itemList.size();
         assertThat(itemCount).isAtLeast(totalCount);
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findAddButton());
+        clickAndWait(sDevice, findAddButton());
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         assertWithMessage("Expected number of items returned to be: " + itemCount)
@@ -645,10 +643,10 @@
         final int itemCount = itemList.size();
         assertThat(itemCount).isAtLeast(videoCount);
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findAddButton());
+        clickAndWait(sDevice, findAddButton());
 
         final ClipData clipData = mActivity.getResult().data.getClipData();
         assertWithMessage("Expected number of items returned to be: " + itemCount)
@@ -681,16 +679,16 @@
         assertPlayerControlsAutoHide(playPauseButton, muteButton);
 
         // Click on StyledPlayerView to make the video controls visible
-        clickAndWait(mDevice, findPlayerView());
+        clickAndWait(sDevice, findPlayerView());
 
         // PlayPause button is now pause button, click the button to pause the video.
-        clickAndWait(mDevice, playPauseButton);
+        clickAndWait(sDevice, playPauseButton);
 
         // Wait for 1s and check that play button is not auto hidden
         assertPlayerControlsDontAutoHide(playPauseButton, muteButton);
 
         // PlayPause button is now play button, click the button to play the video.
-        clickAndWait(mDevice, playPauseButton);
+        clickAndWait(sDevice, playPauseButton);
         // Check that pause button auto-hides in 1s.
         assertPlayerControlsAutoHide(playPauseButton, muteButton);
     }
@@ -709,10 +707,10 @@
         assertThat(itemCount).isEqualTo(videoCount);
 
         for (int i = 0; i < itemCount; i++) {
-            clickAndWait(mDevice, itemList.get(i));
+            clickAndWait(sDevice, itemList.get(i));
         }
 
-        clickAndWait(mDevice, findViewSelectedButton());
+        clickAndWait(sDevice, findViewSelectedButton());
 
         // Wait for playback to start. This is needed in some devices where playback
         // buffering -> ready state takes around 10s.
@@ -725,7 +723,7 @@
     private void waitForBinderCallsToComplete() {
         // Wait for Binder calls to complete and device to be idle
         MediaStore.waitForIdle(mContext.getContentResolver());
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
     }
 
     private void setUpAndAssertStickyPlayerControls(UiObject playerView, UiObject playPauseButton,
@@ -735,7 +733,7 @@
         // Wait for 1s or Play/Pause button to hide
         playPauseButton.waitUntilGone(1000);
         // Click on StyledPlayerView to make the video controls visible
-        clickAndWait(mDevice, playerView);
+        clickAndWait(sDevice, playerView);
         assertPlayerControlsVisible(playPauseButton, muteButton);
     }
 
@@ -811,10 +809,10 @@
     }
 
     private void swipeLeftAndWait() {
-        final int width = mDevice.getDisplayWidth();
-        final int height = mDevice.getDisplayHeight();
-        mDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 10);
-        mDevice.waitForIdle();
+        final int width = sDevice.getDisplayWidth();
+        final int height = sDevice.getDisplayHeight();
+        sDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 10);
+        sDevice.waitForIdle();
     }
 
     private static List<String> getTestParameters() {
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java
index b6eb8f3..20b22c5 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java
@@ -21,12 +21,18 @@
 import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider;
 import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.REGEX_PACKAGE_NAME;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
 import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_BUFFERING;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_ERROR_PERMANENT_FAILURE;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE;
 import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_READY;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -86,7 +92,7 @@
 
         Assume.assumeTrue(getBoolean("sys.photopicker.pickerdb.enabled", true));
 
-        mDevice.executeShellCommand("setprop sys.photopicker.remote_preview true");
+        sDevice.executeShellCommand("setprop sys.photopicker.remote_preview true");
         Assume.assumeTrue(getBoolean("sys.photopicker.remote_preview", true));
 
         mCloudPrimaryMediaGenerator = PickerProviderMediaGenerator.getMediaGenerator(
@@ -128,7 +134,7 @@
         // TODO(b/215187981): Add test for onMediaPause()
 
         // Exit preview mode
-        mDevice.pressBack();
+        sDevice.pressBack();
 
         // Remote Preview calls onSurfaceDestroyed, check if the id is the same (as the
         // CloudMediaProvider is only rendering to one surface id)
@@ -219,6 +225,41 @@
         // test the remote preview APIs
     }
 
+    @Test
+    public void testVideoPreviewProgressIndicator() throws Exception {
+        initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1)));
+        launchPreviewMultiple(/* count */ 1);
+
+        // Remote Preview displays circular progress indicator when playback state is
+        // PLAYBACK_STATE_BUFFERING.
+        verifyProgressIndicatorShowsWhenBuffering(/* surfaceId */ 0);
+    }
+
+    @Test
+    public void testVideoPreviewPermanentError() throws Exception {
+        initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1)));
+        launchPreviewMultiple(/* count */ 1);
+
+        // Remote Preview displays Snackbar to notify the user of an error when playback state is
+        // PLAYBACK_STATE_ERROR_PERMANENT_FAILURE.
+        verifySnackbarShowsWhenPermanentError(/* surfaceId */ 0);
+    }
+
+    @Test
+    public void testVideoPreviewRetriableError() throws Exception {
+        initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1)));
+        final int surfaceId = 0;
+        launchPreviewMultiple(/* count */ 1);
+
+        // Remote Preview displays an AlertDialog to notify the user of an error when playback state
+        // is PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE.
+        verifyAlertDialogShowsWhenRetriableError(surfaceId);
+
+        // Remote Preview calls onMediaPlay when user clicks the retry button in the retriable error
+        // AlertDialog.
+        verifyAlertDialogRetry(surfaceId);
+    }
+
     /**
      * Verify surface controller interactions on swiping from one video to another.
      * Note: This test assumes that the first video is in playing state.
@@ -269,6 +310,54 @@
         mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId));
     }
 
+    private void verifyProgressIndicatorShowsWhenBuffering(int surfaceId) throws Exception {
+        CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_BUFFERING);
+        // Wait for photo picker to receive the event and invoke media play via binder calls.
+        MediaStore.waitForIdle(mContext.getContentResolver());
+        assertWithMessage("Expected circular progress indicator to be visible when state is "
+                + "buffering").that(findPreviewProgressIndicator().waitForExists(SHORT_TIMEOUT))
+                .isTrue();
+    }
+
+    private void verifySnackbarShowsWhenPermanentError(int surfaceId) throws Exception {
+        CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_PERMANENT_FAILURE);
+        // Wait for photo picker to receive the event and invoke media play via binder calls.
+        MediaStore.waitForIdle(mContext.getContentResolver());
+        assertWithMessage("Expected snackbar to be visible when state is permanent error")
+                .that(findPreviewErrorSnackbar().waitForExists(SHORT_TIMEOUT)).isTrue();
+    }
+
+    private void verifyAlertDialogShowsWhenRetriableError(int surfaceId) throws Exception {
+        CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE);
+        // Wait for photo picker to receive the event and invoke media play via binder calls.
+        MediaStore.waitForIdle(mContext.getContentResolver());
+
+        assertWithMessage("Expected alert dialog with title to be visible when state is retriable "
+                + "error").that(findPreviewErrorAlertDialogTitle().waitForExists(SHORT_TIMEOUT))
+                .isTrue();
+        assertWithMessage("Expected alert dialog with text body to be visible when state is "
+                + "retriable error").that(findPreviewErrorAlertDialogBody().exists()).isTrue();
+        assertWithMessage("Expected alert dialog with retry button to be visible when state is "
+                + "retriable error").that(findPreviewErrorAlertDialogRetryButton().exists())
+                .isTrue();
+        assertWithMessage("Expected alert dialog with cancel button to be visible when state is "
+                + "retriable error").that(findPreviewErrorAlertDialogCancelButton().exists())
+                .isTrue();
+    }
+
+    private void verifyAlertDialogRetry(int surfaceId) throws Exception {
+        CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE);
+        // Wait for photo picker to receive the event and invoke media play via binder calls.
+        MediaStore.waitForIdle(mContext.getContentResolver());
+
+        assertWithMessage("Expected alert dialog with retry button to be visible when state is "
+                + "retriable error")
+                .that(findPreviewErrorAlertDialogRetryButton().waitForExists(SHORT_TIMEOUT))
+                .isTrue();
+        clickAndWait(sDevice, findPreviewErrorAlertDialogRetryButton());
+        mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId));
+    }
+
     private void initCloudProviderWithImage(List<Pair<String, String>> mediaPairs)
             throws Exception {
         for (Pair<String, String> pair : mediaPairs) {
@@ -313,12 +402,12 @@
 
         for (final UiObject item : itemList) {
             item.click();
-            mDevice.waitForIdle();
+            sDevice.waitForIdle();
         }
 
         final UiObject viewSelectedButton = findViewSelectedButton();
         viewSelectedButton.click();
-        mDevice.waitForIdle();
+        sDevice.waitForIdle();
 
         // Wait for CloudMediaProvider binder calls to finish.
         MediaStore.waitForIdle(mContext.getContentResolver());
@@ -330,22 +419,47 @@
     }
 
     private void swipeLeftAndWait() throws Exception {
-        final int width = mDevice.getDisplayWidth();
-        final int height = mDevice.getDisplayHeight();
-        mDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10);
-        mDevice.waitForIdle();
+        final int width = sDevice.getDisplayWidth();
+        final int height = sDevice.getDisplayHeight();
+        sDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10);
+        sDevice.waitForIdle();
 
         // Wait for CloudMediaProvider binder calls to finish.
         MediaStore.waitForIdle(mContext.getContentResolver());
     }
 
     private void swipeRightAndWait() throws Exception {
-        final int width = mDevice.getDisplayWidth();
-        final int height = mDevice.getDisplayHeight();
-        mDevice.swipe(width / 4, height / 2, width / 2, height / 2, 10);
-        mDevice.waitForIdle();
+        final int width = sDevice.getDisplayWidth();
+        final int height = sDevice.getDisplayHeight();
+        sDevice.swipe(width / 4, height / 2, width / 2, height / 2, 10);
+        sDevice.waitForIdle();
 
         // Wait for CloudMediaProvider binder calls to finish.
         MediaStore.waitForIdle(mContext.getContentResolver());
     }
+
+    private static UiObject findPreviewProgressIndicator() {
+        return new UiObject(new UiSelector().resourceIdMatches(
+                REGEX_PACKAGE_NAME + ":id/preview_progress_indicator"));
+    }
+
+    private static UiObject findPreviewErrorAlertDialogTitle() {
+        return new UiObject(new UiSelector().text("Trouble playing video"));
+    }
+
+    private static UiObject findPreviewErrorAlertDialogBody() {
+        return new UiObject(new UiSelector().text("Check your internet connection and try again"));
+    }
+
+    private static UiObject findPreviewErrorAlertDialogRetryButton() {
+        return new UiObject(new UiSelector().textMatches("R(etry|ETRY)"));
+    }
+
+    private static UiObject findPreviewErrorAlertDialogCancelButton() {
+        return new UiObject(new UiSelector().textMatches("C(ancel|ANCEL)"));
+    }
+
+    private static UiObject findPreviewErrorSnackbar() {
+        return new UiObject(new UiSelector().text("Can't play video"));
+    }
 }
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java
index 8f58f3e..5cdb592 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java
@@ -95,18 +95,49 @@
     }
 
     public static void findAndClickBrowse(UiDevice uiDevice) throws Exception {
-        assertWithMessage("Timed out waiting for overflow menu to appear")
-                .that(new UiObject(new UiSelector().description("More options"))
-                        .waitForExists(SHORT_TIMEOUT))
-                .isTrue();
-
-        final UiObject overflowMenu = new UiObject(new UiSelector().description("More options"));
+        final UiObject overflowMenu = getOverflowMenuObject(uiDevice);
         clickAndWait(uiDevice, overflowMenu);
 
         final UiObject browseButton = new UiObject(new UiSelector().textContains("Browse"));
         clickAndWait(uiDevice, browseButton);
     }
 
+    public static UiObject findSettingsOverflowMenuItem(UiDevice uiDevice) throws Exception {
+        final UiObject overflowMenu = getOverflowMenuObject(uiDevice);
+        clickAndWait(uiDevice, overflowMenu);
+        return new UiObject(new UiSelector().textContains("Settings"));
+    }
+
+    public static UiObject getOverflowMenuObject(UiDevice uiDevice)  {
+        // Wait for overflow menu to appear.
+        verifyOverflowMenuExists(uiDevice);
+        return new UiObject(new UiSelector().description("More options"));
+    }
+
+    public static void verifyActionBarExists(UiDevice uiDevice) {
+        assertWithMessage("Timed out waiting for action bar to appear")
+                .that(new UiObject(new UiSelector()
+                        .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/action_bar"))
+                        .waitForExists(TIMEOUT))
+                .isTrue();
+    }
+
+    private static void verifyOverflowMenuExists(UiDevice uiDevice) {
+        assertWithMessage("Timed out waiting for overflow menu to appear")
+                .that(new UiObject(new UiSelector().description("More options"))
+                        .waitForExists(TIMEOUT))
+                .isTrue();
+    }
+
+    public static void verifySettingsActivityIsVisible(UiDevice uiDevice) {
+        // id/settings_activity_root is the root layout in activity_photo_picker_settings.xml
+        assertWithMessage("Timed out waiting for settings activity to appear")
+                .that(new UiObject(new UiSelector()
+                .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/settings_activity_root"))
+                .waitForExists(TIMEOUT))
+                .isTrue();
+    }
+
     public static void clickAndWait(UiDevice uiDevice, UiObject uiObject) throws Exception {
         uiObject.click();
         uiDevice.waitForIdle();
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/ResultsAssertionsUtils.java
similarity index 98%
rename from tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
rename to tests/PhotoPicker/src/android/photopicker/cts/util/ResultsAssertionsUtils.java
index 6d86cee..e08dd2a 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/ResultsAssertionsUtils.java
@@ -46,9 +46,9 @@
 import java.util.List;
 
 /**
- * Photo Picker Utility methods for test assertions.
+ * Photo Picker Utility methods for PhotoPicker result assertions.
  */
-public class PhotoPickerAssertionsUtils {
+public class ResultsAssertionsUtils {
     private static final String TAG = "PhotoPickerTestAssertions";
 
     public static void assertPickerUriFormat(Uri uri, int expectedUserId) {
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/UiAssertionUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/UiAssertionUtils.java
new file mode 100644
index 0000000..7caeb6a
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/UiAssertionUtils.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.photopicker.cts.util;
+
+import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiSelector;
+
+/**
+ * Photo Picker Utility methods for PhotoPicker UI assertions.
+ */
+public class UiAssertionUtils {
+    /**
+     * Verifies PhotoPicker UI is shown.
+     */
+    public static void assertThatShowsPickerUi() {
+        // Assert that Bottom Sheet is shown
+        // Add a short timeout wait for PhotoPicker to show
+        assertThat(new UiObject(new UiSelector().resourceIdMatches(
+                PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/bottom_sheet"))
+                .waitForExists(SHORT_TIMEOUT)).isTrue();
+
+        // Assert that privacy text is shown
+        assertThat(new UiObject(new UiSelector().resourceIdMatches(
+                PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/privacy_text"))
+                .exists()).isTrue();
+
+        // Assert that "Photos" and "Albums" headers are shown.
+        assertThat(new UiObject(new UiSelector().text("Photos")).exists()).isTrue();
+        assertThat(new UiObject(new UiSelector().text("Albums")).exists()).isTrue();
+    }
+}
diff --git a/tests/accessibility/Android.bp b/tests/accessibility/Android.bp
index 91ce26b..03c7d1b 100644
--- a/tests/accessibility/Android.bp
+++ b/tests/accessibility/Android.bp
@@ -21,6 +21,7 @@
     sdk_version: "test_current",
     static_libs: [
         "compatibility-device-util-axt",
+        "sts-device-util",
     ],
     srcs: ["common/src/**/*.java"],
 }
@@ -39,6 +40,7 @@
     test_suites: [
         "cts",
         "general-tests",
+        "sts",
     ],
     sdk_version: "test_current",
 }
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index bf3b1a8..056ca59 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -60,6 +60,17 @@
                  android:resource="@xml/speaking_and_vibrating_accessibilityservice"/>
         </service>
 
+        <service android:name=".NoFeedbackAccessibilityService"
+             android:label="@string/title_no_feedback_accessibility_service"
+             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService"/>
+            </intent-filter>
+            <meta-data android:name="android.accessibilityservice"
+                 android:resource="@xml/no_feedback_accessibilityservice"/>
+        </service>
+
         <service android:name=".AccessibilityButtonService"
              android:label="@string/title_accessibility_button_service"
              android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/tests/accessibility/res/values/strings.xml b/tests/accessibility/res/values/strings.xml
index 37d3051..871d5f9 100644
--- a/tests/accessibility/res/values/strings.xml
+++ b/tests/accessibility/res/values/strings.xml
@@ -26,6 +26,9 @@
     <!-- String title for the vibrating accessibility service -->
     <string name="title_speaking_and_vibrating_accessibility_service">Speaking and Vibrating Accessibility Service</string>
 
+    <!-- String title for the no-feedback accessibility service -->
+    <string name="title_no_feedback_accessibility_service">No-Feedback Accessibility Service</string>
+
     <!-- String title for the accessibility button service -->
     <string name="title_accessibility_button_service">Accessibility Button Service</string>
 
diff --git a/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml b/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml
new file mode 100644
index 0000000..168e584
--- /dev/null
+++ b/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index 0f5afd1..27c3aac 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.content.pm.ServiceInfo;
 import android.os.Handler;
+import android.platform.test.annotations.AsbSecurityTest;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
@@ -44,6 +45,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.SettingsStateChangerRule;
 import com.android.compatibility.common.util.SystemUtil;
@@ -63,7 +66,7 @@
  * Class for testing {@link AccessibilityManager}.
  */
 @RunWith(AndroidJUnit4.class)
-public class AccessibilityManagerTest {
+public class AccessibilityManagerTest extends StsExtraBusinessLogicTestCase {
 
     private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
             new AccessibilityDumpOnFailureRule();
@@ -81,6 +84,11 @@
             new InstrumentedAccessibilityServiceTestRule<>(
                     SpeakingAndVibratingAccessibilityService.class, false);
 
+    private InstrumentedAccessibilityServiceTestRule<NoFeedbackAccessibilityService>
+            mNoFeedbackAccessibilityServiceRule =
+            new InstrumentedAccessibilityServiceTestRule<>(
+                    NoFeedbackAccessibilityService.class, false);
+
     private static final Instrumentation sInstrumentation =
             InstrumentationRegistry.getInstrumentation();
 
@@ -93,6 +101,9 @@
     private static final String MULTIPLE_FEEDBACK_TYPES_ACCESSIBILITY_SERVICE_NAME =
         "android.view.accessibility.cts.SpeakingAndVibratingAccessibilityService";
 
+    private static final String NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME =
+            "android.view.accessibility.cts.NoFeedbackAccessibilityService";
+
     public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS =
             "accessibility_non_interactive_ui_timeout_ms";
 
@@ -112,6 +123,7 @@
             // SettingsStateChangerRule will suppress accessibility services, so it should be
             // executed before enabling a11y services and after disabling a11y services.
             .outerRule(mAudioDescriptionSetterRule)
+            .around(mNoFeedbackAccessibilityServiceRule)
             .around(mSpeakingAndVibratingAccessibilityServiceRule)
             .around(mVibratingAccessibilityServiceRule)
             .around(mSpeakingAccessibilityServiceRule)
@@ -241,6 +253,26 @@
         assertTrue("The vibrating service should be enabled.", vibratingServiceEnabled);
     }
 
+    @AsbSecurityTest(cveBugId = {243849844})
+    @Test
+    public void testGetEnabledAccessibilityServiceList_NoFeedback() {
+        mNoFeedbackAccessibilityServiceRule.enableService();
+        List<AccessibilityServiceInfo> enabledServices =
+                mAccessibilityManager.getEnabledAccessibilityServiceList(
+                        AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+        boolean noFeedbackServiceEnabled = false;
+        final int serviceCount = enabledServices.size();
+        for (int i = 0; i < serviceCount; i++) {
+            AccessibilityServiceInfo enabledService = enabledServices.get(i);
+            ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
+            if (mTargetContext.getPackageName().equals(serviceInfo.packageName)
+                    && NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME.equals(serviceInfo.name)) {
+                noFeedbackServiceEnabled = true;
+            }
+        }
+        assertTrue("The no-feedback service should be enabled.", noFeedbackServiceEnabled);
+    }
+
     @Test
     public void testGetEnabledAccessibilityServiceListForType() throws Exception {
         mSpeakingAccessibilityServiceRule.enableService();
diff --git a/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java
new file mode 100644
index 0000000..0c79ae4
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility.cts;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.content.ComponentName;
+
+/**
+ * Stub accessibility service that reports itself as providing no feedback.
+ */
+public class NoFeedbackAccessibilityService extends InstrumentedAccessibilityService {
+    public static final ComponentName COMPONENT_NAME = new ComponentName(
+            "android.view.accessibility.cts",
+            "android.view.accessibility.cts.NoFeedbackAccessibilityService");
+}
diff --git a/tests/accessibilityservice/Android.bp b/tests/accessibilityservice/Android.bp
index 876da5a..9e891ee 100644
--- a/tests/accessibilityservice/Android.bp
+++ b/tests/accessibilityservice/Android.bp
@@ -27,6 +27,7 @@
         "platform-test-annotations",
         "CtsAccessibilityCommon",
         "CtsInputMethodServiceCommon",
+        "sts-device-util",
     ],
     libs: [
         "android.test.runner",
@@ -37,10 +38,12 @@
     test_suites: [
         "cts",
         "general-tests",
+        "sts",
     ],
     sdk_version: "test_current",
     per_testcase_directory: true,
     data: [
         ":CtsInputMethod1",
+        ":CtsAccessibilityMultipleServicesApp",
         ":CtsAccessibilityWidgetProvider"],
 }
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 3bde0fd..ad7994e 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -27,6 +27,8 @@
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 
+    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+
     <application android:theme="@android:style/Theme.Holo.NoActionBar"
          android:requestLegacyExternalStorage="true">
 
diff --git a/tests/accessibilityservice/AndroidTest.xml b/tests/accessibilityservice/AndroidTest.xml
index 48aea2c..8096d29 100644
--- a/tests/accessibilityservice/AndroidTest.xml
+++ b/tests/accessibilityservice/AndroidTest.xml
@@ -24,6 +24,11 @@
         <option name="run-command" value="cmd accessibility set-bind-instant-service-allowed true" />
         <option name="teardown-command" value="cmd accessibility set-bind-instant-service-allowed false" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push-file" key="CtsAccessibilityMultipleServicesApp.apk"
+            value="/data/local/tmp/cts/content/CtsAccessibilityMultipleServicesApp.apk" />
+    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAccessibilityServiceTestCases.apk" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 5f9733c..52fb4c37 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -17,7 +17,9 @@
 package android.accessibilityservice.cts;
 
 import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE;
 import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
+import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventTypeWithAction;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventTypeWithResource;
@@ -76,7 +78,9 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
 import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
 import android.util.Log;
@@ -99,6 +103,9 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.CtsMouseUtil;
+import com.android.compatibility.common.util.ShellUtils;
+import com.android.compatibility.common.util.TestUtils;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -120,7 +127,7 @@
  * are generated and their correct dispatch verified.
  */
 @RunWith(AndroidJUnit4.class)
-public class AccessibilityEndToEndTest {
+public class AccessibilityEndToEndTest extends StsExtraBusinessLogicTestCase {
 
     private static final String LOG_TAG = "AccessibilityEndToEndTest";
 
@@ -1013,6 +1020,71 @@
         }
     }
 
+    @AsbSecurityTest(cveBugId = {243378132})
+    @Test
+    public void testUninstallPackage_DisablesMultipleServices() throws Exception {
+        final String apkPath =
+                "/data/local/tmp/cts/content/CtsAccessibilityMultipleServicesApp.apk";
+        final String packageName = "foo.bar.multipleservices";
+        final ComponentName service1 = ComponentName.createRelative(packageName, ".StubService1");
+        final ComponentName service2 = ComponentName.createRelative(packageName, ".StubService2");
+        // Match AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
+        final String componentNameSeparator = ":";
+
+        final String originalEnabledServicesSetting = getEnabledServicesSetting();
+
+        try {
+            // Install the apk in this test method, instead of as part of the target preparer, to
+            // allow repeated --iterations of the test.
+            com.google.common.truth.Truth.assertThat(
+                    ShellUtils.runShellCommand("pm install " + apkPath)).startsWith("Success");
+
+            // Enable the two services and wait until AccessibilityManager reports them as enabled.
+            final String servicesToEnable = getEnabledServicesSetting() + componentNameSeparator
+                    + service1.flattenToShortString() + componentNameSeparator
+                    + service2.flattenToShortString();
+            ShellCommandBuilder.create(sInstrumentation)
+                    .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                            servicesToEnable)
+                    .putSecureSetting(Settings.Secure.ACCESSIBILITY_ENABLED, "1")
+                    .run();
+            TestUtils.waitUntil("Failed to enable 2 services from package " + packageName,
+                    (int) TIMEOUT_SERVICE_ENABLE / 1000,
+                    () -> getEnabledServices().stream().filter(
+                            info -> info.getId().startsWith(packageName)).count() == 2);
+
+            // Uninstall the package that contains the services.
+            com.google.common.truth.Truth.assertThat(
+                    ShellUtils.runShellCommand("pm uninstall " + packageName)).startsWith(
+                    "Success");
+
+            // Ensure the uninstall removed the services from the secure setting.
+            TestUtils.waitUntil(
+                    "Failed to disable services after uninstalling package " + packageName,
+                    (int) TIMEOUT_SERVICE_ENABLE / 1000,
+                    () -> !getEnabledServicesSetting().contains(packageName));
+        } finally {
+            ShellCommandBuilder.create(sInstrumentation)
+                    .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                            originalEnabledServicesSetting)
+                    .run();
+            ShellUtils.runShellCommand("pm uninstall " + packageName);
+        }
+    }
+
+    private List<AccessibilityServiceInfo> getEnabledServices() {
+        return ((AccessibilityManager) sInstrumentation.getContext().getSystemService(
+                Context.ACCESSIBILITY_SERVICE)).getEnabledAccessibilityServiceList(
+                FEEDBACK_ALL_MASK);
+    }
+
+    private String getEnabledServicesSetting() {
+        final String result = Settings.Secure.getString(
+                sInstrumentation.getContext().getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        return result != null ? result : "";
+    }
+
     private static void assertPackageName(AccessibilityNodeInfo node, String packageName) {
         if (node == null) {
             return;
diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/Android.bp b/tests/accessibilityservice/test-apps/MultipleServicesApp/Android.bp
new file mode 100644
index 0000000..d8fdbce
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/Android.bp
@@ -0,0 +1,24 @@
+// 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: "CtsAccessibilityMultipleServicesApp",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+}
diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/AndroidManifest.xml b/tests/accessibilityservice/test-apps/MultipleServicesApp/AndroidManifest.xml
new file mode 100644
index 0000000..649478e
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="foo.bar.multipleservices"
+     android:targetSandboxVersion="2">
+    <application>
+        <service android:name="foo.bar.multipleservices.StubService1"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService"/>
+            </intent-filter>
+            <meta-data android:name="android.accessibilityservice"
+                android:resource="@xml/stub_service"/>
+        </service>
+        <service android:name="foo.bar.multipleservices.StubService2"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService"/>
+            </intent-filter>
+            <meta-data android:name="android.accessibilityservice"
+                android:resource="@xml/stub_service"/>
+        </service>
+    </application>
+</manifest>
diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/res/xml/stub_service.xml b/tests/accessibilityservice/test-apps/MultipleServicesApp/res/xml/stub_service.xml
new file mode 100644
index 0000000..0cbd139
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/res/xml/stub_service.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService1.java b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService1.java
new file mode 100644
index 0000000..022f6e1
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService1.java
@@ -0,0 +1,29 @@
+/*
+ * 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 foo.bar.multipleservices;
+
+import android.accessibilityservice.AccessibilityService;
+import android.view.accessibility.AccessibilityEvent;
+
+/** A stub accessibility service for testing package uninstall. */
+public class StubService1 extends AccessibilityService {
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {}
+
+    @Override
+    public void onInterrupt() {}
+}
diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService2.java b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService2.java
new file mode 100644
index 0000000..28353c2
--- /dev/null
+++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService2.java
@@ -0,0 +1,29 @@
+/*
+ * 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 foo.bar.multipleservices;
+
+import android.accessibilityservice.AccessibilityService;
+import android.view.accessibility.AccessibilityEvent;
+
+/** A stub accessibility service for testing package uninstall. */
+public class StubService2 extends AccessibilityService {
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {}
+
+    @Override
+    public void onInterrupt() {}
+}
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 0e84662..d449870 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -20,6 +20,7 @@
     <!-- Backup of instant apps is not supported. -->
     <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="mainline-param" value="com.google.android.permission.apex" />
     <!-- Run module in system user because backup tests are not fully supported in secondary user.
      For devices running on secondary user, such as automotive devices, these tests will fail.
      When backup tests are fully functional for secondary users:
diff --git a/tests/backup/TEST_MAPPING b/tests/backup/TEST_MAPPING
index 4e5beb0..1e1dcdb 100644
--- a/tests/backup/TEST_MAPPING
+++ b/tests/backup/TEST_MAPPING
@@ -3,5 +3,15 @@
     {
       "name": "CtsBackupTestCases"
     }
+  ],
+  "mainline-presubmit": [
+    {
+      "name": "CtsBackupTestCases[com.google.android.permission.apex]",
+      "options": [
+        {
+          "include-filter": "android.backup.cts.PermissionTest"
+        }
+      ]
+    }
   ]
 }
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
index 2baa2f1..c448a76 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
@@ -16,12 +16,7 @@
 
 package android.location.cts.fine;
 
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.mock;
@@ -29,8 +24,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager.ResolveInfoFlags;
 import android.location.Geocoder;
 import android.location.Geocoder.GeocodeListener;
 import android.platform.test.annotations.AppModeFull;
@@ -64,17 +57,6 @@
         mGeocoder = new Geocoder(mContext, Locale.US);
     }
 
-    @Test
-    public void testIsPresent() {
-        if (mContext.getPackageManager().queryIntentServices(
-                new Intent("com.android.location.service.GeocodeProvider"), ResolveInfoFlags.of(
-                        MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE)).isEmpty()) {
-            assertFalse(Geocoder.isPresent());
-        } else {
-            assertTrue(Geocoder.isPresent());
-        }
-    }
-
     @ApiTest(apis = "android.location.Geocoder#getFromLocation")
     @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml
index 588ddf8..ef5dbeb 100644
--- a/tests/media/AndroidTest.xml
+++ b/tests/media/AndroidTest.xml
@@ -19,6 +19,14 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+        <option name="set-test-harness" value="false" />
+        <option name="screen-always-on" value="on" />
+        <option name="screen-adaptive-brightness" value="off" />
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+    </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="host" />
         <option name="config-filename" value="CtsMediaV2TestCases" />
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index 9d1d839..6177298 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -594,6 +594,9 @@
     public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
     public static final boolean IS_AT_LEAST_T =
             ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU);
+    //TODO(b/248315681) Remove codenameEquals() check once devices return correct version for U
+    public static final boolean IS_AT_LEAST_U = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU)
+            || ApiLevelUtil.codenameEquals("UpsideDownCake");
     public static final boolean FIRST_SDK_IS_AT_LEAST_T =
             ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU);
     public static final boolean VNDK_IS_AT_LEAST_T =
diff --git a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java
index 518ff86..9039d38 100644
--- a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java
+++ b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java
@@ -351,7 +351,7 @@
             // limit the test to devices launching with T
             assumeTrue("Skipping color range " + mRange + " and color standard " + mStandard +
                             " for devices upgrading to T",
-                    FIRST_SDK_IS_AT_LEAST_T);
+                    FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T);
 
             // TODO (b/219748700): Android software codecs work only with 601LR. Skip for now.
             assumeTrue("Skipping " + mCompName + " for color range " + mRange
diff --git a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
index ebed139..6bcd5c7 100644
--- a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
+++ b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
@@ -27,6 +27,8 @@
 
 import androidx.test.filters.LargeTest;
 
+import com.android.compatibility.common.util.MediaUtils;
+
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
@@ -138,6 +140,11 @@
 
     @Before
     public void setUp() throws IOException {
+        // Few cuttlefish specific color conversion issues were fixed after Android T.
+        if (MediaUtils.onCuttlefish()) {
+            assumeTrue("Color conversion related tests are not valid on cuttlefish releases "
+                    + "through android T", IS_AT_LEAST_U);
+        }
         if (mUseHighBitDepth) {
             assumeTrue("Codec doesn't support ABGR2101010",
                     hasSupportForColorFormat(mCompName, mMime, COLOR_Format32bitABGR2101010));
diff --git a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
index 72e4765..a2563a3 100644
--- a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
@@ -29,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.MediaUtils;
 
 import org.junit.Assume;
 import org.junit.Test;
@@ -265,7 +266,12 @@
         Assume.assumeTrue("Test introduced with Android 11", sIsAtLeastR);
         if (mSurfaceMode) {
             Assume.assumeTrue("Surface mode tests are limited to devices launching with Android T",
-                    FIRST_SDK_IS_AT_LEAST_T);
+                    FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T);
+            // Few cuttlefish specific color conversion issues were fixed after Android T.
+            if (MediaUtils.onCuttlefish()) {
+                Assume.assumeTrue("Color conversion related tests are not valid on cuttlefish "
+                        + "releases through android T", IS_AT_LEAST_U);
+            }
         }
 
         if (mUseHighBitDepth) {
diff --git a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java
index 234a0cd..c82e039 100644
--- a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java
+++ b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java
@@ -16,34 +16,15 @@
 
 package android.ondevicepersonalization.cts;
 
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.ondevicepersonalization.OnDevicePersonalizationManaging;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 /**
- * Test of {@link OnDevicePersonalizationManaging}
+ * Test of {@link OnDevicePersonalizationManager}
  */
 @RunWith(JUnit4.class)
 public class OnDevicePersonalizationServiceTest {
-    private Context mContext;
-    private OnDevicePersonalizationManaging mService;
-
-    @Before
-    public void setup() throws Exception {
-        mContext = ApplicationProvider.getApplicationContext();
-        mService = mContext.getSystemService(OnDevicePersonalizationManaging.class);
-    }
-
     @Test
-    public void testVersion() throws Exception {
-        assertEquals(mService.getVersion(), "1.0");
-    }
+    public void test() throws Exception {}
 }
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
index 14f8516..e6c186c 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
@@ -25,6 +25,7 @@
 import android.media.cts.OutputSurface;
 import android.media.cts.TestArgs;
 import android.opengl.GLES20;
+import android.os.Build;
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -39,6 +40,7 @@
 import java.util.Collection;
 import java.util.List;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.MediaUtils;
 
 import org.junit.Before;
@@ -71,6 +73,7 @@
     private static final boolean WORK_AROUND_BUGS = false;  // avoid fatal codec bugs
     private static final boolean VERBOSE = false;           // lots of logging
     private static final boolean DEBUG_SAVE_FILE = false;   // save copy of encoded movie
+    private static final boolean IS_AFTER_T = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU);
 
     // parameters for the encoder
     private static final int FRAME_RATE = 15;               // 15fps
@@ -160,13 +163,18 @@
     }
 
     @Before
-    public void shouldSkip() {
+    public void shouldSkip() throws IOException {
         MediaFormat format = MediaFormat.createVideoFormat(mMediaType, mWidth, mHeight);
         format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
         format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
         format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
         assumeTrue(MediaUtils.supports(mEncoderName, format));
         assumeTrue(MediaUtils.supports(mDecoderName, format));
+        // Few cuttlefish specific color conversion issues were fixed after Android T.
+        if (MediaUtils.onCuttlefish()) {
+            assumeTrue("Color conversion related tests are not valid on cuttlefish releases "
+                    + "through android T for format: " + format, IS_AFTER_T);
+        }
     }
 
     @Parameterized.Parameters(name = "{index}({0}_{1}_{2}_{3}_{4})")
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
index 7780a13..eff1f51 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
@@ -30,6 +30,7 @@
 import android.media.cts.SdkMediaCodec;
 import android.media.cts.TestArgs;
 import android.opengl.GLES20;
+import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresDevice;
 import android.util.Log;
@@ -37,6 +38,7 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.MediaUtils;
 
 import java.io.FileOutputStream;
@@ -82,6 +84,7 @@
     private static final boolean VERBOSE = false;           // lots of logging
     private static final boolean DEBUG_SAVE_FILE = false;   // save copy of encoded movie
     private static final String DEBUG_FILE_NAME_BASE = "/sdcard/test.";
+    private static final boolean IS_AFTER_T = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU);
 
     // parameters for the encoder
     private static final int FRAME_RATE = 15;               // 15fps
@@ -283,6 +286,11 @@
          */
         public static void runTest(EncodeDecodeTest obj, boolean persisent, boolean useNdk)
                 throws Throwable {
+            // Few cuttlefish specific color conversion issues were fixed after Android T.
+            if (MediaUtils.onCuttlefish()) {
+                assumeTrue("Color conversion related tests are not valid on cuttlefish releases "
+                        + "through android T", IS_AFTER_T);
+            }
             SurfaceToSurfaceWrapper wrapper =
                     new SurfaceToSurfaceWrapper(obj, persisent, useNdk);
             Thread th = new Thread(wrapper, "codec test");
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java
index f5e3e0d..9b98fed 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java
@@ -129,81 +129,6 @@
         return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
     }
 
-    private static int[] getSampleSizes(String path, String[] keys, String[] values)
-            throws IOException {
-        MediaExtractor ex = new MediaExtractor();
-        if (keys == null || values == null) {
-            ex.setDataSource(path);
-        } else {
-            Map<String, String> headers = null;
-            int numheaders = Math.min(keys.length, values.length);
-            for (int i = 0; i < numheaders; i++) {
-                if (headers == null) {
-                    headers = new HashMap<>();
-                }
-                String key = keys[i];
-                String value = values[i];
-                headers.put(key, value);
-            }
-            ex.setDataSource(path, headers);
-        }
-
-        return getSampleSizes(ex);
-    }
-
-    private static int[] getSampleSizes(FileDescriptor fd, long offset, long size)
-            throws IOException {
-        MediaExtractor ex = new MediaExtractor();
-        ex.setDataSource(fd, offset, size);
-        return getSampleSizes(ex);
-    }
-
-    private static int[] getSampleSizes(MediaExtractor ex) {
-        ArrayList<Integer> foo = new ArrayList<Integer>();
-        ByteBuffer buf = ByteBuffer.allocate(1024*1024);
-        int numtracks = ex.getTrackCount();
-        assertTrue("no tracks", numtracks > 0);
-        foo.add(numtracks);
-        for (int i = 0; i < numtracks; i++) {
-            MediaFormat format = ex.getTrackFormat(i);
-            String mime = format.getString(MediaFormat.KEY_MIME);
-            if (mime.startsWith("audio/")) {
-                foo.add(0);
-                foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
-                foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
-                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
-            } else if (mime.startsWith("video/")) {
-                foo.add(1);
-                foo.add(format.getInteger(MediaFormat.KEY_WIDTH));
-                foo.add(format.getInteger(MediaFormat.KEY_HEIGHT));
-                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
-            } else {
-                fail("unexpected mime type: " + mime);
-            }
-            ex.selectTrack(i);
-        }
-        while(true) {
-            int n = ex.readSampleData(buf, 0);
-            if (n < 0) {
-                break;
-            }
-            foo.add(n);
-            foo.add(ex.getSampleTrackIndex());
-            foo.add(ex.getSampleFlags());
-            foo.add((int)ex.getSampleTime()); // just the low bits should be OK
-            byte[] foobar = new byte[n];
-            buf.get(foobar, 0, n);
-            foo.add(adler32(foobar));
-            ex.advance();
-        }
-
-        int [] ret = new int[foo.size()];
-        for (int i = 0; i < ret.length; i++) {
-            ret[i] = foo.get(i);
-        }
-        return ret;
-    }
-
     @Test
     public void testDataSource() throws Exception {
         int testsRun = testDecoder(
diff --git a/tests/tests/media/misc/Android.bp b/tests/tests/media/misc/Android.bp
index 7423792..feaabe4 100644
--- a/tests/tests/media/misc/Android.bp
+++ b/tests/tests/media/misc/Android.bp
@@ -106,7 +106,6 @@
         "cts",
         "general-tests",
         "mts-media",
-        "sts",
     ],
     host_required: ["cts-dynamic-config"],
     min_sdk_version: "29",
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/MediaSessionTest.java b/tests/tests/media/misc/src/android/media/misc/cts/MediaSessionTest.java
index cf4f6d0..6edadaa 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/MediaSessionTest.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/MediaSessionTest.java
@@ -16,7 +16,6 @@
 package android.media.misc.cts;
 
 import static android.media.AudioAttributes.USAGE_GAME;
-import static android.media.cts.Utils.compareRemoteUserInfo;
 import static android.media.misc.cts.MediaSessionTestService.KEY_EXPECTED_QUEUE_SIZE;
 import static android.media.misc.cts.MediaSessionTestService.KEY_EXPECTED_TOTAL_NUMBER_OF_ITEMS;
 import static android.media.misc.cts.MediaSessionTestService.KEY_SESSION_TOKEN;
@@ -25,11 +24,11 @@
 import static android.media.misc.cts.MediaSessionTestService.STEP_SET_UP;
 import static android.media.misc.cts.MediaSessionTestService.TEST_SERIES_OF_SET_QUEUE;
 import static android.media.misc.cts.MediaSessionTestService.TEST_SET_QUEUE;
+import static android.media.cts.Utils.compareRemoteUserInfo;
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
@@ -52,13 +51,10 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.platform.test.annotations.AppModeFull;
-import android.platform.test.annotations.AsbSecurityTest;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
 import android.view.KeyEvent;
 
-import com.android.compatibility.common.util.ApiTest;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -84,11 +80,6 @@
     private static final long TEST_ACTION = 55L;
     private static final int TEST_TOO_MANY_SESSION_COUNT = 1000;
 
-    private static final String TEST_SESSION_TAG_FOREIGN_PACKAGE =
-            "test-session-tag-foreign-package";
-    private static final String TEST_FOREIGN_PACKAGE_NAME = "fakepackage";
-    private static final String TEST_FOREIGN_PACKAGE_CLASS = "com.fakepackage.media.FakeReceiver";
-
     private AudioManager mAudioManager;
     private Handler mHandler = new Handler(Looper.getMainLooper());
     private Object mWaitLock = new Object();
@@ -316,47 +307,6 @@
         }
     }
 
-    @ApiTest(apis = {"android.media.session.MediaSession#setMediaButtonBroadcastReceiver"})
-    @AsbSecurityTest(cveBugId = 238177121)
-    public void testSetMediaButtonBroadcastReceiver_withForeignPackageName_fails()
-            throws Exception {
-        Utils.assertMediaPlaybackStarted(getContext());
-
-        // Create Media Session
-        MyContextWrapper contextWrapper = new MyContextWrapper(getContext());
-        MediaSession mediaSession =
-                new MediaSession(contextWrapper, TEST_SESSION_TAG_FOREIGN_PACKAGE);
-
-        // Bypass client-side check
-        contextWrapper.mOverridePackageName = TEST_FOREIGN_PACKAGE_NAME;
-
-        try {
-            mediaSession.setMediaButtonBroadcastReceiver(
-                    new ComponentName(TEST_FOREIGN_PACKAGE_NAME, TEST_FOREIGN_PACKAGE_CLASS));
-            fail("Component name with different package name was registered.");
-        } catch (IllegalArgumentException ex) {
-            // Expected.
-        } finally {
-            mediaSession.release();
-        }
-    }
-
-    static class MyContextWrapper extends ContextWrapper {
-        String mOverridePackageName;
-
-        MyContextWrapper(Context base) {
-            super(base);
-        }
-
-        @Override
-        public String getPackageName() {
-            if (mOverridePackageName != null) {
-                return mOverridePackageName;
-            }
-            return super.getPackageName();
-        }
-    }
-
     /**
      * Test whether media button receiver can be a explicit broadcast receiver via
      * MediaSession.setMediaButtonReceiver(PendingIntent).
diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java
index 81c0d89..86c6912 100644
--- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java
+++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java
@@ -75,6 +75,7 @@
     private CtsTestServer mServer;
 
     @Before
+    @Override
     public void setUp() throws Throwable {
         super.setUp();
     }
diff --git a/tests/tests/mediastress/AndroidTest.xml b/tests/tests/mediastress/AndroidTest.xml
index c6ed526..215d927 100644
--- a/tests/tests/mediastress/AndroidTest.xml
+++ b/tests/tests/mediastress/AndroidTest.xml
@@ -19,6 +19,14 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+        <option name="set-test-harness" value="false" />
+        <option name="screen-always-on" value="on" />
+        <option name="screen-adaptive-brightness" value="off" />
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+    </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="host" />
         <option name="config-filename" value="cts" />
diff --git a/tests/tests/permission/Android.bp b/tests/tests/permission/Android.bp
index 5c861b5..91a9dfc 100644
--- a/tests/tests/permission/Android.bp
+++ b/tests/tests/permission/Android.bp
@@ -113,6 +113,8 @@
         ":CtsStorageEscalationApp29Full",
         ":CtsStorageEscalationApp29Scoped",
         ":CtsVictimPermissionDefinerApp",
+        ":CtsAppThatRequestsSystemAlertWindow22",
+        ":CtsAppThatRequestsSystemAlertWindow23",
     ],
     per_testcase_directory: true,
 }
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index 56a72c9..eafc6a6 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -92,6 +92,8 @@
         <option name="push" value="CtsStorageEscalationApp29Full.apk->/data/local/tmp/cts/permissions/CtsStorageEscalationApp29Full.apk" />
         <option name="push" value="CtsStorageEscalationApp29Scoped.apk->/data/local/tmp/cts/permissions/CtsStorageEscalationApp29Scoped.apk" />
         <option name="push" value="CtsAppThatHasNotificationListener.apk->/data/local/tmp/cts/permissions/CtsAppThatHasNotificationListener.apk" />
+        <option name="push" value="CtsAppThatRequestsSystemAlertWindow22.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsSystemAlertWindow22.apk" />
+        <option name="push" value="CtsAppThatRequestsSystemAlertWindow23.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsSystemAlertWindow23.apk" />
     </target_preparer>
 
     <!-- Remove additional apps if installed -->
diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow22/Android.bp b/tests/tests/permission/AppThatRequestSystemAlertWindow22/Android.bp
new file mode 100644
index 0000000..43cc9de
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestSystemAlertWindow22/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"],
+}
+
+android_test_helper_app {
+    name: "CtsAppThatRequestsSystemAlertWindow22",
+    target_sdk_version: "22",
+    certificate: ":cts-testkey2",
+    min_sdk_version: "22",
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts-permission",
+        "sts",
+    ],
+}
diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml b/tests/tests/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml
new file mode 100644
index 0000000..8b85b13
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.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.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission3.cts.usesystemalertwindowpermission">
+
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+</manifest>
diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow23/Android.bp b/tests/tests/permission/AppThatRequestSystemAlertWindow23/Android.bp
new file mode 100644
index 0000000..403257d
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestSystemAlertWindow23/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"],
+}
+
+android_test_helper_app {
+    name: "CtsAppThatRequestsSystemAlertWindow23",
+    target_sdk_version: "23",
+    certificate: ":cts-testkey2",
+    min_sdk_version: "23",
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts-permission",
+        "sts",
+    ],
+}
diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml b/tests/tests/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml
new file mode 100644
index 0000000..8b85b13
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.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.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission3.cts.usesystemalertwindowpermission">
+
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+</manifest>
diff --git a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
index 28d58d6..513e1df 100644
--- a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
+++ b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
@@ -33,6 +33,7 @@
 import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueDoesNotExist
 import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueExist
 import android.permission.cts.SafetyCenterUtils.assertSafetyCenterStarted
+import android.permission.cts.SafetyCenterUtils.deleteDeviceConfigPrivacyProperty
 import android.permission.cts.SafetyCenterUtils.deviceSupportsSafetyCenter
 import android.permission.cts.SafetyCenterUtils.setDeviceConfigPrivacyProperty
 import android.platform.test.annotations.AppModeFull
@@ -114,6 +115,28 @@
     }
 
     @Test
+    fun testJobSendsNotificationOnEnable() {
+        mAccessibilityServiceRule.enableService()
+        runJobAndWaitUntilCompleted()
+        assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+
+        setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString())
+        cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+        InstrumentedAccessibilityService.disableAllServices()
+        setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString())
+        setDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS, "0")
+
+        // enable service again and verify a notification
+        try {
+            mAccessibilityServiceRule.enableService()
+            runJobAndWaitUntilCompleted()
+            assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+        } finally {
+            deleteDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS)
+        }
+    }
+
+    @Test
     fun testJobSendsIssuesToSafetyCenter() {
         mAccessibilityServiceRule.enableService()
         runJobAndWaitUntilCompleted()
@@ -294,6 +317,7 @@
         private const val ACCESSIBILITY_SOURCE_ENABLED = "sc_accessibility_source_enabled"
         private const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
         private const val ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled"
+        private const val ACCESSIBILITY_JOB_INTERVAL_MILLIS = "sc_accessibility_job_interval_millis"
 
         private const val ACCESSIBILITY_JOB_ID = 6
         private const val ACCESSIBILITY_NOTIFICATION_ID = 4
diff --git a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
index f88b7ec..67bb916 100644
--- a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
@@ -433,7 +433,7 @@
      * @return The notification or `null` if there is none
      */
     protected StatusBarNotification getNotification(boolean cancelNotification) throws Throwable {
-        return NotificationUtils.getNotificationForPackageAndId(
+        return NotificationListenerUtils.getNotificationForPackageAndId(
                 PERMISSION_CONTROLLER_PKG,
                 NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID,
                 cancelNotification);
@@ -444,6 +444,6 @@
      */
     protected void clearNotifications() throws Throwable {
         // Clear notification if present
-        NotificationUtils.clearNotificationsForPackage(PERMISSION_CONTROLLER_PKG);
+        NotificationListenerUtils.cancelNotifications(PERMISSION_CONTROLLER_PKG);
     }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt b/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt
index 0ee8a3d..dffefe7 100644
--- a/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt
+++ b/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt
@@ -16,16 +16,15 @@
 
 package android.permission.cts
 
-import android.service.notification.StatusBarNotification
-import org.junit.Assert
 import android.permission.cts.TestUtils.ensure
 import android.permission.cts.TestUtils.eventually
+import android.service.notification.StatusBarNotification
+import org.junit.Assert
 
 object NotificationListenerUtils {
 
     private const val NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS = 5000L
     private const val NOTIFICATION_WAIT_MILLIS = 2000L
-    private val notificationService = NotificationListener.getInstance()
 
     @JvmStatic
     fun assertEmptyNotification(packageName: String, notificationId: Int) {
@@ -47,6 +46,7 @@
 
     @JvmStatic
     fun cancelNotification(packageName: String, notificationId: Int) {
+        val notificationService = NotificationListener.getInstance()
         val notification = getNotification(packageName, notificationId)
         if (notification != null) {
             notificationService.cancelNotification(notification.key)
@@ -58,6 +58,7 @@
 
     @JvmStatic
     fun cancelNotifications(packageName: String) {
+        val notificationService = NotificationListener.getInstance()
         val notifications = getNotifications(packageName)
         if (notifications.isNotEmpty()) {
             notifications.forEach { notification ->
@@ -79,6 +80,7 @@
     @JvmStatic
     fun getNotifications(packageName: String): List<StatusBarNotification> {
         val notifications: MutableList<StatusBarNotification> = ArrayList()
+        val notificationService = NotificationListener.getInstance()
         for (notification in notificationService.activeNotifications) {
             if (notification.packageName == packageName) {
                 notifications.add(notification)
@@ -86,4 +88,32 @@
         }
         return notifications
     }
-}
\ No newline at end of file
+
+    /**
+     * Get a notification listener notification that is currently visible.
+     *
+     * @param cancelNotification if `true` the notification is canceled inside this method
+     * @return The notification or `null` if there is none
+     */
+    @JvmStatic
+    @Throws(Throwable::class)
+    fun getNotificationForPackageAndId(
+            pkg: String,
+            id: Int,
+            cancelNotification: Boolean
+    ): StatusBarNotification? {
+        val notifications: List<StatusBarNotification> = getNotifications(pkg)
+        if (notifications.isEmpty()) {
+            return null
+        }
+        for (notification in notifications) {
+            if (notification.id == id) {
+                if (cancelNotification) {
+                    cancelNotification(pkg, id)
+                }
+                return notification
+            }
+        }
+        return null
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/NotificationUtils.kt b/tests/tests/permission/src/android/permission/cts/NotificationUtils.kt
deleted file mode 100644
index 7c50837..0000000
--- a/tests/tests/permission/src/android/permission/cts/NotificationUtils.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission.cts
-
-import android.service.notification.StatusBarNotification
-import org.junit.Assert
-import java.util.concurrent.TimeUnit
-
-/**
- * Utility methods to interact with NotificationManager through the CTS NotificationListenerService
- * to get or clear notifications.
- */
-object NotificationUtils {
-
-    private val NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5)
-
-    /**
-     * Get a notification listener notification that is currently visible.
-     *
-     * @param cancelNotification if `true` the notification is canceled inside this method
-     * @return The notification or `null` if there is none
-     */
-    @JvmStatic
-    @Throws(Throwable::class)
-    fun getNotificationForPackageAndId(
-        pkg: String,
-        id: Int,
-        cancelNotification: Boolean
-    ): StatusBarNotification? {
-        val notificationService = NotificationListener.getInstance()
-        val notifications: List<StatusBarNotification> = getNotificationsForPackage(pkg)
-        if (notifications.isEmpty()) {
-            return null
-        }
-        for (notification in notifications) {
-            if (notification.id == id) {
-                if (cancelNotification) {
-                    clearNotification(notification)
-                }
-                return notification
-            }
-        }
-        return null
-    }
-
-    /**
-     * Clears all currently visible notifications for a specified package.
-     */
-    @JvmStatic
-    @Throws(Throwable::class)
-    fun clearNotificationsForPackage(pkg: String) {
-        val notifications: List<StatusBarNotification> = getNotificationsForPackage(pkg)
-        if (notifications.isEmpty()) {
-            return
-        }
-
-        clearNotifications(notifications)
-    }
-
-    /** Clears the specified notification and ensures (asserts) it was removed */
-    @JvmStatic
-    @Throws(Throwable::class)
-    fun clearNotification(notification: StatusBarNotification) {
-        val notificationService = NotificationListener.getInstance()
-        notificationService.cancelNotification(notification.key)
-
-        // Wait for notification to get canceled
-        TestUtils.eventually({
-            Assert.assertFalse(
-                listOf(*notificationService.activeNotifications)
-                    .contains(notification)
-            )
-        }, NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS)
-    }
-
-    private fun clearNotifications(notifications: List<StatusBarNotification>) {
-        val notificationService = NotificationListener.getInstance()
-        notifications.forEach { notificationService.cancelNotification(it.key) }
-
-        // Wait for notification to get canceled
-        TestUtils.eventually({
-            val activeNotifications: List<StatusBarNotification> =
-                listOf(*notificationService.activeNotifications)
-            Assert.assertFalse(
-                activeNotifications.any { notifications.contains(it) }
-            )
-        }, NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS)
-    }
-
-    /**
-     * Get all notifications associated with a given package that are currently visible.
-     * @param pkg Package for which to filter the notifications by
-     * @return [List] of [StatusBarNotification]
-     */
-    @Throws(Exception::class)
-    private fun getNotificationsForPackage(pkg: String): List<StatusBarNotification> {
-        val notificationService = NotificationListener.getInstance()
-        val notifications: MutableList<StatusBarNotification> = ArrayList()
-        for (notification in notificationService.activeNotifications) {
-            if (notification.packageName == pkg) {
-                notifications.add(notification)
-            }
-        }
-        return notifications
-    }
-}
\ No newline at end of file
diff --git a/tests/tests/permission/src/android/permission/cts/RevokeSawPermissionTest.kt b/tests/tests/permission/src/android/permission/cts/RevokeSawPermissionTest.kt
new file mode 100644
index 0000000..fe5373e
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/RevokeSawPermissionTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts
+
+import android.content.pm.PackageManager
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.After
+import org.junit.Assert
+import org.junit.Test
+
+private val APP_PKG_NAME = "android.permission3.cts.usesystemalertwindowpermission"
+private val APK_22 = "/data/local/tmp/cts/permissions/" +
+        "CtsAppThatRequestsSystemAlertWindow22.apk"
+private val APK_23 = "/data/local/tmp/cts/permissions/" +
+        "CtsAppThatRequestsSystemAlertWindow23.apk"
+
+class RevokeSawPermissionTest {
+
+    fun installApp(apk: String) {
+        SystemUtil.runShellCommand("pm install -r $apk")
+    }
+
+    @After
+    fun uninstallApp() {
+        SystemUtil.runShellCommand("pm uninstall $APP_PKG_NAME")
+    }
+
+    @AsbSecurityTest(cveBugId = [247512334L])
+    @Test
+    fun testPre23AppsWithSystemAlertWindowGetDeniedOnUpgrade() {
+        installApp(APK_22)
+        assertAppHasPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, true)
+        installApp(APK_23)
+        assertAppHasPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, false)
+    }
+
+    private fun assertAppHasPermission(permissionName: String, expectPermission: Boolean) {
+        Assert.assertEquals(
+            if (expectPermission) {
+                PackageManager.PERMISSION_GRANTED
+            } else {
+                PackageManager.PERMISSION_DENIED
+            },
+            InstrumentationRegistry.getInstrumentation().getTargetContext().packageManager
+                .checkPermission(permissionName, APP_PKG_NAME)
+        )
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt
index 931ecd1..7514079 100644
--- a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt
+++ b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt
@@ -88,6 +88,16 @@
     }
 
     @JvmStatic
+    fun deleteDeviceConfigPrivacyProperty(
+        propertyName: String,
+        uiAutomation: UiAutomation = instrumentation.uiAutomation
+    ) {
+        runWithShellPermissionIdentity(uiAutomation) {
+            DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_PRIVACY, propertyName)
+        }
+    }
+
+    @JvmStatic
     private fun getSafetyCenterIssues(
         automation: UiAutomation = instrumentation.uiAutomation
     ): List<SafetyCenterIssue> {
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index edfcf3a..ca4794d 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -71,6 +71,9 @@
     private static final String MANAGE_COMPANION_DEVICES_PERMISSION
             = "android.permission.MANAGE_COMPANION_DEVICES";
 
+    private static final String SET_UNRESTRICTED_GESTURE_EXCLUSION
+            = "android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION";
+
     private static final String LOG_TAG = "PermissionProtectionTest";
 
     private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -475,6 +478,8 @@
                 return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE);
             case MANAGE_COMPANION_DEVICES_PERMISSION:
                 return parseDate(SECURITY_PATCH).before(MANAGE_COMPANION_DEVICES_PATCH_DATE);
+            case SET_UNRESTRICTED_GESTURE_EXCLUSION:
+                return true;
             default:
                 return false;
         }
diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
index d5bd49b..47faaa6 100644
--- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
+++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
@@ -92,8 +92,12 @@
 
     private val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
     private val isCar = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
-    private val micLabel = getPermissionControllerString(MIC_LABEL_NAME)
-    private val cameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME)
+    private val safetyCenterMicLabel = getPermissionControllerString(MIC_LABEL_NAME)
+    private val safetyCenterCameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME)
+    private val cameraLabel = packageManager.getPermissionGroupInfo(
+        Manifest.permission_group.CAMERA, 0).loadLabel(packageManager).toString().toLowerCase()
+    private val micLabel = packageManager.getPermissionGroupInfo(
+        Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString().toLowerCase()
     private var wasEnabled = false
     private var isScreenOn = false
     private var screenTimeoutBeforeTest: Long = 0L
@@ -120,8 +124,6 @@
             Settings.System.putLong(
                 context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 1800000L
             )
-            DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
-                SAFETY_CENTER_ENABLED, false.toString(), false)
         }
 
         if (!isScreenOn) {
@@ -407,22 +409,28 @@
                 return@eventually
             }
             if (useMic) {
+                var micIdentifier: String
                 var iconView = if (safetyCenterEnabled) {
-                    waitFindObjectOrNull(By.text(micLabel))
+                    micIdentifier = safetyCenterMicLabel
+                    waitFindObject(By.text(micIdentifier))
                 } else {
-                    uiDevice.findObject(UiSelector().descriptionContains(micLabel))
+                    micIdentifier = micLabel
+                    waitFindObject(By.descContains(micIdentifier))
                 }
-                assertNotNull("View with description $micLabel not found", iconView)
+                assertNotNull("View with text/description $micIdentifier not found", iconView)
             }
             if (useCamera) {
+                var camIdentifier: String
                 var iconView = if (safetyCenterEnabled) {
-                    waitFindObjectOrNull(By.text(cameraLabel))
+                    camIdentifier = safetyCenterCameraLabel
+                    waitFindObject(By.text(camIdentifier))
                 } else {
-                    uiDevice.findObject(UiSelector().descriptionContains(cameraLabel))
+                    camIdentifier = cameraLabel
+                    waitFindObject(By.descContains(camIdentifier))
                 }
-                assertNotNull("View with text $APP_LABEL not found", iconView)
+                assertNotNull("View with text/description $camIdentifier not found", iconView)
             }
-            var appView = waitFindObjectOrNull(By.textContains(APP_LABEL))
+            var appView = waitFindObject(By.textContains(APP_LABEL))
             assertNotNull("View with text $APP_LABEL not found", appView)
             if (safetyCenterEnabled) {
                 assertTrue("Did not find safety center views",
@@ -456,11 +464,11 @@
         }
 
         if (safetyCenterEnabled) {
-            var micView = waitFindObjectOrNull(By.text(micLabel))
+            var micView = waitFindObject(By.text(safetyCenterMicLabel))
             assertNotNull("View with text $micLabel not found", micView)
-            var camView = waitFindObjectOrNull(By.text(cameraLabel))
+            var camView = waitFindObject(By.text(safetyCenterCameraLabel))
             assertNotNull("View with text $cameraLabel not found", camView)
-            var shellView = waitFindObjectOrNull(By.textContains(shellLabel))
+            var shellView = waitFindObject(By.textContains(shellLabel))
             assertNotNull("View with text $shellLabel not found", shellView)
         } else {
             val usageViews = uiDevice.findObjects(By.res(PRIVACY_ITEM_ID))
@@ -516,9 +524,9 @@
         assumeTrue(isSafetyCenterEnabled)
     }
 
-    protected fun waitFindObjectOrNull(selector: BySelector): UiObject2? {
+    protected fun waitFindObject(selector: BySelector): UiObject2? {
         waitForIdle()
-        return findObjectWithRetry({ t -> UiAutomatorUtils.waitFindObjectOrNull(selector, t) })
+        return findObjectWithRetry({ t -> UiAutomatorUtils.waitFindObject(selector, t) })
     }
 
     private fun findObjectWithRetry(
@@ -551,4 +559,4 @@
             throw RuntimeException(e)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index 0cf3dd5..9f4cda5 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -33,6 +33,7 @@
         "compatibility-common-util-devicesidelib",
         "guava",
         "platform-test-annotations",
+        "permission-test-util-lib",
         "sts-device-util",
         "hamcrest-library",
         "NeneInternal",
@@ -80,7 +81,17 @@
         ":CtsDeviceInfo",
         ":RolePermissionOverrideTestApp",
         ":SplitBluetoothPermissionTestApp",
-    ],
+        ":CtsPermissionBackupAppCert1",
+        ":CtsPermissionBackupAppCert1Dup",
+        ":CtsPermissionBackupAppCert2",
+        ":CtsPermissionBackupAppCert3",
+        ":CtsPermissionBackupAppCert4",
+        ":CtsPermissionBackupAppCert12",
+        ":CtsPermissionBackupAppCert12Dup",
+        ":CtsPermissionBackupAppCert34",
+        ":CtsPermissionBackupAppCert123",
+        ":CtsPermissionBackupAppCert4History124",
+     ],
 }
 
 android_test_helper_app {
@@ -89,6 +100,95 @@
     manifest: "testdata/packageinstallertestapp.xml",
 }
 
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert1",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-1",
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert1Dup",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-1",
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert2",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-2",
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert3",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-3",
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert4",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-4",
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert12",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-1",
+    additional_certificates: [
+        ":permission-test-cert-2",
+    ],
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert12Dup",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-1",
+    additional_certificates: [
+        ":permission-test-cert-2",
+    ],
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert34",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-3",
+    additional_certificates: [
+        ":permission-test-cert-4",
+    ],
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert123",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-1",
+    additional_certificates: [
+        ":permission-test-cert-2",
+        ":permission-test-cert-3",
+    ],
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupAppCert4History124",
+    min_sdk_version: "30",
+    certificate: ":permission-test-cert-4",
+    additional_certificates: [
+        ":permission-test-cert-1",
+
+    ],
+    rotationMinSdkVersion: "30",
+    lineage: ":permission-test-cert-with-rotation-history",
+    manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
 android_app_certificate {
     name: "security_cts_test_certificate",
     certificate: "security_cts_test_cert",
@@ -98,3 +198,30 @@
     name: "RolePermissionOverrideTestApp",
     manifest: "testdata/rolepermissionoverridetestapp.xml",
 }
+
+android_app_certificate {
+    name: "permission-test-cert-1",
+    certificate: "test-cert-1",
+}
+
+android_app_certificate {
+    name: "permission-test-cert-2",
+    certificate: "test-cert-2",
+}
+
+android_app_certificate {
+    name: "permission-test-cert-3",
+    certificate: "test-cert-3",
+}
+
+android_app_certificate {
+    name: "permission-test-cert-4",
+    certificate: "test-cert-4",
+}
+
+filegroup {
+    name: "permission-test-cert-with-rotation-history",
+    srcs: [
+        "test-cert-with-1-2-4-in-rotation-history",
+    ],
+}
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 4ddeca0..72a110b 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -18,6 +18,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="android.security.cts">
 
+    <permission-tree android:name="com.android.cts"/>
+
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
@@ -201,9 +203,34 @@
             </intent-filter>
         </activity>
 
+        <receiver android:name="android.security.cts.CVE_2022_20420.PocDeviceAdminReceiver"
+            android:exported="true"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin_CVE_2022_20420" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
         <activity android:name="android.security.cts.ActivityManagerTest$ActivityOptionsActivity" />
         <activity android:name="android.security.cts.ActivityManagerTest$BaseActivity" />
 
+        <provider android:name="android.security.cts.CVE_2022_20358.PocContentProvider"
+            android:authorities="android.security.cts.CVE_2022_20358.provider"
+            android:enabled="true"
+            android:exported="true" />
+
+        <service android:name="android.security.cts.CVE_2022_20358.PocSyncService"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                android:resource="@xml/syncadapter" />
+        </service>
+
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/AndroidTest.xml b/tests/tests/security/AndroidTest.xml
index 9bd5eb7..73e6bc7 100644
--- a/tests/tests/security/AndroidTest.xml
+++ b/tests/tests/security/AndroidTest.xml
@@ -52,6 +52,16 @@
         <option name="cleanup" value="true" />
         <option name="push" value="RolePermissionOverrideTestApp.apk->/data/local/tmp/cts/security/RolePermissionOverrideTestApp.apk" />
         <option name="push" value="SplitBluetoothPermissionTestApp.apk->/data/local/tmp/cts/security/SplitBluetoothPermissionTestApp.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert1.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert1.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert1Dup.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert1Dup.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert2.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert2.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert3.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert3.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert4.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert4.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert12.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert12.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert12Dup.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert12Dup.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert123.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert123.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert34.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert34.apk" />
+        <option name="push" value="CtsPermissionBackupAppCert4History124.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert4History124.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
@@ -61,4 +71,9 @@
         <option name="test-timeout" value="900000" />
         <option name="hidden-api-checks" value="false" />
     </test>
+
+    <target_preparer class="android.cts.backup.BackupPreparer">
+        <option name="enable-backup-if-needed" value="true" />
+        <option name="select-local-transport" value="true" />
+    </target_preparer>
 </configuration>
diff --git a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl
index b9694c3..24e55c5 100644
--- a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl
+++ b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl
@@ -22,4 +22,5 @@
     int getAllocationSize(in BitmapWrapper bitmap);
     boolean didReceiveBitmap(in BitmapWrapper bitmap);
     boolean ping();
+    void exit();
 }
diff --git a/tests/tests/security/res/raw/cve_2022_22084.qcp b/tests/tests/security/res/raw/cve_2022_22084.qcp
new file mode 100644
index 0000000..c41d21e
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_22084.qcp
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2022_22085.dts b/tests/tests/security/res/raw/cve_2022_22085.dts
new file mode 100644
index 0000000..3a88631
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_22085.dts
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2022_22086.3gp b/tests/tests/security/res/raw/cve_2022_22086.3gp
new file mode 100644
index 0000000..715d10c
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_22086.3gp
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2022_25659.mkv b/tests/tests/security/res/raw/cve_2022_25659.mkv
new file mode 100644
index 0000000..9eda647
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_25659.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2022_25669.3gp b/tests/tests/security/res/raw/cve_2022_25669.3gp
new file mode 100644
index 0000000..f5ba05a
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_25669.3gp
Binary files differ
diff --git a/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml b/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml
new file mode 100644
index 0000000..cb567e3
--- /dev/null
+++ b/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<device-admin>
+    <uses-policies>
+    </uses-policies>
+</device-admin>
diff --git a/tests/tests/security/res/xml/syncadapter.xml b/tests/tests/security/res/xml/syncadapter.xml
new file mode 100644
index 0000000..478fad5
--- /dev/null
+++ b/tests/tests/security/res/xml/syncadapter.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="CVE_2022_20358_acc"
+    android:isAlwaysSyncable="true" />
diff --git a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
index 397c012..7bb74ff 100644
--- a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
+++ b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
@@ -18,29 +18,25 @@
 
 import static org.junit.Assert.fail;
 
-import android.app.Activity;
+import android.annotation.SuppressLint;
 import android.os.BaseBundle;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.platform.test.annotations.AsbSecurityTest;
 import android.view.AbsSavedState;
 import android.view.View;
-import android.view.View.BaseSavedState;
-import android.annotation.SuppressLint;
+
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
 
-import java.io.InputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.lang.reflect.Field;
 import java.util.Random;
 
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import android.security.cts.R;
-import android.platform.test.annotations.AsbSecurityTest;
-
 @RunWith(AndroidJUnit4.class)
 public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase {
 
@@ -601,6 +597,95 @@
         testAmbiguator(ambiguator);
     }
 
+    /*
+     * b/240138294
+     */
+    @AsbSecurityTest(cveBugId = 240138294)
+    @Test
+    public void test_lazyValueNegativeLength() throws Exception {
+        Ambiguator ambiguator = new Ambiguator() {
+            @Override
+            public Bundle make(Bundle preReSerialize, Bundle postReSerialize) {
+                // Find key that has hash below everything else
+                Random random = new Random(1234);
+                int minHash = 0;
+                for (String s : preReSerialize.keySet()) {
+                    minHash = Math.min(minHash, s.hashCode());
+                }
+                for (String s : postReSerialize.keySet()) {
+                    minHash = Math.min(minHash, s.hashCode());
+                }
+
+                String negativePrefix, positivePrefix;
+                // When read as value, jump back to the start of the header (8 bytes)
+                negativePrefix = getStringEncodingInt(-8);
+                // Size of the malicious bundle before the 'cmd' key
+                positivePrefix = getStringEncodingInt(48);
+
+                String key1, key2, key3;
+                int key1Hash, key2Hash, key3Hash;
+
+                do {
+                    key1 = randomString(random);
+                    // 16 characters total, will be read as type parcelable array when
+                    // read as value
+                    key2 = negativePrefix + randomString(random, 14);
+                    key3 = positivePrefix + randomString(random, 14);
+                    key1Hash = key1.hashCode();
+                    key2Hash = key3.hashCode(); // 2 and 3 are swapped
+                    key3Hash = key2.hashCode();
+                } while (!(key1Hash < key2Hash && key2Hash < key3Hash && key3Hash < minHash));
+
+                // Pad bundles - ensures keys are in right hash order
+                padBundle(postReSerialize, preReSerialize.size() + 2, minHash, random);
+                padBundle(preReSerialize, postReSerialize.size() - 2, minHash, random);
+
+                // Write bundle
+                Parcel parcel = Parcel.obtain();
+
+                int sizePosition = parcel.dataPosition();
+                parcel.writeInt(0);
+                parcel.writeInt(BUNDLE_MAGIC_NATIVE);
+                int startPosition = parcel.dataPosition();
+
+                parcel.writeInt(preReSerialize.size() + 3); // Num key-value pairs
+
+                parcel.writeString(key1); // Key 1
+                parcel.writeString(key2); // Value 1/Key 2
+                parcel.writeInt(VAL_NULL);
+                parcel.writeString(key3);
+                parcel.writeInt(VAL_BUNDLE);
+                parcel.writeBundle(postReSerialize); // Value 3
+
+                // Data from preReSerialize bundle
+                writeBundleSkippingHeaders(parcel, preReSerialize);
+
+                // Fix up bundle size
+                int bundleDataSize = parcel.dataPosition() - startPosition;
+                parcel.setDataPosition(sizePosition);
+                parcel.writeInt(bundleDataSize);
+
+                parcel.setDataPosition(0);
+                Bundle bundle = parcel.readBundle();
+                parcel.recycle();
+                return bundle;
+            }
+
+            private String getStringEncodingInt(int i) {
+                Parcel parcel = Parcel.obtain();
+                parcel.writeInt(2);
+                parcel.writeInt(i);
+                parcel.writeInt(0);
+                parcel.setDataPosition(0);
+                String s = parcel.readString();
+                parcel.recycle();
+                return s;
+            }
+        };
+
+        testAmbiguator(ambiguator);
+    }
+
     private void testAmbiguator(Ambiguator ambiguator) {
         Bundle bundle;
         Bundle verifyMe = new Bundle();
@@ -653,6 +738,7 @@
         protected static final int PROCSTATS_SPARSE_MAPPING_TABLE_ARRAY_SIZE = 4096;
 
         protected static final int BUNDLE_MAGIC = 0x4C444E42;
+        protected static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'
         protected static final int INNER_BUNDLE_PADDING = 1;
 
         protected Field parcelledDataField;
@@ -711,8 +797,12 @@
         }
 
         protected static String randomString(Random random) {
+            return randomString(random, 6);
+        }
+
+        protected static String randomString(Random random, int len) {
             StringBuilder b = new StringBuilder();
-            for (int i = 0; i < 6; i++) {
+            for (int i = 0; i < len; i++) {
                 b.append((char)(' ' + random.nextInt('~' - ' ' + 1)));
             }
             return b.toString();
diff --git a/tests/tests/security/src/android/security/cts/BitmapService.java b/tests/tests/security/src/android/security/cts/BitmapService.java
index c532e05..ec39ab0 100644
--- a/tests/tests/security/src/android/security/cts/BitmapService.java
+++ b/tests/tests/security/src/android/security/cts/BitmapService.java
@@ -40,6 +40,11 @@
         public boolean ping() {
             return true;
         }
+
+        @Override
+        public void exit() {
+            System.exit(0);
+        }
     };
 
     @Nullable
diff --git a/tests/tests/security/src/android/security/cts/BitmapTest.java b/tests/tests/security/src/android/security/cts/BitmapTest.java
index e824deb..0527366 100644
--- a/tests/tests/security/src/android/security/cts/BitmapTest.java
+++ b/tests/tests/security/src/android/security/cts/BitmapTest.java
@@ -25,11 +25,12 @@
 import android.os.BadParcelableException;
 import android.os.IBinder;
 import android.platform.test.annotations.AsbSecurityTest;
-import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
 import com.google.common.util.concurrent.AbstractFuture;
 
 import org.junit.After;
@@ -80,8 +81,10 @@
     public void tearDown() {
         if (mRemoteConnection != null) {
             final Context context = mInstrumentation.getContext();
-            context.stopService(mIntent);
             context.unbindService(mRemoteConnection);
+            try {
+                mRemote.exit();
+            } catch (Exception ex) { }
             mRemote = null;
             mRemoteConnection = null;
         }
@@ -94,8 +97,7 @@
             mIntent.setComponent(new ComponentName(
                     "android.security.cts", "android.security.cts.BitmapService"));
             mRemoteConnection = new PeerConnection();
-            context.bindService(mIntent, mRemoteConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
+            context.bindService(mIntent, mRemoteConnection, Context.BIND_AUTO_CREATE);
             mRemote = mRemoteConnection.get();
         }
         return mRemote;
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java
new file mode 100644
index 0000000..b1ff168
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java
@@ -0,0 +1,120 @@
+/*
+ * 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_20358;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.accounts.Account;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ISyncAdapter;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class CVE_2022_20358 extends StsExtraBusinessLogicTestCase implements ServiceConnection {
+    static final int TIMEOUT_SEC = 10;
+    Semaphore mWaitResultServiceConn;
+    boolean mIsAssumeFail = false;
+    String mAssumeFailMsg = "";
+
+    @AsbSecurityTest(cveBugId = 203229608)
+    @Test
+    public void testPocCVE_2022_20358() {
+        try {
+            // Bind to the PocSyncService
+            Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+            Context context = instrumentation.getContext();
+            Intent intent = new Intent(context, PocSyncService.class);
+            intent.setAction("android.content.SyncAdapter");
+            CompletableFuture<String> callbackReturn = new CompletableFuture<>();
+            RemoteCallback cb = new RemoteCallback((Bundle result) -> {
+                callbackReturn.complete(result.getString("fail"));
+            });
+            intent.putExtra("callback", cb);
+            context.bindService(intent, this, Context.BIND_AUTO_CREATE);
+
+            // Wait for some result from the PocSyncService
+            mWaitResultServiceConn = new Semaphore(0);
+            assumeTrue(mWaitResultServiceConn.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS));
+            assumeTrue(mAssumeFailMsg, !mIsAssumeFail);
+
+            // Wait for a result to be set from onPerformSync() of PocSyncAdapter
+            callbackReturn.get(TIMEOUT_SEC, TimeUnit.SECONDS);
+
+            // In presence of vulnerability, the above call succeeds and TimeoutException is not
+            // triggered so failing the test
+            fail("Vulnerable to b/203229608!!");
+        } catch (Exception e) {
+            if (e instanceof TimeoutException) {
+                // The fix is present so returning from here
+                return;
+            }
+            assumeNoException(e);
+        }
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {
+        try {
+            if (mWaitResultServiceConn == null) {
+                mWaitResultServiceConn = new Semaphore(0);
+            }
+            ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service);
+            Account account = new Account("CVE_2022_20358_user", "CVE_2022_20358_acc");
+            adapter.startSync(null, "android.security.cts.CVE_2022_20358.provider", account, null);
+            mWaitResultServiceConn.release();
+        } catch (Exception e) {
+            try {
+                mWaitResultServiceConn.release();
+                mAssumeFailMsg = e.getMessage();
+                mIsAssumeFail = true;
+            } catch (Exception ex) {
+                // ignore all exceptions
+            }
+        }
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        try {
+            mWaitResultServiceConn.release();
+        } catch (Exception e) {
+            // ignore all exceptions
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java
new file mode 100644
index 0000000..0bc8c2c
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java
@@ -0,0 +1,56 @@
+/*
+ * 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_20358;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class PocContentProvider extends ContentProvider {
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return null;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java
new file mode 100644
index 0000000..08fbf92
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java
@@ -0,0 +1,79 @@
+/*
+ * 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_20358;
+
+import android.accounts.Account;
+import android.app.Service;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+
+public class PocSyncService extends Service {
+    private static PocSyncAdapter sSyncAdapter = null;
+    private static final Object sSyncAdapterLock = new Object();
+    RemoteCallback mCb;
+
+    @Override
+    public void onCreate() {
+        try {
+            synchronized (sSyncAdapterLock) {
+                if (sSyncAdapter == null) {
+                    sSyncAdapter = new PocSyncAdapter(this);
+                }
+            }
+        } catch (Exception e) {
+            // ignore all exceptions
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        try {
+            mCb = (RemoteCallback) intent.getExtra("callback");
+        } catch (Exception e) {
+            // ignore all exceptions
+        }
+        return sSyncAdapter.getSyncAdapterBinder();
+    }
+
+    public class PocSyncAdapter extends AbstractThreadedSyncAdapter {
+
+        public PocSyncAdapter(Context context) {
+            super(context, false);
+        }
+
+        @Override
+        public void onPerformSync(Account account, Bundle extras, String authority,
+                ContentProviderClient provider, SyncResult syncResult) {
+            try {
+                if (account.type.equals("CVE_2022_20358_acc")
+                        && account.name.equals("CVE_2022_20358_user")) {
+                    Bundle res = new Bundle();
+                    res.putString("fail", "");
+                    mCb.sendResult(res);
+                }
+            } catch (Exception e) {
+                // ignore all exceptions
+            }
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java b/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java
new file mode 100644
index 0000000..35d576e
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java
@@ -0,0 +1,112 @@
+/*
+ * 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_20420;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.ActivityManager;
+import android.app.UiAutomation;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IDeviceIdleController;
+import android.os.PowerExemptionManager;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class CVE_2022_20420 extends StsExtraBusinessLogicTestCase {
+    private static final int TIMEOUT_MS = 10000;
+    private static final int USER_ID = 0;
+    private Context mContext;
+    private DevicePolicyManager mPolicyManager;
+    private ComponentName mComponentName;
+    private UiAutomation mAutomation;
+
+    @After
+    public void tearDown() {
+        try {
+            mAutomation.dropShellPermissionIdentity();
+            mPolicyManager.removeActiveAdmin(mComponentName);
+        } catch (Exception ignored) {
+            // ignore all exceptions as the test has been completed.
+        }
+    }
+
+    @AsbSecurityTest(cveBugId = 238377411)
+    @Test
+    public void testDeviceAdminAppRestricted() {
+        try {
+            // Add test app to Power Save Whitelist.
+            mContext = getInstrumentation().getTargetContext();
+            mAutomation = getInstrumentation().getUiAutomation();
+            mAutomation.adoptShellPermissionIdentity(android.Manifest.permission.DEVICE_POWER,
+                    android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+            IDeviceIdleController mDeviceIdleService =
+                    IDeviceIdleController.Stub.asInterface(ServiceManager.getService("deviceidle"));
+            mDeviceIdleService.addPowerSaveWhitelistApp(mContext.getPackageName());
+
+            // Set test app as "Active Admin".
+            mPolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+            mComponentName = new ComponentName(mContext, PocDeviceAdminReceiver.class);
+            mPolicyManager.setActiveAdmin(mComponentName, true, USER_ID);
+            CompletableFuture<Boolean> future = new CompletableFuture<>();
+            BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    future.complete(true);
+                }
+            };
+            mContext.registerReceiver(broadcastReceiver,
+                    new IntentFilter("broadcastCVE_2022_20420"));
+            future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+            // Call vulnerable function getBackgroundRestrictionExemptionReason()
+            ActivityManager activityManager = mContext.getSystemService(ActivityManager.class);
+            int reason = activityManager.getBackgroundRestrictionExemptionReason(Process.myUid());
+            assumeTrue(
+                    "Reason code other than REASON_ACTIVE_DEVICE_ADMIN/REASON_ALLOWLISTED_PACKAGE"
+                            + " returned by getBackgroundRestrictionExemptionReason() = " + reason,
+                    reason == PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN
+                            || reason == PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE);
+            assertFalse("Vulnerable to b/238377411 !!",
+                    reason == PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE);
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java b/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java
new file mode 100644
index 0000000..c9c1b6f
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * 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_20420;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class PocDeviceAdminReceiver extends DeviceAdminReceiver {
+
+    @Override
+    public void onEnabled(Context context, Intent intent) {
+        try {
+            context.sendBroadcast(new Intent("broadcastCVE_2022_20420"));
+        } catch (Exception e) {
+            // ignore all exceptions.
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java
new file mode 100644
index 0000000..af581a1
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+// This PoC has been written taking reference from:
+// File: frameworks/base/core/tests/coretests/src/android/os/BundleTest.java
+// Function: readFromParcelWithRwHelper_whenThrowingAndDefusing_returnsNull()
+
+package android.security.cts.CVE_2022_20452;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeNoException;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CVE_2022_20452 extends StsExtraBusinessLogicTestCase {
+
+    @AsbSecurityTest(cveBugId = 240138318)
+    @Test
+    public void testPocCVE_2022_20452() {
+        try {
+            // Create a bundle with some parcelable object and a random string
+            Bundle bundle = new Bundle();
+            Parcelable parcelable = new CustomParcelable();
+            bundle.putParcelable("keyParcelable", parcelable);
+            bundle.putString("keyStr", "valStr");
+
+            // Read bundle contents into a parcel and also set read write helper for the parcel
+            Parcel parcelledBundle = Parcel.obtain();
+            bundle.writeToParcel(parcelledBundle, 0);
+            parcelledBundle.setDataPosition(0);
+            parcelledBundle.setReadWriteHelper(new Parcel.ReadWriteHelper());
+
+            // First set 'shouldDefuse' to true, then read contents of parcel into a bundle.
+            // In presence of fix, this will cause a ClassNotFoundException because bundle will not
+            // be able to find the class for 'CustomParcelable' as the class loader is not set, so
+            // Parcel will not be read properly and the code will return without reading the string.
+            Bundle.setShouldDefuse(true);
+            Bundle testBundle = new Bundle();
+            testBundle.readFromParcel(parcelledBundle);
+
+            // If the vulnerability is active, we will be able to read string from bundle.
+            assertNull("Vulnerable to b/240138318 !!", testBundle.getString("keyStr"));
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java
new file mode 100644
index 0000000..f076eee
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java
@@ -0,0 +1,50 @@
+/*
+ * 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_20452;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CustomParcelable implements Parcelable {
+    private boolean mDummyValue = true;
+
+    CustomParcelable() {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeBoolean(mDummyValue);
+    }
+
+    public static final Creator<CustomParcelable> CREATOR =
+            new Creator<CustomParcelable>() {
+                @Override
+                public CustomParcelable createFromParcel(Parcel in) {
+                    return new CustomParcelable();
+                }
+
+                @Override
+                public CustomParcelable[] newArray(int size) {
+                    return new CustomParcelable[size];
+                }
+            };
+}
diff --git a/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt b/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt
new file mode 100644
index 0000000..1170939
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.security.cts
+
+import android.Manifest.permission.*
+import android.app.AppOpsManager
+import android.content.pm.PackageManager.*
+import android.os.ParcelFileDescriptor
+import android.permission.cts.PermissionUtils.grantPermission
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.BackupUtils
+import com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN
+import com.android.compatibility.common.util.BusinessLogicTestCase
+import com.android.compatibility.common.util.ShellUtils.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase
+import java.io.InputStream
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests that permissions for backed up apps are restored only after checking that their signing
+ * certificates are compared.
+ *
+ * @see [com.android.permissioncontroller.permission.service.BackupHelper]
+ */
+@AppModeFull
+@RunWith(AndroidJUnit4::class)
+class PermissionBackupCertificateCheckTest : StsExtraBusinessLogicTestCase() {
+    private val backupUtils: BackupUtils =
+        object : BackupUtils() {
+            override fun executeShellCommand(command: String): InputStream {
+                val pfd =
+                    BusinessLogicTestCase.getInstrumentation()
+                        .uiAutomation
+                        .executeShellCommand(command)
+                return ParcelFileDescriptor.AutoCloseInputStream(pfd)
+            }
+        }
+
+    private var isBackupSupported = false
+
+    private val targetContext = InstrumentationRegistry.getTargetContext()
+
+    @Before
+    fun setUp() {
+        val packageManager = BusinessLogicTestCase.getInstrumentation().context.packageManager
+        isBackupSupported =
+            (packageManager != null && packageManager.hasSystemFeature(FEATURE_BACKUP))
+
+        if (isBackupSupported) {
+            assertTrue("Backup not enabled", backupUtils.isBackupEnabled)
+            assertTrue("LocalTransport not selected", backupUtils.isLocalTransportSelected)
+            backupUtils.executeShellCommandSync("setprop log.tag.$APP_LOG_TAG VERBOSE")
+        }
+    }
+
+    @After
+    fun tearDown() {
+        uninstallIfInstalled(APP)
+        clearFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET)
+        clearFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has the
+     * same certificate as the backed up app.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_sameCert_restoresRuntimePermissions() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_1_DUP)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has a
+     * different certificate as the backed up app.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_diffCert_doesNotGrantRuntimePermissions() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_3)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has the
+     * backed up app's certificate in its signing history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_midHistoryToRotated_restoresRuntimePermissions() {
+        install(APP_APK_CERT_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has the
+     * backed up app's certificate as the original certificate in its signing history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_origToRotated_restoresRuntimePermissions() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the backed up app has the
+     * restored app's certificate in its signing history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_rotatedToMidHistory_restoresRuntimePermissions() {
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_2)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the backed up app has the
+     * restored app's certificate in its signing history as its original certificate.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_rotatedToOrig_restoresRuntimePermissions() {
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_1)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the backed up app has the same
+     * certificate as the restored app, but the restored app additionally has signing certificate
+     * history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_sameWithHistory_restoresRuntimePermissions() {
+        install(APP_APK_CERT_4)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the backed up app has the same
+     * certificate as the restored app, but the backed up app additionally has signing certificate
+     * history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_sameWithoutHistory_restoresRuntimePermissions() {
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has
+     * signing history, but the backed up app's certificate is not in this signing history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_notInBackedUpHistory_doesNotRestoreRuntimePerms() {
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_3)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has
+     * signing history, but the backed up app's certificate is not in this signing history.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_notInRestoredHistory_doesNotRestoreRuntimePerms() {
+        install(APP_APK_CERT_3)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has
+     * multiple certificates, and the backed up app also has identical multiple certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_sameMultCerts_restoresRuntimePermissions() {
+        install(APP_APK_CERT_1_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_1_2_DUP)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has
+     * multiple certificates, and the backed up app do not have identical multiple certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_diffMultCerts_doesNotRestoreRuntimePermissions() {
+        install(APP_APK_CERT_1_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_3_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the app being restored has
+     * multiple certificates, and the backed up app's certificate is present in th restored app's
+     * certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_singleToMultiCert_restoresRuntimePerms() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_1_2_3)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the backed up app and the app
+     * being restored have multiple certificates, and the backed up app's certificates are a subset
+     * of the restored app's certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_multCertsToSuperset_doesNotRestoreRuntimePerms() {
+        install(APP_APK_CERT_1_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_1_2_3)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of regular runtime permissions, when the backed up app and the app
+     * being restored have multiple certificates, and the backed up app's certificates are a
+     * superset of the restored app's certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_multCertsToSubset_doesNotRestoreRuntimePermissions() {
+        install(APP_APK_CERT_1_2_3)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_1_2)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+        }
+    }
+
+    /**
+     * Test backup and restore of tri-state permissions, when both foreground and background runtime
+     * permissions are not granted and the backed up and restored app have compatible certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_fgBgDenied_matchingCerts_restoresFgBgPermissions() {
+        install(APP_APK_CERT_2)
+        if (!isBackupSupported) {
+            return
+        }
+        // Make a token change to permission state, to enable to us to determine when restore is
+        // complete.
+        grantPermission(APP, WRITE_CONTACTS)
+        // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+        // to ensure that permissions are backed up.
+        setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET)
+        setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+
+            // Wait until restore is complete.
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, WRITE_CONTACTS))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+            assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+        }
+    }
+
+    /**
+     * Test backup and restore of tri-state permissions, when both foreground and background runtime
+     * permissions are not granted and the backed up and restored app don't have compatible
+     * certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_fgBgDenied_notMatchingCerts_doesNotRestorePerms() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        // Make a token change to permission state, to enable to us to determine when restore is
+        // complete.
+        grantPermission(APP, WRITE_CONTACTS)
+        // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+        // to ensure that permissions are backed up.
+        setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET)
+        setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_2)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+
+            // Wait until restore is complete.
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, WRITE_CONTACTS))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+            assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+        }
+    }
+
+    /**
+     * Test backup and restore of tri-state permissions, when foreground runtime permission is
+     * granted and the backed up and restored app have compatible certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_fgGranted_matchingCerts_restoresFgBgPermissions() {
+        install(APP_APK_CERT_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+        // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+        // to ensure that permissions are backed up.
+        setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+            assertEquals(AppOpsManager.MODE_FOREGROUND, getAppOp(APP, ACCESS_FINE_LOCATION))
+        }
+    }
+
+    /**
+     * Test backup and restore of tri-state permissions, when foreground runtime permission is
+     * granted and the backed up and restored app don't have compatible certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_fgGranted_notMatchingCerts_doesNotRestoreFgBgPerms() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+        // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+        // to ensure that permissions are backed up.
+        setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_2)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+            assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+        }
+    }
+
+    /**
+     * Test backup and restore of tri-state permissions, when foreground and background runtime
+     * permissions are granted and the backed up and restored app have compatible certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_fgBgGranted_matchingCerts_restoresFgBgPermissions() {
+        install(APP_APK_CERT_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+        grantPermission(APP, ACCESS_BACKGROUND_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+            assertEquals(AppOpsManager.MODE_ALLOWED, getAppOp(APP, ACCESS_FINE_LOCATION))
+        }
+    }
+
+    /**
+     * Test backup and restore of tri-state permissions, when foreground and background runtime
+     * permissions are granted and the backed up and restored app don't have compatible
+     * certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_fgBgGranted_notMatchingCerts_restoresFgBgPerms() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+        grantPermission(APP, ACCESS_BACKGROUND_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_2)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually {
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+            assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+        }
+    }
+
+    /**
+     * Test backup and restore of flags when the backed up app and restored app have compatible
+     * certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_matchingCerts_restoresFlags() {
+        install(APP_APK_CERT_2)
+        if (!isBackupSupported) {
+            return
+        }
+        setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually { assertTrue(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)) }
+    }
+
+    /**
+     * Test backup and restore of flags when the backed up app and restored app don't have
+     * compatible certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_notMatchingCerts_doesNotRestoreFlag() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        install(APP_APK_CERT_2)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+        eventually { assertFalse(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)) }
+    }
+
+    /**
+     * Test backup and delayed restore of regular runtime permission, i.e. when an app is installed
+     * after restore has run, and the backed up app and restored app have compatible certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_appInstalledLater_matchingCerts_restoresCorrectly() {
+        install(APP_APK_CERT_2)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+
+        eventually { assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) }
+    }
+
+    /**
+     * Test backup and delayed restore of regular runtime permission, i.e. when an app is installed
+     * after restore has run, and the backed up app and restored app don't have compatible
+     * certificates.
+     */
+    @Test
+    @AsbSecurityTest(cveBugId = [184847040])
+    fun testRestore_appInstalledLater_notMatchingCerts_doesNotRestore() {
+        install(APP_APK_CERT_1)
+        if (!isBackupSupported) {
+            return
+        }
+        grantPermission(APP, ACCESS_FINE_LOCATION)
+
+        backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+        uninstallIfInstalled(APP)
+        backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+        install(APP_APK_CERT_4_HISTORY_1_2_4)
+
+        eventually { assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) }
+    }
+
+    private fun install(apk: String) {
+        val output = runShellCommand("pm install -r $apk")
+        assertEquals("Success", output)
+    }
+
+    private fun uninstallIfInstalled(packageName: String) {
+        runShellCommand("pm uninstall $packageName")
+    }
+
+    private fun setFlag(app: String, permission: String, flag: Int) {
+        runWithShellPermissionIdentity {
+            targetContext.packageManager.updatePermissionFlags(
+                permission, app, flag, flag, targetContext.user)
+        }
+    }
+
+    private fun clearFlag(app: String, permission: String, flag: Int) {
+        runWithShellPermissionIdentity {
+            targetContext.packageManager.updatePermissionFlags(
+                permission, app, flag, 0, targetContext.user)
+        }
+    }
+
+    private fun isFlagSet(app: String, permission: String, flag: Int): Boolean {
+        return try {
+            callWithShellPermissionIdentity<Int> {
+                targetContext.packageManager.getPermissionFlags(permission, app, targetContext.user)
+            } and flag == flag
+        } catch (e: Exception) {
+            throw RuntimeException(e)
+        }
+    }
+
+    private fun checkPermission(app: String, permission: String): Int {
+        return targetContext.packageManager.checkPermission(permission, app)
+    }
+
+    private fun getAppOp(app: String, permission: String): Int {
+        return try {
+            callWithShellPermissionIdentity {
+                targetContext
+                    .getSystemService<AppOpsManager>(AppOpsManager::class.java)!!
+                    .unsafeCheckOpRaw(
+                        AppOpsManager.permissionToOp(permission)!!,
+                        targetContext.packageManager.getPackageUid(app, 0),
+                        app)
+            }
+        } catch (e: Exception) {
+            throw RuntimeException(e)
+        }
+    }
+
+    companion object {
+        /** The name of the package of the apps under test */
+        private const val APP = "android.security.permissionbackup"
+        /** The apk of the packages */
+        private const val APK_PATH = "/data/local/tmp/cts/security/"
+        private const val APP_APK_CERT_1 = "${APK_PATH}CtsPermissionBackupAppCert1.apk"
+        private const val APP_APK_CERT_1_DUP = "${APK_PATH}CtsPermissionBackupAppCert1Dup.apk"
+        private const val APP_APK_CERT_2 = "${APK_PATH}CtsPermissionBackupAppCert2.apk"
+        private const val APP_APK_CERT_3 = "${APK_PATH}CtsPermissionBackupAppCert3.apk"
+        private const val APP_APK_CERT_4 = "${APK_PATH}CtsPermissionBackupAppCert4.apk"
+        private const val APP_APK_CERT_1_2 = "${APK_PATH}CtsPermissionBackupAppCert12.apk"
+        private const val APP_APK_CERT_1_2_DUP = "${APK_PATH}CtsPermissionBackupAppCert12Dup.apk"
+        private const val APP_APK_CERT_1_2_3 = "${APK_PATH}CtsPermissionBackupAppCert123.apk"
+        private const val APP_APK_CERT_3_4 = "${APK_PATH}CtsPermissionBackupAppCert34.apk"
+        private const val APP_APK_CERT_4_HISTORY_1_2_4 =
+            "${APK_PATH}CtsPermissionBackupAppCert4History124.apk"
+        private const val APP_LOG_TAG = "PermissionBackupApp"
+        /** The name of the package for backup */
+        private const val ANDROID_PACKAGE = "android"
+        private const val TIMEOUT_MILLIS: Long = 10000
+
+        /**
+         * Make sure that a [Runnable] eventually finishes without throwing an [Exception].
+         *
+         * @param r The [Runnable] to run.
+         */
+        fun eventually(r: Runnable) {
+            val start = System.currentTimeMillis()
+            while (true) {
+                try {
+                    r.run()
+                    return
+                } catch (e: Throwable) {
+                    if (System.currentTimeMillis() - start < TIMEOUT_MILLIS) {
+                        try {
+                            Thread.sleep(100)
+                        } catch (ignored: InterruptedException) {
+                            throw RuntimeException(e)
+                        }
+                    } else {
+                        throw e
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/PermissionMemoryFootprintTest.kt b/tests/tests/security/src/android/security/cts/PermissionMemoryFootprintTest.kt
new file mode 100644
index 0000000..c774760
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PermissionMemoryFootprintTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.content.pm.PackageManager
+import android.content.pm.PermissionInfo
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PermissionMemoryFootprintTest : StsExtraBusinessLogicTestCase() {
+    companion object {
+        const val MAX_NUM_PERMISSIONS = 32000
+        const val PKG_TREE_NAME = "com.android.cts"
+        val LONG_DESCRIPTION = " ".repeat(MAX_NUM_PERMISSIONS / 10)
+        val SHORT_DESCRIPTION = " ".repeat(MAX_NUM_PERMISSIONS / 100)
+
+        val permInfo = PermissionInfo().apply {
+            labelRes = 1
+            protectionLevel = PermissionInfo.PROTECTION_NORMAL
+        }
+    }
+
+    val packageManager: PackageManager = InstrumentationRegistry.getInstrumentation()
+                    .getTargetContext().packageManager!!
+
+    @Throws(SecurityException::class)
+    private fun createOrRemovePermissions(
+        largePerm: Boolean = true,
+        add: Boolean = true,
+        numPerms: Int = MAX_NUM_PERMISSIONS,
+    ): Int {
+        var numPermsCreated = 0
+        for (i in 1..numPerms) {
+            try {
+                permInfo.name = "$PKG_TREE_NAME.$i"
+                permInfo.nonLocalizedDescription = if (largePerm) {
+                    LONG_DESCRIPTION
+                } else {
+                    SHORT_DESCRIPTION
+                }
+
+                if (add) {
+                    packageManager.addPermission(permInfo)
+                } else {
+                    packageManager.removePermission(permInfo.name)
+                }
+            } catch (e: SecurityException) {
+                break
+            }
+            numPermsCreated = i
+        }
+        return numPermsCreated
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = [242537498])
+    fun checkAppsCreatingPermissionsAreCapped() {
+        var numCreated = 0
+        try {
+            numCreated = createOrRemovePermissions()
+            Assert.assertNotEquals("Expected at least one permission", numCreated, 0)
+            Assert.assertNotEquals(numCreated, MAX_NUM_PERMISSIONS)
+        } finally {
+            createOrRemovePermissions(add = false, numPerms = numCreated)
+        }
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = [242537498])
+    fun checkAppsCantIncreasePermissionSizeAfterCreating() {
+        var numCreatedShort = 0
+        try {
+            numCreatedShort = createOrRemovePermissions(largePerm = false)
+            Assert.assertNotEquals("Expected at least one permission", numCreatedShort, 0)
+            val numCreatedLong = createOrRemovePermissions(numPerms = 1)
+            Assert.assertEquals("Expected to not be able to create a large permission",
+                0, numCreatedLong)
+        } finally {
+            createOrRemovePermissions(add = false, numPerms = numCreatedShort)
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 7706081..9fd1014 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1377,8 +1377,10 @@
         };
         server.start();
         String uri = "rtsp://127.0.0.1:8080/cve_2016_3880";
-        final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener(new CrashUtils.Config()
-                .setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT));
+        final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener(
+                new CrashUtils.Config()
+                        .setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT)
+                        .appendAbortMessageExcludes("CHECK\\(IsRTSPVersion"));
         LooperThread t = new LooperThread(new Runnable() {
             @Override
             public void run() {
@@ -1809,6 +1811,36 @@
      before any existing test methods
      ***********************************************************/
     @Test
+    @AsbSecurityTest(cveBugId = 235102508)
+    public void testStagefright_cve_2022_25669() throws Exception {
+         doStagefrightTest(R.raw.cve_2022_25669);
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = 223209306)
+    public void testStagefright_cve_2022_22085() throws Exception {
+         doStagefrightTest(R.raw.cve_2022_22085);
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = 223209816)
+    public void testStagefright_cve_2022_22084() throws Exception {
+         doStagefrightTest(R.raw.cve_2022_22084);
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = 223211218)
+    public void testStagefright_cve_2022_22086() throws Exception {
+         doStagefrightTest(R.raw.cve_2022_22086);
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = 228101819)
+    public void testStagefright_cve_2022_25659() throws Exception {
+         doStagefrightTest(R.raw.cve_2022_25659);
+    }
+
+    @Test
     @AsbSecurityTest(cveBugId = 223210917)
     public void testStagefright_cve_2022_22083() throws Exception {
          doStagefrightTest(R.raw.cve_2022_22083);
diff --git a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java
index 73474a1..eeed518 100644
--- a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java
+++ b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java
@@ -76,7 +76,9 @@
 
     @After
     public void tearDown() throws Exception {
-        mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
+        if (mWallpaperManager != null) {
+            mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
+        }
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .dropShellPermissionIdentity();
     }
diff --git a/tests/tests/security/test-cert-1.pk8 b/tests/tests/security/test-cert-1.pk8
new file mode 100644
index 0000000..f781c30
--- /dev/null
+++ b/tests/tests/security/test-cert-1.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-1.x509.pem b/tests/tests/security/test-cert-1.x509.pem
new file mode 100644
index 0000000..06adcfe
--- /dev/null
+++ b/tests/tests/security/test-cert-1.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbDCCARGgAwIBAgIJAMoPtk37ZudyMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM
+B2VjLXAyNTYwHhcNMTYwMzMxMTQ1ODA2WhcNNDMwODE3MTQ1ODA2WjASMRAwDgYD
+VQQDDAdlYy1wMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpl8RPSLLSROQ
+gwesMe4roOkTi3hfrGU20U6izpDStL/hlLUM3I4Wn1SnOpke8Pp2MpglvgeMx4J0
+BwPaRLTX66NQME4wHQYDVR0OBBYEFNQTNWi5WzAVizIgceqMQ/9bBczIMB8GA1Ud
+IwQYMBaAFNQTNWi5WzAVizIgceqMQ/9bBczIMAwGA1UdEwQFMAMBAf8wCgYIKoZI
+zj0EAwIDSQAwRgIhAPUEoIZsrvAp9BcULFy3E1THn/zR1kBhjfyk8Z4W23jWAiEA
++O6kgpeZwGytCMbT0tLsBeBXQVTnR+oP27gELLZVqt0=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-2.pk8 b/tests/tests/security/test-cert-2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/tests/tests/security/test-cert-2.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-2.x509.pem b/tests/tests/security/test-cert-2.x509.pem
new file mode 100644
index 0000000..f8e5e65
--- /dev/null
+++ b/tests/tests/security/test-cert-2.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbTCCAROgAwIBAgIJAIhVvR3SsrIlMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM
+B2VjLXAyNTYwHhcNMTgwNzEzMTc0MTUxWhcNMjgwNzEwMTc0MTUxWjAUMRIwEAYD
+VQQDDAllYy1wMjU2XzIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdTMoEcq2X
+7jzs7w2pPWK0UMZ4gzOzbnVTzen3SrXfALu6a6lQ5oRh1wu8JxtiFR2tLeK/YgPN
+IHaAHHqdRCLho1AwTjAdBgNVHQ4EFgQUeZHZKwII/ESL9QbU78n/9CjLXl8wHwYD
+VR0jBBgwFoAU1BM1aLlbMBWLMiBx6oxD/1sFzMgwDAYDVR0TBAUwAwEB/zAKBggq
+hkjOPQQDAgNIADBFAiAnaauxtJ/C9TR5xK6SpmMdq/1SLJrLC7orQ+vrmcYwEQIh
+ANJg+x0fF2z5t/pgCYv9JDGfSQWj5f2hAKb+Giqxn/Ce
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-3.pk8 b/tests/tests/security/test-cert-3.pk8
new file mode 100644
index 0000000..d7309dd
--- /dev/null
+++ b/tests/tests/security/test-cert-3.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-3.x509.pem b/tests/tests/security/test-cert-3.x509.pem
new file mode 100644
index 0000000..c028ff7
--- /dev/null
+++ b/tests/tests/security/test-cert-3.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbjCCARWgAwIBAgIJAIOU9crRaomnMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMM
+CWVjLXAyNTZfMjAeFw0xODA3MTQwMDA1MjZaFw0yODA3MTEwMDA1MjZaMBQxEjAQ
+BgNVBAMMCWVjLXAyNTZfMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMeYkMO
+nbb8WSjZdfxOR0GbrPyy4HyJKZ5s1+NE3SGt/TCNWMtJoaKj/srM7qSGIGnzC+Fk
+O8wlUEDYCJ37N0OjUDBOMB0GA1UdDgQWBBRvjQgosT769Xf8hrDpn6PlS8vP8DAf
+BgNVHSMEGDAWgBR5kdkrAgj8RIv1BtTvyf/0KMteXzAMBgNVHRMEBTADAQH/MAoG
+CCqGSM49BAMCA0cAMEQCICVr2qJ4TCc+TMKRpZWkZ3ne6d6QRNyferggMJVn35/p
+AiAaStjGmJG1qMR0NP6VQO0fSXm1+tNIPz+gTVZ3NVpXng==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-4.pk8 b/tests/tests/security/test-cert-4.pk8
new file mode 100644
index 0000000..3675d50
--- /dev/null
+++ b/tests/tests/security/test-cert-4.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-4.x509.pem b/tests/tests/security/test-cert-4.x509.pem
new file mode 100644
index 0000000..4060400
--- /dev/null
+++ b/tests/tests/security/test-cert-4.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBezCCASCgAwIBAgIUbIy4qBhDPB5kMfsW+zrg+1rWCqcwCgYIKoZIzj0EAwIw
+FDESMBAGA1UEAwwJZWMtcDI1Nl8zMB4XDTIwMDUxMzE5MTUyOFoXDTMwMDUxMTE5
+MTUyOFowFDESMBAGA1UEAwwJZWMtcDI1Nl80MFkwEwYHKoZIzj0CAQYIKoZIzj0D
+AQcDQgAE20pgAx55rUnLdZAH1oVdRGm5HIurBlQ08vupca3n5NGVmaD2e15wjP2n
+VD5WMMN2nTfgk2QNfHaKFRRM0OXc9KNQME4wHQYDVR0OBBYEFG54lwMyVUM2tu6J
+JOqnAjDjk/Z4MB8GA1UdIwQYMBaAFG+NCCixPvr1d/yGsOmfo+VLy8/wMAwGA1Ud
+EwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAM54bnnsdUdEYILpyvkQYU/4B1j5
+gZ+w8UhpUGer4PzUAiEApIgeMy3ewhFq0rWc+JHQ8zH/fifne3xiBseYjZtTkzA=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-with-1-2-4-in-rotation-history b/tests/tests/security/test-cert-with-1-2-4-in-rotation-history
new file mode 100644
index 0000000..7326e46
--- /dev/null
+++ b/tests/tests/security/test-cert-with-1-2-4-in-rotation-history
Binary files differ
diff --git a/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml b/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml
new file mode 100644
index 0000000..2b75d8c
--- /dev/null
+++ b/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.permissionbackup" >
+
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+    <application
+        android:label="Android Permission Backup CTS App">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java
index 00e4dfe..fc5bbbd 100644
--- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java
@@ -29,6 +29,8 @@
 import android.net.Uri;
 import android.provider.Telephony;
 
+import com.android.compatibility.common.util.ApiTest;
+
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -127,6 +129,20 @@
 
     }
 
+    /**
+     *  Verifies uri path outside the directory of mms parts  is not allowed.
+     */
+    @Test
+    @ApiTest(apis = "com.android.providers.telephony.MmsProvider#update")
+    public void testMmsPartUpdate_invalidUri() {
+        ContentValues cv = new ContentValues();
+        Uri uri = Uri.parse("content://mms/resetFilePerm/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F.."
+                + "%2F..%2F..%2F..%2F..%2Fdata%2Fuser_de%2F0%2Fcom.android.providers.telephony"
+                + "%2Fdatabases");
+        int cursorUpdate = mContentResolver.update(uri, cv, null, null);
+        assertThat(cursorUpdate).isEqualTo(0);
+    }
+
     @Test
     public void testMmsPartDelete_canDeleteById() {
         Uri mmsUri = insertIntoMmsTable(MMS_SUBJECT_ONE);
diff --git a/tests/tests/webkit/Android.bp b/tests/tests/webkit/Android.bp
index d8175dd..a046dae 100644
--- a/tests/tests/webkit/Android.bp
+++ b/tests/tests/webkit/Android.bp
@@ -25,6 +25,7 @@
         "android.test.base",
     ],
     static_libs: [
+        "androidx.test.core",
         "compatibility-device-util-axt",
         "ctsdeviceutillegacy-axt",
         "ctstestserver",
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index abe44ea..7ca3f46 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -19,6 +19,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.*;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -49,18 +50,13 @@
 import android.print.PrintDocumentAdapter.LayoutResultCallback;
 import android.print.PrintDocumentAdapter.WriteResultCallback;
 import android.print.PrintDocumentInfo;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewGroup;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextSelection;
-import android.webkit.ConsoleMessage;
 import android.webkit.CookieSyncManager;
 import android.webkit.DownloadListener;
 import android.webkit.JavascriptInterface;
@@ -81,47 +77,53 @@
 import android.webkit.cts.WebViewSyncLoader.WaitForProgressClient;
 import android.widget.LinearLayout;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.FlakyTest;
 
 import com.android.compatibility.common.util.NullWebViewUtils;
 import com.android.compatibility.common.util.PollingCheck;
+
 import com.google.common.util.concurrent.SettableFuture;
 
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.util.EncodingUtils;
 import org.apache.http.util.EntityUtils;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 @AppModeFull
-public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
+@RunWith(AndroidJUnit4.class)
+public class WebViewTest {
     private static final int INITIAL_PROGRESS = 100;
     private static final String X_REQUESTED_WITH = "X-Requested-With";
     private static final String PRINTER_TEST_FILE = "print.pdf";
@@ -132,59 +134,62 @@
     private static final String SIMPLE_HTML = "<html><body>simple html</body></html>";
 
     /**
-     * This is the minimum number of milliseconds to wait for scrolling to
-     * start. If no scrolling has started before this timeout then it is
-     * assumed that no scrolling will happen.
+     * This is the minimum number of milliseconds to wait for scrolling to start. If no scrolling
+     * has started before this timeout then it is assumed that no scrolling will happen.
      */
     private static final long MIN_SCROLL_WAIT_MS = 1000;
 
     /**
-     * This is the minimum number of milliseconds to wait for findAll to
-     * find all the matches. If matches are not found, the Listener would
-     * call findAll again until it times out.
+     * This is the minimum number of milliseconds to wait for findAll to find all the matches. If
+     * matches are not found, the Listener would call findAll again until it times out.
      */
     private static final long MIN_FIND_WAIT_MS = 3000;
 
     /**
-     * Once scrolling has started, this is the interval that scrolling
-     * is checked to see if there is a change. If no scrolling change
-     * has happened in the given time then it is assumed that scrolling
-     * has stopped.
+     * Once scrolling has started, this is the interval that scrolling is checked to see if there is
+     * a change. If no scrolling change has happened in the given time then it is assumed that
+     * scrolling has stopped.
      */
     private static final long SCROLL_WAIT_INTERVAL_MS = 200;
 
+    @Rule
+    public ActivityScenarioRule mActivityScenarioRule =
+            new ActivityScenarioRule(WebViewCtsActivity.class);
+
+    private ActivityScenario mScenario;
+    private WebViewCtsActivity mActivity;
     private WebView mWebView;
     private CtsTestServer mWebServer;
     private WebViewOnUiThread mOnUiThread;
     private WebIconDatabase mIconDb;
 
-    public WebViewTest() {
-        super("com.android.cts.webkit", WebViewCtsActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        final WebViewCtsActivity activity = getActivity();
-        mWebView = activity.getWebView();
+    @Before
+    public void setUp() throws Exception {
+        mScenario = mActivityScenarioRule.getScenario();
+        mScenario.onActivity(
+                activity -> {
+                    mActivity = (WebViewCtsActivity) activity;
+                    mWebView = mActivity.getWebView();
+                });
         if (mWebView != null) {
             new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
                 @Override
-                    protected boolean check() {
-                        return activity.hasWindowFocus();
+                protected boolean check() {
+                    return mActivity.hasWindowFocus();
                 }
             }.run();
-            File f = activity.getFileStreamPath("snapshot");
+            File f = mActivity.getFileStreamPath("snapshot");
             if (f.exists()) {
                 f.delete();
             }
 
             mOnUiThread = new WebViewOnUiThread(mWebView);
         }
+        Assume.assumeTrue("WebView is not available", NullWebViewUtils.isWebViewAvailable());
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void cleanup() throws Exception {
         if (mOnUiThread != null) {
             mOnUiThread.cleanUp();
         }
@@ -196,171 +201,176 @@
             mIconDb.close();
             mIconDb = null;
         }
-        super.tearDown();
+        mActivity = null;
     }
 
     private void startWebServer(boolean secure) throws Exception {
         assertNull(mWebServer);
-        mWebServer = new CtsTestServer(getActivity(), secure);
+        mWebServer = new CtsTestServer(mActivity, secure);
     }
 
     private void stopWebServer() throws Exception {
         assertNotNull(mWebServer);
         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
-        ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy)
-                .permitNetwork()
-                .build();
+        ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy).permitNetwork().build();
         StrictMode.setThreadPolicy(tmpPolicy);
         mWebServer.shutdown();
         mWebServer = null;
         StrictMode.setThreadPolicy(oldPolicy);
     }
 
-    @UiThreadTest
+    @Test
     public void testConstructor() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        WebView webView = new WebView(getActivity());
-        webView.destroy();
-        webView = new WebView(getActivity(), null);
-        webView.destroy();
-        webView = new WebView(getActivity(), null, 0);
-        webView.destroy();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    WebView webView = new WebView(mActivity);
+                    webView.destroy();
+                    webView = new WebView(mActivity, null);
+                    webView.destroy();
+                    webView = new WebView(mActivity, null, 0);
+                    webView.destroy();
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testCreatingWebViewWithDeviceEncrpytionFails() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        Context deviceEncryptedContext = getActivity().createDeviceProtectedStorageContext();
-        try {
-            new WebView(deviceEncryptedContext);
-            fail("WebView should have thrown exception when creating with a device " +
-                "protected storage context");
-        } catch (IllegalArgumentException e) {}
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    Context deviceEncryptedContext =
+                            mActivity.createDeviceProtectedStorageContext();
+                    try {
+                        new WebView(deviceEncryptedContext);
+                        fail(
+                                "WebView should have thrown exception when creating with a device "
+                                        + "protected storage context");
+                    } catch (IllegalArgumentException e) {
+                    }
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testCreatingWebViewWithMultipleEncryptionContext() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    // Credential encryption is the default. Create one here for the sake of
+                    // clarity.
+                    Context credentialEncryptedContext =
+                            mActivity.createCredentialProtectedStorageContext();
+                    Context deviceEncryptedContext =
+                            mActivity.createDeviceProtectedStorageContext();
 
-        // Credential encrpytion is the default. Create one here for the sake of clarity.
-        Context credentialEncryptedContext = getActivity().createCredentialProtectedStorageContext();
-        Context deviceEncryptedContext = getActivity().createDeviceProtectedStorageContext();
+                    // No exception should be thrown with credential encryption context.
+                    WebView webView = new WebView(credentialEncryptedContext);
+                    webView.destroy();
 
-        // No exception should be thrown with credential encryption context.
-        WebView webView = new WebView(credentialEncryptedContext);
-        webView.destroy();
-
-        try {
-            new WebView(deviceEncryptedContext);
-            fail("WebView should have thrown exception when creating with a device " +
-                "protected storage context");
-        } catch (IllegalArgumentException e) {}
+                    try {
+                        new WebView(deviceEncryptedContext);
+                        fail(
+                                "WebView should have thrown exception when creating with a device "
+                                        + "protected storage context");
+                    } catch (IllegalArgumentException e) {
+                    }
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testCreatingWebViewCreatesCookieSyncManager() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        WebView webView = new WebView(getActivity());
-        assertNotNull(CookieSyncManager.getInstance());
-        webView.destroy();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    WebView webView = new WebView(mActivity);
+                    assertNotNull(CookieSyncManager.getInstance());
+                    webView.destroy();
+                });
     }
 
+    @Test
     // Static methods should be safe to call on non-UI threads
     public void testFindAddress() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         /*
          * Info about USPS
          * http://en.wikipedia.org/wiki/Postal_address#United_States
          * http://www.usps.com/
          */
         // full address
-        assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826",
+        assertEquals(
+                "455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826",
                 WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826"));
         // Zipcode is optional.
-        assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA",
+        assertEquals(
+                "455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA",
                 WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA"));
         // not an address
         assertNull(WebView.findAddress("This is not an address: no town, no state, no zip."));
 
         // would be an address, except for numbers that are not ASCII
-        assertNull(WebView.findAddress(
-                "80\uD835\uDFEF \uD835\uDFEF\uD835\uDFEFth Avenue Sunnyvale, CA 94089"));
+        assertNull(
+                WebView.findAddress(
+                        "80\uD835\uDFEF \uD835\uDFEF\uD835\uDFEFth Avenue Sunnyvale, CA 94089"));
     }
 
-    @UiThreadTest
+    @Test
     public void testScrollBarOverlay() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    // These functions have no effect; just verify they don't crash
+                    mWebView.setHorizontalScrollbarOverlay(true);
+                    mWebView.setVerticalScrollbarOverlay(false);
 
-        // These functions have no effect; just verify they don't crash
-        mWebView.setHorizontalScrollbarOverlay(true);
-        mWebView.setVerticalScrollbarOverlay(false);
-
-        assertTrue(mWebView.overlayHorizontalScrollbar());
-        assertFalse(mWebView.overlayVerticalScrollbar());
+                    assertTrue(mWebView.overlayHorizontalScrollbar());
+                    assertFalse(mWebView.overlayVerticalScrollbar());
+                });
     }
 
+    @Test
     @Presubmit
-    @UiThreadTest
     public void testLoadUrl() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        assertNull(mWebView.getUrl());
-        assertNull(mWebView.getOriginalUrl());
-        assertEquals(INITIAL_PROGRESS, mWebView.getProgress());
-
         startWebServer(false);
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(100, mWebView.getProgress());
-        assertEquals(url, mWebView.getUrl());
-        assertEquals(url, mWebView.getOriginalUrl());
-        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
 
-        // verify that the request also includes X-Requested-With header
-        HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
-        Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
-        assertEquals(1, matchingHeaders.length);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    assertNull(mWebView.getUrl());
+                    assertNull(mWebView.getOriginalUrl());
+                    assertEquals(INITIAL_PROGRESS, mWebView.getProgress());
 
-        Header header = matchingHeaders[0];
-        assertEquals(mWebView.getContext().getApplicationInfo().packageName, header.getValue());
+                    String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url);
+                    assertEquals(100, mWebView.getProgress());
+                    assertEquals(url, mWebView.getUrl());
+                    assertEquals(url, mWebView.getOriginalUrl());
+                    assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
+
+                    // verify that the request also includes X-Requested-With header
+                    HttpRequest request =
+                            mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
+                    Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
+                    assertEquals(1, matchingHeaders.length);
+
+                    Header header = matchingHeaders[0];
+                    assertEquals(
+                            mWebView.getContext().getApplicationInfo().packageName,
+                            header.getValue());
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testPostUrlWithNonNetworkUrl() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final String nonNetworkUrl = "file:///android_asset/" + TestHtmlConstants.HELLO_WORLD_URL;
 
         mOnUiThread.postUrlAndWaitForCompletion(nonNetworkUrl, new byte[1]);
 
-        assertEquals("Non-network URL should have loaded", TestHtmlConstants.HELLO_WORLD_TITLE,
-                mWebView.getTitle());
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    assertEquals(
+                            "Non-network URL should have loaded",
+                            TestHtmlConstants.HELLO_WORLD_TITLE,
+                            mWebView.getTitle());
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testPostUrlWithNetworkUrl() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         startWebServer(false);
+
         final String networkUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         final String postDataString = "username=my_username&password=my_password";
         final byte[] postData = EncodingUtils.getBytes(postDataString, "BASE64");
@@ -368,83 +378,84 @@
         mOnUiThread.postUrlAndWaitForCompletion(networkUrl, postData);
 
         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
-        assertEquals("The last request should be POST", request.getRequestLine().getMethod(),
-                "POST");
+        assertEquals(
+                "The last request should be POST", request.getRequestLine().getMethod(), "POST");
 
-        assertTrue("The last request should have a request body",
+        assertTrue(
+                "The last request should have a request body",
                 request instanceof HttpEntityEnclosingRequest);
         HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
         String entityString = EntityUtils.toString(entity);
         assertEquals(entityString, postDataString);
     }
 
-    @UiThreadTest
+    @Test
     public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        Uri.Builder uriBuilder = new Uri.Builder().scheme(
-                ContentResolver.SCHEME_CONTENT).authority(MockContentProvider.AUTHORITY);
-        uriBuilder.appendPath("foo.html").appendQueryParameter("param","bar");
-        String url = uriBuilder.build().toString();
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        // verify the parameter is not stripped.
-        Uri uri = Uri.parse(mWebView.getTitle());
-        assertEquals("bar", uri.getQueryParameter("param"));
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    Uri.Builder uriBuilder =
+                            new Uri.Builder()
+                                    .scheme(ContentResolver.SCHEME_CONTENT)
+                                    .authority(MockContentProvider.AUTHORITY);
+                    uriBuilder.appendPath("foo.html").appendQueryParameter("param", "bar");
+                    String url = uriBuilder.build().toString();
+                    mOnUiThread.loadUrlAndWaitForCompletion(url);
+                    // verify the parameter is not stripped.
+                    Uri uri = Uri.parse(mWebView.getTitle());
+                    assertEquals("bar", uri.getQueryParameter("param"));
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testAppInjectedXRequestedWithHeaderIsNotOverwritten() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         startWebServer(false);
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        HashMap<String, String> map = new HashMap<String, String>();
-        final String requester = "foo";
-        map.put(X_REQUESTED_WITH, requester);
-        mOnUiThread.loadUrlAndWaitForCompletion(url, map);
 
-        // verify that the request also includes X-Requested-With header
-        // but is not overwritten by the webview
-        HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
-        Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
-        assertEquals(1, matchingHeaders.length);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+                    HashMap<String, String> map = new HashMap<String, String>();
+                    final String requester = "foo";
+                    map.put(X_REQUESTED_WITH, requester);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url, map);
 
-        Header header = matchingHeaders[0];
-        assertEquals(requester, header.getValue());
+                    // verify that the request also includes X-Requested-With header
+                    // but is not overwritten by the webview
+                    HttpRequest request =
+                            mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
+                    Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
+                    assertEquals(1, matchingHeaders.length);
+
+                    Header header = matchingHeaders[0];
+                    assertEquals(requester, header.getValue());
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testAppCanInjectHeadersViaImmutableMap() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         startWebServer(false);
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        HashMap<String, String> map = new HashMap<String, String>();
-        final String requester = "foo";
-        map.put(X_REQUESTED_WITH, requester);
-        mOnUiThread.loadUrlAndWaitForCompletion(url, Collections.unmodifiableMap(map));
 
-        // verify that the request also includes X-Requested-With header
-        // but is not overwritten by the webview
-        HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
-        Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
-        assertEquals(1, matchingHeaders.length);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+                    HashMap<String, String> map = new HashMap<String, String>();
+                    final String requester = "foo";
+                    map.put(X_REQUESTED_WITH, requester);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url, Collections.unmodifiableMap(map));
 
-        Header header = matchingHeaders[0];
-        assertEquals(requester, header.getValue());
+                    // verify that the request also includes X-Requested-With header
+                    // but is not overwritten by the webview
+                    HttpRequest request =
+                            mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
+                    Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
+                    assertEquals(1, matchingHeaders.length);
+
+                    Header header = matchingHeaders[0];
+                    assertEquals(requester, header.getValue());
+                });
     }
 
+    @Test
     public void testCanInjectHeaders() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         final String X_FOO = "X-foo";
         final String X_FOO_VALUE = "test";
 
@@ -458,7 +469,7 @@
         mOnUiThread.loadUrlAndWaitForCompletion(url, map);
 
         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
-        for (Map.Entry<String,String> value : map.entrySet()) {
+        for (Map.Entry<String, String> value : map.entrySet()) {
             String header = value.getKey();
             Header[] matchingHeaders = request.getHeaders(header);
             assertEquals("header " + header + " not found", 1, matchingHeaders.length);
@@ -466,49 +477,47 @@
         }
     }
 
+    @Test
     @SuppressWarnings("deprecation")
-    @UiThreadTest
     public void testGetVisibleTitleHeight() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         startWebServer(false);
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(0, mWebView.getVisibleTitleHeight());
+
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url);
+                    assertEquals(0, mWebView.getVisibleTitleHeight());
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testGetOriginalUrl() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         startWebServer(false);
-        final String finalUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        final String redirectUrl =
-                mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
 
-        assertNull(mWebView.getUrl());
-        assertNull(mWebView.getOriginalUrl());
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    final String finalUrl =
+                            mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+                    final String redirectUrl =
+                            mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
 
-        // By default, WebView sends an intent to ask the system to
-        // handle loading a new URL. We set a WebViewClient as
-        // WebViewClient.shouldOverrideUrlLoading() returns false, so
-        // the WebView will load the new URL.
-        mWebView.setWebViewClient(new WaitForLoadedClient(mOnUiThread));
-        mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl);
+                    assertNull(mWebView.getUrl());
+                    assertNull(mWebView.getOriginalUrl());
 
-        assertEquals(finalUrl, mWebView.getUrl());
-        assertEquals(redirectUrl, mWebView.getOriginalUrl());
+                    // By default, WebView sends an intent to ask the system to
+                    // handle loading a new URL. We set a WebViewClient as
+                    // WebViewClient.shouldOverrideUrlLoading() returns false, so
+                    // the WebView will load the new URL.
+                    mWebView.setWebViewClient(new WaitForLoadedClient(mOnUiThread));
+                    mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl);
+
+                    assertEquals(finalUrl, mWebView.getUrl());
+                    assertEquals(redirectUrl, mWebView.getOriginalUrl());
+                });
     }
 
+    @Test
     public void testStopLoading() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         assertEquals(INITIAL_PROGRESS, mOnUiThread.getProgress());
 
         startWebServer(false);
@@ -522,6 +531,7 @@
                 mPageLoaded = true;
                 notify();
             }
+
             public synchronized boolean getPageLoaded() {
                 return mPageLoaded;
             }
@@ -542,61 +552,58 @@
         assertFalse(jsInterface.getPageLoaded());
     }
 
-    @UiThreadTest
+    @Test
     public void testGoBackAndForward() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        assertGoBackOrForwardBySteps(false, -1);
-        assertGoBackOrForwardBySteps(false, 1);
-
         startWebServer(false);
-        String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
-        String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        mOnUiThread.loadUrlAndWaitForCompletion(url1);
-        pollingCheckWebBackForwardList(url1, 0, 1);
-        assertGoBackOrForwardBySteps(false, -1);
-        assertGoBackOrForwardBySteps(false, 1);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    assertGoBackOrForwardBySteps(false, -1);
+                    assertGoBackOrForwardBySteps(false, 1);
 
-        mOnUiThread.loadUrlAndWaitForCompletion(url2);
-        pollingCheckWebBackForwardList(url2, 1, 2);
-        assertGoBackOrForwardBySteps(true, -1);
-        assertGoBackOrForwardBySteps(false, 1);
+                    String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+                    String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
+                    String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        mOnUiThread.loadUrlAndWaitForCompletion(url3);
-        pollingCheckWebBackForwardList(url3, 2, 3);
-        assertGoBackOrForwardBySteps(true, -2);
-        assertGoBackOrForwardBySteps(false, 1);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url1);
+                    pollingCheckWebBackForwardList(url1, 0, 1);
+                    assertGoBackOrForwardBySteps(false, -1);
+                    assertGoBackOrForwardBySteps(false, 1);
 
-        mWebView.goBack();
-        pollingCheckWebBackForwardList(url2, 1, 3);
-        assertGoBackOrForwardBySteps(true, -1);
-        assertGoBackOrForwardBySteps(true, 1);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url2);
+                    pollingCheckWebBackForwardList(url2, 1, 2);
+                    assertGoBackOrForwardBySteps(true, -1);
+                    assertGoBackOrForwardBySteps(false, 1);
 
-        mWebView.goForward();
-        pollingCheckWebBackForwardList(url3, 2, 3);
-        assertGoBackOrForwardBySteps(true, -2);
-        assertGoBackOrForwardBySteps(false, 1);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url3);
+                    pollingCheckWebBackForwardList(url3, 2, 3);
+                    assertGoBackOrForwardBySteps(true, -2);
+                    assertGoBackOrForwardBySteps(false, 1);
 
-        mWebView.goBackOrForward(-2);
-        pollingCheckWebBackForwardList(url1, 0, 3);
-        assertGoBackOrForwardBySteps(false, -1);
-        assertGoBackOrForwardBySteps(true, 2);
+                    mWebView.goBack();
+                    pollingCheckWebBackForwardList(url2, 1, 3);
+                    assertGoBackOrForwardBySteps(true, -1);
+                    assertGoBackOrForwardBySteps(true, 1);
 
-        mWebView.goBackOrForward(2);
-        pollingCheckWebBackForwardList(url3, 2, 3);
-        assertGoBackOrForwardBySteps(true, -2);
-        assertGoBackOrForwardBySteps(false, 1);
+                    mWebView.goForward();
+                    pollingCheckWebBackForwardList(url3, 2, 3);
+                    assertGoBackOrForwardBySteps(true, -2);
+                    assertGoBackOrForwardBySteps(false, 1);
+
+                    mWebView.goBackOrForward(-2);
+                    pollingCheckWebBackForwardList(url1, 0, 3);
+                    assertGoBackOrForwardBySteps(false, -1);
+                    assertGoBackOrForwardBySteps(true, 2);
+
+                    mWebView.goBackOrForward(2);
+                    pollingCheckWebBackForwardList(url3, 2, 3);
+                    assertGoBackOrForwardBySteps(true, -2);
+                    assertGoBackOrForwardBySteps(false, 1);
+                });
     }
 
+    @Test
     public void testAddJavascriptInterface() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
 
@@ -641,77 +648,66 @@
 
         // Verify that only methods annotated with @JavascriptInterface are exposed
         // on the JavaScript interface object.
-        assertEquals("\"function\"",
+        assertEquals(
+                "\"function\"",
                 mOnUiThread.evaluateJavascriptSync("typeof interface.provideResult"));
 
-        assertEquals("\"undefined\"",
+        assertEquals(
+                "\"undefined\"",
                 mOnUiThread.evaluateJavascriptSync("typeof interface.wasProvideResultCalled"));
 
-        assertEquals("\"undefined\"",
-                mOnUiThread.evaluateJavascriptSync("typeof interface.getClass"));
+        assertEquals(
+                "\"undefined\"", mOnUiThread.evaluateJavascriptSync("typeof interface.getClass"));
     }
 
+    @Test
     public void testAddJavascriptInterfaceNullObject() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
-        String setTitleToPropertyTypeHtml = "<html><head></head>" +
-                "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
+        String setTitleToPropertyTypeHtml =
+                "<html><head></head><body onload=\"document.title = typeof"
+                        + " window.injectedObject;\"></body></html>";
 
         // Test that the property is initially undefined.
-        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null);
         assertEquals("undefined", mOnUiThread.getTitle());
 
         // Test that adding a null object has no effect.
         mOnUiThread.addJavascriptInterface(null, "injectedObject");
-        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null);
         assertEquals("undefined", mOnUiThread.getTitle());
 
         // Test that adding an object gives an object type.
         final Object obj = new Object();
         mOnUiThread.addJavascriptInterface(obj, "injectedObject");
-        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null);
         assertEquals("object", mOnUiThread.getTitle());
 
         // Test that trying to replace with a null object has no effect.
         mOnUiThread.addJavascriptInterface(null, "injectedObject");
-        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null);
         assertEquals("object", mOnUiThread.getTitle());
     }
 
+    @Test
     public void testRemoveJavascriptInterface() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
-        String setTitleToPropertyTypeHtml = "<html><head></head>" +
-                "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
+        String setTitleToPropertyTypeHtml =
+                "<html><head></head><body onload=\"document.title = typeof"
+                        + " window.injectedObject;\"></body></html>";
 
         // Test that adding an object gives an object type.
         mOnUiThread.addJavascriptInterface(new Object(), "injectedObject");
-        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null);
         assertEquals("object", mOnUiThread.getTitle());
 
         // Test that reloading the page after removing the object leaves the property undefined.
         mOnUiThread.removeJavascriptInterface("injectedObject");
-        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null);
         assertEquals("undefined", mOnUiThread.getTitle());
     }
 
+    @Test
     public void testUseRemovedJavascriptInterface() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         class RemovedObject {
             @Override
             @JavascriptInterface
@@ -735,6 +731,7 @@
                 mIsResultAvailable = true;
                 notify();
             }
+
             public synchronized String getResult() {
                 while (!mIsResultAvailable) {
                     try {
@@ -752,30 +749,31 @@
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.addJavascriptInterface(new RemovedObject(), "removedObject");
         mOnUiThread.addJavascriptInterface(resultObject, "resultObject");
-        mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
-                "<body onload=\"window.removedObject.remove();" +
-                "resultObject.setResult(removedObject.toString());\"></body></html>",
-                "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><head></head>"
+                        + "<body onload=\"window.removedObject.remove();"
+                        + "resultObject.setResult(removedObject.toString());\"></body></html>",
+                "text/html",
+                null);
         assertEquals("removedObject", resultObject.getResult());
     }
 
+    @Test
     public void testAddJavascriptInterfaceExceptions() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         WebSettings settings = mOnUiThread.getSettings();
         settings.setJavaScriptEnabled(true);
         settings.setJavaScriptCanOpenWindowsAutomatically(true);
 
-        final AtomicBoolean mJsInterfaceWasCalled = new AtomicBoolean(false) {
-            @JavascriptInterface
-            public synchronized void call() {
-                set(true);
-                // The main purpose of this test is to ensure an exception here does not
-                // crash the implementation.
-                throw new RuntimeException("Javascript Interface exception");
-            }
-        };
+        final AtomicBoolean mJsInterfaceWasCalled =
+                new AtomicBoolean(false) {
+                    @JavascriptInterface
+                    public synchronized void call() {
+                        set(true);
+                        // The main purpose of this test is to ensure an exception here does not
+                        // crash the implementation.
+                        throw new RuntimeException("Javascript Interface exception");
+                    }
+                };
 
         mOnUiThread.addJavascriptInterface(mJsInterfaceWasCalled, "interface");
 
@@ -783,16 +781,15 @@
 
         assertFalse(mJsInterfaceWasCalled.get());
 
-        assertEquals("\"pass\"", mOnUiThread.evaluateJavascriptSync(
-                "try {interface.call(); 'fail'; } catch (exception) { 'pass'; } "));
+        assertEquals(
+                "\"pass\"",
+                mOnUiThread.evaluateJavascriptSync(
+                        "try {interface.call(); 'fail'; } catch (exception) { 'pass'; } "));
         assertTrue(mJsInterfaceWasCalled.get());
     }
 
+    @Test
     public void testJavascriptInterfaceCustomPropertiesClearedOnReload() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
 
         mOnUiThread.addJavascriptInterface(new Object(), "interface");
@@ -807,12 +804,9 @@
         assertEquals("false", mOnUiThread.evaluateJavascriptSync("'custom_property' in interface"));
     }
 
+    @Test
     @FlakyTest(bugId = 171702662)
     public void testJavascriptInterfaceForClientPopup() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
         mOnUiThread.getSettings().setSupportMultipleWindows(true);
@@ -831,31 +825,40 @@
         childOnUiThread.addJavascriptInterface(obj, "interface");
 
         final SettableFuture<Void> onCreateWindowFuture = SettableFuture.create();
-        mOnUiThread.setWebChromeClient(new WebViewSyncLoader.WaitForProgressClient(mOnUiThread) {
-            @Override
-            public boolean onCreateWindow(
-                WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
-                getActivity().addContentView(childWebView, new ViewGroup.LayoutParams(
-                            ViewGroup.LayoutParams.FILL_PARENT,
-                            ViewGroup.LayoutParams.WRAP_CONTENT));
-                WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
-                transport.setWebView(childWebView);
-                resultMsg.sendToTarget();
-                onCreateWindowFuture.set(null);
-                return true;
-            }
-        });
+        mOnUiThread.setWebChromeClient(
+                new WebViewSyncLoader.WaitForProgressClient(mOnUiThread) {
+                    @Override
+                    public boolean onCreateWindow(
+                            WebView view,
+                            boolean isDialog,
+                            boolean isUserGesture,
+                            Message resultMsg) {
+                        mActivity.addContentView(
+                                childWebView,
+                                new ViewGroup.LayoutParams(
+                                        ViewGroup.LayoutParams.FILL_PARENT,
+                                        ViewGroup.LayoutParams.WRAP_CONTENT));
+                        WebView.WebViewTransport transport =
+                                (WebView.WebViewTransport) resultMsg.obj;
+                        transport.setWebView(childWebView);
+                        resultMsg.sendToTarget();
+                        onCreateWindowFuture.set(null);
+                        return true;
+                    }
+                });
 
         startWebServer(false);
-        mOnUiThread.loadUrlAndWaitForCompletion(mWebServer.
-                getAssetUrl(TestHtmlConstants.POPUP_URL));
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.POPUP_URL));
         WebkitUtils.waitForFuture(onCreateWindowFuture);
 
         childOnUiThread.loadUrlAndWaitForCompletion("about:blank");
 
         assertEquals("true", childOnUiThread.evaluateJavascriptSync("'interface' in window"));
 
-        assertEquals("The injected object should be functional", "42",
+        assertEquals(
+                "The injected object should be functional",
+                "42",
                 childOnUiThread.evaluateJavascriptSync("interface.test()"));
     }
 
@@ -871,17 +874,17 @@
         }
     }
 
-    private Picture waitForPictureToHaveColor(int color,
-            final TestPictureListener listener) throws Throwable {
+    private Picture waitForPictureToHaveColor(int color, final TestPictureListener listener)
+            throws Throwable {
         final int MAX_ON_NEW_PICTURE_ITERATIONS = 5;
         final AtomicReference<Picture> pictureRef = new AtomicReference<Picture>();
         for (int i = 0; i < MAX_ON_NEW_PICTURE_ITERATIONS; i++) {
             final int oldCallCount = listener.callCount;
-            WebkitUtils.onMainThreadSync(() -> {
-                pictureRef.set(mWebView.capturePicture());
-            });
-            if (isPictureFilledWithColor(pictureRef.get(), color))
-                break;
+            WebkitUtils.onMainThreadSync(
+                    () -> {
+                        pictureRef.set(mWebView.capturePicture());
+                    });
+            if (isPictureFilledWithColor(pictureRef.get(), color)) break;
             new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
                 @Override
                 protected boolean check() {
@@ -892,10 +895,8 @@
         return pictureRef.get();
     }
 
+    @Test
     public void testCapturePicture() throws Exception, Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final TestPictureListener listener = new TestPictureListener();
 
         startWebServer(false);
@@ -906,20 +907,20 @@
         // The default background color is white.
         Picture oldPicture = waitForPictureToHaveColor(Color.WHITE, listener);
 
-        WebkitUtils.onMainThread(() -> {
-            mWebView.setBackgroundColor(Color.CYAN);
-        });
+        WebkitUtils.onMainThread(
+                () -> {
+                    mWebView.setBackgroundColor(Color.CYAN);
+                });
         mOnUiThread.reloadAndWaitForCompletion();
         waitForPictureToHaveColor(Color.CYAN, listener);
 
-        assertTrue("The content of the previously captured picture should not update automatically",
+        assertTrue(
+                "The content of the previously captured picture should not update automatically",
                 isPictureFilledWithColor(oldPicture, Color.WHITE));
     }
 
+    @Test
     public void testSetPictureListener() throws Exception, Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final class MyPictureListener implements PictureListener {
             public int callCount;
             public WebView webView;
@@ -961,149 +962,145 @@
         }.run();
     }
 
-    @UiThreadTest
+    @Test
     public void testAccessHttpAuthUsernamePassword() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        try {
-            WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    try {
+                        WebViewDatabase.getInstance(mActivity).clearHttpAuthUsernamePassword();
 
-            String host = "http://localhost:8080";
-            String realm = "testrealm";
-            String userName = "user";
-            String password = "password";
+                        String host = "http://localhost:8080";
+                        String realm = "testrealm";
+                        String userName = "user";
+                        String password = "password";
 
-            String[] result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNull(result);
+                        String[] result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNull(result);
 
-            mWebView.setHttpAuthUsernamePassword(host, realm, userName, password);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(userName, result[0]);
-            assertEquals(password, result[1]);
+                        mWebView.setHttpAuthUsernamePassword(host, realm, userName, password);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(userName, result[0]);
+                        assertEquals(password, result[1]);
 
-            String newPassword = "newpassword";
-            mWebView.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(userName, result[0]);
-            assertEquals(newPassword, result[1]);
+                        String newPassword = "newpassword";
+                        mWebView.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(userName, result[0]);
+                        assertEquals(newPassword, result[1]);
 
-            String newUserName = "newuser";
-            mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(newUserName, result[0]);
-            assertEquals(newPassword, result[1]);
+                        String newUserName = "newuser";
+                        mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(newUserName, result[0]);
+                        assertEquals(newPassword, result[1]);
 
-            // the user is set to null, can not change any thing in the future
-            mWebView.setHttpAuthUsernamePassword(host, realm, null, password);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertNull(result[0]);
-            assertEquals(password, result[1]);
+                        // the user is set to null, can not change any thing in the future
+                        mWebView.setHttpAuthUsernamePassword(host, realm, null, password);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertNull(result[0]);
+                        assertEquals(password, result[1]);
 
-            mWebView.setHttpAuthUsernamePassword(host, realm, userName, null);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(userName, result[0]);
-            assertNull(result[1]);
+                        mWebView.setHttpAuthUsernamePassword(host, realm, userName, null);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(userName, result[0]);
+                        assertNull(result[1]);
 
-            mWebView.setHttpAuthUsernamePassword(host, realm, null, null);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertNull(result[0]);
-            assertNull(result[1]);
+                        mWebView.setHttpAuthUsernamePassword(host, realm, null, null);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertNull(result[0]);
+                        assertNull(result[1]);
 
-            mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
-            result = mWebView.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(newUserName, result[0]);
-            assertEquals(newPassword, result[1]);
-        } finally {
-            WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword();
-        }
+                        mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
+                        result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(newUserName, result[0]);
+                        assertEquals(newPassword, result[1]);
+                    } finally {
+                        WebViewDatabase.getInstance(mActivity).clearHttpAuthUsernamePassword();
+                    }
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testWebViewDatabaseAccessHttpAuthUsernamePassword() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        WebViewDatabase webViewDb = WebViewDatabase.getInstance(getActivity());
-        try {
-            webViewDb.clearHttpAuthUsernamePassword();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    WebViewDatabase webViewDb = WebViewDatabase.getInstance(mActivity);
+                    try {
+                        webViewDb.clearHttpAuthUsernamePassword();
 
-            String host = "http://localhost:8080";
-            String realm = "testrealm";
-            String userName = "user";
-            String password = "password";
+                        String host = "http://localhost:8080";
+                        String realm = "testrealm";
+                        String userName = "user";
+                        String password = "password";
 
-            String[] result =
-                    mWebView.getHttpAuthUsernamePassword(host,
-                            realm);
-            assertNull(result);
+                        String[] result = mWebView.getHttpAuthUsernamePassword(host, realm);
+                        assertNull(result);
 
-            webViewDb.setHttpAuthUsernamePassword(host, realm, userName, password);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(userName, result[0]);
-            assertEquals(password, result[1]);
+                        webViewDb.setHttpAuthUsernamePassword(host, realm, userName, password);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(userName, result[0]);
+                        assertEquals(password, result[1]);
 
-            String newPassword = "newpassword";
-            webViewDb.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(userName, result[0]);
-            assertEquals(newPassword, result[1]);
+                        String newPassword = "newpassword";
+                        webViewDb.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(userName, result[0]);
+                        assertEquals(newPassword, result[1]);
 
-            String newUserName = "newuser";
-            webViewDb.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(newUserName, result[0]);
-            assertEquals(newPassword, result[1]);
+                        String newUserName = "newuser";
+                        webViewDb.setHttpAuthUsernamePassword(
+                                host, realm, newUserName, newPassword);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(newUserName, result[0]);
+                        assertEquals(newPassword, result[1]);
 
-            // the user is set to null, can not change any thing in the future
-            webViewDb.setHttpAuthUsernamePassword(host, realm, null, password);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertNull(result[0]);
-            assertEquals(password, result[1]);
+                        // the user is set to null, can not change any thing in the future
+                        webViewDb.setHttpAuthUsernamePassword(host, realm, null, password);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertNull(result[0]);
+                        assertEquals(password, result[1]);
 
-            webViewDb.setHttpAuthUsernamePassword(host, realm, userName, null);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(userName, result[0]);
-            assertNull(result[1]);
+                        webViewDb.setHttpAuthUsernamePassword(host, realm, userName, null);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(userName, result[0]);
+                        assertNull(result[1]);
 
-            webViewDb.setHttpAuthUsernamePassword(host, realm, null, null);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertNull(result[0]);
-            assertNull(result[1]);
+                        webViewDb.setHttpAuthUsernamePassword(host, realm, null, null);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertNull(result[0]);
+                        assertNull(result[1]);
 
-            webViewDb.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
-            result = webViewDb.getHttpAuthUsernamePassword(host, realm);
-            assertNotNull(result);
-            assertEquals(newUserName, result[0]);
-            assertEquals(newPassword, result[1]);
-        } finally {
-            webViewDb.clearHttpAuthUsernamePassword();
-        }
+                        webViewDb.setHttpAuthUsernamePassword(
+                                host, realm, newUserName, newPassword);
+                        result = webViewDb.getHttpAuthUsernamePassword(host, realm);
+                        assertNotNull(result);
+                        assertEquals(newUserName, result[0]);
+                        assertEquals(newPassword, result[1]);
+                    } finally {
+                        webViewDb.clearHttpAuthUsernamePassword();
+                    }
+                });
     }
 
+    @Test
     public void testLoadData() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final String firstTitle = "Hello, World!";
         final String HTML_CONTENT =
-                "<html><head><title>" + firstTitle + "</title></head><body></body>" +
-                "</html>";
-        mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT,
-                "text/html", null);
+                "<html><head><title>" + firstTitle + "</title></head><body></body>" + "</html>";
+        mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT, "text/html", null);
         assertEquals(firstTitle, mOnUiThread.getTitle());
 
         startWebServer(false);
@@ -1111,20 +1108,25 @@
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         final String secondTitle = "Foo bar";
         mOnUiThread.loadDataAndWaitForCompletion(
-                "<html><head><title>" + secondTitle + "</title></head><body onload=\"" +
-                "document.title = " +
-                "document.getElementById('frame').contentWindow.location.href;" +
-                "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
-                "text/html", null);
-        assertEquals("Page title should not change, because it should be an error to access a "
-                + "cross-site frame's href.",
-                secondTitle, mOnUiThread.getTitle());
+                "<html><head><title>"
+                        + secondTitle
+                        + "</title></head><body onload=\""
+                        + "document.title = "
+                        + "document.getElementById('frame').contentWindow.location.href;"
+                        + "\"><iframe id=\"frame\" src=\""
+                        + crossOriginUrl
+                        + "\"></body></html>",
+                "text/html",
+                null);
+        assertEquals(
+                "Page title should not change, because it should be an error to access a "
+                        + "cross-site frame's href.",
+                secondTitle,
+                mOnUiThread.getTitle());
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_resolvesRelativeToBaseUrl() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         assertNull(mOnUiThread.getUrl());
         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
 
@@ -1135,103 +1137,106 @@
         startWebServer(false);
         final String baseUrl = mWebServer.getAssetUrl("foo.html");
         mWebServer.resetRequestState();
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                baseUrl,
                 HTML_HEADER + "<body><img src=\"" + imgUrl + "\"/></body></html>",
-                "text/html", "UTF-8", null);
-        assertTrue("The resource request should make it to the server",
+                "text/html",
+                "UTF-8",
+                null);
+        assertTrue(
+                "The resource request should make it to the server",
                 mWebServer.wasResourceRequested(imgUrl));
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_historyUrl() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final String baseUrl = "http://www.baseurl.com/";
         final String historyUrl = "http://www.example.com/";
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
-                SIMPLE_HTML,
-                "text/html", "UTF-8", historyUrl);
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                baseUrl, SIMPLE_HTML, "text/html", "UTF-8", historyUrl);
         assertEquals(historyUrl, mOnUiThread.getUrl());
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_nullHistoryUrlShowsAsAboutBlank() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         // Check that reported URL is "about:blank" when supplied history URL
         // is null.
         final String baseUrl = "http://www.baseurl.com/";
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
-                SIMPLE_HTML,
-                "text/html", "UTF-8", null);
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                baseUrl, SIMPLE_HTML, "text/html", "UTF-8", null);
         assertEquals("about:blank", mOnUiThread.getUrl());
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_javascriptCanAccessOrigin() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         // Test that JavaScript can access content from the same origin as the base URL.
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         startWebServer(false);
         final String baseUrl = mWebServer.getAssetUrl("foo.html");
         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
-                HTML_HEADER + "<body onload=\"" +
-                "document.title = document.getElementById('frame').contentWindow.location.href;" +
-                "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
-                "text/html", "UTF-8", null);
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                baseUrl,
+                HTML_HEADER
+                        + "<body onload=\"document.title ="
+                        + " document.getElementById('frame').contentWindow.location.href;\"><iframe"
+                        + " id=\"frame\" src=\""
+                        + crossOriginUrl
+                        + "\"></body></html>",
+                "text/html",
+                "UTF-8",
+                null);
         assertEquals(crossOriginUrl, mOnUiThread.getTitle());
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_dataBaseUrlIgnoresHistoryUrl() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         // Check that when the base URL uses the 'data' scheme, a 'data' scheme URL is used and the
         // history URL is ignored.
         final String baseUrl = "data:foo";
         final String historyUrl = "http://www.example.com/";
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
-                SIMPLE_HTML,
-                "text/html", "UTF-8", historyUrl);
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                baseUrl, SIMPLE_HTML, "text/html", "UTF-8", historyUrl);
 
         final String currentUrl = mOnUiThread.getUrl();
-        assertEquals("Current URL (" + currentUrl + ") should be a data URI", 0,
+        assertEquals(
+                "Current URL (" + currentUrl + ") should be a data URI",
+                0,
                 mOnUiThread.getUrl().indexOf("data:text/html"));
-        assertThat("Current URL (" + currentUrl + ") should contain the simple HTML we loaded",
-                mOnUiThread.getUrl().indexOf("simple html"), greaterThan(0));
+        assertThat(
+                "Current URL (" + currentUrl + ") should contain the simple HTML we loaded",
+                mOnUiThread.getUrl().indexOf("simple html"),
+                greaterThan(0));
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_unencodedContentHttpBaseUrl() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         // Check that when a non-data: base URL is used, we treat the String to load as
         // a raw string and just dump it into the WebView, i.e. not decoding any URL entities.
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("http://www.foo.com",
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                "http://www.foo.com",
                 HTML_HEADER + "<title>Hello World%21</title><body>bar</body></html>",
-                "text/html", "UTF-8", null);
+                "text/html",
+                "UTF-8",
+                null);
         assertEquals("Hello World%21", mOnUiThread.getTitle());
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_urlEncodedContentDataBaseUrl() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         // Check that when a data: base URL is used, we treat the String to load as a data: URL
         // and run load steps such as decoding URL entities (i.e., contrary to the test case
         // above.)
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
-                HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null);
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                "data:foo",
+                HTML_HEADER + "<title>Hello World%21</title></html>",
+                "text/html",
+                "UTF-8",
+                null);
         assertEquals("Hello World!", mOnUiThread.getTitle());
     }
 
+    @Test
     public void testLoadDataWithBaseUrl_nullSafe() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null);
         assertEquals("about:blank", mOnUiThread.getUrl());
     }
@@ -1245,7 +1250,7 @@
     private String readTextFile(File file, Charset encoding)
             throws FileNotFoundException, IOException {
         FileInputStream stream = new FileInputStream(file);
-        byte[] bytes = new byte[(int)file.length()];
+        byte[] bytes = new byte[(int) file.length()];
         stream.read(bytes);
         stream.close();
         return new String(bytes, encoding);
@@ -1254,26 +1259,24 @@
     private void doSaveWebArchive(String baseName, boolean autoName, final String expectName)
             throws Throwable {
         final Semaphore saving = new Semaphore(0);
-        ValueCallback<String> callback = new ValueCallback<String>() {
-            @Override
-            public void onReceiveValue(String savedName) {
-                assertEquals(expectName, savedName);
-                saving.release();
-            }
-        };
+        ValueCallback<String> callback =
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String savedName) {
+                        assertEquals(expectName, savedName);
+                        saving.release();
+                    }
+                };
 
         mOnUiThread.saveWebArchive(baseName, autoName, callback);
         assertTrue(saving.tryAcquire(WebkitUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Test
     public void testSaveWebArchive() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         final String testPage = "testSaveWebArchive test page";
 
-        File dir = getActivity().getFilesDir();
+        File dir = mActivity.getFilesDir();
         String dirStr = dir.toString();
 
         File test = new File(dir, "test.mht");
@@ -1324,16 +1327,14 @@
         }
     }
 
-    private static class WaitForFindResultsListener
-            implements WebView.FindListener {
+    private static class WaitForFindResultsListener implements WebView.FindListener {
         private final SettableFuture<Integer> mFuture;
         private final WebView mWebView;
         private final int mMatchesWanted;
         private final String mStringWanted;
         private final boolean mRetry;
 
-        public WaitForFindResultsListener(
-                WebView wv, String wanted, int matches, boolean retry) {
+        WaitForFindResultsListener(WebView wv, String wanted, int matches, boolean retry) {
             mFuture = SettableFuture.create();
             mWebView = wv;
             mMatchesWanted = matches;
@@ -1346,62 +1347,66 @@
         }
 
         @Override
-        public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
-                boolean isDoneCounting) {
+        public void onFindResultReceived(
+                int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
             try {
-                assertEquals("WebView.FindListener callbacks should occur on the UI thread",
-                        Looper.myLooper(), Looper.getMainLooper());
+                assertEquals(
+                        "WebView.FindListener callbacks should occur on the UI thread",
+                        Looper.myLooper(),
+                        Looper.getMainLooper());
             } catch (Throwable t) {
                 mFuture.setException(t);
             }
             if (isDoneCounting) {
-                //If mRetry set to true and matches aren't equal, call findAll again
+                // If mRetry set to true and matches aren't equal, call findAll again
                 if (mRetry && numberOfMatches != mMatchesWanted) {
                     mWebView.findAll(mStringWanted);
-                }
-                else {
+                } else {
                     mFuture.set(numberOfMatches);
                 }
             }
         }
     }
 
-    public void testFindAll()  throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
+    @Test
+    public void testFindAll() throws Throwable {
         // Make the page scrollable, so we can detect the scrolling to make sure the
         // content fully loaded.
         mOnUiThread.setInitialScale(100);
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
         // create a paragraph high enough to take up the entire screen
-        String p = "<p style=\"height:" + dimension + "px;\">" +
-                "Find all instances of find on the page and highlight them.</p>";
+        String p =
+                "<p style=\"height:"
+                        + dimension
+                        + "px;\">"
+                        + "Find all instances of find on the page and highlight them.</p>";
 
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "</body></html>", "text/html", null);
 
         WaitForFindResultsListener l = new WaitForFindResultsListener(mWebView, "find", 2, true);
         mOnUiThread.setFindListener(l);
         mOnUiThread.findAll("find");
-        assertEquals(2, (int)WebkitUtils.waitForFuture(l.future()));
+        assertEquals(2, (int) WebkitUtils.waitForFuture(l.future()));
     }
 
+    @Test
     public void testFindNext() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         // Reset the scaling so that finding the next "all" text will require scrolling.
         mOnUiThread.setInitialScale(100);
 
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
         // create a paragraph high enough to take up the entire screen
-        String p = "<p style=\"height:" + dimension + "px;\">" +
-                "Find all instances of a word on the page and highlight them.</p>";
+        String p =
+                "<p style=\"height:"
+                        + dimension
+                        + "px;\">"
+                        + "Find all instances of a word on the page and highlight them.</p>";
 
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + p + "</body></html>", "text/html", null);
         WaitForFindResultsListener l = new WaitForFindResultsListener(mWebView, "all", 2, true);
         mOnUiThread.setFindListener(l);
 
@@ -1438,7 +1443,7 @@
 
         // clear the result
         mOnUiThread.clearMatches();
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // can not scroll any more
         mOnUiThread.findNext(false);
@@ -1450,20 +1455,21 @@
         assertEquals(mOnUiThread.getScrollY(), previousScrollY);
     }
 
+    @Test
     public void testDocumentHasImages() throws Exception, Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final class DocumentHasImageCheckHandler extends Handler {
             private SettableFuture<Integer> mFuture;
+
             public DocumentHasImageCheckHandler(Looper looper) {
                 super(looper);
                 mFuture = SettableFuture.create();
             }
+
             @Override
             public void handleMessage(Message msg) {
                 mFuture.set(msg.arg1);
             }
+
             public Future<Integer> future() {
                 return mFuture;
             }
@@ -1474,17 +1480,20 @@
 
         // Create a handler on the UI thread.
         final DocumentHasImageCheckHandler handler =
-            new DocumentHasImageCheckHandler(mWebView.getHandler().getLooper());
+                new DocumentHasImageCheckHandler(mWebView.getHandler().getLooper());
 
-        WebkitUtils.onMainThreadSync(() -> {
-            mOnUiThread.loadDataAndWaitForCompletion("<html><body><img src=\""
-                    + imgUrl + "\"/></body></html>", "text/html", null);
-            Message response = new Message();
-            response.setTarget(handler);
-            assertFalse(handler.future().isDone());
-            mWebView.documentHasImages(response);
-        });
-        assertEquals(1, (int)WebkitUtils.waitForFuture(handler.future()));
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    mOnUiThread.loadDataAndWaitForCompletion(
+                            "<html><body><img src=\"" + imgUrl + "\"/></body></html>",
+                            "text/html",
+                            null);
+                    Message response = new Message();
+                    response.setTarget(handler);
+                    assertFalse(handler.future().isDone());
+                    mWebView.documentHasImages(response);
+                });
+        assertEquals(1, (int) WebkitUtils.waitForFuture(handler.future()));
     }
 
     private static void waitForFlingDone(WebViewOnUiThread webview) {
@@ -1518,23 +1527,24 @@
         new ScrollDiffPollingCheck(webview).run();
     }
 
+    @Test
     public void testPageScroll() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
-        String p = "<p style=\"height:" + dimension + "px;\">" +
-                "Scroll by half the size of the page.</p>";
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + p + "</body></html>", "text/html", null);
+        String p =
+                "<p style=\"height:"
+                        + dimension
+                        + "px;\">"
+                        + "Scroll by half the size of the page.</p>";
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + p + "</body></html>", "text/html", null);
 
         // Wait for UI thread to settle and receive page dimentions from renderer
         // such that we can invoke page down.
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
-                 return mOnUiThread.pageDown(false);
+                return mOnUiThread.pageDown(false);
             }
         }.run();
 
@@ -1565,7 +1575,7 @@
 
         // jump to the top
         assertTrue(mOnUiThread.pageUp(true));
-         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
+        new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
                 return topScrollY == mOnUiThread.getScrollY();
@@ -1573,17 +1583,15 @@
         }.run();
     }
 
+    @Test
     public void testGetContentHeight() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        mOnUiThread.loadDataAndWaitForCompletion(
-                "<html><body></body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body></body></html>", "text/html", null);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
-                return mOnUiThread.getScale() != 0 && mOnUiThread.getContentHeight() != 0
-                    && mOnUiThread.getHeight() != 0;
+                return mOnUiThread.getScale() != 0
+                        && mOnUiThread.getContentHeight() != 0
+                        && mOnUiThread.getHeight() != 0;
             }
         }.run();
 
@@ -1611,14 +1619,17 @@
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         final float scaleFactor = Math.max(1.0f, 1.0f / mOnUiThread.getScale());
         final int pageHeight =
-                (int)(Math.ceil(Math.max(metrics.widthPixels, metrics.heightPixels)
-                * scaleFactor));
+                (int)
+                        (Math.ceil(
+                                Math.max(metrics.widthPixels, metrics.heightPixels) * scaleFactor));
 
         // set the margin to 0
-        final String p = "<p style=\"height:" + pageHeight
-                + "px;margin:0px auto;\">Get the height of HTML content.</p>";
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "</body></html>", "text/html", null);
+        final String p =
+                "<p style=\"height:"
+                        + pageHeight
+                        + "px;margin:0px auto;\">Get the height of HTML content.</p>";
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "</body></html>", "text/html", null);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
@@ -1627,8 +1638,8 @@
         }.run();
 
         final int extraSpace = mOnUiThread.getContentHeight() - pageHeight;
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + p + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + p + "</body></html>", "text/html", null);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
@@ -1643,53 +1654,57 @@
         }.run();
     }
 
-    @UiThreadTest
+    @Test
     public void testPlatformNotifications() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        WebView.enablePlatformNotifications();
-        WebView.disablePlatformNotifications();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    WebView.enablePlatformNotifications();
+                    WebView.disablePlatformNotifications();
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testAccessPluginList() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        assertNotNull(WebView.getPluginList());
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    assertNotNull(WebView.getPluginList());
 
-        // can not find a way to install plugins
-        mWebView.refreshPlugins(false);
+                    // can not find a way to install plugins
+                    mWebView.refreshPlugins(false);
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testDestroy() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        // Create a new WebView, since we cannot call destroy() on a view in the hierarchy
-        WebView localWebView = new WebView(getActivity());
-        localWebView.destroy();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    // Create a new WebView, since we cannot call destroy() on a view in the
+                    // hierarchy
+                    WebView localWebView = new WebView(mActivity);
+                    localWebView.destroy();
+                });
     }
 
+    @Test
     public void testFlingScroll() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         final int dimension = 10 * Math.max(metrics.widthPixels, metrics.heightPixels);
-        String p = "<p style=\"height:" + dimension + "px;" +
-                "width:" + dimension + "px\">Test fling scroll.</p>";
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "</body></html>", "text/html", null);
+        String p =
+                "<p style=\"height:"
+                        + dimension
+                        + "px;"
+                        + "width:"
+                        + dimension
+                        + "px\">Test fling scroll.</p>";
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "</body></html>", "text/html", null);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
                 return mOnUiThread.getContentHeight() >= dimension;
             }
         }.run();
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         final int previousScrollX = mOnUiThread.getScrollX();
         final int previousScrollY = mOnUiThread.getScrollY();
@@ -1699,24 +1714,27 @@
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
-                return mOnUiThread.getScrollX() > previousScrollX &&
-                        mOnUiThread.getScrollY() > previousScrollY;
+                return mOnUiThread.getScrollX() > previousScrollX
+                        && mOnUiThread.getScrollY() > previousScrollY;
             }
         }.run();
     }
 
+    @Test
     public void testRequestFocusNodeHref() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         startWebServer(false);
+
         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
-        final String links = "<DL><p><DT><A HREF=\"" + url1
-                + "\">HTML_URL1</A><DT><A HREF=\"" + url2
-                + "\">HTML_URL2</A></DL><p>";
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + links + "</body></html>", "text/html", null);
-        getInstrumentation().waitForIdleSync();
+        final String links =
+                "<DL><p><DT><A HREF=\""
+                        + url1
+                        + "\">HTML_URL1</A><DT><A HREF=\""
+                        + url2
+                        + "\">HTML_URL2</A></DL><p>";
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + links + "</body></html>", "text/html", null);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
         final Message hrefMsg = new Message();
@@ -1724,7 +1742,7 @@
 
         // focus on first link
         handler.reset();
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
         mOnUiThread.requestFocusNodeHref(hrefMsg);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1749,7 +1767,7 @@
         handler.reset();
         final Message hrefMsg2 = new Message();
         hrefMsg2.setTarget(handler);
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
         mOnUiThread.requestFocusNodeHref(hrefMsg2);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1757,8 +1775,7 @@
                 boolean done = false;
                 final String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
                 if (handler.hasCalledHandleMessage()) {
-                    if (handler.mResultUrl != null &&
-                            handler.mResultUrl.equals(url2)) {
+                    if (handler.mResultUrl != null && handler.mResultUrl.equals(url2)) {
                         done = true;
                     } else {
                         handler.reset();
@@ -1775,10 +1792,8 @@
         mOnUiThread.requestFocusNodeHref(null);
     }
 
+    @Test
     public void testRequestImageRef() throws Exception, Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final class ImageLoaded {
             public SettableFuture<Void> mImageLoaded = SettableFuture.create();
 
@@ -1799,19 +1814,22 @@
         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL);
         mOnUiThread.loadDataAndWaitForCompletion(
                 "<html><head><title>Title</title><style type='text/css'>"
-                + "%23imgElement { -webkit-transform: translate3d(0,0,1); }"
-                + "%23imgElement.finish { -webkit-transform: translate3d(0,0,0);"
-                + " -webkit-transition-duration: 1ms; }</style>"
-                + "<script type='text/javascript'>function imgLoad() {"
-                + "imgElement = document.getElementById('imgElement');"
-                + "imgElement.addEventListener('webkitTransitionEnd',"
-                + "function(e) { imageLoaded.loaded(); });"
-                + "imgElement.className = 'finish';}</script>"
-                + "</head><body><img id='imgElement' src='" + imgUrl
-                + "' width='100%' height='100%' onLoad='imgLoad()'/>"
-                + "</body></html>", "text/html", null);
+                        + "%23imgElement { -webkit-transform: translate3d(0,0,1); }"
+                        + "%23imgElement.finish { -webkit-transform: translate3d(0,0,0);"
+                        + " -webkit-transition-duration: 1ms; }</style>"
+                        + "<script type='text/javascript'>function imgLoad() {"
+                        + "imgElement = document.getElementById('imgElement');"
+                        + "imgElement.addEventListener('webkitTransitionEnd',"
+                        + "function(e) { imageLoaded.loaded(); });"
+                        + "imgElement.className = 'finish';}</script>"
+                        + "</head><body><img id='imgElement' src='"
+                        + imgUrl
+                        + "' width='100%' height='100%' onLoad='imgLoad()'/>"
+                        + "</body></html>",
+                "text/html",
+                null);
         WebkitUtils.waitForFuture(imageLoaded.future());
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
         final Message msg = new Message();
@@ -1824,10 +1842,11 @@
         int middleY = location[1] + mOnUiThread.getWebView().getHeight() / 2;
 
         long time = SystemClock.uptimeMillis();
-        getInstrumentation().sendPointerSync(
-                MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN,
-                        middleX, middleY, 0));
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation()
+                .sendPointerSync(
+                        MotionEvent.obtain(
+                                time, time, MotionEvent.ACTION_DOWN, middleX, middleY, 0));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         mOnUiThread.requestImageRef(msg);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1849,23 +1868,22 @@
         assertEquals(imgUrl, handler.mResultUrl);
     }
 
-    @UiThreadTest
+    @Test
     public void testDebugDump() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        mWebView.debugDump();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    mWebView.debugDump();
+                });
     }
 
+    @Test
     public void testGetHitTestResult() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        final String anchor = "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1
-                + "\">normal anchor</a></p>";
+        final String anchor =
+                "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1 + "\">normal anchor</a></p>";
         final String blankAnchor = "<p><a href=\"\">blank anchor</a></p>";
-        final String form = "<p><form><input type=\"text\" name=\"Test\"><br>"
-                + "<input type=\"submit\" value=\"Submit\"></form></p>";
+        final String form =
+                "<p><form><input type=\"text\" name=\"Test\"><br>"
+                        + "<input type=\"submit\" value=\"Submit\"></form></p>";
         String phoneNo = "3106984000";
         final String tel = "<p><a href=\"tel:" + phoneNo + "\">Phone</a></p>";
         String email = "test@gmail.com";
@@ -1873,10 +1891,20 @@
         String location = "shanghai";
         final String geo = "<p><a href=\"geo:0,0?q=" + location + "\">Location</a></p>";
 
-        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("fake://home",
-                "<html><body>" + anchor + blankAnchor + form + tel + mailto +
-                geo + "</body></html>", "text/html", "UTF-8", null);
-        getInstrumentation().waitForIdleSync();
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                "fake://home",
+                "<html><body>"
+                        + anchor
+                        + blankAnchor
+                        + form
+                        + tel
+                        + mailto
+                        + geo
+                        + "</body></html>",
+                "text/html",
+                "UTF-8",
+                null);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // anchor
         moveFocusDown();
@@ -1921,16 +1949,13 @@
         assertEquals(location, hitTestResult.getExtra());
     }
 
+    @Test
     public void testSetInitialScale() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         final String p = "<p style=\"height:1000px;width:1000px\">Test setInitialScale.</p>";
-        final float defaultScale =
-            getActivity().getResources().getDisplayMetrics().density;
+        final float defaultScale = mActivity.getResources().getDisplayMetrics().density;
 
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "</body></html>", "text/html", null);
 
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1941,8 +1966,8 @@
 
         mOnUiThread.setInitialScale(0);
         // modify content to fool WebKit into re-loading
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "2" + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "2" + "</body></html>", "text/html", null);
 
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1952,8 +1977,8 @@
         }.run();
 
         mOnUiThread.setInitialScale(50);
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "3" + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "3" + "</body></html>", "text/html", null);
 
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1963,8 +1988,8 @@
         }.run();
 
         mOnUiThread.setInitialScale(0);
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "4" + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "4" + "</body></html>", "text/html", null);
 
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
@@ -1974,112 +1999,112 @@
         }.run();
     }
 
-    @UiThreadTest
+    @Test
     public void testClearHistory() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         startWebServer(false);
-        String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
-        String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        mOnUiThread.loadUrlAndWaitForCompletion(url1);
-        pollingCheckWebBackForwardList(url1, 0, 1);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+                    String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
+                    String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        mOnUiThread.loadUrlAndWaitForCompletion(url2);
-        pollingCheckWebBackForwardList(url2, 1, 2);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url1);
+                    pollingCheckWebBackForwardList(url1, 0, 1);
 
-        mOnUiThread.loadUrlAndWaitForCompletion(url3);
-        pollingCheckWebBackForwardList(url3, 2, 3);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url2);
+                    pollingCheckWebBackForwardList(url2, 1, 2);
 
-        mWebView.clearHistory();
+                    mOnUiThread.loadUrlAndWaitForCompletion(url3);
+                    pollingCheckWebBackForwardList(url3, 2, 3);
 
-        // only current URL is left after clearing
-        pollingCheckWebBackForwardList(url3, 0, 1);
+                    mWebView.clearHistory();
+
+                    // only current URL is left after clearing
+                    pollingCheckWebBackForwardList(url3, 0, 1);
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testSaveAndRestoreState() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        assertNull("Should return null when there's nothing to save",
-                mWebView.saveState(new Bundle()));
-
         startWebServer(false);
-        String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
-        String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        // make a history list
-        mOnUiThread.loadUrlAndWaitForCompletion(url1);
-        pollingCheckWebBackForwardList(url1, 0, 1);
-        mOnUiThread.loadUrlAndWaitForCompletion(url2);
-        pollingCheckWebBackForwardList(url2, 1, 2);
-        mOnUiThread.loadUrlAndWaitForCompletion(url3);
-        pollingCheckWebBackForwardList(url3, 2, 3);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    assertNull(
+                            "Should return null when there's nothing to save",
+                            mWebView.saveState(new Bundle()));
 
-        // save the list
-        Bundle bundle = new Bundle();
-        WebBackForwardList saveList = mWebView.saveState(bundle);
-        assertNotNull(saveList);
-        assertEquals(3, saveList.getSize());
-        assertEquals(2, saveList.getCurrentIndex());
-        assertEquals(url1, saveList.getItemAtIndex(0).getUrl());
-        assertEquals(url2, saveList.getItemAtIndex(1).getUrl());
-        assertEquals(url3, saveList.getItemAtIndex(2).getUrl());
+                    String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+                    String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
+                    String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        // change the content to a new "blank" web view without history
-        final WebView newWebView = new WebView(getActivity());
+                    // make a history list
+                    mOnUiThread.loadUrlAndWaitForCompletion(url1);
+                    pollingCheckWebBackForwardList(url1, 0, 1);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url2);
+                    pollingCheckWebBackForwardList(url2, 1, 2);
+                    mOnUiThread.loadUrlAndWaitForCompletion(url3);
+                    pollingCheckWebBackForwardList(url3, 2, 3);
 
-        WebBackForwardList copyListBeforeRestore = newWebView.copyBackForwardList();
-        assertNotNull(copyListBeforeRestore);
-        assertEquals(0, copyListBeforeRestore.getSize());
+                    // save the list
+                    Bundle bundle = new Bundle();
+                    WebBackForwardList saveList = mWebView.saveState(bundle);
+                    assertNotNull(saveList);
+                    assertEquals(3, saveList.getSize());
+                    assertEquals(2, saveList.getCurrentIndex());
+                    assertEquals(url1, saveList.getItemAtIndex(0).getUrl());
+                    assertEquals(url2, saveList.getItemAtIndex(1).getUrl());
+                    assertEquals(url3, saveList.getItemAtIndex(2).getUrl());
 
-        // restore the list
-        final WebBackForwardList restoreList = newWebView.restoreState(bundle);
-        assertNotNull(restoreList);
-        assertEquals(3, restoreList.getSize());
-        assertEquals(2, saveList.getCurrentIndex());
+                    // change the content to a new "blank" web view without history
+                    final WebView newWebView = new WebView(mActivity);
 
-        // wait for the list items to get inflated
-        new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
-            @Override
-            protected boolean check() {
-                return restoreList.getItemAtIndex(0).getUrl() != null &&
-                       restoreList.getItemAtIndex(1).getUrl() != null &&
-                       restoreList.getItemAtIndex(2).getUrl() != null;
-            }
-        }.run();
-        assertEquals(url1, restoreList.getItemAtIndex(0).getUrl());
-        assertEquals(url2, restoreList.getItemAtIndex(1).getUrl());
-        assertEquals(url3, restoreList.getItemAtIndex(2).getUrl());
+                    WebBackForwardList copyListBeforeRestore = newWebView.copyBackForwardList();
+                    assertNotNull(copyListBeforeRestore);
+                    assertEquals(0, copyListBeforeRestore.getSize());
 
-        WebBackForwardList copyListAfterRestore = newWebView.copyBackForwardList();
-        assertNotNull(copyListAfterRestore);
-        assertEquals(3, copyListAfterRestore.getSize());
-        assertEquals(2, copyListAfterRestore.getCurrentIndex());
-        assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
-        assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
-        assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
+                    // restore the list
+                    final WebBackForwardList restoreList = newWebView.restoreState(bundle);
+                    assertNotNull(restoreList);
+                    assertEquals(3, restoreList.getSize());
+                    assertEquals(2, saveList.getCurrentIndex());
 
-        newWebView.destroy();
+                    // wait for the list items to get inflated
+                    new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
+                        @Override
+                        protected boolean check() {
+                            return restoreList.getItemAtIndex(0).getUrl() != null
+                                    && restoreList.getItemAtIndex(1).getUrl() != null
+                                    && restoreList.getItemAtIndex(2).getUrl() != null;
+                        }
+                    }.run();
+                    assertEquals(url1, restoreList.getItemAtIndex(0).getUrl());
+                    assertEquals(url2, restoreList.getItemAtIndex(1).getUrl());
+                    assertEquals(url3, restoreList.getItemAtIndex(2).getUrl());
+
+                    WebBackForwardList copyListAfterRestore = newWebView.copyBackForwardList();
+                    assertNotNull(copyListAfterRestore);
+                    assertEquals(3, copyListAfterRestore.getSize());
+                    assertEquals(2, copyListAfterRestore.getCurrentIndex());
+                    assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
+                    assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
+                    assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
+
+                    newWebView.destroy();
+                });
     }
 
+    @Test
     public void testRequestChildRectangleOnScreen() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         // It is needed to make test pass on some devices.
         mOnUiThread.setLayoutToMatchParent();
 
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
         String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\">&nbsp;</p>";
-        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
-                + "</body></html>", "text/html", null);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body>" + p + "</body></html>", "text/html", null);
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
@@ -2097,11 +2122,8 @@
         assertThat(mOnUiThread.getScrollY(), greaterThan(origY));
     }
 
+    @Test
     public void testSetDownloadListener() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         final SettableFuture<Void> downloadStartFuture = SettableFuture.create();
         final class MyDownloadListener implements DownloadListener {
             public String url;
@@ -2110,8 +2132,12 @@
             public String contentDisposition;
 
             @Override
-            public void onDownloadStart(String url, String userAgent, String contentDisposition,
-                    String mimetype, long contentLength) {
+            public void onDownloadStart(
+                    String url,
+                    String userAgent,
+                    String contentDisposition,
+                    String mimetype,
+                    long contentLength) {
                 this.url = url;
                 this.mimeType = mimetype;
                 this.contentLength = contentLength;
@@ -2135,9 +2161,10 @@
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.loadDataAndWaitForCompletion(
                 "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>",
-                "text/html", null);
+                "text/html",
+                null);
         // Wait for layout to complete before setting focus.
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         WebkitUtils.waitForFuture(downloadStartFuture);
         assertEquals(url, listener.url);
@@ -2146,28 +2173,26 @@
         assertEquals(mimeType, listener.mimeType);
     }
 
-    @UiThreadTest
+    @Test
     public void testSetLayoutParams() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(600, 800);
-        mWebView.setLayoutParams(params);
-        assertSame(params, mWebView.getLayoutParams());
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(600, 800);
+                    mWebView.setLayoutParams(params);
+                    assertSame(params, mWebView.getLayoutParams());
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testSetMapTrackballToArrowKeys() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        mWebView.setMapTrackballToArrowKeys(true);
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    mWebView.setMapTrackballToArrowKeys(true);
+                });
     }
 
+    @Test
     public void testSetNetworkAvailable() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         WebSettings settings = mOnUiThread.getSettings();
         settings.setJavaScriptEnabled(true);
         startWebServer(false);
@@ -2197,42 +2222,39 @@
         }.run();
     }
 
+    @Test
     public void testSetWebChromeClient() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         final SettableFuture<Void> future = SettableFuture.create();
-        mOnUiThread.setWebChromeClient(new WaitForProgressClient(mOnUiThread) {
-            @Override
-            public void onProgressChanged(WebView view, int newProgress) {
-                super.onProgressChanged(view, newProgress);
-                future.set(null);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
+        mOnUiThread.setWebChromeClient(
+                new WaitForProgressClient(mOnUiThread) {
+                    @Override
+                    public void onProgressChanged(WebView view, int newProgress) {
+                        super.onProgressChanged(view, newProgress);
+                        future.set(null);
+                    }
+                });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         assertFalse(future.isDone());
 
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         WebkitUtils.waitForFuture(future);
     }
 
+    @Test
     public void testPauseResumeTimers() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         class Monitor {
             private boolean mIsUpdated;
 
             @JavascriptInterface
             public synchronized void update() {
-                mIsUpdated  = true;
+                mIsUpdated = true;
                 notify();
             }
+
             public synchronized boolean waitForUpdate() {
                 while (!mIsUpdated) {
                     try {
@@ -2249,25 +2271,28 @@
                 mIsUpdated = false;
                 return true;
             }
-        };
+        }
+
         final Monitor monitor = new Monitor();
-        final String updateMonitorHtml = "<html>" +
-                "<body onload=\"monitor.update();\"></body></html>";
+        final String updateMonitorHtml =
+                "<html>" + "<body onload=\"monitor.update();\"></body></html>";
 
         // Test that JavaScript is executed even with timers paused.
-        WebkitUtils.onMainThreadSync(() -> {
-            mWebView.getSettings().setJavaScriptEnabled(true);
-            mWebView.addJavascriptInterface(monitor, "monitor");
-            mWebView.pauseTimers();
-            mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml,
-                    "text/html", null);
-        });
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    mWebView.getSettings().setJavaScriptEnabled(true);
+                    mWebView.addJavascriptInterface(monitor, "monitor");
+                    mWebView.pauseTimers();
+                    mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml, "text/html", null);
+                });
         assertTrue(monitor.waitForUpdate());
 
         // Start a timer and test that it does not fire.
         mOnUiThread.loadDataAndWaitForCompletion(
-                "<html><body onload='setTimeout(function(){monitor.update();},100)'>" +
-                "</body></html>", "text/html", null);
+                "<html><body onload='setTimeout(function(){monitor.update();},100)'>"
+                        + "</body></html>",
+                "text/html",
+                null);
         assertFalse(monitor.waitForUpdate());
 
         // Resume timers and test that the timer fires.
@@ -2276,37 +2301,31 @@
     }
 
     // verify query parameters can be passed correctly to android asset files
+    @Test
     public void testAndroidAssetQueryParam() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         WebSettings settings = mOnUiThread.getSettings();
         settings.setJavaScriptEnabled(true);
         // test passing a parameter
-        String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.PARAM_ASSET_URL+"?val=SUCCESS");
+        String fileUrl =
+                TestHtmlConstants.getFileUrl(TestHtmlConstants.PARAM_ASSET_URL + "?val=SUCCESS");
         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
         assertEquals("SUCCESS", mOnUiThread.getTitle());
     }
 
     // verify anchors work correctly for android asset files
+    @Test
     public void testAndroidAssetAnchor() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         WebSettings settings = mOnUiThread.getSettings();
         settings.setJavaScriptEnabled(true);
         // test using an anchor
-        String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.ANCHOR_ASSET_URL+"#anchor");
+        String fileUrl =
+                TestHtmlConstants.getFileUrl(TestHtmlConstants.ANCHOR_ASSET_URL + "#anchor");
         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
         assertEquals("anchor", mOnUiThread.getTitle());
     }
 
+    @Test
     public void testEvaluateJavascript() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
 
@@ -2325,26 +2344,27 @@
     }
 
     // Verify Print feature can create a PDF file with a correct preamble.
+    @Test
     public void testPrinting() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
-                "<body>foo</body></html>",
-                "text/html", null);
-        final PrintDocumentAdapter adapter =  mOnUiThread.createPrintDocumentAdapter();
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><head></head>" + "<body>foo</body></html>", "text/html", null);
+        final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter();
         printDocumentStart(adapter);
-        PrintAttributes attributes = new PrintAttributes.Builder()
-                .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
-                .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
-                .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
-                .build();
-        final WebViewCtsActivity activity = getActivity();
+        PrintAttributes attributes =
+                new PrintAttributes.Builder()
+                        .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
+                        .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
+                        .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
+                        .build();
+        final WebViewCtsActivity activity = mActivity;
         final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
-        final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
-                ParcelFileDescriptor.parseMode("w"));
+        final ParcelFileDescriptor descriptor =
+                ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("w"));
         final SettableFuture<Void> result = SettableFuture.create();
-        printDocumentLayout(adapter, null, attributes,
+        printDocumentLayout(
+                adapter,
+                null,
+                attributes,
                 new LayoutResultCallback() {
                     // Called on UI thread
                     @Override
@@ -2369,44 +2389,46 @@
     }
 
     // Verify Print feature can create a PDF file with correct number of pages.
+    @Test
     public void testPrintingPagesCount() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
         String content = "<html><head></head><body>";
         for (int i = 0; i < 500; ++i) {
             content += "<br />abcdefghijk<br />";
         }
         content += "</body></html>";
         mOnUiThread.loadDataAndWaitForCompletion(content, "text/html", null);
-        final PrintDocumentAdapter adapter =  mOnUiThread.createPrintDocumentAdapter();
+        final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter();
         printDocumentStart(adapter);
-        PrintAttributes attributes = new PrintAttributes.Builder()
-                .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
-                .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
-                .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
-                .build();
-        final WebViewCtsActivity activity = getActivity();
+        PrintAttributes attributes =
+                new PrintAttributes.Builder()
+                        .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
+                        .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
+                        .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
+                        .build();
+        final WebViewCtsActivity activity = mActivity;
         final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
-        final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
-                ParcelFileDescriptor.parseMode("w"));
+        final ParcelFileDescriptor descriptor =
+                ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("w"));
         final SettableFuture<Void> result = SettableFuture.create();
-        printDocumentLayout(adapter, null, attributes,
+        printDocumentLayout(
+                adapter,
+                null,
+                attributes,
                 new LayoutResultCallback() {
                     // Called on UI thread
                     @Override
                     public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
-                        PageRange[] pageRanges = new PageRange[] {
-                            new PageRange(1, 1), new PageRange(4, 7)
-                        };
+                        PageRange[] pageRanges =
+                                new PageRange[] {new PageRange(1, 1), new PageRange(4, 7)};
                         savePrintedPage(adapter, descriptor, pageRanges, result);
                     }
                 });
         try {
             WebkitUtils.waitForFuture(result);
             assertThat(file.length(), greaterThan(0L));
-            PdfRenderer renderer = new PdfRenderer(
-                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
+            PdfRenderer renderer =
+                    new PdfRenderer(
+                            ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
             assertEquals(5, renderer.getPageCount());
         } finally {
             descriptor.close();
@@ -2419,34 +2441,35 @@
      * androidx.webkit.WebViewCompatTest#testVisualStateCallbackCalled. Modifications to this test
      * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
      */
+    @Test
     public void testVisualStateCallbackCalled() throws Exception {
         // Check that the visual state callback is called correctly.
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         final long kRequest = 100;
 
         mOnUiThread.loadUrl("about:blank");
 
         final SettableFuture<Long> visualStateFuture = SettableFuture.create();
-        mOnUiThread.postVisualStateCallback(kRequest, new VisualStateCallback() {
-            public void onComplete(long requestId) {
-                visualStateFuture.set(requestId);
-            }
-        });
+        mOnUiThread.postVisualStateCallback(
+                kRequest,
+                new VisualStateCallback() {
+                    public void onComplete(long requestId) {
+                        visualStateFuture.set(requestId);
+                    }
+                });
 
         assertEquals(kRequest, (long) WebkitUtils.waitForFuture(visualStateFuture));
     }
 
     private static boolean setSafeBrowsingAllowlistSync(List<String> allowlist) {
         final SettableFuture<Boolean> safeBrowsingAllowlistFuture = SettableFuture.create();
-        WebView.setSafeBrowsingWhitelist(allowlist, new ValueCallback<Boolean>() {
-            @Override
-            public void onReceiveValue(Boolean success) {
-                safeBrowsingAllowlistFuture.set(success);
-            }
-        });
+        WebView.setSafeBrowsingWhitelist(
+                allowlist,
+                new ValueCallback<Boolean>() {
+                    @Override
+                    public void onReceiveValue(Boolean success) {
+                        safeBrowsingAllowlistFuture.set(success);
+                    }
+                });
         return WebkitUtils.waitForFuture(safeBrowsingAllowlistFuture);
     }
 
@@ -2456,11 +2479,8 @@
      * Modifications to this test should be reflected in that test as necessary. See
      * http://go/modifying-webview-cts.
      */
+    @Test
     public void testSetSafeBrowsingAllowlistWithMalformedList() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         List allowlist = new ArrayList<String>();
         // Protocols are not supported in the allowlist
         allowlist.add("http://google.com");
@@ -2469,33 +2489,35 @@
 
     /**
      * This should remain functionally equivalent to
-     * androidx.webkit.WebViewCompatTest#testSetSafeBrowsingAllowlistWithValidList. Modifications
-     * to this test should be reflected in that test as necessary. See
-     * http://go/modifying-webview-cts.
+     * androidx.webkit.WebViewCompatTest#testSetSafeBrowsingAllowlistWithValidList. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
      */
+    @Test
     public void testSetSafeBrowsingAllowlistWithValidList() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         List allowlist = new ArrayList<String>();
         allowlist.add("safe-browsing");
         assertTrue("Valid allowlist should be successful", setSafeBrowsingAllowlistSync(allowlist));
 
         final SettableFuture<Void> pageFinishedFuture = SettableFuture.create();
-        mOnUiThread.setWebViewClient(new WebViewClient() {
-            @Override
-            public void onPageFinished(WebView view, String url) {
-                pageFinishedFuture.set(null);
-            }
+        mOnUiThread.setWebViewClient(
+                new WebViewClient() {
+                    @Override
+                    public void onPageFinished(WebView view, String url) {
+                        pageFinishedFuture.set(null);
+                    }
 
-            @Override
-            public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType,
-                    SafeBrowsingResponse callback) {
-                pageFinishedFuture.setException(new IllegalStateException(
-                        "Should not invoke onSafeBrowsingHit for " + request.getUrl()));
-            }
-        });
+                    @Override
+                    public void onSafeBrowsingHit(
+                            WebView view,
+                            WebResourceRequest request,
+                            int threatType,
+                            SafeBrowsingResponse callback) {
+                        pageFinishedFuture.setException(
+                                new IllegalStateException(
+                                        "Should not invoke onSafeBrowsingHit for "
+                                                + request.getUrl()));
+                    }
+                });
 
         mOnUiThread.loadUrl("chrome://safe-browsing/match?type=malware");
 
@@ -2508,24 +2530,24 @@
      * androidx.webkit.WebViewCompatTest#testGetWebViewClient. Modifications to this test should be
      * reflected in that test as necessary. See http://go/modifying-webview-cts.
      */
-    @UiThreadTest
+    @Test
     public void testGetWebViewClient() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    // getWebViewClient should return a default WebViewClient if it hasn't been set
+                    // yet
+                    WebView webView = new WebView(mActivity);
+                    WebViewClient client = webView.getWebViewClient();
+                    assertNotNull(client);
+                    assertTrue(client instanceof WebViewClient);
 
-        // getWebViewClient should return a default WebViewClient if it hasn't been set yet
-        WebView webView = new WebView(getActivity());
-        WebViewClient client = webView.getWebViewClient();
-        assertNotNull(client);
-        assertTrue(client instanceof WebViewClient);
-
-        // getWebViewClient should return the client after it has been set
-        WebViewClient client2 = new WebViewClient();
-        assertNotSame(client, client2);
-        webView.setWebViewClient(client2);
-        assertSame(client2, webView.getWebViewClient());
-        webView.destroy();
+                    // getWebViewClient should return the client after it has been set
+                    WebViewClient client2 = new WebViewClient();
+                    assertNotSame(client, client2);
+                    webView.setWebViewClient(client2);
+                    assertSame(client2, webView.getWebViewClient());
+                    webView.destroy();
+                });
     }
 
     /**
@@ -2533,56 +2555,48 @@
      * androidx.webkit.WebViewCompatTest#testGetWebChromeClient. Modifications to this test should
      * be reflected in that test as necessary. See http://go/modifying-webview-cts.
      */
-    @UiThreadTest
+    @Test
     public void testGetWebChromeClient() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    // getWebChromeClient should return null if the client hasn't been set yet
+                    WebView webView = new WebView(mActivity);
+                    WebChromeClient client = webView.getWebChromeClient();
+                    assertNull(client);
 
-        // getWebChromeClient should return null if the client hasn't been set yet
-        WebView webView = new WebView(getActivity());
-        WebChromeClient client = webView.getWebChromeClient();
-        assertNull(client);
-
-        // getWebChromeClient should return the client after it has been set
-        WebChromeClient client2 = new WebChromeClient();
-        assertNotSame(client, client2);
-        webView.setWebChromeClient(client2);
-        assertSame(client2, webView.getWebChromeClient());
-        webView.destroy();
+                    // getWebChromeClient should return the client after it has been set
+                    WebChromeClient client2 = new WebChromeClient();
+                    assertNotSame(client, client2);
+                    webView.setWebChromeClient(client2);
+                    assertSame(client2, webView.getWebChromeClient());
+                    webView.destroy();
+                });
     }
 
-    @UiThreadTest
+    @Test
     public void testSetCustomTextClassifier() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         class CustomTextClassifier implements TextClassifier {
             @Override
             public TextSelection suggestSelection(
-                CharSequence text,
-                int startIndex,
-                int endIndex,
-                LocaleList defaultLocales) {
+                    CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
                 return new TextSelection.Builder(0, 1).build();
             }
 
             @Override
             public TextClassification classifyText(
-                CharSequence text,
-                int startIndex,
-                int endIndex,
-                LocaleList defaultLocales) {
+                    CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
                 return new TextClassification.Builder().build();
             }
-        };
+        }
 
-        TextClassifier classifier = new CustomTextClassifier();
-        WebView webView = new WebView(getActivity());
-        webView.setTextClassifier(classifier);
-        assertSame(webView.getTextClassifier(), classifier);
-        webView.destroy();
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    TextClassifier classifier = new CustomTextClassifier();
+                    WebView webView = new WebView(mActivity);
+                    webView.setTextClassifier(classifier);
+                    assertSame(webView.getTextClassifier(), classifier);
+                    webView.destroy();
+                });
     }
 
     private static class MockContext extends ContextWrapper {
@@ -2604,23 +2618,25 @@
 
     /**
      * This should remain functionally equivalent to
-     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingUseApplicationContext. Modifications to
-     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingUseApplicationContext. Modifications
+     * to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
      */
+    @Test
     public void testStartSafeBrowsingUseApplicationContext() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        final MockContext ctx = new MockContext(getActivity());
+        final MockContext ctx =
+                new MockContext(
+                        ApplicationProvider.getApplicationContext().getApplicationContext());
         final SettableFuture<Boolean> startSafeBrowsingFuture = SettableFuture.create();
-        WebView.startSafeBrowsing(ctx, new ValueCallback<Boolean>() {
-            @Override
-            public void onReceiveValue(Boolean value) {
-                startSafeBrowsingFuture.set(ctx.wasGetApplicationContextCalled());
-                return;
-            }
-        });
+        WebView.startSafeBrowsing(
+                ctx,
+                new ValueCallback<Boolean>() {
+                    @Override
+                    public void onReceiveValue(Boolean value) {
+                        startSafeBrowsingFuture.set(ctx.wasGetApplicationContextCalled());
+                        return;
+                    }
+                });
         assertTrue(WebkitUtils.waitForFuture(startSafeBrowsingFuture));
     }
 
@@ -2630,40 +2646,40 @@
      * Modifications to this test should be reflected in that test as necessary. See
      * http://go/modifying-webview-cts.
      */
+    @Test
     public void testStartSafeBrowsingWithNullCallbackDoesntCrash() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        WebView.startSafeBrowsing(getActivity().getApplicationContext(), null);
+        WebView.startSafeBrowsing(
+                ApplicationProvider.getApplicationContext().getApplicationContext(), null);
     }
 
     /**
      * This should remain functionally equivalent to
-     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingInvokesCallback. Modifications to
-     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingInvokesCallback. Modifications to this
+     * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
      */
+    @Test
     public void testStartSafeBrowsingInvokesCallback() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         final SettableFuture<Boolean> startSafeBrowsingFuture = SettableFuture.create();
-        WebView.startSafeBrowsing(getActivity().getApplicationContext(),
+        WebView.startSafeBrowsing(
+                ApplicationProvider.getApplicationContext().getApplicationContext(),
                 new ValueCallback<Boolean>() {
-            @Override
-            public void onReceiveValue(Boolean value) {
-                startSafeBrowsingFuture.set(Looper.getMainLooper().isCurrentThread());
-                return;
-            }
-        });
+                    @Override
+                    public void onReceiveValue(Boolean value) {
+                        startSafeBrowsingFuture.set(Looper.getMainLooper().isCurrentThread());
+                        return;
+                    }
+                });
         assertTrue(WebkitUtils.waitForFuture(startSafeBrowsingFuture));
     }
 
-    private void savePrintedPage(final PrintDocumentAdapter adapter,
-            final ParcelFileDescriptor descriptor, final PageRange[] pageRanges,
+    private void savePrintedPage(
+            final PrintDocumentAdapter adapter,
+            final ParcelFileDescriptor descriptor,
+            final PageRange[] pageRanges,
             final SettableFuture<Void> result) {
-        adapter.onWrite(pageRanges, descriptor,
+        adapter.onWrite(
+                pageRanges,
+                descriptor,
                 new CancellationSignal(),
                 new WriteResultCallback() {
                     @Override
@@ -2679,18 +2695,26 @@
     }
 
     private void printDocumentStart(final PrintDocumentAdapter adapter) {
-        WebkitUtils.onMainThreadSync(() -> {
-            adapter.onStart();
-        });
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    adapter.onStart();
+                });
     }
 
-    private void printDocumentLayout(final PrintDocumentAdapter adapter,
-            final PrintAttributes oldAttributes, final PrintAttributes newAttributes,
+    private void printDocumentLayout(
+            final PrintDocumentAdapter adapter,
+            final PrintAttributes oldAttributes,
+            final PrintAttributes newAttributes,
             final LayoutResultCallback layoutResultCallback) {
-        WebkitUtils.onMainThreadSync(() -> {
-            adapter.onLayout(oldAttributes, newAttributes, new CancellationSignal(),
-                    layoutResultCallback, null);
-        });
+        WebkitUtils.onMainThreadSync(
+                () -> {
+                    adapter.onLayout(
+                            oldAttributes,
+                            newAttributes,
+                            new CancellationSignal(),
+                            layoutResultCallback,
+                            null);
+                });
     }
 
     private static class HrefCheckHandler extends Handler {
@@ -2710,7 +2734,7 @@
             return mResultUrl;
         }
 
-        public void reset(){
+        public void reset() {
             mResultUrl = null;
             mHadRecieved = false;
         }
@@ -2724,13 +2748,13 @@
 
     private void moveFocusDown() throws Throwable {
         // send down key and wait for idle
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
         // waiting for idle isn't always sufficient for the key to be fully processed
         Thread.sleep(500);
     }
 
-    private void pollingCheckWebBackForwardList(final String currUrl, final int currIndex,
-            final int size) {
+    private void pollingCheckWebBackForwardList(
+            final String currUrl, final int currIndex, final int size) {
         new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) {
             @Override
             protected boolean check() {
@@ -2740,8 +2764,8 @@
         }.run();
     }
 
-    private boolean checkWebBackForwardList(WebBackForwardList list, String currUrl,
-            int currIndex, int size) {
+    private boolean checkWebBackForwardList(
+            WebBackForwardList list, String currUrl, int currIndex, int size) {
         return (list != null)
                 && (list.getSize() == size)
                 && (list.getCurrentIndex() == currIndex)
@@ -2750,8 +2774,7 @@
 
     private void assertGoBackOrForwardBySteps(boolean expected, int steps) {
         // skip if steps equals to 0
-        if (steps == 0)
-            return;
+        if (steps == 0) return;
 
         int start = steps > 0 ? 1 : steps;
         int end = steps > 0 ? steps : -1;
@@ -2770,15 +2793,14 @@
     }
 
     private boolean isPictureFilledWithColor(Picture picture, int color) {
-        if (picture.getWidth() == 0 || picture.getHeight() == 0)
-            return false;
+        if (picture.getWidth() == 0 || picture.getHeight() == 0) return false;
 
-        Bitmap bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(),
-                Config.ARGB_8888);
+        Bitmap bitmap =
+                Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), Config.ARGB_8888);
         picture.draw(new Canvas(bitmap));
 
-        for (int i = 0; i < bitmap.getWidth(); i ++) {
-            for (int j = 0; j < bitmap.getHeight(); j ++) {
+        for (int i = 0; i < bitmap.getWidth(); i++) {
+            for (int j = 0; j < bitmap.getHeight(); j++) {
                 if (color != bitmap.getPixel(i, j)) {
                     return false;
                 }
@@ -2788,14 +2810,13 @@
     }
 
     /**
-     * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started,
-     * scrolling is checked every SCROLL_WAIT_INTERVAL_MS for changes. Once
-     * changes have stopped, the function exits. If no scrolling has happened
-     * then the function exits after MIN_SCROLL_WAIT milliseconds.
+     * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started, scrolling is checked
+     * every SCROLL_WAIT_INTERVAL_MS for changes. Once changes have stopped, the function exits. If
+     * no scrolling has happened then the function exits after MIN_SCROLL_WAIT milliseconds.
+     *
      * @param previousScrollY The Y scroll position prior to waiting.
      */
-    private void waitForScrollingComplete(int previousScrollY)
-            throws InterruptedException {
+    private void waitForScrollingComplete(int previousScrollY) throws InterruptedException {
         int scrollY = previousScrollY;
         // wait at least MIN_SCROLL_WAIT for something to happen.
         long noChangeMinWait = SystemClock.uptimeMillis() + MIN_SCROLL_WAIT_MS;
@@ -2817,11 +2838,8 @@
      * androidx.webkit.WebViewCompatTest#testGetSafeBrowsingPrivacyPolicyUrl. Modifications to this
      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
      */
+    @Test
     public void testGetSafeBrowsingPrivacyPolicyUrl() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         assertNotNull(WebView.getSafeBrowsingPrivacyPolicyUrl());
         try {
             new URL(WebView.getSafeBrowsingPrivacyPolicyUrl().toString());
@@ -2830,11 +2848,8 @@
         }
     }
 
+    @Test
     public void testWebViewClassLoaderReturnsNonNull() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
         assertNotNull(WebView.getWebViewClassLoader());
     }
 }
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
index fad09f4..c0e5624 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -291,10 +291,8 @@
                 || state == NetworkInfo.DetailedState.DISCONNECTED) {
             state = waitForNextNetworkState();
         }
-        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
-            if (state != NetworkInfo.DetailedState.CONNECTING) {
-                return false;
-            }
+        if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)
+                && state == NetworkInfo.DetailedState.CONNECTING) {
             state = waitForNextNetworkState();
         }
         return state == NetworkInfo.DetailedState.CONNECTED;
diff --git a/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java b/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java
index 6205ee8..41cc640 100644
--- a/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java
@@ -562,7 +562,7 @@
      */
     @Test
     public void testAwareRttWithPeerHandle() throws InterruptedException {
-        if (WifiFeature.isAwareSupported(getContext())) {
+        if (!WifiFeature.isAwareSupported(getContext())) {
             return;
         }
         PeerHandle peerHandle = mock(PeerHandle.class);
diff --git a/tests/uwb/Android.bp b/tests/uwb/Android.bp
index 94a4f50..55e7ba6 100644
--- a/tests/uwb/Android.bp
+++ b/tests/uwb/Android.bp
@@ -36,6 +36,7 @@
         "mockito-target-minus-junit4",
         "com.uwb.support.fira",
         "com.uwb.support.multichip",
+        "com.uwb.support.oemextension",
     ],
     srcs: ["src/**/*.java"],
     platform_apis: true,
diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
index 097f799..f10eb11 100644
--- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
+++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
@@ -26,10 +26,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.UiAutomation;
@@ -53,16 +56,24 @@
 
 import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.modules.utils.build.SdkLevel;
 
+import com.google.uwb.support.fira.FiraControleeParams;
 import com.google.uwb.support.fira.FiraOpenSessionParams;
 import com.google.uwb.support.fira.FiraParams;
 import com.google.uwb.support.fira.FiraProtocolVersion;
+import com.google.uwb.support.fira.FiraSpecificationParams;
 import com.google.uwb.support.multichip.ChipInfoParams;
+import com.google.uwb.support.oemextension.DeviceStatus;
+import com.google.uwb.support.oemextension.RangingReportMetadata;
+import com.google.uwb.support.oemextension.SessionStatus;
 
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
@@ -82,6 +93,9 @@
     private final Context mContext = InstrumentationRegistry.getContext();
     private UwbManager mUwbManager;
     private String mDefaultChipId;
+    public static final int UWB_SESSION_STATE_IDLE = 0x03;
+    public static final byte DEVICE_STATE_ACTIVE = 0x02;
+    public static final int REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0x00;
 
     @Before
     public void setup() throws Exception {
@@ -494,6 +508,16 @@
             /* pass */
             Log.i(TAG, "Failed with expected security exception: " + e);
         }
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            mUwbManager.unregisterUwbVendorUciCallback(cb);
+            /* pass */
+        } catch (SecurityException e) {
+            /* fail */
+            fail();
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
     }
 
     @Test
@@ -525,6 +549,8 @@
         public boolean onOpenFailedCalled;
         public boolean onStartedCalled;
         public boolean onStartFailedCalled;
+        public boolean onReconfiguredCalled;
+        public boolean onReconfiguredFailedCalled;
         public boolean onClosedCalled;
         public RangingSession rangingSession;
         public RangingReport rangingReport;
@@ -566,9 +592,15 @@
             mCtrlCountDownLatch.countDown();
         }
 
-        public void onReconfigured(@NonNull PersistableBundle params) { }
+        public void onReconfigured(@NonNull PersistableBundle params) {
+            onReconfiguredCalled = true;
+            mCtrlCountDownLatch.countDown();
+        }
 
-        public void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params) { }
+        public void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params) {
+            onReconfiguredFailedCalled = true;
+            mCtrlCountDownLatch.countDown();
+        }
 
         public void onStopped(@Reason int reason, @NonNull PersistableBundle parameters) { }
 
@@ -902,6 +934,107 @@
         }
     }
 
+    @Test
+    @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"})
+    public void testFiraRangingSessionWithProvisionedSTS() throws Exception {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        // Needs UWB_PRIVILEGED permission which is held by shell.
+        uiAutomation.adoptShellPermissionIdentity();
+        CancellationSignal cancellationSignal = null;
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        CountDownLatch resultCountDownLatch = new CountDownLatch(1);
+        RangingSessionCallback rangingSessionCallback =
+                new RangingSessionCallback(countDownLatch, resultCountDownLatch);
+        PersistableBundle bundle = mUwbManager.getSpecificationInfo();
+        if (bundle.keySet().contains(FiraParams.PROTOCOL_NAME)) {
+            bundle = requireNonNull(bundle.getPersistableBundle(FiraParams.PROTOCOL_NAME));
+        }
+        FiraSpecificationParams params =
+                FiraSpecificationParams.fromBundle(bundle);
+        EnumSet<FiraParams.StsCapabilityFlag> stsCapabilities = EnumSet.of(
+                FiraParams.StsCapabilityFlag.HAS_STATIC_STS_SUPPORT,
+                FiraParams.StsCapabilityFlag.HAS_PROVISIONED_STS_SUPPORT);
+        assumeTrue(params.getStsCapabilities() == stsCapabilities);
+
+        FiraOpenSessionParams firaOpenSessionParams = new FiraOpenSessionParams.Builder()
+                .setProtocolVersion(new FiraProtocolVersion(1, 1))
+                .setSessionId(1)
+                .setStsConfig(FiraParams.STS_CONFIG_PROVISIONED)
+                .setSessionKey(new byte[]{
+                    0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+                    0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
+                })
+                .setSubsessionKey(new byte[]{
+                    0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+                    0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
+                })
+                .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
+                .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR)
+                .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST)
+                .setDeviceAddress(UwbAddress.fromBytes(new byte[] {0x5, 6}))
+                .setDestAddressList(List.of(UwbAddress.fromBytes(new byte[] {0x5, 6})))
+                .build();
+        try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
+            cancellationSignal = mUwbManager.openRangingSession(
+                firaOpenSessionParams.toBundle(),
+                Executors.newSingleThreadExecutor(),
+                rangingSessionCallback,
+                mDefaultChipId);
+            // Wait for the on opened callback.
+            assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(rangingSessionCallback.onOpenedCalled).isTrue();
+            assertThat(rangingSessionCallback.onOpenFailedCalled).isFalse();
+            assertThat(rangingSessionCallback.rangingSession).isNotNull();
+
+            countDownLatch = new CountDownLatch(1);
+            rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+            rangingSessionCallback.rangingSession.start(new PersistableBundle());
+            // Wait for the on started callback.
+            assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(rangingSessionCallback.onStartedCalled).isTrue();
+            assertThat(rangingSessionCallback.onStartFailedCalled).isFalse();
+
+            countDownLatch = new CountDownLatch(1);
+            rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+            UwbAddress uwbAddress = UwbAddress.fromBytes(new byte[] {0x5, 5});
+            rangingSessionCallback.rangingSession.addControlee(
+                    new FiraControleeParams.Builder()
+                    .setAddressList(new UwbAddress[] {uwbAddress})
+                    .setSubSessionIdList(new int[] {1})
+                    .build().toBundle()
+            );
+            // Wait for the on reconfigured callback.
+            assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(rangingSessionCallback.onReconfiguredCalled).isTrue();
+            assertThat(rangingSessionCallback.onReconfiguredFailedCalled).isFalse();
+
+            // Wait for the on ranging report callback.
+            assertThat(resultCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(rangingSessionCallback.rangingReport).isNotNull();
+
+            // Check the UWB state.
+            assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE);
+
+            // Stop ongoing session.
+            rangingSessionCallback.rangingSession.stop();
+        } finally {
+            if (cancellationSignal != null) {
+                countDownLatch = new CountDownLatch(1);
+                rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+
+                // Close session.
+                cancellationSignal.cancel();
+
+                // Wait for the on closed callback.
+                assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+                assertThat(rangingSessionCallback.onClosedCalled).isTrue();
+            }
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
     private class AdapterStateCallback implements UwbManager.AdapterStateCallback {
         private final CountDownLatch mCountDownLatch;
         private final @State Integer mWaitForState;
@@ -1011,4 +1144,207 @@
             uiAutomation.dropShellPermissionIdentity();
         }
     }
+
+    private class UwbOemExtensionCallback implements UwbManager.UwbOemExtensionCallback {
+        public PersistableBundle mSessionChangeNtf;
+        public PersistableBundle mDeviceStatusNtf;
+        public PersistableBundle mSessionConfig;
+        public RangingReport mRangingReport;
+        public boolean onSessionConfigCompleteCalled = false;
+        public boolean onRangingReportReceivedCalled = false;
+        public boolean onSessionChangedCalled = false;
+        public boolean onDeviceStatusNtfCalled = false;
+
+        @Override
+        public void onSessionStatusNotificationReceived(
+                @NonNull PersistableBundle sessionStatusBundle) {
+            mSessionChangeNtf = sessionStatusBundle;
+            onSessionChangedCalled = true;
+        }
+
+        @Override
+        public void onDeviceStatusNotificationReceived(PersistableBundle deviceStatusBundle) {
+            mDeviceStatusNtf = deviceStatusBundle;
+            onDeviceStatusNtfCalled = true;
+        }
+
+        @NonNull
+        @Override
+        public int onSessionConfigurationComplete(@NonNull PersistableBundle openSessionBundle) {
+            mSessionConfig = openSessionBundle;
+            onSessionConfigCompleteCalled = true;
+            return 0;
+        }
+
+        @NonNull
+        @Override
+        public RangingReport onRangingReportReceived(
+                @NonNull RangingReport rangingReport) {
+            onRangingReportReceivedCalled = true;
+            mRangingReport = rangingReport;
+            return mRangingReport;
+        }
+    }
+
+    @Test
+    @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"})
+    public void testOemCallbackExtension() throws Exception {
+        Assume.assumeTrue(SdkLevel.isAtLeastU());
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        CancellationSignal cancellationSignal = null;
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        CountDownLatch resultCountDownLatch = new CountDownLatch(1);
+        UwbOemExtensionCallback uwbOemExtensionCallback = new UwbOemExtensionCallback();
+
+        int sessionId = 1;
+        RangingSessionCallback rangingSessionCallback =
+                new RangingSessionCallback(countDownLatch, resultCountDownLatch);
+        FiraOpenSessionParams firaOpenSessionParams = new FiraOpenSessionParams.Builder()
+                .setProtocolVersion(new FiraProtocolVersion(1, 1))
+                .setSessionId(sessionId)
+                .setStsConfig(FiraParams.STS_CONFIG_STATIC)
+                .setVendorId(new byte[]{0x5, 0x6})
+                .setStaticStsIV(new byte[]{0x5, 0x6, 0x9, 0xa, 0x4, 0x6})
+                .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
+                .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR)
+                .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST)
+                .setDeviceAddress(UwbAddress.fromBytes(new byte[] {0x5, 6}))
+                .setDestAddressList(List.of(UwbAddress.fromBytes(new byte[] {0x5, 6})))
+                .build();
+        try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
+            mUwbManager.registerUwbOemExtensionCallback(
+                    Executors.newSingleThreadExecutor(), uwbOemExtensionCallback);
+            // Try to start a ranging session with invalid params, should fail.
+            cancellationSignal = mUwbManager.openRangingSession(
+                    firaOpenSessionParams.toBundle(),
+                    Executors.newSingleThreadExecutor(),
+                    rangingSessionCallback,
+                    mDefaultChipId);
+            // Wait for the on opened callback.
+            assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(uwbOemExtensionCallback.onSessionConfigCompleteCalled).isTrue();
+            assertThat(uwbOemExtensionCallback.mSessionConfig).isNotNull();
+
+            FiraOpenSessionParams openSessionParamsBundle = FiraOpenSessionParams
+                    .fromBundle(uwbOemExtensionCallback.mSessionConfig);
+            assertEquals(openSessionParamsBundle.getSessionId(), sessionId);
+            assertEquals(openSessionParamsBundle.getStsConfig(), FiraParams.STS_CONFIG_STATIC);
+            assertEquals(openSessionParamsBundle.getDeviceType(),
+                    FiraParams.RANGING_DEVICE_TYPE_CONTROLLER);
+
+            assertThat(uwbOemExtensionCallback.onSessionChangedCalled).isTrue();
+            assertThat(uwbOemExtensionCallback.mSessionChangeNtf).isNotNull();
+
+            SessionStatus sessionStatusBundle = SessionStatus
+                    .fromBundle(uwbOemExtensionCallback.mSessionChangeNtf);
+            assertEquals(sessionStatusBundle.getSessionId(), sessionId);
+            assertEquals(sessionStatusBundle.getState(), UWB_SESSION_STATE_IDLE);
+            assertEquals(sessionStatusBundle.getReasonCode(),
+                    REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS);
+
+            countDownLatch = new CountDownLatch(1);
+            rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+            rangingSessionCallback.rangingSession.start(new PersistableBundle());
+            // Wait for the on started callback.
+            assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(uwbOemExtensionCallback.onSessionChangedCalled).isTrue();
+            assertThat(uwbOemExtensionCallback.mSessionChangeNtf).isNotNull();
+            assertThat(uwbOemExtensionCallback.onDeviceStatusNtfCalled).isTrue();
+            assertThat(uwbOemExtensionCallback.mDeviceStatusNtf).isNotNull();
+
+            DeviceStatus deviceStatusBundle = DeviceStatus
+                    .fromBundle(uwbOemExtensionCallback.mDeviceStatusNtf);
+            assertEquals(deviceStatusBundle.getDeviceState(), DEVICE_STATE_ACTIVE);
+
+            // Wait for the on ranging report callback.
+            assertThat(resultCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(rangingSessionCallback.rangingReport).isNotNull();
+            assertThat(uwbOemExtensionCallback.onRangingReportReceivedCalled).isTrue();
+            assertThat(uwbOemExtensionCallback.mRangingReport).isNotNull();
+            PersistableBundle reportMetadataBundle = uwbOemExtensionCallback
+                    .mRangingReport.getRangingReportMetadata();
+            RangingReportMetadata reportMetadata = RangingReportMetadata
+                    .fromBundle(reportMetadataBundle);
+            assertEquals(reportMetadata.getSessionId(), sessionId);
+            assertThat(reportMetadata.getRawNtfData()).isNotEmpty();
+
+            // Check the UWB state.
+            assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE);
+
+            // Stop ongoing session.
+            rangingSessionCallback.rangingSession.stop();
+        } finally {
+            if (cancellationSignal != null) {
+                countDownLatch = new CountDownLatch(1);
+                rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+
+                // Close session.
+                cancellationSignal.cancel();
+
+                // Wait for the on closed callback.
+                assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+                assertThat(rangingSessionCallback.onClosedCalled).isTrue();
+            }
+            try {
+                mUwbManager.unregisterUwbOemExtensionCallback(uwbOemExtensionCallback);
+            } catch (SecurityException e) {
+                /* pass */
+                fail();
+            }
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+    public void testRegisterUwbOemExtensionCallbackWithoutUwbPrivileged() {
+        Assume.assumeTrue(SdkLevel.isAtLeastU());
+        UwbManager.UwbOemExtensionCallback cb = new UwbOemExtensionCallback();
+        try {
+            mUwbManager.registerUwbOemExtensionCallback(
+                    Executors.newSingleThreadExecutor(), cb);
+            // should fail if the call was successful without UWB_PRIVILEGED permission.
+            fail();
+        } catch (SecurityException e) {
+            /* pass */
+            Log.i(TAG, "Failed with expected security exception: " + e);
+        }
+    }
+
+    @Test
+    @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+    public void testUnregisterUwbOemExtensionCallbackWithoutUwbPrivileged() {
+        Assume.assumeTrue(SdkLevel.isAtLeastU());
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        UwbManager.UwbOemExtensionCallback cb = new UwbOemExtensionCallback();
+        try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
+            mUwbManager.registerUwbOemExtensionCallback(
+                    Executors.newSingleThreadExecutor(), cb);
+        } catch (SecurityException e) {
+            /* fail */
+            fail();
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
+        try {
+            mUwbManager.unregisterUwbOemExtensionCallback(cb);
+            // should fail if the call was successful without UWB_PRIVILEGED permission.
+            fail();
+        } catch (SecurityException e) {
+            /* pass */
+            Log.i(TAG, "Failed with expected security exception: " + e);
+        }
+        try {
+            // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+            uiAutomation.adoptShellPermissionIdentity();
+            mUwbManager.unregisterUwbOemExtensionCallback(cb);
+        } catch (SecurityException e) {
+            /* pass */
+            fail();
+        }
+    }
 }
diff --git a/tests/uwb/src/android/uwb/cts/UwbTestUtils.java b/tests/uwb/src/android/uwb/cts/UwbTestUtils.java
index 041b66c..a7689de 100644
--- a/tests/uwb/src/android/uwb/cts/UwbTestUtils.java
+++ b/tests/uwb/src/android/uwb/cts/UwbTestUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.PersistableBundle;
 import android.os.SystemClock;
 import android.uwb.AngleMeasurement;
 import android.uwb.AngleOfArrivalMeasurement;
@@ -63,6 +64,12 @@
         return getRangingMeasurement(getUwbAddress(false));
     }
 
+    public static PersistableBundle getTestRangingMetadata() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt("TEST_KEY", 1);
+        return bundle;
+    }
+
     public static RangingMeasurement getRangingMeasurement(UwbAddress address) {
         return new RangingMeasurement.Builder()
                 .setDistanceMeasurement(getDistanceMeasurement())
@@ -74,6 +81,7 @@
                 .setLineOfSight(RangingMeasurement.NLOS)
                 .setMeasurementFocus(RangingMeasurement.MEASUREMENT_FOCUS_RANGE)
                 .setRssiDbm(-85)
+                .setRangingMeasurementMetadata(getTestRangingMetadata())
                 .build();
     }
 
diff --git a/tools/cts-media-preparer-app/Android.bp b/tools/cts-media-preparer-app/Android.bp
index cbad568..7fe2a66 100644
--- a/tools/cts-media-preparer-app/Android.bp
+++ b/tools/cts-media-preparer-app/Android.bp
@@ -34,7 +34,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-media",
     ],
     sdk_version: "test_current",
     min_sdk_version: "29",