Merge "Move adjacent tasks forward together to ensure occluding state" into sc-v2-dev
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index be3bff9..0044efa 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3642,12 +3642,25 @@
             Please make sure you can connect to it. The test is successful if the config is editable
             and can be connected to.
     </string>
+    <string name="device_owner_wifi_config_unlocked_modification_test_info" product="automotive">
+            Please press the button to ensure WiFi config lockdown is NOT in effect. Then go to
+            Settings &gt; WiFi and see if the CTSVerifier created WiFi configuration is editable
+            (it\'s ok if it\'s not editable, but then take note so the next test can be skipped).
+            Please make sure you can connect to it. The test is successful if the config can be
+            connected to.
+    </string>
     <string name="device_owner_wifi_config_locked_modification_test">Locked config is not modifiable in Settings</string>
     <string name="device_owner_wifi_config_locked_modification_test_info">
             Please press the button to ensure WiFi config lockdown is in effect. Then go to
             Settings &gt; WiFi and see if the CTSVerifier created WiFi configuration can NOT be edited
             or removed. The test is successful if the config is NOT modifiable.
     </string>
+    <string name="device_owner_wifi_config_locked_modification_test_info" product="automotive">
+            NOTE: if the WiFi was not editable in the previous step, please skip this test.
+            Please press the button to ensure WiFi config lockdown is in effect. Then go to
+            Settings &gt; WiFi and see if the CTSVerifier created WiFi configuration can NOT be edited
+            or removed. The test is successful if the config is NOT modifiable.
+    </string>
     <string name="device_owner_wifi_config_locked_connection_test">Locked config can be connected to</string>
     <string name="device_owner_wifi_config_locked_connection_test_info">
             Please press the button to ensure WiFi config lockdown is in effect. Then go to
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
index c9f124d..e7bced9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
@@ -80,6 +80,13 @@
     }
 
     /**
+     * Checks whether the device supports file transfer.
+     */
+    public static boolean isUsbFileTransferSupported(Context context) {
+        return !isWatchOrAutomotive(context);
+    }
+
+    /**
      * Checks whether the device is watch .
      */
     private static boolean isWatch(Context context) {
@@ -118,5 +125,4 @@
     public static boolean isKeyguardShownWhenUserDoesntHaveCredentials(Context context) {
         return !isAutomotive(context);
     }
-
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 505ace2..05230ea 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -206,7 +206,7 @@
             mAm = getSystemService(ActivityManager.class);
             mAdmin = DeviceAdminTestReceiver.getReceiverComponentName();
             final String command = intent.getStringExtra(EXTRA_COMMAND);
-            Log.i(TAG, "Command: " + command);
+            Log.i(TAG, "Command: " + command + " forDeviceOwner: " + forDeviceOwner);
             switch (command) {
                 case COMMAND_SET_USER_RESTRICTION: {
                     String restrictionKey = intent.getStringExtra(EXTRA_USER_RESTRICTION);
@@ -282,6 +282,8 @@
                 case COMMAND_SET_GLOBAL_SETTING: {
                     final String setting = intent.getStringExtra(EXTRA_SETTING);
                     final String value = intent.getStringExtra(EXTRA_VALUE);
+                    Log.d(TAG, "Setting global property '" + setting + "' to '" + value
+                            + "' using " + mDpm);
                     mDpm.setGlobalSetting(mAdmin, setting, value);
                 } break;
                 case COMMAND_REMOVE_DEVICE_OWNER: {
@@ -380,6 +382,7 @@
                     uninstallHelperPackage();
                 } break;
                 case COMMAND_SET_PERMISSION_GRANT_STATE: {
+                    Log.d(TAG, "Granting permission using " + mDpm);
                     mDpm.setPermissionGrantState(mAdmin, getPackageName(),
                             intent.getStringExtra(EXTRA_PERMISSION),
                             intent.getIntExtra(EXTRA_GRANT_STATE,
@@ -483,16 +486,22 @@
                     mDpm.setMaximumFailedPasswordsForWipe(mAdmin, 0);
                 } break;
                 case COMMAND_SET_DEFAULT_IME: {
-                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())
+                            && !UserManager.isHeadlessSystemUserMode()) {
                         return;
                     }
+                    Log.d(TAG, "Setting " + Settings.Secure.DEFAULT_INPUT_METHOD + " using "
+                            + mDpm);
                     mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD,
                             getPackageName());
                 } break;
                 case COMMAND_CLEAR_DEFAULT_IME: {
-                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())
+                            && !UserManager.isHeadlessSystemUserMode()) {
                         return;
                     }
+                    Log.d(TAG, "Clearing " + Settings.Secure.DEFAULT_INPUT_METHOD + " using "
+                            + mDpm);
                     mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD, null);
                 } break;
                 case COMMAND_CREATE_MANAGED_USER:{
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index d444b38..44fb73e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -34,6 +34,7 @@
 import android.view.View;
 
 import com.android.bedstead.dpmwrapper.TestAppSystemServiceFactory;
+import com.android.compatibility.common.util.CddTest;
 import com.android.cts.verifier.ArrayTestListAdapter;
 import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
 import com.android.cts.verifier.PassFailButtons;
@@ -49,6 +50,7 @@
  * adb shell dpm set-device-owner
  *  'com.android.cts.verifier/com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver'
  */
+@CddTest(requirement="7.7")
 public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListActivity {
     private static final String TAG = "DeviceOwnerPositiveTestActivity";
 
@@ -353,8 +355,7 @@
         }
 
         // DISALLOW_USB_FILE_TRANSFER
-        // TODO(b/189282625): replace FEATURE_WATCH with a more specific feature
-        if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+        if (FeatureUtil.isUsbFileTransferSupported(this)) {
             adapter.add(createInteractiveTestItem(this, DISALLOW_USB_FILE_TRANSFER_ID,
                     R.string.device_owner_disallow_usb_file_transfer_test,
                     R.string.device_owner_disallow_usb_file_transfer_test_info,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
index 589e261..4a4eae4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
@@ -80,41 +80,52 @@
 
         String action = getIntent().getAction();
         Log.d(TAG, "onCreate(): action = " + action);
-        switch (action) {
-            case ACTION_CHECK_DEVICE_OWNER_FOR_REQUESTING_BUGREPORT: {
-                DevicePolicyManager dpm = TestAppSystemServiceFactory.getDevicePolicyManager(this,
-                        DeviceAdminTestReceiver.class, /* forDeviceOwner= */ true);
-                if (dpm.isDeviceOwnerApp(getPackageName())) {
-                    TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
-                            null, null);
-                } else {
-                    TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
-                            getString(R.string.device_owner_incorrect_device_owner, myUserId()),
-                            null);
-                }
-                finish();
-                return;
-            } case ACTION_CHECK_PROFILE_OWNER_FOR_REQUESTING_BUGREPORT: {
-                DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
-                if (dpm.isProfileOwnerApp(getPackageName())) {
-                    TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
-                            null, null);
-                } else {
-                    TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
-                            getString(R.string.device_owner_incorrect_profile_owner, myUserId()),
-                            null);
-                }
-                finish();
-                return;
-            } case ACTION_CHECK_CURRENT_USER_AFFILIATED_FOR_REQUESTING_BUGREPORT: {
-                DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
-                if (dpm.isAffiliatedUser()) {
-                    TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
-                            null, null);
-                } else {
-                    TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
-                            getString(R.string.device_owner_user_not_affiliated, myUserId()), null);
-                }
+        boolean validAction = true;
+        if (action != null) {
+            DevicePolicyManager dpm = null;
+            switch (action) {
+                case ACTION_CHECK_DEVICE_OWNER_FOR_REQUESTING_BUGREPORT:
+                    dpm = TestAppSystemServiceFactory.getDevicePolicyManager(this,
+                            DeviceAdminTestReceiver.class, /* forDeviceOwner= */ true);
+                    if (dpm.isDeviceOwnerApp(getPackageName())) {
+                        TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                                null, null);
+                    } else {
+                        TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                                getString(R.string.device_owner_incorrect_device_owner, myUserId()),
+                                null);
+                    }
+                    break;
+                case ACTION_CHECK_PROFILE_OWNER_FOR_REQUESTING_BUGREPORT:
+                    dpm = getSystemService(DevicePolicyManager.class);
+                    if (dpm.isProfileOwnerApp(getPackageName())) {
+                        TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                                null, null);
+                    } else {
+                        TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                                getString(R.string.device_owner_incorrect_profile_owner,
+                                        myUserId()),
+                                null);
+                    }
+                    break;
+                case ACTION_CHECK_CURRENT_USER_AFFILIATED_FOR_REQUESTING_BUGREPORT:
+                    dpm = getSystemService(DevicePolicyManager.class);
+                    if (dpm.isAffiliatedUser()) {
+                        TestResult.setPassedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                                null, null);
+                    } else {
+                        TestResult.setFailedResult(this, getIntent().getStringExtra(EXTRA_TEST_ID),
+                                getString(R.string.device_owner_user_not_affiliated, myUserId()),
+                                null);
+                    }
+                    break;
+                default:
+                    Log.w(TAG, "invalid action on intent: " + action);
+                    validAction = false;
+                    break;
+            }
+            if (validAction) {
+                Log.d(TAG, "Finishing activity");
                 finish();
                 return;
             }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index 5e8a92f..577a6c8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -16,11 +16,11 @@
 
 package com.android.cts.verifier.managedprovisioning;
 
+import static com.android.cts.verifier.managedprovisioning.Utils.createInteractiveTestItem;
+
 import android.Manifest;
 import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.database.DataSetObserver;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -31,8 +31,6 @@
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 
-import static com.android.cts.verifier.managedprovisioning.Utils.createInteractiveTestItem;
-
 /**
  * Test class to verify privacy information is shown for devices managed by a Device Owner.
  */
@@ -92,6 +90,12 @@
                 .putExtra(CommandReceiverActivity.EXTRA_COMMAND, command);
     }
 
+    private Intent buildCommandIntentForCurrentUser(String command) {
+        return buildCommandIntent(command)
+                .putExtra(CommandReceiverActivity.EXTRA_USE_CURRENT_USER_DPM, true);
+    }
+
+
     private TestListItem buildCommandTest(String id, int titleRes, int infoRes,
             int commandButtonRes, String command) {
         return createInteractiveTestItem(this, id, titleRes, infoRes,
@@ -105,12 +109,14 @@
             String permission) {
         return createInteractiveTestItem(this, id, titleRes, infoRes,
                 new ButtonInfo[] {
-                        new ButtonInfo(R.string.enterprise_privacy_reset, buildCommandIntent(
+                        new ButtonInfo(R.string.enterprise_privacy_reset,
+                                buildCommandIntentForCurrentUser(
                                 CommandReceiverActivity.COMMAND_SET_PERMISSION_GRANT_STATE)
                                 .putExtra(CommandReceiverActivity.EXTRA_PERMISSION, permission)
                                 .putExtra(CommandReceiverActivity.EXTRA_GRANT_STATE,
                                         DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT)),
-                        new ButtonInfo(R.string.enterprise_privacy_grant, buildCommandIntent(
+                        new ButtonInfo(R.string.enterprise_privacy_grant,
+                                buildCommandIntentForCurrentUser(
                                 CommandReceiverActivity.COMMAND_SET_PERMISSION_GRANT_STATE)
                                 .putExtra(CommandReceiverActivity.EXTRA_PERMISSION, permission)
                                 .putExtra(CommandReceiverActivity.EXTRA_GRANT_STATE,
@@ -182,11 +188,12 @@
                         new ButtonInfo(R.string.enterprise_privacy_open_settings,
                                 new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
                         new ButtonInfo(R.string.enterprise_privacy_set_keyboard,
-                                buildCommandIntent(CommandReceiverActivity
-                                        .COMMAND_SET_DEFAULT_IME)),
+                                buildCommandIntentForCurrentUser(
+                                        CommandReceiverActivity.COMMAND_SET_DEFAULT_IME)),
                         new ButtonInfo(R.string.enterprise_privacy_finish,
-                                buildCommandIntent(CommandReceiverActivity
-                                        .COMMAND_CLEAR_DEFAULT_IME))}));
+                                buildCommandIntentForCurrentUser(
+                                        CommandReceiverActivity.COMMAND_CLEAR_DEFAULT_IME))
+                }));
         adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ALWAYS_ON_VPN,
                 R.string.enterprise_privacy_always_on_vpn,
                 R.string.enterprise_privacy_always_on_vpn_info,
diff --git a/common/device-side/bedstead/activitycontext/src/main/java/com/android/activitycontext/ActivityContext.java b/common/device-side/bedstead/activitycontext/src/main/java/com/android/activitycontext/ActivityContext.java
index 4f63260..d892597 100644
--- a/common/device-side/bedstead/activitycontext/src/main/java/com/android/activitycontext/ActivityContext.java
+++ b/common/device-side/bedstead/activitycontext/src/main/java/com/android/activitycontext/ActivityContext.java
@@ -27,10 +27,14 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.exceptions.NeneException;
+import com.android.bedstead.nene.users.UserReference;
 import com.android.compatibility.common.util.ShellIdentityUtils.QuadFunction;
 import com.android.compatibility.common.util.ShellIdentityUtils.TriFunction;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -68,45 +72,58 @@
         if (runnable == null) {
             throw new NullPointerException();
         }
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
 
-        if (!instrumentation.getContext().getPackageName().equals(
-                instrumentation.getTargetContext().getPackageName())) {
-            throw new IllegalStateException("ActivityContext can only be used in test apps which"
-                    + " instrument themselves. Consider ActivityScenario for this case.");
-        }
+        // As we show an Activity we must be in the foreground
+        UserReference currentUser = TestApis.users().current();
+        try {
+            TestApis.users().instrumented().switchTo();
 
-        synchronized (ActivityContext.class) {
-            sRunnable = runnable;
+            Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
 
-            sLatch = new CountDownLatch(1);
-            sReturnValue = null;
-            sThrowValue = null;
-
-            Intent intent = new Intent();
-            intent.setClass(sContext, ActivityContext.class);
-            intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
-            sContext.startActivity(intent);
-        }
-
-        sLatch.await();
-
-        synchronized (ActivityContext.class) {
-            sRunnable = null;
-
-            if (sThrowValue != null) {
-                if (sThrowValue instanceof RuntimeException) {
-                    throw (RuntimeException) sThrowValue;
-                }
-
-                if (sThrowValue instanceof Error) {
-                    throw (Error) sThrowValue;
-                }
-
-                throw new IllegalStateException("Invalid value for sThrowValue");
+            if (!instrumentation.getContext().getPackageName().equals(
+                    instrumentation.getTargetContext().getPackageName())) {
+                throw new IllegalStateException(
+                        "ActivityContext can only be used in test apps which instrument themselves."
+                                + " Consider ActivityScenario for this case.");
             }
 
-            return (E) sReturnValue;
+            synchronized (ActivityContext.class) {
+                sRunnable = runnable;
+
+                sLatch = new CountDownLatch(1);
+                sReturnValue = null;
+                sThrowValue = null;
+
+                Intent intent = new Intent();
+                intent.setClass(sContext, ActivityContext.class);
+                intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+                sContext.startActivity(intent);
+            }
+
+            if (!sLatch.await(5, TimeUnit.MINUTES)) {
+                throw new NeneException("Timed out while waiting for lambda with context to"
+                        + " complete.");
+            }
+
+            synchronized (ActivityContext.class) {
+                sRunnable = null;
+
+                if (sThrowValue != null) {
+                    if (sThrowValue instanceof RuntimeException) {
+                        throw (RuntimeException) sThrowValue;
+                    }
+
+                    if (sThrowValue instanceof Error) {
+                        throw (Error) sThrowValue;
+                    }
+
+                    throw new IllegalStateException("Invalid value for sThrowValue");
+                }
+
+                return (E) sReturnValue;
+            }
+        } finally {
+            currentUser.switchTo();
         }
     }
 
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java
index 6d43808..7ffabbe 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java
@@ -16,7 +16,10 @@
 
 package com.android.bedstead.harrier;
 
+import android.os.Bundle;
+
 import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
 import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest;
@@ -72,18 +75,23 @@
         return getAnnotationWeight(a) - getAnnotationWeight(b);
     }
 
-    private static int getAnnotationWeight(Annotation a) {
-        if (!a.annotationType().getPackage().getName().startsWith(BEDSTEAD_PACKAGE_NAME)) {
+    private static int getAnnotationWeight(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            // Special case, not important
+            return AnnotationRunPrecedence.PRECEDENCE_NOT_IMPORTANT;
+        }
+
+        if (!annotation.annotationType().getPackage().getName().startsWith(BEDSTEAD_PACKAGE_NAME)) {
             return AnnotationRunPrecedence.FIRST;
         }
 
         try {
-            return (int) a.annotationType().getMethod("weight").invoke(a);
+            return (int) annotation.annotationType().getMethod("weight").invoke(annotation);
         } catch (NoSuchMethodException e) {
             // Default to PRECEDENCE_NOT_IMPORTANT if no weight is found on the annotation.
             return AnnotationRunPrecedence.PRECEDENCE_NOT_IMPORTANT;
         } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new NeneException("Failed to invoke weight on this annotation: " + a, e);
+            throw new NeneException("Failed to invoke weight on this annotation: " + annotation, e);
         }
     }
 
@@ -92,7 +100,7 @@
      */
     public static final class BedsteadFrameworkMethod extends FrameworkMethod {
 
-        private final Class<? extends Annotation> mParameterizedAnnotation;
+        private final Annotation mParameterizedAnnotation;
         private final Map<Class<? extends Annotation>, Annotation> mAnnotationsMap =
                 new HashMap<>();
         private Annotation[] mAnnotations;
@@ -103,8 +111,7 @@
 
         public BedsteadFrameworkMethod(Method method, Annotation parameterizedAnnotation) {
             super(method);
-            this.mParameterizedAnnotation = (parameterizedAnnotation == null) ? null
-                    : parameterizedAnnotation.annotationType();
+            mParameterizedAnnotation = parameterizedAnnotation;
 
             calculateAnnotations();
         }
@@ -124,6 +131,9 @@
 
             this.mAnnotations = annotations.toArray(new Annotation[0]);
             for (Annotation annotation : annotations) {
+                if (annotation instanceof DynamicParameterizedAnnotation) {
+                    continue; // don't return this
+                }
                 mAnnotationsMap.put(annotation.annotationType(), annotation);
             }
         }
@@ -133,7 +143,7 @@
             if (mParameterizedAnnotation == null) {
                 return super.getName();
             }
-            return super.getName() + "[" + mParameterizedAnnotation.getSimpleName() + "]";
+            return super.getName() + "[" + getParameterName(mParameterizedAnnotation) + "]";
         }
 
         @Override
@@ -162,13 +172,20 @@
         }
     }
 
+    private static String getParameterName(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            return ((DynamicParameterizedAnnotation) annotation).name();
+        }
+        return annotation.annotationType().getSimpleName();
+    }
+
     /**
      * Resolve annotations recursively.
      *
      * @param parameterizedAnnotation The class of the parameterized annotation to expand, if any
      */
     public static void resolveRecursiveAnnotations(List<Annotation> annotations,
-            @Nullable Class<? extends Annotation> parameterizedAnnotation) {
+            @Nullable Annotation parameterizedAnnotation) {
         int index = 0;
         while (index < annotations.size()) {
             Annotation annotation = annotations.get(index);
@@ -181,11 +198,34 @@
         }
     }
 
+    private static boolean isParameterizedAnnotation(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            return true;
+        }
+
+        return annotation.annotationType().getAnnotation(ParameterizedAnnotation.class) != null;
+    }
+
+    private static Annotation[] getIndirectAnnotations(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            return ((DynamicParameterizedAnnotation) annotation).annotations();
+        }
+        return annotation.annotationType().getAnnotations();
+    }
+
+    private static boolean isRepeatingAnnotation(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            return false;
+        }
+
+        return annotation.annotationType().getAnnotation(RepeatingAnnotation.class) != null;
+    }
+
     private static List<Annotation> getReplacementAnnotations(Annotation annotation,
-            @Nullable Class<? extends Annotation> parameterizedAnnotation) {
+            @Nullable Annotation parameterizedAnnotation) {
         List<Annotation> replacementAnnotations = new ArrayList<>();
 
-        if (annotation.annotationType().getAnnotation(RepeatingAnnotation.class) != null) {
+        if (isRepeatingAnnotation(annotation)) {
             try {
                 Annotation[] annotations =
                         (Annotation[]) annotation.annotationType()
@@ -197,14 +237,12 @@
             }
         }
 
-        if (annotation.annotationType().getAnnotation(ParameterizedAnnotation.class) != null
-                && !annotation.annotationType().equals(parameterizedAnnotation)) {
+        if (isParameterizedAnnotation(annotation) && !annotation.equals(parameterizedAnnotation)) {
             return replacementAnnotations;
         }
 
-        for (Annotation indirectAnnotation : annotation.annotationType().getAnnotations()) {
-            String annotationPackage = indirectAnnotation.annotationType().getPackage().getName();
-            if (shouldSkipAnnotation(annotationPackage)) {
+        for (Annotation indirectAnnotation : getIndirectAnnotations(annotation)) {
+            if (shouldSkipAnnotation(annotation)) {
                 continue;
             }
 
@@ -212,12 +250,21 @@
                     indirectAnnotation, parameterizedAnnotation));
         }
 
-        replacementAnnotations.add(annotation);
+        if (!(annotation instanceof DynamicParameterizedAnnotation)) {
+            // We drop the fake annotation once it's replaced
+            replacementAnnotations.add(annotation);
+        }
 
         return replacementAnnotations;
     }
 
-    private static boolean shouldSkipAnnotation(String annotationPackage) {
+    private static boolean shouldSkipAnnotation(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            return false;
+        }
+
+        String annotationPackage = annotation.annotationType().getPackage().getName();
+
         for (String ignoredPackage : sIgnoredAnnotationPackages) {
             if (ignoredPackage.endsWith(".*")) {
                 if (annotationPackage.startsWith(
@@ -236,6 +283,14 @@
         super(testClass);
     }
 
+    private boolean annotationShouldBeSkipped(Annotation annotation) {
+        if (annotation instanceof DynamicParameterizedAnnotation) {
+            return false;
+        }
+
+        return annotation.annotationType().equals(IncludeNone.class);
+    }
+
     @Override
     protected List<FrameworkMethod> computeTestMethods() {
         TestClass testClass = getTestClass();
@@ -252,7 +307,7 @@
             }
 
             for (Annotation annotation : parameterizedAnnotations) {
-                if (annotation.annotationType().equals(IncludeNone.class)) {
+                if (annotationShouldBeSkipped(annotation)) {
                     // Special case - does not generate a run
                     continue;
                 }
@@ -328,7 +383,7 @@
         parseEnterpriseAnnotations(annotations);
 
         for (Annotation annotation : annotations) {
-            if (annotation.annotationType().getAnnotation(ParameterizedAnnotation.class) != null) {
+            if (isParameterizedAnnotation(annotation)) {
                 parameterizedAnnotations.add(annotation);
             }
         }
@@ -376,7 +431,7 @@
                 EnterprisePolicy enterprisePolicy =
                         policy.getAnnotation(EnterprisePolicy.class);
                 List<Annotation> replacementAnnotations =
-                        Policy.cannotSetPolicyStates(policy.getName(), enterprisePolicy);
+                        Policy.cannotSetPolicyStates(policy.getName(), enterprisePolicy, ((CannotSetPolicyTest) annotation).includeDeviceAdminStates(), ((CannotSetPolicyTest) annotation).includeNonDeviceAdminStates());
                 replacementAnnotations.sort(BedsteadJUnit4::annotationSorter);
 
                 annotations.addAll(index, replacementAnnotations);
@@ -418,4 +473,17 @@
 
         return rules;
     }
+
+    /**
+     * True if the test is running in debug mode.
+     *
+     * <p>This will result in additional debugging information being added which would otherwise
+     * be dropped to improve test performance.
+     *
+     * <p>To enable this, pass the "bedstead-debug" instrumentation arg as "true"
+     */
+    public static boolean isDebug() {
+        Bundle arguments = InstrumentationRegistry.getArguments();
+        return Boolean.parseBoolean(arguments.getString("bedstead-debug", "false"));
+    }
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index ba6004f..75cf62b 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -60,7 +60,9 @@
 import com.android.bedstead.harrier.annotations.RequireSdkVersion;
 import com.android.bedstead.harrier.annotations.RequireUserSupported;
 import com.android.bedstead.harrier.annotations.TestTag;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDelegate;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoProfileOwner;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasProfileOwner;
@@ -85,7 +87,11 @@
 import com.android.bedstead.nene.utils.ShellCommand;
 import com.android.bedstead.nene.utils.Tags;
 import com.android.bedstead.nene.utils.Versions;
+import com.android.bedstead.remotedpc.RemoteDelegate;
 import com.android.bedstead.remotedpc.RemoteDpc;
+import com.android.bedstead.remotedpc.RemotePolicyManager;
+import com.android.bedstead.testapp.TestApp;
+import com.android.bedstead.testapp.TestAppInstance;
 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 import com.android.eventlib.EventLogs;
 
@@ -346,6 +352,17 @@
                 continue;
             }
 
+            if (annotation instanceof EnsureHasDelegate) {
+                EnsureHasDelegate ensureHasDelegateAnnotation =
+                        (EnsureHasDelegate) annotation;
+                ensureHasDelegate(
+                        ensureHasDelegateAnnotation.admin(),
+                        Arrays.asList(ensureHasDelegateAnnotation.scopes()),
+                        ensureHasDelegateAnnotation.isPrimary());
+                continue;
+            }
+
+
             if (annotation instanceof EnsureHasDeviceOwner) {
                 EnsureHasDeviceOwner ensureHasDeviceOwnerAnnotation =
                         (EnsureHasDeviceOwner) annotation;
@@ -356,6 +373,13 @@
                 continue;
             }
 
+            if (annotation instanceof EnsureHasNoDelegate) {
+                EnsureHasNoDelegate ensureHasNoDelegateAnnotation =
+                        (EnsureHasNoDelegate) annotation;
+                ensureHasNoDelegate(ensureHasNoDelegateAnnotation.admin());
+                continue;
+            }
+
             if (annotation instanceof EnsureHasNoDeviceOwner) {
                 ensureHasNoDeviceOwner();
                 continue;
@@ -602,6 +626,9 @@
 
                 Tags.clearTags();
                 Tags.addTag(Tags.USES_DEVICESTATE);
+                if (TestApis.packages().instrumented().isInstantApp()) {
+                    Tags.addTag(Tags.INSTANT_APP);
+                }
 
                 try {
                     TestApis.users().setStopBgUsersOnSwitch(STOP_USER_ON_SWITCH_FALSE);
@@ -875,7 +902,7 @@
             mProfiles = new HashMap<>();
     private DevicePolicyController mDeviceOwner;
     private Map<UserReference, DevicePolicyController> mProfileOwners = new HashMap<>();
-    private DevicePolicyController mPrimaryDpc;
+    private RemotePolicyManager mPrimaryPolicyManager;
 
     private final List<UserReference> mCreatedUsers = new ArrayList<>();
     private final List<UserBuilder> mRemovedUsers = new ArrayList<>();
@@ -1288,9 +1315,12 @@
             broadcastReceiver.unregisterQuietly();
         }
         mRegisteredBroadcastReceivers.clear();
-        mPrimaryDpc = null;
+        mPrimaryPolicyManager = null;
     }
 
+    private Set<TestAppInstance> mInstalledTestApps = new HashSet<>();
+    private Set<TestAppInstance> mUninstalledTestApps = new HashSet<>();
+
     private void teardownShareableState() {
         if (mOriginalSwitchedUser != null) {
             if (!mOriginalSwitchedUser.exists()) {
@@ -1350,6 +1380,16 @@
         }
 
         mRemovedUsers.clear();
+
+        for (TestAppInstance installedTestApp : mInstalledTestApps) {
+            installedTestApp.uninstall();
+        }
+        mInstalledTestApps.clear();
+
+        for (TestAppInstance uninstalledTestApp : mUninstalledTestApps) {
+            uninstalledTestApp.testApp().install(uninstalledTestApp.user());
+        }
+        mUninstalledTestApps.clear();
     }
 
     private UserReference createProfile(
@@ -1391,16 +1431,102 @@
         }
     }
 
+    private void ensureHasDelegate(
+            EnsureHasDelegate.AdminType adminType, List<String> scopes, boolean isPrimary) {
+        RemotePolicyManager dpc = getDeviceAdmin(adminType);
+
+
+        boolean specifiesAdminType = adminType != EnsureHasDelegate.AdminType.PRIMARY;
+        boolean currentPrimaryPolicyManagerIsNotDelegator = mPrimaryPolicyManager != dpc;
+
+        if (isPrimary && mPrimaryPolicyManager != null
+                && (specifiesAdminType || currentPrimaryPolicyManagerIsNotDelegator)) {
+            throw new IllegalStateException(
+                    "Only one DPC can be marked as primary per test (current primary is "
+                            + mPrimaryPolicyManager + ")");
+        }
+
+        if (!dpc.user().equals(TestApis.users().instrumented())) {
+            // INTERACT_ACROSS_USERS_FULL is required for RemoteDPC
+            ensureCanGetPermission(INTERACT_ACROSS_USERS_FULL);
+        }
+
+        ensureTestAppInstalled(RemoteDelegate.sTestApp, dpc.user());
+        RemoteDelegate delegate = new RemoteDelegate(RemoteDelegate.sTestApp, dpc().user());
+        dpc.devicePolicyManager().setDelegatedScopes(
+                dpc.componentName(), delegate.packageName(), scopes);
+
+        if (isPrimary) {
+            mPrimaryPolicyManager = delegate;
+        }
+    }
+
+    private void ensureHasNoDelegate(EnsureHasNoDelegate.AdminType adminType) {
+        if (adminType == EnsureHasNoDelegate.AdminType.ANY) {
+            for (UserReference user : TestApis.users().all()) {
+                ensureTestAppNotInstalled(RemoteDelegate.sTestApp, user);
+            }
+            return;
+        }
+        RemotePolicyManager dpc =
+                adminType == EnsureHasNoDelegate.AdminType.PRIMARY ? mPrimaryPolicyManager
+                : adminType == EnsureHasNoDelegate.AdminType.DEVICE_OWNER ? deviceOwner()
+                : adminType == EnsureHasNoDelegate.AdminType.PROFILE_OWNER ? profileOwner() : null;
+        if (dpc == null) {
+            throw new IllegalStateException("Unknown Admin Type " + adminType);
+        }
+
+        ensureTestAppNotInstalled(RemoteDelegate.sTestApp, dpc.user());
+    }
+
+    private RemotePolicyManager getDeviceAdmin(EnsureHasDelegate.AdminType adminType) {
+        switch (adminType) {
+            case DEVICE_OWNER:
+                return deviceOwner();
+            case PROFILE_OWNER:
+                return profileOwner();
+            case PRIMARY:
+                return dpc();
+            default:
+                throw new IllegalStateException("Unknown device admin type " + adminType);
+        }
+    }
+
+    private void ensureTestAppInstalled(TestApp testApp, UserReference user) {
+        if (TestApis.packages().find(testApp.packageName()).installedOnUser(user)) {
+            return;
+        }
+
+        mInstalledTestApps.add(testApp.install(user));
+    }
+
+    private void ensureTestAppNotInstalled(TestApp testApp, UserReference user) {
+        if (!TestApis.packages().find(testApp.packageName()).installedOnUser(user)) {
+            return;
+        }
+
+        TestAppInstance instance = testApp.instance(user);
+
+        if (mInstalledTestApps.contains(instance)) {
+            mInstalledTestApps.remove(instance);
+        } else {
+            mUninstalledTestApps.add(instance);
+        }
+
+        testApp.uninstall(user);
+    }
+
     private void ensureHasDeviceOwner(FailureMode failureMode, boolean isPrimary,
             Set<String> affiliationIds) {
         // TODO(scottjonathan): Should support non-remotedpc device owner (default to remotedpc)
 
         UserReference userReference = TestApis.users().system();
 
-        if (isPrimary && mPrimaryDpc != null && !userReference.equals(mPrimaryDpc.user())) {
+        if (isPrimary && mPrimaryPolicyManager != null && !userReference.equals(
+                mPrimaryPolicyManager.user())) {
             throw new IllegalStateException(
                     "Only one DPC can be marked as primary per test (current primary is "
-                            + mPrimaryDpc + ")");
+                            + mPrimaryPolicyManager + ")");
         }
         if (!userReference.equals(TestApis.users().instrumented())) {
             // INTERACT_ACROSS_USERS_FULL is required for RemoteDPC
@@ -1452,7 +1578,7 @@
         }
 
         if (isPrimary) {
-            mPrimaryDpc = mDeviceOwner;
+            mPrimaryPolicyManager = RemoteDpc.forDevicePolicyController(mDeviceOwner);
         }
         
         RemoteDpc.forDevicePolicyController(mDeviceOwner)
@@ -1468,7 +1594,8 @@
 
     private void ensureHasProfileOwner(
             UserReference user, boolean isPrimary, Set<String> affiliationIds) {
-        if (isPrimary && mPrimaryDpc != null && !user.equals(mPrimaryDpc.user())) {
+        if (isPrimary && mPrimaryPolicyManager != null
+                && !user.equals(mPrimaryPolicyManager.user())) {
             throw new IllegalStateException("Only one DPC can be marked as primary per test");
         }
 
@@ -1498,7 +1625,7 @@
         }
 
         if (isPrimary) {
-            mPrimaryDpc = mProfileOwners.get(user);
+            mPrimaryPolicyManager = RemoteDpc.forDevicePolicyController(mProfileOwners.get(user));
         }
 
         if (affiliationIds != null) {
@@ -1667,20 +1794,22 @@
     }
 
     /**
-     * Get the most appropriate {@link RemoteDpc} instance for the device state.
+     * Get the most appropriate {@link RemotePolicyManager} instance for the device state.
      *
      * <p>This method should only be used by tests which are annotated with {@link PolicyTest}.
      *
-     * <p>If no DPC is set as the "primary" DPC for the device state, then this method will first
+     * <p>This may be a DPC, a delegate, or a normal app with or without given permissions.
+     *
+     * <p>If no policy manager is set as "primary" for the device state, then this method will first
      * check for a profile owner in the current user, or else check for a device owner.
      *
      * <p>If no Harrier-managed profile owner or device owner exists, an exception will be thrown.
      *
      * <p>If the profile owner or device owner is not a RemoteDPC then an exception will be thrown.
      */
-    public RemoteDpc dpc() {
-        if (mPrimaryDpc != null) {
-            return RemoteDpc.forDevicePolicyController(mPrimaryDpc);
+    public RemotePolicyManager dpc() {
+        if (mPrimaryPolicyManager != null) {
+            return mPrimaryPolicyManager;
         }
 
         if (mProfileOwners.containsKey(TestApis.users().instrumented())) {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DynamicParameterizedAnnotation.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DynamicParameterizedAnnotation.java
new file mode 100644
index 0000000..15afb2b
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DynamicParameterizedAnnotation.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A fake annotation used inside {@link Policy} and {@link BedsteadJUnit4} to inject
+ * new parameterizations.
+ */
+public final class DynamicParameterizedAnnotation implements Annotation {
+    private final String mName;
+    private final Annotation[] mAnnotations;
+
+    DynamicParameterizedAnnotation(String name, Annotation[] annotations) {
+        mName = name;
+        mAnnotations = annotations;
+    }
+
+    /** Get the parameterization name. */
+    public String name() {
+        return mName;
+    }
+
+    /** Get the annotations applied to the parameterization. */
+    public Annotation[] annotations() {
+        return mAnnotations;
+    }
+
+    @Override
+    public Class<? extends Annotation> annotationType() {
+        // This is special cased in BedsteadJUnit4 so will never be called
+        return null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DynamicParameterizedAnnotation)) return false;
+        DynamicParameterizedAnnotation that = (DynamicParameterizedAnnotation) o;
+        return Objects.equals(mName, that.mName) && Arrays.equals(mAnnotations,
+                that.mAnnotations);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mName);
+        result = 31 * result + Arrays.hashCode(mAnnotations);
+        return result;
+    }
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java
index c588ace..e01c8f1 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java
@@ -16,6 +16,18 @@
 
 package com.android.bedstead.harrier;
 
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_SELECTION;
+import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP;
+import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_PACKAGE;
+import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES;
+import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
+import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
+import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
+
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_AFFILIATED_PROFILE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER;
@@ -28,9 +40,11 @@
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_PARENT;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_UNAFFILIATED_OTHER_USERS;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.CAN_BE_DELEGATED;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.DO_NOT_APPLY_TO_NEGATIVE_TESTS;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.NO;
 
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate;
 import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
 import com.android.bedstead.harrier.annotations.parameterized.IncludeNone;
 import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnAffiliatedDeviceOwnerSecondaryUser;
@@ -47,21 +61,94 @@
 
 import com.google.auto.value.AutoAnnotation;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * Utility class for enterprise policy tests.
  */
 public final class Policy {
 
+    // Delegate scopes to be used for a "CannotSet" state. All delegate scopes except the ones which
+    // should allow use of the API will be granted
+    private static final ImmutableSet<String> ALL_DELEGATE_SCOPES = ImmutableSet.of(
+            DELEGATION_CERT_INSTALL,
+            DELEGATION_APP_RESTRICTIONS,
+            DELEGATION_BLOCK_UNINSTALL,
+            DELEGATION_PERMISSION_GRANT,
+            DELEGATION_PACKAGE_ACCESS,
+            DELEGATION_ENABLE_SYSTEM_APP,
+            DELEGATION_INSTALL_EXISTING_PACKAGE,
+            DELEGATION_KEEP_UNINSTALLED_PACKAGES,
+            DELEGATION_NETWORK_LOGGING,
+            DELEGATION_CERT_SELECTION,
+            DELEGATION_SECURITY_LOGGING
+    );
+
+    // This is a map containing all Include* annotations and the flags which lead to them
+    // This is not validated - every state must have a single APPLIED_BY annotation
+    private static final ImmutableMap<Integer, Function<EnterprisePolicy, Set<Annotation>>>
+            STATE_ANNOTATIONS =
+            ImmutableMap.<Integer, Function<EnterprisePolicy, Set<Annotation>>>builder()
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER, singleAnnotation(includeRunOnDeviceOwnerUser()))
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnDeviceOwnerUser(), /* isPrimary= */ true))
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND, singleAnnotation(includeRunOnBackgroundDeviceOwnerUser()))
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnBackgroundDeviceOwnerUser(), /* isPrimary= */ true))
+
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_UNAFFILIATED_OTHER_USERS, singleAnnotation(includeRunOnNonAffiliatedDeviceOwnerSecondaryUser()))
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_UNAFFILIATED_OTHER_USERS | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnNonAffiliatedDeviceOwnerSecondaryUser(), /* isPrimary= */ true))
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_AFFILIATED_OTHER_USERS, singleAnnotation(includeRunOnAffiliatedDeviceOwnerSecondaryUser()))
+                    .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_AFFILIATED_OTHER_USERS | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnAffiliatedDeviceOwnerSecondaryUser(), /* isPrimary= */ true))
+
+                    .put(APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER, singleAnnotation(includeRunOnAffiliatedProfileOwnerSecondaryUser()))
+                    .put(APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnAffiliatedProfileOwnerSecondaryUser(), /* isPrimary= */ true))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER, singleAnnotation(includeRunOnUnaffiliatedProfileOwnerSecondaryUser()))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnUnaffiliatedProfileOwnerSecondaryUser(), /* isPrimary= */ true))
+                    .put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO | APPLIES_TO_OWN_USER, singleAnnotation(includeRunOnProfileOwnerPrimaryUser()))
+                    .put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnProfileOwnerPrimaryUser(), /* isPrimary= */ true))
+
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_OWN_USER, singleAnnotation(includeRunOnProfileOwnerProfileWithNoDeviceOwner()))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnProfileOwnerProfileWithNoDeviceOwner(), /* isPrimary= */ true))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_PARENT, singleAnnotation(includeRunOnParentOfProfileOwnerWithNoDeviceOwner()))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_PARENT | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnParentOfProfileOwnerWithNoDeviceOwner(), /* isPrimary= */ true))
+
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_UNAFFILIATED_OTHER_USERS, singleAnnotation(includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile()))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_UNAFFILIATED_OTHER_USERS | CAN_BE_DELEGATED, generateDelegateAnnotation(includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile(), /* isPrimary= */ true))
+                    .build();
+    // This must contain one key for every APPLIED_BY that is being used, and maps to the
+    // "default" for testing that DPC type
+    // in general this will be a state which runs on the same user as the dpc.
+    private static final ImmutableMap<Integer, Function<EnterprisePolicy, Set<Annotation>>>
+            DPC_STATE_ANNOTATIONS_BASE =
+            ImmutableMap.<Integer, Function<EnterprisePolicy, Set<Annotation>>>builder()
+                    .put(APPLIED_BY_DEVICE_OWNER, (flags) -> hasFlag(flags.dpc(), APPLIED_BY_DEVICE_OWNER | APPLIES_IN_BACKGROUND) ? ImmutableSet.of(includeRunOnBackgroundDeviceOwnerUser()) : ImmutableSet.of(includeRunOnDeviceOwnerUser()))
+                    .put(APPLIED_BY_AFFILIATED_PROFILE_OWNER, singleAnnotation(includeRunOnAffiliatedProfileOwnerSecondaryUser()))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER, singleAnnotation(includeRunOnProfileOwnerPrimaryUser()))
+                    .put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO, singleAnnotation(includeRunOnProfileOwnerPrimaryUser()))
+                    .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE, singleAnnotation(includeRunOnProfileOwnerProfileWithNoDeviceOwner()))
+                    .build();
+    private static final Map<Integer, Function<EnterprisePolicy, Set<Annotation>>>
+            DPC_STATE_ANNOTATIONS = DPC_STATE_ANNOTATIONS_BASE.entrySet().stream()
+            .collect(Collectors.toMap(Map.Entry::getKey, Policy::addGeneratedStates));
+    private static final int APPLIED_BY_FLAGS =
+            APPLIED_BY_DEVICE_OWNER | APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE
+                    | APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE
+                    | APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER
+                    | APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER;
+    private static final Map<Function<EnterprisePolicy, Set<Annotation>>, Set<Integer>>
+            ANNOTATIONS_MAP = calculateAnnotationsMap(STATE_ANNOTATIONS);
+
     private Policy() {
 
     }
@@ -126,49 +213,42 @@
         return new AutoAnnotation_Policy_includeRunOnBackgroundDeviceOwnerUser();
     }
 
-    // This is a map containing all Include* annotations and the flags which lead to them
-    // This is not validated - every state must have a single APPLIED_BY annotation
-    private static final ImmutableMap<Integer, Annotation> STATE_ANNOTATIONS = ImmutableMap.<Integer, Annotation>builder()
-            .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER, includeRunOnDeviceOwnerUser())
-            .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND, includeRunOnBackgroundDeviceOwnerUser())
+    @AutoAnnotation
+    private static EnsureHasDelegate ensureHasDelegate(EnsureHasDelegate.AdminType admin,
+            String[] scopes, boolean isPrimary) {
+        return new AutoAnnotation_Policy_ensureHasDelegate(admin, scopes, isPrimary);
+    }
 
-            .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_UNAFFILIATED_OTHER_USERS, includeRunOnNonAffiliatedDeviceOwnerSecondaryUser())
-            .put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_AFFILIATED_OTHER_USERS, includeRunOnAffiliatedDeviceOwnerSecondaryUser())
+    private static Function<EnterprisePolicy, Set<Annotation>> singleAnnotation(
+            Annotation annotation) {
+        return (i) -> ImmutableSet.of(annotation);
+    }
 
-            .put(APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER, includeRunOnAffiliatedProfileOwnerSecondaryUser())
-            .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER, includeRunOnUnaffiliatedProfileOwnerSecondaryUser())
-            .put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO | APPLIES_TO_OWN_USER, includeRunOnProfileOwnerPrimaryUser())
+    private static Function<EnterprisePolicy, Set<Annotation>> generateDelegateAnnotation(
+            Annotation annotation, boolean isPrimary) {
+        return (policy) -> {
+            Annotation[] existingAnnotations = annotation.annotationType().getAnnotations();
+            return Arrays.stream(policy.delegatedScopes())
+                    .map(scope -> {
+                        Annotation[] newAnnotations = Arrays.copyOf(existingAnnotations,
+                                existingAnnotations.length + 1);
+                        newAnnotations[newAnnotations.length - 1] = ensureHasDelegate(
+                                EnsureHasDelegate.AdminType.PRIMARY, new String[]{scope},
+                                isPrimary);
 
-            .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_OWN_USER, includeRunOnProfileOwnerProfileWithNoDeviceOwner())
-            .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_PARENT, includeRunOnParentOfProfileOwnerWithNoDeviceOwner())
+                        return new DynamicParameterizedAnnotation(
+                                annotation.annotationType().getSimpleName() + "Delegate:" + scope,
+                                newAnnotations);
+                    }).collect(Collectors.toSet());
+        };
+    }
 
-            .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_UNAFFILIATED_OTHER_USERS, includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile())
-            .build();
+    private static Map<Function<EnterprisePolicy, Set<Annotation>>, Set<Integer>> calculateAnnotationsMap(
+            Map<Integer, Function<EnterprisePolicy, Set<Annotation>>> annotations) {
+        Map<Function<EnterprisePolicy, Set<Annotation>>, Set<Integer>> b = new HashMap<>();
 
-    // This must contain one key for every APPLIED_BY that is being used, and maps to the "default" for testing that DPC type
-    // in general this will be a state which runs on the same user as the dpc.
-    private static final ImmutableMap<Integer, Annotation> DPC_STATE_ANNOTATIONS = ImmutableMap.<Integer, Annotation>builder()
-            .put(APPLIED_BY_DEVICE_OWNER, includeRunOnDeviceOwnerUser())
-            .put(APPLIED_BY_AFFILIATED_PROFILE_OWNER, includeRunOnAffiliatedProfileOwnerSecondaryUser())
-            .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER, includeRunOnProfileOwnerPrimaryUser())
-            .put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO, includeRunOnProfileOwnerPrimaryUser())
-            .put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE, includeRunOnProfileOwnerProfileWithNoDeviceOwner())
-            .build();
-
-    private static final int APPLIED_BY_FLAGS =
-            APPLIED_BY_DEVICE_OWNER | APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE
-                    | APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE
-                    | APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER
-                    | APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER;
-
-
-    private static final Map<Annotation, Set<Integer>> ANNOTATIONS_MAP = calculateAnnotationsMap(STATE_ANNOTATIONS);
-
-    private static Map<Annotation, Set<Integer>> calculateAnnotationsMap(
-            Map<Integer, Annotation> annotations) {
-        Map<Annotation, Set<Integer>> b = new HashMap<>();
-
-        for (Map.Entry<Integer, Annotation> i : annotations.entrySet()) {
+        for (Map.Entry<Integer, Function<EnterprisePolicy, Set<Annotation>>> i :
+                annotations.entrySet()) {
             if (!b.containsKey(i.getValue())) {
                 b.put(i.getValue(), new HashSet<>());
             }
@@ -179,20 +259,39 @@
         return b;
     }
 
+    private static Function<EnterprisePolicy, Set<Annotation>> addGeneratedStates(
+            ImmutableMap.Entry<Integer, Function<EnterprisePolicy, Set<Annotation>>> entry) {
+        return (policy) -> {
+            if (hasFlag(policy.dpc(), entry.getKey() | CAN_BE_DELEGATED)) {
+                Set<Annotation> results = new HashSet<>(entry.getValue().apply(policy));
+                results.addAll(results.stream().flatMap(
+                        t -> generateDelegateAnnotation(t, /* isPrimary= */ true).apply(
+                                policy).stream())
+                        .collect(Collectors.toSet()));
+
+                return results;
+            }
+
+            return entry.getValue().apply(policy);
+        };
+    }
+
 
     /**
-     * Get positive state annotations for the given policy.
+     * Get parameterized test runs for the given policy.
      *
      * <p>These are states which should be run where the policy is able to be applied.
      */
-    public static List<Annotation> positiveStates(String policyName, EnterprisePolicy enterprisePolicy) {
+    public static List<Annotation> positiveStates(String policyName,
+            EnterprisePolicy enterprisePolicy) {
         Set<Annotation> annotations = new HashSet<>();
 
         validateFlags(policyName, enterprisePolicy.dpc());
 
-        for (Map.Entry<Annotation, Set<Integer>> annotation : ANNOTATIONS_MAP.entrySet()) {
+        for (Map.Entry<Function<EnterprisePolicy, Set<Annotation>>, Set<Integer>> annotation :
+                ANNOTATIONS_MAP.entrySet()) {
             if (isPositive(enterprisePolicy.dpc(), annotation.getValue())) {
-                annotations.add(annotation.getKey());
+                annotations.addAll(annotation.getKey().apply(enterprisePolicy));
             }
         }
 
@@ -215,13 +314,15 @@
 
     private static boolean isNegative(int[] policyFlags, Set<Integer> annotationFlags) {
         for (int annotationFlag : annotationFlags) {
-            if (hasFlag(annotationFlag, DO_NOT_APPLY_TO_NEGATIVE_TESTS, /* nonMatchingFlag= */ NO)) {
+            if (hasFlag(annotationFlag, DO_NOT_APPLY_TO_NEGATIVE_TESTS, /* nonMatchingFlag= */
+                    NO)) {
                 return false; // We don't support using this annotation for negative tests
             }
 
             int appliedByFlag = APPLIED_BY_FLAGS & annotationFlag;
             int otherFlags = annotationFlag ^ appliedByFlag; // remove the appliedByFlag
-            if (hasFlag(policyFlags, /* matchingFlag= */ appliedByFlag, /* nonMatchingFlag= */ otherFlags)) {
+            if (hasFlag(policyFlags, /* matchingFlag= */ appliedByFlag, /* nonMatchingFlag= */
+                    otherFlags)) {
                 return true;
             }
         }
@@ -230,18 +331,20 @@
     }
 
     /**
-     * Get negative state annotations for the given policy.
+     * Get negative parameterized test runs for the given policy.
      *
      * <p>These are states which should be run where the policy is not able to be applied.
      */
-    public static List<Annotation> negativeStates(String policyName, EnterprisePolicy enterprisePolicy) {
+    public static List<Annotation> negativeStates(String policyName,
+            EnterprisePolicy enterprisePolicy) {
         Set<Annotation> annotations = new HashSet<>();
 
         validateFlags(policyName, enterprisePolicy.dpc());
 
-        for (Map.Entry<Annotation, Set<Integer>> annotation : ANNOTATIONS_MAP.entrySet()) {
+        for (Map.Entry<Function<EnterprisePolicy, Set<Annotation>>, Set<Integer>> annotation :
+                ANNOTATIONS_MAP.entrySet()) {
             if (isNegative(enterprisePolicy.dpc(), annotation.getValue())) {
-                annotations.add(annotation.getKey());
+                annotations.addAll(annotation.getKey().apply(enterprisePolicy));
             }
         }
 
@@ -254,23 +357,60 @@
     }
 
     /**
-     * Get state annotations where the policy cannot be set for the given policy.
+     * Get parameterized test runs where the policy cannot be set for the given policy.
      */
-    public static List<Annotation> cannotSetPolicyStates(String policyName, EnterprisePolicy enterprisePolicy) {
+    public static List<Annotation> cannotSetPolicyStates(String policyName,
+            EnterprisePolicy enterprisePolicy, boolean includeDeviceAdminStates,
+            boolean includeNonDeviceAdminStates) {
         Set<Annotation> annotations = new HashSet<>();
 
         validateFlags(policyName, enterprisePolicy.dpc());
 
-        // TODO(scottjonathan): Always include a state without a dpc
+        if (includeDeviceAdminStates) {
+            int allFlags = 0;
+            for (int p : enterprisePolicy.dpc()) {
+                allFlags = allFlags | p;
+            }
 
-        int allFlags = 0;
-        for (int p : enterprisePolicy.dpc()) {
-            allFlags = allFlags | p;
+            for (Map.Entry<Integer, Function<EnterprisePolicy, Set<Annotation>>> appliedByFlag :
+                    DPC_STATE_ANNOTATIONS.entrySet()) {
+                if ((appliedByFlag.getKey() & allFlags) == 0) {
+                    annotations.addAll(appliedByFlag.getValue().apply(enterprisePolicy));
+                }
+            }
         }
 
-        for (Map.Entry<Integer, Annotation> appliedByFlag : DPC_STATE_ANNOTATIONS.entrySet()) {
-            if ((appliedByFlag.getKey() & allFlags) == 0) {
-                annotations.add(appliedByFlag.getValue());
+        if (includeNonDeviceAdminStates) {
+            Set<String> validScopes = ImmutableSet.copyOf(enterprisePolicy.delegatedScopes());
+            String[] scopes = ALL_DELEGATE_SCOPES.stream()
+                    .filter(i -> !validScopes.contains(i))
+                    .toArray(String[]::new);
+            Annotation[] existingAnnotations = IncludeRunOnDeviceOwnerUser.class.getAnnotations();
+
+            if (BedsteadJUnit4.isDebug()) {
+                // Add a non-DPC with no delegate scopes
+                Annotation[] newAnnotations = Arrays.copyOf(existingAnnotations,
+                        existingAnnotations.length + 1);
+                newAnnotations[newAnnotations.length - 1] = ensureHasDelegate(
+                        EnsureHasDelegate.AdminType.PRIMARY, new String[]{}, /* isPrimary= */ true);
+                annotations.add(
+                        new DynamicParameterizedAnnotation("DelegateWithNoScopes", newAnnotations));
+
+                for (String scope : scopes) {
+                    newAnnotations = Arrays.copyOf(existingAnnotations,
+                            existingAnnotations.length + 1);
+                    newAnnotations[newAnnotations.length - 1] = ensureHasDelegate(
+                            EnsureHasDelegate.AdminType.PRIMARY, new String[]{scope}, /* isPrimary= */ true);
+                    annotations.add(
+                            new DynamicParameterizedAnnotation("DelegateWithScope:" + scope, newAnnotations));
+                }
+            } else {
+                Annotation[] newAnnotations = Arrays.copyOf(existingAnnotations,
+                        existingAnnotations.length + 1);
+                newAnnotations[newAnnotations.length - 1] = ensureHasDelegate(
+                        EnsureHasDelegate.AdminType.PRIMARY, scopes, /* isPrimary= */ true);
+                annotations.add(
+                        new DynamicParameterizedAnnotation("DelegateWithoutValidScope", newAnnotations));
             }
         }
 
@@ -296,9 +436,10 @@
             allFlags = allFlags | p;
         }
 
-        for (Map.Entry<Integer, Annotation> appliedByFlag : DPC_STATE_ANNOTATIONS.entrySet()) {
+        for (Map.Entry<Integer, Function<EnterprisePolicy, Set<Annotation>>> appliedByFlag :
+                DPC_STATE_ANNOTATIONS.entrySet()) {
             if ((appliedByFlag.getKey() & allFlags) == appliedByFlag.getKey()) {
-                annotations.add(appliedByFlag.getValue());
+                annotations.addAll(appliedByFlag.getValue().apply(enterprisePolicy));
             }
         }
 
@@ -311,8 +452,14 @@
 
         if (singleTestOnly) {
             // We select one annotation in an arbitrary but deterministic way
-            annotationList.sort(Comparator.comparing(a -> a.annotationType().getName()));
-            Annotation firstAnnotation = annotationList.get(0);
+            annotationList.sort(Comparator.comparing(
+                    a -> a instanceof DynamicParameterizedAnnotation
+                            ? "DynamicParameterizedAnnotation" : a.annotationType().getName()));
+
+            // We don't want a delegate to be the representative test
+            Annotation firstAnnotation = annotationList.stream()
+                    .filter(i -> !(i instanceof  DynamicParameterizedAnnotation))
+                    .findFirst().get();
             annotationList.clear();
             annotationList.add(firstAnnotation);
         }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunNotOnSecondaryUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunNotOnSecondaryUser.java
new file mode 100644
index 0000000..446bb85
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunNotOnSecondaryUser.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import static com.android.bedstead.harrier.annotations.AnnotationRunPrecedence.EARLY;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should run on a non-secondary user.
+ *
+ * <p>Your test configuration should be such that this test is only run where a non-secondary user
+ * is created and the test is being run on that user.
+ *
+ * <p>Optionally, you can guarantee that these methods do not run on a secondary user by
+ * using {@link DeviceState}.
+ *
+ * <p>This annotation by default opts a test into multi-user presubmit. New tests should also be
+ * annotated {@link Postsubmit} until they are shown to meet the multi-user presubmit
+ * requirements.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+// To ensure that the test doesn't run on the secondary user we require that the test run on
+// the primary user to ensure consistent behaviour.
+@RequireRunOnPrimaryUser
+public @interface RequireRunNotOnSecondaryUser {
+    /**
+     * Weight sets the order that annotations will be resolved.
+     *
+     * <p>Annotations with a lower weight will be resolved before annotations with a higher weight.
+     *
+     * <p>If there is an order requirement between annotations, ensure that the weight of the
+     * annotation which must be resolved first is lower than the one which must be resolved later.
+     *
+     * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
+     */
+    int weight() default EARLY;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/SlowApiTest.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/SlowApiTest.java
new file mode 100644
index 0000000..241a5ca
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/SlowApiTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to indicate that a test can take a long time to run.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SlowApiTest {
+    String value();
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java
index 9315103..d0f1985 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java
@@ -45,6 +45,17 @@
     Class<?> policy();
 
     /**
+     * If true, then this will run in states where the app is a device admin but is not one which is
+     * allowed to make the call.
+     */
+    boolean includeDeviceAdminStates() default true;
+
+    /**
+     * If true, then this will run in states where the app is not a device admin.
+     */
+    boolean includeNonDeviceAdminStates() default true;
+
+    /**
      * Weight sets the order that annotations will be resolved.
      *
      * <p>Annotations with a lower weight will be resolved before annotations with a higher weight.
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDelegate.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDelegate.java
new file mode 100644
index 0000000..c581561
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDelegate.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations.enterprise;
+
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner.DO_PO_WEIGHT;
+
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test requires that the given admin delegates the given scope to a test app.
+ *
+ * <p>You should use {@link DeviceState} to ensure that the device enters
+ * the correct state for the method. You can use {@link DeviceState#delegate()} to interact with
+ * the delegate.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnsureHasDelegate {
+
+    int ENSURE_HAS_DELEGATE_WEIGHT = DO_PO_WEIGHT + 1; // Should run after setting DO/PO
+
+    enum AdminType {
+        DEVICE_OWNER,
+        PROFILE_OWNER,
+        PRIMARY
+    }
+
+    /**
+     * The admin that should delegate this scope.
+     *
+     * <p>If this is set to {@link AdminType#PRIMARY} and {@link #isPrimary()} is true, then the
+     * delegate will replace the primary dpc as primary without error.
+     */
+    AdminType admin();
+
+    /** The scope being delegated. */
+    String[] scopes();
+
+    /**
+     * Whether this delegate should be returned by calls to {@link DeviceState#policyManager()}.
+     *
+     * <p>Only one policy manager per test should be marked as primary.
+     */
+    boolean isPrimary() default false;
+
+    /**
+     * Weight sets the order that annotations will be resolved.
+     *
+     * <p>Annotations with a lower weight will be resolved before annotations with a higher weight.
+     *
+     * <p>If there is an order requirement between annotations, ensure that the weight of the
+     * annotation which must be resolved first is lower than the one which must be resolved later.
+     *
+     * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
+     */
+    int weight() default ENSURE_HAS_DELEGATE_WEIGHT;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java
index 483a73e..2fdfc9d 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java
@@ -49,13 +49,17 @@
 @Retention(RetentionPolicy.RUNTIME)
 @RequireFeature(FEATURE_DEVICE_ADMIN)
 public @interface EnsureHasDeviceOwner {
+
+    int DO_PO_WEIGHT = MIDDLE;
+
      /** Behaviour if the device owner cannot be set. */
     FailureMode failureMode() default FailureMode.FAIL;
 
     /**
-     * Whether this DPC should be returned by calls to {@link DeviceState#dpc()}.
+     * Whether this DPC should be returned by calls to {@link DeviceState#dpc()} or
+     * {@link DeviceState#policyManager()}}.
      *
-     * <p>Only one device policy controller per test should be marked as primary.
+     * <p>Only one policy manager per test should be marked as primary.
      */
     boolean isPrimary() default false;
 
@@ -74,5 +78,5 @@
      *
      * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
      */
-    int weight() default MIDDLE;
+    int weight() default DO_PO_WEIGHT;
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDelegate.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDelegate.java
new file mode 100644
index 0000000..c0516f3
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDelegate.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations.enterprise;
+
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate.ENSURE_HAS_DELEGATE_WEIGHT;
+
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test requires that the given admin does not delegate the given scope to a test app.
+ *
+ * <p>You should use {@link DeviceState} to ensure that the device enters
+ * the correct state for the method.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnsureHasNoDelegate {
+
+    enum AdminType {
+        DEVICE_OWNER,
+        PROFILE_OWNER,
+        PRIMARY,
+        ANY
+    }
+
+
+    /**
+     * The admin that should not delegate this scope.
+     *
+     * <p>Defaults to any admin
+     *
+     * <p>If this is set to {@link AdminType#PRIMARY} and {@link #isPrimary()} is true, then the
+     * delegate will replace the primary dpc as primary without error.
+     */
+    AdminType admin() default AdminType.ANY;
+
+    /**
+     * Weight sets the order that annotations will be resolved.
+     *
+     * <p>Annotations with a lower weight will be resolved before annotations with a higher weight.
+     *
+     * <p>If there is an order requirement between annotations, ensure that the weight of the
+     * annotation which must be resolved first is lower than the one which must be resolved later.
+     *
+     * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
+     */
+    int weight() default ENSURE_HAS_DELEGATE_WEIGHT + 1; // Should run after setting delegate
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java
index 197a5fb..307b554 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java
@@ -16,7 +16,7 @@
 
 package com.android.bedstead.harrier.annotations.enterprise;
 
-import static com.android.bedstead.harrier.annotations.AnnotationRunPrecedence.MIDDLE;
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner.DO_PO_WEIGHT;
 
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
@@ -47,5 +47,5 @@
      *
      * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
      */
-    int weight() default MIDDLE;
+    int weight() default DO_PO_WEIGHT;
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDpc.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDpc.java
index 72881ba..c97fd7f 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDpc.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDpc.java
@@ -16,7 +16,7 @@
 
 package com.android.bedstead.harrier.annotations.enterprise;
 
-import static com.android.bedstead.harrier.annotations.AnnotationRunPrecedence.MIDDLE;
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner.DO_PO_WEIGHT;
 
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
@@ -53,5 +53,5 @@
      *
      * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
      */
-    int weight() default MIDDLE;
+    int weight() default DO_PO_WEIGHT;
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java
index 2d24990..561da3c 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java
@@ -17,7 +17,7 @@
 package com.android.bedstead.harrier.annotations.enterprise;
 
 import static com.android.bedstead.harrier.DeviceState.UserType.CURRENT_USER;
-import static com.android.bedstead.harrier.annotations.AnnotationRunPrecedence.MIDDLE;
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner.DO_PO_WEIGHT;
 
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
@@ -49,5 +49,5 @@
      *
      * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
      */
-    int weight() default MIDDLE;
+    int weight() default DO_PO_WEIGHT;
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java
index eb5c9e6..a408a4d 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java
@@ -19,7 +19,7 @@
 import static android.content.pm.PackageManager.FEATURE_DEVICE_ADMIN;
 
 import static com.android.bedstead.harrier.DeviceState.UserType.CURRENT_USER;
-import static com.android.bedstead.harrier.annotations.AnnotationRunPrecedence.MIDDLE;
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner.DO_PO_WEIGHT;
 
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.AnnotationRunPrecedence;
@@ -45,9 +45,10 @@
     DeviceState.UserType onUser() default CURRENT_USER;
 
     /**
-     * Whether this DPC should be returned by calls to {@link DeviceState#dpc()}.
+     * Whether this DPC should be returned by calls to {@link DeviceState#dpc()} or
+     * {@link DeviceState#policyManager()}}.
      *
-     * <p>Only one device policy controller per test should be marked as primary.
+     * <p>Only one policy manager per test should be marked as primary.
      */
     boolean isPrimary() default false;
 
@@ -66,5 +67,5 @@
      *
      * <p>Weight can be set to a {@link AnnotationRunPrecedence} constant, or to any {@link int}.
      */
-    int weight() default MIDDLE;
+    int weight() default DO_PO_WEIGHT;
 }
\ No newline at end of file
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnterprisePolicy.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnterprisePolicy.java
index f484370..9b3c16a 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnterprisePolicy.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnterprisePolicy.java
@@ -80,8 +80,6 @@
     int APPLIES_TO_AFFILIATED_CHILD_PROFILES = 1 << 4;
     /** A policy that applies to the parent of the profile of the package which applied the policy. */
     int APPLIES_TO_PARENT = 1 << 5;
-    /** A policy that applies to the parent of the profile of the package which applied the policy if that profile is a COPE profile. */
-    int APPLIES_TO_COPE_PARENT = 1 << 6;
 
     /** A policy that applies to affiliated or unaffiliate profiles of the package which applied the policy. */
     int APPLIES_TO_CHILD_PROFILES =
@@ -97,12 +95,16 @@
     // Applied by
 
     /** A policy that can be applied by a device owner. */
-    int APPLIED_BY_DEVICE_OWNER = 1 << 7;
+    int APPLIED_BY_DEVICE_OWNER = 1 << 6;
     /** A policy that can be applied by a profile owner of an unaffiliated profile. */
-    int APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE = 1 << 8;
+    int APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE = 1 << 7;
     /** A policy that can be applied by a profile owner of an affiliated profile */
-    int APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE = 1 << 9;
-    /** A policy that can be applied by a profile owner of an affiliated or unaffiliated profile. */
+    int APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE = 1 << 8;
+    /** A policy that can be applied by a profile owner of a cope profile */
+    int APPLIED_BY_COPE_PROFILE_OWNER = 1 << 9;
+
+    /** A policy that can be applied by a profile owner of an affiliated or unaffiliated profile.
+     * This does not include cope profiles. */
     int APPLIED_BY_PROFILE_OWNER_PROFILE =
             APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE
                     | APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE;
@@ -164,6 +166,14 @@
      * {@link APPLIES_IN_BACKGROUND}. */
     int DOES_NOT_APPLY_IN_BACKGROUND = 1 << 18;
 
+
+    /**
+     * A policy which can be applied by a delegate.
+     *
+     * See {@link #delegatedScopes()} for the scopes which enable this.
+     */
+    int CAN_BE_DELEGATED = 1 << 19;
+
     /** Flags indicating DPC states which can set the policy. */
     int[] dpc() default {};
 
@@ -184,7 +194,7 @@
     /**
      * {@link DelegatedScope} indicating which delegated scopes can control the policy.
      *
-     * <p>Note that this currently does not generate any additional tests but may do in future.
+     * <p>This applies to {@link #dpc()} entries with the {@link #CAN_BE_DELEGATED} flag.
      */
-    DelegatedScope[] delegatedScopes() default {};
+    String[] delegatedScopes() default {};
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictions.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictions.java
index 5944757..9e30dad 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictions.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictions.java
@@ -16,10 +16,13 @@
 
 package com.android.bedstead.harrier.policies;
 
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_IN_BACKGROUND;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.CAN_BE_DELEGATED;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -34,8 +37,11 @@
  * {@link DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle)} and
  * {@link DevicePolicyManager#getApplicationRestrictions(ComponentName, String)}.
  */
-@EnterprisePolicy(dpc = {
-        APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND,
-        APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER})
+@EnterprisePolicy(
+        dpc = {
+            APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND | CAN_BE_DELEGATED,
+            APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED},
+        delegatedScopes = DELEGATION_APP_RESTRICTIONS
+        )
 public final class ApplicationRestrictions {
 }
\ No newline at end of file
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/DeprecatedPasswordAPIs.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictionsManagingPackage.java
similarity index 62%
rename from common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/DeprecatedPasswordAPIs.java
rename to common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictionsManagingPackage.java
index 5eb0053..379b071 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/DeprecatedPasswordAPIs.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/ApplicationRestrictionsManagingPackage.java
@@ -18,14 +18,24 @@
 
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_IN_BACKGROUND;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
 
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.os.Bundle;
+
 import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
 
 /**
- * Policy for test cases testing password management APIs that are deprecated and not supported in
- * some platforms.
+ * Policy for application restrictions.
+ *
+ * <p>This is used by the method
+ * {@link DevicePolicyManager#setApplicationRestrictionsManagingPackage(ComponentName, String, Bundle)}
  */
-@EnterprisePolicy(dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER)
-public final class DeprecatedPasswordAPIs {
+@EnterprisePolicy(
+        dpc = {
+                APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND,
+                APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER})
+public final class ApplicationRestrictionsManagingPackage {
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetPermissionGrantState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetPermissionGrantState.java
index 12bb005..bc3861a 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetPermissionGrantState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetPermissionGrantState.java
@@ -21,12 +21,12 @@
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.CAN_BE_DELEGATED;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 
 import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
-import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.DelegatedScope;
 
 /**
  * Policies around setting the grant state of a basic permission.
@@ -36,8 +36,7 @@
  * granting permissions not covered by other policies.
  */
 @EnterprisePolicy(
-        dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER,
-        delegatedScopes = @DelegatedScope(
-                scope = DELEGATION_PERMISSION_GRANT, appliesTo = APPLIES_TO_OWN_USER))
+        dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER | CAN_BE_DELEGATED,
+        delegatedScopes = DELEGATION_PERMISSION_GRANT)
 public final class SetPermissionGrantState {
 }
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetSmsPermissionGranted.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetSmsPermissionGranted.java
index 67f5615..fac7dcd 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetSmsPermissionGranted.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/SetSmsPermissionGranted.java
@@ -16,9 +16,12 @@
 
 package com.android.bedstead.harrier.policies;
 
+import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
+
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER_USER;
 import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.CAN_BE_DELEGATED;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -34,6 +37,7 @@
  */
 // TODO(198311372): Check if APPLIED_BY_PROFILE_OWNER_USER is expected
 @EnterprisePolicy(
-        dpc = APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIED_BY_PROFILE_OWNER_USER)
+        dpc = APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIED_BY_PROFILE_OWNER_USER | CAN_BE_DELEGATED,
+        delegatedScopes = DELEGATION_PERMISSION_GRANT)
 public final class SetSmsPermissionGranted {
 }
diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
index d4c8eb8..ba3c2ef 100644
--- a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
+++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
@@ -27,6 +29,8 @@
 import static com.android.bedstead.harrier.OptionalBoolean.TRUE;
 import static com.android.bedstead.harrier.annotations.RequireAospBuild.GMS_CORE_PACKAGE;
 import static com.android.bedstead.harrier.annotations.RequireCnGmsBuild.CHINA_GOOGLE_SERVICES_FEATURE;
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate.AdminType.DEVICE_OWNER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate.AdminType.PRIMARY;
 import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
 import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
 import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME;
@@ -37,6 +41,8 @@
 
 import android.app.ActivityManager;
 import android.os.Build;
+import android.os.Bundle;
+import android.platform.test.annotations.AppModeFull;
 
 import com.android.bedstead.harrier.annotations.EnsureDoesNotHavePermission;
 import com.android.bedstead.harrier.annotations.EnsureHasNoSecondaryUser;
@@ -59,6 +65,7 @@
 import com.android.bedstead.harrier.annotations.RequireNotLowRamDevice;
 import com.android.bedstead.harrier.annotations.RequirePackageInstalled;
 import com.android.bedstead.harrier.annotations.RequirePackageNotInstalled;
+import com.android.bedstead.harrier.annotations.RequireRunNotOnSecondaryUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnTvProfile;
@@ -66,6 +73,7 @@
 import com.android.bedstead.harrier.annotations.RequireSdkVersion;
 import com.android.bedstead.harrier.annotations.RequireUserSupported;
 import com.android.bedstead.harrier.annotations.TestTag;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDpc;
@@ -81,6 +89,8 @@
 import com.android.bedstead.nene.packages.Package;
 import com.android.bedstead.nene.users.UserReference;
 import com.android.bedstead.nene.utils.Tags;
+import com.android.bedstead.remotedpc.RemoteDelegate;
+import com.android.bedstead.remotedpc.RemoteDpc;
 
 import org.junit.ClassRule;
 import org.junit.Ignore;
@@ -282,6 +292,7 @@
 
     @Test
     @EnsureDoesNotHavePermission(TEST_PERMISSION_1)
+    @AppModeFull // withoutPermission does not work on instant apps
     public void ensureDoesNotHavePermission_permissionIsDenied() {
         assertThat(TestApis.context().instrumentedContext()
                 .checkSelfPermission(TEST_PERMISSION_1)).isEqualTo(PERMISSION_DENIED);
@@ -289,6 +300,7 @@
 
     @Test
     @EnsureDoesNotHavePermission({TEST_PERMISSION_1, TEST_PERMISSION_2})
+    @AppModeFull // withoutPermission does not work on instant apps
     public void ensureDoesNotHavePermission_multiplePermissions_permissionsAreDenied() {
         assertThat(TestApis.context().instrumentedContext()
                 .checkSelfPermission(TEST_PERMISSION_1)).isEqualTo(PERMISSION_DENIED);
@@ -301,6 +313,7 @@
     @EnsureDoesNotHavePermission(TEST_PERMISSION_2)
     @RequireSdkVersion(min = Build.VERSION_CODES.R,
             reason = "Used permissions not available prior to R")
+    @AppModeFull // withoutPermission does not work on instant apps
     public void ensureHasPermissionAndDoesNotHavePermission_permissionsAreCorrect() {
         assertThat(TestApis.context().instrumentedContext()
                 .checkSelfPermission(TEST_PERMISSION_1)).isEqualTo(PERMISSION_GRANTED);
@@ -585,6 +598,19 @@
     }
 
     @Test
+    @RequireRunNotOnSecondaryUser
+    public void requireRunNotOnSecondaryUser_currentUserIsNotSecondary() {
+        assertThat(TestApis.users().current().type().name()).isNotEqualTo(SECONDARY_USER_TYPE_NAME);
+    }
+
+    @Test
+    @RequireRunNotOnSecondaryUser
+    public void requireRunNotOnSecondaryUser_instrumentedUserIsNotSecondary() {
+        assertThat(TestApis.users().instrumented().type().name())
+                .isNotEqualTo(SECONDARY_USER_TYPE_NAME);
+    }
+
+    @Test
     @EnsureHasSecondaryUser(switchedToUser = FALSE) // We don't test the default as it's ANY
     public void ensureHasUser_specifyIsNotSwitchedToUser_isNotCurrentUser() {
         assertThat(TestApis.users().current()).isNotEqualTo(sDeviceState.secondaryUser());
@@ -624,7 +650,7 @@
     @IncludeRunOnBackgroundDeviceOwnerUser
     public void includeRunOnBackgroundDeviceOwnerUserAnnotation_isRunningOnDeviceOwnerUser() {
         assertThat(TestApis.users().instrumented())
-                .isEqualTo(sDeviceState.dpc().devicePolicyController().user());
+                .isEqualTo(sDeviceState.dpc().user());
     }
 
     @Test
@@ -702,4 +728,51 @@
     public void testTagAnnoation_testTagIsSet() {
         assertThat(Tags.hasTag("TestTag")).isTrue();
     }
+
+    @Test
+    @EnsureHasDeviceOwner
+    @EnsureHasDelegate(admin = DEVICE_OWNER, scopes = DELEGATION_CERT_INSTALL, isPrimary = true)
+    public void ensureHasPrimaryDelegateAnnotation_dpcReturnsDelegate() {
+        assertThat(sDeviceState.dpc()).isInstanceOf(RemoteDelegate.class);
+    }
+
+    @Test
+    @EnsureHasDeviceOwner
+    @EnsureHasDelegate(admin = DEVICE_OWNER, scopes = DELEGATION_CERT_INSTALL, isPrimary = false)
+    public void ensureHasNonPrimaryDelegateAnnotation_dpcReturnsDpc() {
+        assertThat(sDeviceState.dpc()).isInstanceOf(RemoteDpc.class);
+    }
+
+    @Test
+    @EnsureHasDeviceOwner
+    @EnsureHasDelegate(admin = DEVICE_OWNER, scopes = DELEGATION_CERT_INSTALL, isPrimary = true)
+    public void ensureHasDelegateAnnotation_dpcCanUseDelegatedFunctionality() {
+        assertThat(sDeviceState.dpc().devicePolicyManager().getEnrollmentSpecificId()).isNotNull();
+    }
+
+    @Test
+    @EnsureHasDeviceOwner
+    @EnsureHasDelegate(admin = DEVICE_OWNER,
+            scopes = {DELEGATION_CERT_INSTALL, DELEGATION_APP_RESTRICTIONS}, isPrimary = true)
+    public void ensureHasDelegateAnnotation_multipleScopes_dpcCanUseAllDelegatedFunctionality() {
+        assertThat(sDeviceState.dpc().devicePolicyManager().getEnrollmentSpecificId()).isNotNull();
+        sDeviceState.dpc().devicePolicyManager()
+                .setApplicationRestrictions(
+                        sDeviceState.dpc().componentName(),
+                        sDeviceState.dpc().packageName(), new Bundle());
+    }
+
+    @Test
+    @EnsureHasDeviceOwner(isPrimary = true)
+    @EnsureHasDelegate(admin = PRIMARY, scopes = {})
+    public void ensureHasDelegateAnnotation_primaryAdminWithoutReplace_dpcReturnsDpc() {
+        assertThat(sDeviceState.dpc()).isInstanceOf(RemoteDpc.class);
+    }
+
+    @Test
+    @EnsureHasDeviceOwner(isPrimary = true)
+    @EnsureHasDelegate(admin = PRIMARY, scopes = {}, isPrimary = true)
+    public void ensureHasDelegateAnnotation_primaryAdminAndReplace_dpcReturnsDelegate() {
+        assertThat(sDeviceState.dpc()).isInstanceOf(RemoteDelegate.class);
+    }
 }
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
index 0259477..2d8d21d 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
@@ -209,7 +209,7 @@
      */
     private void setDeviceOwnerOnly(DevicePolicyManager devicePolicyManager,
             ComponentName component, String name, int deviceOwnerUserId) {
-        if (Versions.meetsMinimumSdkVersionRequirement(Versions.S_V2)) {
+        if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S_V2)) {
             devicePolicyManager.setDeviceOwnerOnly(component, name, deviceOwnerUserId);
         } else {
             devicePolicyManager.setDeviceOwner(component, name, deviceOwnerUserId);
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java
index d8b557f..e475d25 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java
@@ -47,7 +47,8 @@
 
     @Override
     public void remove() {
-        if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
+        if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)
+                || TestApis.packages().instrumented().isInstantApp()) {
             removePreS();
             return;
         }
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
index 801a068..3b76374 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
@@ -516,6 +516,11 @@
         PackageInfo packageInfo = packageInfoFromAnyUser(GET_PERMISSIONS);
 
         if (packageInfo == null) {
+            if (TestApis.packages().instrumented().isInstantApp()) {
+                Log.i(LOG_TAG, "Tried to get requestedPermissions for "
+                        + mPackageName + " but can't on instant apps");
+                return new HashSet<>();
+            }
             throw new NeneException("Error getting requestedPermissions, does not exist");
         }
 
@@ -790,6 +795,25 @@
         return (appInfo.flags & FLAG_SYSTEM) > 0;
     }
 
+    @Experimental
+    public boolean isInstantApp() {
+        return sPackageManager.isInstantApp(mPackageName);
+    }
+
+    /**
+     * Gets the shared user id of the package.
+     */
+    @Experimental
+    public String sharedUserId() {
+        PackageInfo packageInfo = packageInfoFromAnyUser(/* flags= */ 0);
+
+        if (packageInfo == null) {
+            throw new NeneException("Error getting sharedUserId, does not exist");
+        }
+
+        return packageInfo.sharedUserId;
+    }
+
     private static final class ProcessInfo {
         final String mPackageName;
         final int mPid;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
index a6ad211..6169224 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
@@ -58,6 +58,8 @@
 import com.android.bedstead.nene.utils.Versions;
 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 
+import org.junit.AssumptionViolatedException;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -196,6 +198,15 @@
             throw new NullPointerException();
         }
 
+        if (!Versions.meetsMinimumSdkVersionRequirement(R)
+                || TestApis.packages().instrumented().isInstantApp()) {
+            AdbPackageParser.ParseResult packages = parseDumpsys();
+            return packages.mPackages.values().stream()
+                    .filter(p -> p.installedOnUsers().contains(user))
+                    .map(p -> find(p.packageName()))
+                    .collect(Collectors.toSet());
+        }
+
         if (user.equals(TestApis.users().instrumented())) {
             return TestApis.context().instrumentedContext().getPackageManager()
                     .getInstalledPackages(/* flags= */ 0)
@@ -204,14 +215,6 @@
                     .collect(Collectors.toSet());
         }
 
-        if (!Versions.meetsMinimumSdkVersionRequirement(R)) {
-            AdbPackageParser.ParseResult packages = parseDumpsys();
-            return packages.mPackages.values().stream()
-                    .filter(p -> p.installedOnUsers().contains(user))
-                    .map(p -> find(p.packageName()))
-                    .collect(Collectors.toSet());
-        }
-
         try (PermissionContext p = TestApis.permissions()
                 .withPermission(INTERACT_ACROSS_USERS_FULL)) {
             return TestApis.context().androidContextAsUser(user).getPackageManager()
@@ -325,8 +328,8 @@
      *
      * <p>If the package is marked testOnly, it will still be installed.
      *
-     * <p>On versions of Android prior to Q, this will return null. On other versions it will return
-     * the installed package.
+     * <p>On versions of Android prior to Q, or when running as an instant app, this will return
+     * null. On other versions it will return the installed package.
      */
     @Nullable
     public Package install(UserReference user, byte[] apkFile) {
@@ -343,6 +346,31 @@
                     + "(Trying to install into user " + user + ")");
         }
 
+        if (TestApis.packages().instrumented().isInstantApp()) {
+            // We should install using stdin with the byte array
+            try {
+                ShellCommand.builderForUser(user, "pm install")
+                        .addOperand("-t") // Allow installing test apks
+                        .addOperand("-r") // Replace existing apps
+                        .addOption("-S", apkFile.length) // Install from stdin
+                        .writeToStdIn(apkFile)
+                        .validate(ShellCommandUtils::startsWithSuccess)
+                        .execute();
+            } catch (AdbException e) {
+                throw new NeneException("Error installing from instant app", e);
+            }
+
+            // Arbitrary sleep because the shell command doesn't block and we can't listen for
+            // the broadcast (instant app)
+            try {
+                Thread.sleep(10_000);
+            } catch (InterruptedException e) {
+                throw new NeneException("Interrupted while waiting for install", e);
+            }
+
+            return null;
+        }
+
         // This is not inside the try because if the install is unsuccessful we don't want to await
         // the broadcast
         BlockingBroadcastReceiver broadcastReceiver =
@@ -371,7 +399,8 @@
 
             try (BlockingIntentSender intentSender = BlockingIntentSender.create()) {
                 try (PermissionContext p =
-                             TestApis.permissions().withPermission(INSTALL_PACKAGES)) {
+                             TestApis.permissions().withPermission(
+                                     INSTALL_PACKAGES, INSTALL_TEST_ONLY_PACKAGE)) {
                     session.commit(intentSender.intentSender());
                     session.close();
 
@@ -387,7 +416,6 @@
                     }
                 }
             }
-
             return waitForPackageAddedBroadcast(broadcastReceiver);
         } catch (IOException e) {
             throw new NeneException("Could not install package", e);
@@ -404,6 +432,11 @@
         File outputDir = Environment.getExternalStorageDirectory();
         File outputFile = null;
 
+        if (TestApis.packages().instrumented().isInstantApp()) {
+            // We can't manage files as an instant app, so we must skip this test
+            throw new AssumptionViolatedException("Cannot install packages as instant app");
+        }
+
         try (PermissionContext p =
                      TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
             // TODO(b/202705721): Replace this with fixed name
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java
index 3442035..2336fd0 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.bedstead.nene.permissions;
 
+import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.utils.Versions;
 
@@ -97,6 +98,11 @@
             }
         }
 
+        if (TestApis.packages().instrumented().isInstantApp()) {
+            throw new NeneException(
+                    "Tests which use withoutPermission must not run as instant apps");
+        }
+
         mDeniedPermissions.addAll(Arrays.asList(permissions));
 
         mPermissions.applyPermissions();
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
index 2806d25..fa797bb 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
@@ -214,6 +214,15 @@
             return;
         }
 
+        if (TestApis.packages().instrumented().isInstantApp()) {
+            // Instant Apps aren't able to know the permissions of shell so we can't know if we can
+            // adopt it - we'll assume we can adopt and log
+            Log.i(LOG_TAG,
+                    "Adopting all shell permissions as can't check shell: " + mPermissionContexts);
+            ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity();
+            return;
+        }
+
         if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) {
             ShellCommandUtils.uiAutomation().dropShellPermissionIdentity();
         }
@@ -344,6 +353,16 @@
         applyPermissions();
     }
 
+    /**
+     * Returns all of the permissions which are currently able to be used.
+     */
+    public Set<String> usablePermissions() {
+        Set<String> usablePermissions = new HashSet<>();
+        usablePermissions.addAll(mShellPermissions);
+        usablePermissions.addAll(mInstrumentedRequestedPermissions);
+        return usablePermissions;
+    }
+
     private void removePermissionContextsUntilCanApply() {
         try {
             mPermissionContexts.remove(mPermissionContexts.size() - 1);
@@ -373,6 +392,7 @@
         mExistingPermissions = ShellCommandUtils.uiAutomation().getAdoptedShellPermissions();
     }
 
+    @SuppressWarnings("NewApi")
     private void restoreExistingPermissions() {
         if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
             return;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
index c5b5b89..4dcd12f 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
@@ -161,7 +161,7 @@
             Poll.forValue("User running", this::isRunning)
                     .toBeEqualTo(false)
                     // TODO(b/203630556): Replace stopping with something faster
-                    .timeout(Duration.ofMinutes(4))
+                    .timeout(Duration.ofMinutes(10))
                     .errorOnFail()
                     .await();
         } catch (AdbException e) {
@@ -175,6 +175,11 @@
      * Make the user the foreground user.
      */
     public UserReference switchTo() {
+        if (TestApis.users().current().equals(this)) {
+            // Already switched to
+            return this;
+        }
+
         // This is created outside of the try because we don't want to wait for the broadcast
         // on versions less than R
         BlockingBroadcastReceiver broadcastReceiver =
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
index 0b11f7a..8813a46 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
@@ -21,12 +21,12 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.os.Build.VERSION.SDK_INT;
 import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.S_V2;
 import static android.os.Process.myUserHandle;
 
 import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
 import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
 import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME;
-import static com.android.bedstead.nene.utils.Versions.S_V2;
 
 import android.app.ActivityManager;
 import android.content.Context;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Tags.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Tags.java
index 901d586..5d80cec 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Tags.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Tags.java
@@ -27,6 +27,7 @@
 
     public static final String USES_DEVICESTATE = "uses_devicestate";
     public static final String USES_NOTIFICATIONS = "uses_notifications";
+    public static final String INSTANT_APP = "instant_app";
 
     private Tags() {
 
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java
index 0047245..4feb6b6 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java
@@ -28,7 +28,7 @@
 /** SDK Version checks. */
 public final class Versions {
 
-    public static final int S_V2 = CUR_DEVELOPMENT;
+    public static final int T = CUR_DEVELOPMENT;
 
     /** Any version. */
     public static final int ANY = -1;
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
index 6b0620f..bf2e881 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
@@ -31,7 +31,7 @@
 import com.android.bedstead.harrier.annotations.AfterClass;
 import com.android.bedstead.harrier.annotations.BeforeClass;
 import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
-import com.android.bedstead.harrier.annotations.RequireRunOnSystemUser;
+import com.android.bedstead.harrier.annotations.RequireRunNotOnSecondaryUser;
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.permissions.PermissionContext;
@@ -119,7 +119,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO: this actually should be anything but secondary user
+    @RequireRunNotOnSecondaryUser
     public void installExisting_alreadyInstalled_installsInUser() {
         sInstrumentedPackage.installExisting(sDeviceState.secondaryUser());
 
@@ -132,7 +132,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO(b/201319776): this should be anything but secondary user
+    @RequireRunNotOnSecondaryUser
     public void uninstallForAllUsers_isUninstalledForAllUsers() throws Exception {
         Package pkg = TestApis.packages().install(sTestAppApkFile);
         pkg.installExisting(sDeviceState.secondaryUser());
@@ -144,7 +144,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO(b/201319776): this should be anything but secondary user
+    @RequireRunNotOnSecondaryUser
     public void uninstall_packageIsInstalledForDifferentUser_isUninstalledForUser()
             throws Exception {
         Package pkg = TestApis.packages().install(sTestAppApkFile);
@@ -173,7 +173,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO(b/201319776): this should be anything but secondary user
+    @RequireRunNotOnSecondaryUser
     public void uninstall_packageNotInstalledForUser_doesNotThrowException() {
         TestApis.packages().install(sTestAppApkFile);
 
@@ -217,7 +217,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO(b/201319776): this should be anything but secondary user
+    @RequireRunNotOnSecondaryUser
     public void grantPermission_permissionIsUserSpecific_permissionIsGrantedOnlyForThatUser() {
         try (TestAppInstance instance1 = sTestApp.install();
              TestAppInstance instance2 = sTestApp.install(sDeviceState.secondaryUser())) {
@@ -329,7 +329,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO(201319776): Just needs to be not the secondary user
+    @RequireRunNotOnSecondaryUser
     public void denyPermission_permissionIsUserSpecific_permissionIsDeniedOnlyForThatUser() {
         try (TestAppInstance instance1 = sTestApp.install();
              TestAppInstance instance2 = sTestApp.install(sDeviceState.secondaryUser())) {
@@ -358,7 +358,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnSystemUser // TODO(b/201319776): this should be anything but secondary user
+    @RequireRunNotOnSecondaryUser
     public void installedOnUsers_doesNotIncludeUserWithoutPackageInstalled() throws Exception {
         Package pkg = TestApis.packages().install(sTestAppApkFile);
         pkg.uninstall(sDeviceState.secondaryUser());
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
index 6c3fd37..209e36c 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
@@ -37,6 +37,7 @@
 import com.android.bedstead.harrier.annotations.EnsureHasPermission;
 import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
 import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
+import com.android.bedstead.harrier.annotations.RequireRunNotOnSecondaryUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
 import com.android.bedstead.harrier.annotations.RequireSdkVersion;
 import com.android.bedstead.nene.TestApis;
@@ -135,7 +136,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnPrimaryUser // TODO(201319776): Use @RequireRunNotOnSecondaryUser
+    @RequireRunNotOnSecondaryUser
     public void stop_userStarted_userIsStopped() {
         sDeviceState.secondaryUser().stop();
 
@@ -144,7 +145,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnPrimaryUser // TODO(201319776): Use @RequireRunNotOnSecondaryUser
+    @RequireRunNotOnSecondaryUser
     public void stop_userNotStarted_doesNothing() {
         sDeviceState.secondaryUser().stop();
 
@@ -155,7 +156,7 @@
 
     @Test
     @EnsureHasSecondaryUser
-    @RequireRunOnPrimaryUser // TODO(201319776): Use @RequireRunNotOnSecondaryUser
+    @RequireRunNotOnSecondaryUser
     @RequireSdkVersion(min = Q)
     public void switchTo_userIsSwitched() {
         try (PermissionContext p =
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java
index f683175..1769a01 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java
@@ -42,6 +42,7 @@
 import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
 import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
 import com.android.bedstead.harrier.annotations.RequireHeadlessSystemUserMode;
+import com.android.bedstead.harrier.annotations.RequireRunNotOnSecondaryUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
@@ -486,7 +487,7 @@
     }
 
     @Test
-    @RequireRunOnPrimaryUser // TODO(201319776): Use @RequireRunNotOnSecondaryUser
+    @RequireRunNotOnSecondaryUser
     @EnsureHasSecondaryUser
     @RequireHeadlessSystemUserMode
     public void switch_hasSetStopBgUsersOnSwitch_stopsUser() throws Exception {
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/NullableQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/NullableQuery.java
new file mode 100644
index 0000000..90be710
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/NullableQuery.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.queryable.queries;
+
+/**
+ * A {@link Query} for a nullable type.
+ *
+ *
+ * @param <E> Type of query
+ * @param <F> Type of object being queried
+ */
+public interface NullableQuery<E, F> extends Query<F> {
+    /** Require the {@link F} is equal to {@code string}. */
+    E isEqualTo(F value);
+
+    /** Require the {@link F} is not equal to {@code string}. */
+    E isNotEqualTo(F value);
+
+    /** Require the {@link F} is null. */
+    default E isNull() {
+        return isEqualTo(null);
+    }
+
+    /** Require the {@link F} is not null. */
+    default E isNotNull() {
+        return isNotEqualTo(null);
+    }
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/SetQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/SetQueryHelper.java
index a659887..089a508 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/SetQueryHelper.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/SetQueryHelper.java
@@ -190,21 +190,27 @@
     public String describeQuery(String fieldName) {
         List<String> queryStrings = new ArrayList<>();
         queryStrings.add(mSizeQuery.describeQuery(fieldName + ".size"));
-        if (!mContainsByQuery.isEmpty() && !mContainsByType.isEmpty()) {
+        if (!mContainsByQuery.isEmpty()) {
             queryStrings.add(fieldName + " contains matches of ["
                     + mContainsByQuery.stream().map(t -> "{" + t.describeQuery("")
-                    + "}").collect(Collectors.joining(", ")) + "]"
-                    + mContainsByType.stream().map(t -> "{" + t.toString()
                     + "}").collect(Collectors.joining(", ")) + "]");
         }
-        if (!mDoesNotContainByQuery.isEmpty() && !mDoesNotContainByType.isEmpty()) {
-            queryStrings.add(fieldName + " does not contain anything matching any of ["
-                    + mDoesNotContainByQuery.stream().map(t -> "{" + t.describeQuery("")
-                    + "}").collect(Collectors.joining(", ")) + "]"
-                    + mDoesNotContainByType.stream().map(t -> "{" + t.toString()
-                    + "}").collect(Collectors.joining(", ")) + "]");
+        if (!mContainsByType.isEmpty()) {
+            queryStrings.add(fieldName + " contains ["
+                    + mContainsByType.stream().map(Object::toString)
+                    .collect(Collectors.joining(", ")) + "]");
         }
 
+        if (!mDoesNotContainByQuery.isEmpty()) {
+            queryStrings.add(fieldName + " does not contain anything matching any of ["
+                    + mDoesNotContainByQuery.stream().map(t -> "{" + t.describeQuery("")
+                    + "}").collect(Collectors.joining(", ")) + "]");
+        }
+        if (!mDoesNotContainByType.isEmpty()) {
+            queryStrings.add(fieldName + " does not contain ["
+                    + mDoesNotContainByType.stream().map(Object::toString)
+                    .collect(Collectors.joining(", ")) + "]");
+        }
         return Queryable.joinQueryStrings(queryStrings);
     }
 }
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQuery.java
index 03c32b9..2db0fa5 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQuery.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQuery.java
@@ -18,15 +18,14 @@
 
 import com.android.queryable.Queryable;
 
-import java.io.Serializable;
-
-/** Query for a {@link String}. */
-public interface StringQuery<E extends Queryable> extends Query<String> {
+/**
+ * Query for a {@link String}.
+ *
+ * @param <E> Type of query
+ */
+public interface StringQuery<E extends Queryable> extends NullableQuery<E, String> {
 
     static StringQuery<StringQuery<?>> string() {
         return new StringQueryHelper<>();
     }
-
-    /** Require the {@link String} is equal to {@code string}. */
-    E isEqualTo(String string);
 }
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQueryHelper.java
index b7406d8..05ef5ae 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQueryHelper.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/StringQueryHelper.java
@@ -20,7 +20,9 @@
 
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /** Implementation of {@link StringQuery}. */
 public final class StringQueryHelper<E extends Queryable>
@@ -30,6 +32,7 @@
 
     private final transient E mQuery;
     private String mEqualsValue;
+    private Set<String> mNotEqualsValues = new HashSet<>();
 
     StringQueryHelper() {
         mQuery = (E) this;
@@ -41,7 +44,13 @@
 
     @Override
     public E isEqualTo(String string) {
-        this.mEqualsValue = string;
+        mEqualsValue = string;
+        return mQuery;
+    }
+
+    @Override
+    public E isNotEqualTo(String string) {
+        mNotEqualsValues.add(string);
         return mQuery;
     }
 
@@ -50,6 +59,9 @@
         if (mEqualsValue != null && !mEqualsValue.equals(value)) {
             return false;
         }
+        if (mNotEqualsValues.contains(value)) {
+            return false;
+        }
 
         return true;
     }
@@ -59,6 +71,13 @@
     }
 
     /**
+     * True if this query has not been configured.
+     */
+    public boolean isEmpty() {
+        return mEqualsValue == null && mNotEqualsValues.isEmpty();
+    }
+
+    /**
      * True if this query is for an exact string match.
      */
     public boolean isQueryingForExactMatch() {
@@ -72,6 +91,10 @@
             queryStrings.add(fieldName + "=\"" + mEqualsValue + "\"");
         }
 
+        for (String notEquals : mNotEqualsValues) {
+            queryStrings.add(fieldName + "!=\"" + notEquals + "\"");
+        }
+
         return Queryable.joinQueryStrings(queryStrings);
     }
 }
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/StringQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/StringQueryHelperTest.java
index 9797b8c..dcfb4d1 100644
--- a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/StringQueryHelperTest.java
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/StringQueryHelperTest.java
@@ -58,4 +58,55 @@
 
         assertThat(stringQueryHelper.matches(STRING_VALUE)).isFalse();
     }
+
+    @Test
+    public void matches_isNotEqualTo_meetsRestriction_returnsTrue() {
+        StringQueryHelper<Queryable> stringQueryHelper =
+                new StringQueryHelper<>(mQuery);
+
+        stringQueryHelper.isNotEqualTo(DIFFERENT_STRING_VALUE);
+
+        assertThat(stringQueryHelper.matches(STRING_VALUE)).isTrue();
+    }
+
+    @Test
+    public void matches_isNotEqualTo_doesNotMeetRestriction_returnsFalse() {
+        StringQueryHelper<Queryable> stringQueryHelper =
+                new StringQueryHelper<>(mQuery);
+
+        stringQueryHelper.isNotEqualTo(DIFFERENT_STRING_VALUE);
+
+        assertThat(stringQueryHelper.matches(DIFFERENT_STRING_VALUE)).isFalse();
+    }
+
+    @Test
+    public void matches_isNotEqualTo_multipleRestrictions_doesNotMeetRestriction_returnsFalse() {
+        StringQueryHelper<Queryable> stringQueryHelper =
+                new StringQueryHelper<>(mQuery);
+
+        stringQueryHelper.isNotEqualTo(DIFFERENT_STRING_VALUE);
+        stringQueryHelper.isNotEqualTo(STRING_VALUE);
+
+        assertThat(stringQueryHelper.matches(DIFFERENT_STRING_VALUE)).isFalse();
+    }
+
+    @Test
+    public void matches_isNull_meetsRestriction_returnsTrue() {
+        StringQueryHelper<Queryable> stringQueryHelper =
+                new StringQueryHelper<>(mQuery);
+
+        stringQueryHelper.isNull();
+
+        assertThat(stringQueryHelper.matches(null)).isTrue();
+    }
+
+    @Test
+    public void matches_isNotNull_doesNotMeetRestriction_returnsFalse() {
+        StringQueryHelper<Queryable> stringQueryHelper =
+                new StringQueryHelper<>(mQuery);
+
+        stringQueryHelper.isNotNull();
+
+        assertThat(stringQueryHelper.matches(null)).isFalse();
+    }
 }
diff --git a/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDelegate.java b/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDelegate.java
new file mode 100644
index 0000000..714208f
--- /dev/null
+++ b/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDelegate.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.remotedpc;
+
+import android.content.ComponentName;
+
+import androidx.annotation.Nullable;
+
+import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.testapp.TestApp;
+import com.android.bedstead.testapp.TestAppProvider;
+
+/**
+ * {@link RemotePolicyManager} subclass representing an app which has been delegated to.
+ */
+public final class RemoteDelegate extends RemotePolicyManager {
+
+    private static final TestAppProvider sTestAppProvider = new TestAppProvider();
+    public static final TestApp sTestApp = sTestAppProvider.query()
+            .wherePackageName().isEqualTo("com.android.Delegate")
+            .get();
+
+    public RemoteDelegate(TestApp testApp, UserReference user) {
+        super(testApp, user);
+    }
+
+    @Nullable
+    @Override
+    public ComponentName componentName() {
+        return null;
+    }
+}
diff --git a/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDpc.java b/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDpc.java
index ea230b2..01519a0 100644
--- a/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDpc.java
+++ b/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemoteDpc.java
@@ -19,7 +19,6 @@
 import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.os.Build;
 import android.os.UserHandle;
 
@@ -33,14 +32,10 @@
 import com.android.bedstead.nene.users.UserReference;
 import com.android.bedstead.nene.utils.Versions;
 import com.android.bedstead.testapp.TestApp;
-import com.android.bedstead.testapp.TestAppInstance;
 import com.android.bedstead.testapp.TestAppProvider;
 
 /** Entry point to RemoteDPC. */
-public final class RemoteDpc extends TestAppInstance {
-
-    // This must be instrumentation not instrumented to access the resources
-    private static final Context sContext = TestApis.context().instrumentationContext();
+public final class RemoteDpc extends RemotePolicyManager {
 
     public static final ComponentName DPC_COMPONENT_NAME = new ComponentName(
             "com.android.RemoteDPC",
@@ -258,16 +253,9 @@
     }
 
     /**
-     * Get the {@link TestAppInstance} for the DPC.
-     *
-     */
-    public TestAppInstance app() {
-        return sTestApp.instance(mDevicePolicyController.user());
-    }
-
-    /**
      * Get the {@link ComponentName} of the DPC.
      */
+    @Override
     public ComponentName componentName() {
         return DPC_COMPONENT_NAME;
     }
diff --git a/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemotePolicyManager.java b/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemotePolicyManager.java
new file mode 100644
index 0000000..8532ef3
--- /dev/null
+++ b/common/device-side/bedstead/remotedpc/src/main/java/com/android/bedstead/remotedpc/RemotePolicyManager.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.remotedpc;
+
+import android.content.ComponentName;
+
+import androidx.annotation.Nullable;
+
+import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.testapp.TestApp;
+import com.android.bedstead.testapp.TestAppInstance;
+
+/** A Remote app which can change device policy */
+public abstract class RemotePolicyManager extends TestAppInstance {
+
+    RemotePolicyManager(TestApp testApp, UserReference user) {
+        super(testApp, user);
+    }
+
+    /**
+     * Get the {@link ComponentName} of the device admin for the policy manager.
+     *
+     * <p>Null if there is no device admin
+     */
+    @Nullable
+    public abstract ComponentName componentName();
+}
diff --git a/common/device-side/bedstead/testapp/Android.bp b/common/device-side/bedstead/testapp/Android.bp
index 410f94b..bf92e97 100644
--- a/common/device-side/bedstead/testapp/Android.bp
+++ b/common/device-side/bedstead/testapp/Android.bp
@@ -99,7 +99,7 @@
 
 java_genrule {
     name: "TestApp_Apps",
-    srcs: [":EmptyTestApp", ":EmptyTestApp2", ":DeviceAdminTestApp", ":LockTaskApp", ":RemoteDPCTestApp", ":SmsApp", ":AccountManagementApp"],
+    srcs: [":EmptyTestApp", ":EmptyTestApp2", ":DeviceAdminTestApp", ":LockTaskApp", ":DelegateTestApp", ":RemoteDPCTestApp", ":SmsApp", ":AccountManagementApp"],
     out: ["TestApp_Apps.res.zip"],
     tools: ["soong_zip", "index_testapps", "aapt2"],
     cmd: "mkdir -p $(genDir)/res/raw"
@@ -107,6 +107,7 @@
          + " && cp $(location :EmptyTestApp2) $(genDir)/res/raw"
          + " && cp $(location :DeviceAdminTestApp) $(genDir)/res/raw"
          + " && cp $(location :LockTaskApp) $(genDir)/res/raw"
+         + " && cp $(location :DelegateTestApp) $(genDir)/res/raw"
          + " && cp $(location :RemoteDPCTestApp) $(genDir)/res/raw"
          + " && cp $(location :SmsApp) $(genDir)/res/raw"
          + " && cp $(location :AccountManagementApp) $(genDir)/res/raw"
@@ -152,6 +153,15 @@
 }
 
 android_test_helper_app {
+    name: "DelegateTestApp",
+    static_libs: [
+        "TestApp_TestApps"
+    ],
+    manifest: "manifests/DelegateManifest.xml",
+    min_sdk_version: "28"
+}
+
+android_test_helper_app {
     name: "RemoteDPCTestApp",
     static_libs: [
         "TestApp_TestApps",
diff --git a/common/device-side/bedstead/testapp/manifests/DelegateManifest.xml b/common/device-side/bedstead/testapp/manifests/DelegateManifest.xml
new file mode 100644
index 0000000..5bd3772
--- /dev/null
+++ b/common/device-side/bedstead/testapp/manifests/DelegateManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.Delegate" android:targetSandboxVersion="2">
+
+    <application
+        android:appComponentFactory="com.android.bedstead.testapp.TestAppAppComponentFactory">
+
+        <!-- Don't allow this test app to be returned by queries unless filtered by package name -->
+        <meta-data android:name="testapp-package-query-only" android:value="true" />
+
+    </application>
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>
+</manifest>
\ No newline at end of file
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/NotFoundException.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/NotFoundException.java
index 669bb13..55e5831e 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/NotFoundException.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/NotFoundException.java
@@ -19,6 +19,6 @@
 /** {@link Exception} thrown when a query doesn't match any test apps. */
 public class NotFoundException extends RuntimeException {
     public NotFoundException(TestAppQueryBuilder query) {
-
+        super("Could not find testapp matching query: " + query.describeQuery(null));
     }
 }
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestApp.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestApp.java
index 102e8da..ac75967 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestApp.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestApp.java
@@ -22,6 +22,8 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.packages.Package;
@@ -34,11 +36,12 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Objects;
 import java.util.Set;
 
 /** Represents a single test app which can be installed and interacted with. */
 @TestAppSender
-public class TestApp {
+public final class TestApp {
     // Must be instrumentation context to access resources
     private static final Context sContext = TestApis.context().instrumentationContext();
     private final TestAppDetails mDetails;
@@ -208,4 +211,23 @@
                 + ", details=" + mDetails
                 + "}";
     }
+
+    /** The shared user id of the test app, if any. */
+    @Nullable
+    public String sharedUserId() {
+        return mDetails.sharedUserId();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TestApp)) return false;
+        TestApp testApp = (TestApp) o;
+        return mDetails.mApp.getPackageName().equals(testApp.mDetails.mApp.getPackageName());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDetails.mApp.getPackageName());
+    }
 }
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivities.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivities.java
index ce5023f..d95def6 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivities.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivities.java
@@ -53,7 +53,7 @@
         PackageManager p = TestApis.context().instrumentedContext().getPackageManager();
         try {
             PackageInfo packageInfo = p.getPackageInfo(
-                    mInstance.testApp().packageName(), /* flags= */ PackageManager.GET_ACTIVITIES);
+                    mInstance.packageName(), /* flags= */ PackageManager.GET_ACTIVITIES);
             for (android.content.pm.ActivityInfo activityInfo : packageInfo.activities) {
                 if (activityInfo.name.startsWith("androidx")) {
                     // Special case: androidx adds non-logging activities
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivitiesQueryBuilder.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivitiesQueryBuilder.java
index 3eb3633..761dec6 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivitiesQueryBuilder.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppActivitiesQueryBuilder.java
@@ -55,7 +55,7 @@
                 return new UnresolvedTestAppActivity(mTestAppActivities.mInstance,
                         TestApis.packages().component(
                                 new ComponentName(
-                                        mTestAppActivities.mInstance.testApp().packageName(),
+                                        mTestAppActivities.mInstance.packageName(),
                                         activity.className())));
             }
         }
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppBinder.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppBinder.java
index 4a4b5c8..282b68d 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppBinder.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppBinder.java
@@ -52,7 +52,7 @@
 
         Intent bindIntent = new Intent();
         bindIntent.setComponent(new ComponentName(
-                mTestAppInstance.testApp().packageName(),
+                mTestAppInstance.packageName(),
                 bindToService.getClassName()));
 
         Log.i(LOG_TAG, "Attempting to bind to " + bindIntent);
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java
index 71dab7b..b9b34ec 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java
@@ -18,6 +18,8 @@
 
 import android.os.Bundle;
 
+import androidx.annotation.Nullable;
+
 import com.android.queryable.info.ActivityInfo;
 import com.android.queryable.info.ServiceInfo;
 
@@ -33,6 +35,18 @@
     final Set<ActivityInfo> mActivities = new HashSet<>();
     final Set<ServiceInfo> mServices = new HashSet<>();
 
+    /**
+     * Get the shared user ID of the test app, or {@code Null} if none.
+     */
+    @Nullable
+    public String sharedUserId() {
+        if (mApp.getSharedUserId().isEmpty()) {
+            return null;
+        }
+
+        return mApp.getSharedUserId();
+    }
+
     @Override
     public String toString() {
         return "TestAppDetails{"
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppEvents.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppEvents.java
index a0db3b4..2a925dd 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppEvents.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppEvents.java
@@ -73,238 +73,238 @@
     @Override
     public ActivityCreatedEvent.ActivityCreatedEventQuery activityCreated() {
         return ActivityCreatedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public ActivityDestroyedEvent.ActivityDestroyedEventQuery activityDestroyed() {
         return ActivityDestroyedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public ActivityPausedEvent.ActivityPausedEventQuery activityPaused() {
         return ActivityPausedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public ActivityRestartedEvent.ActivityRestartedEventQuery activityRestarted() {
         return ActivityRestartedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public ActivityResumedEvent.ActivityResumedEventQuery activityResumed() {
         return ActivityResumedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public ActivityStartedEvent.ActivityStartedEventQuery activityStarted() {
         return ActivityStartedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public ActivityStoppedEvent.ActivityStoppedEventQuery activityStopped() {
         return ActivityStoppedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public BroadcastReceivedEvent.BroadcastReceivedEventQuery broadcastReceived() {
         return BroadcastReceivedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminBugreportFailedEvent.DeviceAdminBugreportFailedEventQuery bugReportFailed() {
         return DeviceAdminBugreportFailedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminBugreportSharedEvent.DeviceAdminBugreportSharedEventQuery bugReportShared() {
         return DeviceAdminBugreportSharedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminBugreportSharingDeclinedEvent.DeviceAdminBugreportSharingDeclinedEventQuery bugReportSharingDeclined() {
         return DeviceAdminBugreportSharingDeclinedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminChoosePrivateKeyAliasEvent.DeviceAdminChoosePrivateKeyAliasEventQuery choosePrivateKeyAlias() {
         return DeviceAdminChoosePrivateKeyAliasEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminDisabledEvent.DeviceAdminDisabledEventQuery deviceAdminDisabled() {
         return DeviceAdminDisabledEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminDisableRequestedEvent.DeviceAdminDisableRequestedEventQuery deviceAdminDisableRequested() {
         return DeviceAdminDisableRequestedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminEnabledEvent.DeviceAdminEnabledEventQuery deviceAdminEnabled() {
         return DeviceAdminEnabledEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminLockTaskModeEnteringEvent.DeviceAdminLockTaskModeEnteringEventQuery lockTaskModeEntering() {
         return DeviceAdminLockTaskModeEnteringEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminLockTaskModeExitingEvent.DeviceAdminLockTaskModeExitingEventQuery lockTaskModeExiting() {
         return DeviceAdminLockTaskModeExitingEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminNetworkLogsAvailableEvent.DeviceAdminNetworkLogsAvailableEventQuery networkLogsAvailable() {
         return DeviceAdminNetworkLogsAvailableEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminOperationSafetyStateChangedEvent.DeviceAdminOperationSafetyStateChangedEventQuery operationSafetyStateChanged() {
         return DeviceAdminOperationSafetyStateChangedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminPasswordChangedEvent.DeviceAdminPasswordChangedEventQuery passwordChanged() {
         return DeviceAdminPasswordChangedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminPasswordExpiringEvent.DeviceAdminPasswordExpiringEventQuery passwordExpiring() {
         return DeviceAdminPasswordExpiringEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminPasswordFailedEvent.DeviceAdminPasswordFailedEventQuery passwordFailed() {
         return DeviceAdminPasswordFailedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminPasswordSucceededEvent.DeviceAdminPasswordSucceededEventQuery passwordSucceeded() {
         return DeviceAdminPasswordSucceededEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminProfileProvisioningCompleteEvent.DeviceAdminProfileProvisioningCompleteEventQuery profileProvisioningComplete() {
         return DeviceAdminProfileProvisioningCompleteEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminReadyForUserInitializationEvent.DeviceAdminReadyForUserInitializationEventQuery readyForUserInitialization() {
         return DeviceAdminReadyForUserInitializationEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminSecurityLogsAvailableEvent.DeviceAdminSecurityLogsAvailableEventQuery securityLogsAvailable() {
         return DeviceAdminSecurityLogsAvailableEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminSystemUpdatePendingEvent.DeviceAdminSystemUpdatePendingEventQuery systemUpdatePending() {
         return DeviceAdminSystemUpdatePendingEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminTransferAffiliatedProfileOwnershipCompleteEvent.DeviceAdminTransferAffiliatedProfileOwnershipCompleteEventQuery transferAffiliatedProfileOwnershipComplete() {
         return DeviceAdminTransferAffiliatedProfileOwnershipCompleteEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminTransferOwnershipCompleteEvent.DeviceAdminTransferOwnershipCompleteEventQuery transferOwnershipComplete() {
         return DeviceAdminTransferOwnershipCompleteEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminUserAddedEvent.DeviceAdminUserAddedEventQuery userAdded() {
         return DeviceAdminUserAddedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminUserRemovedEvent.DeviceAdminUserRemovedEventQuery userRemoved() {
         return DeviceAdminUserRemovedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminUserStartedEvent.DeviceAdminUserStartedEventQuery userStarted() {
         return DeviceAdminUserStartedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminUserStoppedEvent.DeviceAdminUserStoppedEventQuery userStopped() {
         return DeviceAdminUserStoppedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 
     @Override
     public DeviceAdminUserSwitchedEvent.DeviceAdminUserSwitchedEventQuery userSwitched() {
         return DeviceAdminUserSwitchedEvent.queryPackage(
-                mTestApp.testApp().packageName())
+                mTestApp.packageName())
                 .onUser(mTestApp.user());
     }
 }
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstance.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstance.java
index 56e0443..68cf755 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstance.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstance.java
@@ -58,6 +58,7 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -115,6 +116,13 @@
     }
 
     /**
+     * See {@link TestApp#packageName()}.
+     */
+    public String packageName() {
+       return testApp().packageName();
+    }
+
+    /**
      * The {@link UserReference} this instance refers to.
      */
     public UserReference user() {
@@ -375,4 +383,17 @@
                 + ", keepAliveManually=" + mKeepAliveManually
                 + '}';
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TestAppInstance)) return false;
+        TestAppInstance that = (TestAppInstance) o;
+        return mTestApp.equals(that.mTestApp) && mUser.equals(that.mUser);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTestApp, mUser);
+    }
 }
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java
index 6699a6d..5afc802 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java
@@ -48,6 +48,7 @@
             new SetQueryHelper<>(this);
     SetQueryHelper<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> mServices =
             new SetQueryHelper<>(this);
+    StringQueryHelper<TestAppQueryBuilder> mSharedUserId = new StringQueryHelper<>(this);
 
     TestAppQueryBuilder(TestAppProvider provider) {
         if (provider == null) {
@@ -110,6 +111,13 @@
     }
 
     /**
+     * Query for a {@link TestApp} by its sharedUserId;
+     */
+    public StringQuery<TestAppQueryBuilder> whereSharedUserId() {
+        return mSharedUserId;
+    }
+
+    /**
      * Query for a {@link TestApp} by its activities.
      */
     public SetQuery<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> whereActivities() {
@@ -186,6 +194,16 @@
             return false;
         }
 
+        if (mSharedUserId.isEmpty()) {
+            if (details.sharedUserId() != null) {
+                return false;
+            }
+        } else {
+            if (!StringQueryHelper.matches(mSharedUserId, details.sharedUserId())) {
+                return false;
+            }
+        }
+
         if (details.mMetadata.getString("testapp-package-query-only", "false")
                 .equals("true")) {
             if (!mPackageName.isQueryingForExactMatch()) {
@@ -207,6 +225,7 @@
                 mActivities.describeQuery("activities"),
                 mServices.describeQuery("services"),
                 mPermissions.describeQuery("permissions"),
+                mSharedUserId.describeQuery("sharedUserId"),
                 mTestOnly.describeQuery("testOnly")
         ) + "}";
     }
diff --git a/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto b/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto
index 80b13ec..3f5c84d 100644
--- a/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto
+++ b/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto
@@ -15,6 +15,7 @@
   repeated Service services = 6;
   bool test_only = 7;
   repeated Metadata metadata = 8;
+  string sharedUserId = 9;
 }
 
 message UsesSdk {
diff --git a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java
index 287c085..03fbceb 100644
--- a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java
+++ b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java
@@ -336,7 +336,14 @@
 
             methodBuilder.nextControlFlow(
                     "catch ($T e)", PROFILE_RUNTIME_EXCEPTION_CLASSNAME)
-                    .addStatement("throw ($T) e.getCause()", RuntimeException.class)
+                    .addStatement("throw ($T) e.getCause()", RuntimeException.class);
+
+            for (TypeMirror m : method.getThrownTypes()) {
+                methodBuilder.nextControlFlow("catch ($T e)", m)
+                        .addStatement("throw e");
+            }
+
+            methodBuilder
                     .nextControlFlow("catch ($T e)", Throwable.class)
                     .addStatement(
                             "throw new $T($S, e)",
@@ -437,6 +444,11 @@
                 methodBuilder.addStatement("return $L", runLogic);
             }
 
+            for (TypeMirror m : method.getThrownTypes()) {
+                methodBuilder.nextControlFlow("catch ($T e)", m)
+                        .addStatement("throw e");
+            }
+
             methodBuilder.nextControlFlow(
                     "catch ($T e)", PROFILE_RUNTIME_EXCEPTION_CLASSNAME)
                     .addStatement("throw ($T) e.getCause()", RuntimeException.class)
@@ -536,6 +548,10 @@
                             .addModifiers(Modifier.PUBLIC)
                             .addAnnotation(Override.class);
 
+            for (TypeMirror m : method.getThrownTypes()) {
+                methodBuilder.addException(ClassName.get(m));
+            }
+
             methodBuilder.addParameter(
                     ParameterSpec.builder(String.class, "activityClassName").build());
 
@@ -580,7 +596,14 @@
 
             methodBuilder.nextControlFlow(
                     "catch ($T e)", PROFILE_RUNTIME_EXCEPTION_CLASSNAME)
-                    .addStatement("throw ($T) e.getCause()", RuntimeException.class)
+                    .addStatement("throw ($T) e.getCause()", RuntimeException.class);
+
+            for (TypeMirror m : method.getThrownTypes()) {
+                methodBuilder.nextControlFlow("catch ($T e)", m)
+                        .addStatement("throw e");
+            }
+
+            methodBuilder
                     .nextControlFlow("catch ($T e)", Throwable.class)
                     .addStatement(
                             "throw new $T($S, e)",
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceTest.java
index 9e7dcac..45fcd98 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceTest.java
@@ -88,14 +88,14 @@
 
     @Test
     public void activities_any_returnsActivity() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.activities().any()).isNotNull();
         }
     }
 
     @Test
     public void uninstall_uninstalls() {
-        TestAppInstance testAppInstance = sTestApp.install(sUser);
+        TestAppInstance testAppInstance = sTestApp.install();
 
         testAppInstance.uninstall();
 
@@ -105,7 +105,7 @@
 
     @Test
     public void autoclose_uninstalls() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Intentionally empty
         }
 
@@ -123,7 +123,7 @@
     @Test
     @Ignore("b/203758521 Need to re-add support for killing processes")
     public void killProcess_keepAlive_processIsRunningAgain() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.keepAlive();
 
 //            testAppInstance.process().kill();
@@ -141,7 +141,7 @@
     @Test
     @Ignore("b/203758521 need to re-add support for killing processes")
     public void stop_processIsNotRunning() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.activities().any().start();
 
 //            testAppInstance.stop();
@@ -153,7 +153,7 @@
     @Test
     @Ignore("b/203758521 need to re-add support for killing processes")
     public void stop_previouslyCalledKeepAlive_processDoesNotRestart() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.activities().any().start();
             testAppInstance.keepAlive();
 
@@ -165,14 +165,14 @@
 
     @Test
     public void process_isNotRunning_returnsNull() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.process()).isNull();
         }
     }
 
     @Test
     public void process_isRunning_isNotNull() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.activities().any().start();
 
             Poll.forValue("TestApp process", testAppInstance::process)
@@ -184,7 +184,7 @@
 
     @Test
     public void registerReceiver_receivesBroadcast() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 
             sContext.sendBroadcast(INTENT);
@@ -197,7 +197,7 @@
 
     @Test
     public void registerReceiver_multipleIntentFilters_receivesAllMatchingBroadcasts() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.registerReceiver(INTENT_FILTER);
             testAppInstance.registerReceiver(INTENT_FILTER_2);
 
@@ -215,7 +215,7 @@
 
     @Test
     public void registerReceiver_processIsRunning() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
 
             testAppInstance.registerReceiver(INTENT_FILTER);
 
@@ -226,7 +226,7 @@
     @Test
     @Ignore("b/203758521 need to re-add support for killing processes")
     public void stop_registeredReceiver_doesNotReceiveBroadcast() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 
 //            testAppInstance.stop();
@@ -241,7 +241,7 @@
 
     @Test
     public void unregisterReceiver_registeredReceiver_doesNotReceiveBroadcast() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 
             testAppInstance.unregisterReceiver(INTENT_FILTER);
@@ -256,7 +256,7 @@
 
     @Test
     public void unregisterReceiver_doesNotUnregisterOtherReceivers() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.registerReceiver(INTENT_FILTER);
             testAppInstance.registerReceiver(INTENT_FILTER_2);
 
@@ -277,7 +277,7 @@
 
     @Test
     public void keepAlive_processIsRunning() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
 
             testAppInstance.keepAlive();
 
@@ -288,7 +288,7 @@
     @Test
     @Ignore("b/203758521 need to re-add support for killing processes")
     public void registerReceiver_appIsKilled_stillReceivesBroadcast() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 //            testApp.pkg().runningProcess(sUser).kill();
             Poll.forValue("running process", () -> sTestApp.pkg().runningProcess(sUser))
@@ -308,7 +308,7 @@
     @Test
     @RequireSdkVersion(min = S, reason = "isSafeOperation only available on S+")
     public void devicePolicyManager_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             testAppInstance.devicePolicyManager()
                     .isSafeOperation(OPERATION_SAFETY_REASON_DRIVING_DISTRACTION);
@@ -317,7 +317,7 @@
 
     @Test
     public void userManager_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             testAppInstance.userManager().getUserProfiles();
         }
@@ -326,7 +326,7 @@
     @Test
     @RequireSdkVersion(min = Q, reason = "Wifimanager API only available on Q+")
     public void wifiManager_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             testAppInstance.wifiManager().getMaxNumberOfNetworkSuggestionsPerApp();
         }
@@ -334,7 +334,7 @@
 
     @Test
     public void hardwarePropertiesManager_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Arbitrary call - there are no methods on this service which don't require permissions
             assertThrows(SecurityException.class, () -> {
                 testAppInstance.hardwarePropertiesManager().getCpuUsages();
@@ -344,28 +344,28 @@
 
     @Test
     public void packageManager_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.packageManager().hasSystemFeature("")).isFalse();
         }
     }
 
     @Test
     public void crossProfileApps_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.crossProfileApps().getTargetUserProfiles()).isEmpty();
         }
     }
 
     @Test
     public void launcherApps_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.launcherApps().hasShortcutHostPermission()).isFalse();
         }
     }
 
     @Test
     public void accountManager_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             assertThat(testAppInstance.accountManager().getAccounts()).isNotNull();
         }
@@ -373,7 +373,7 @@
 
     @Test
     public void context_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.context().getSystemServiceName(DevicePolicyManager.class))
                     .isEqualTo(Context.DEVICE_POLICY_SERVICE);
         }
@@ -381,7 +381,7 @@
 
     @Test
     public void context_getContentResolver_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             assertThat(testAppInstance.context().getContentResolver().getPersistedUriPermissions())
                     .isNotNull();
@@ -390,7 +390,7 @@
 
     @Test
     public void keyChain_returnsUsableInstance() {
-        try (TestAppInstance testAppInstance = sTestApp.install(sUser)) {
+        try (TestAppInstance testAppInstance = sTestApp.install()) {
             assertThat(testAppInstance.keyChain().isKeyAlgorithmSupported("A")).isFalse();
         }
     }
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppProviderTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppProviderTest.java
index 7d80b57..bc270b4 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppProviderTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppProviderTest.java
@@ -23,6 +23,7 @@
 import static org.testng.Assert.assertThrows;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -231,4 +232,31 @@
 
         assertThat(testApp.activities()).isEmpty();
     }
+
+    @Test
+    public void query_doesNotSpecifySharedUserId_sharedUserIdIsNull() {
+        TestApp testApp = mTestAppProvider.query()
+                .get();
+
+        assertThat(testApp.sharedUserId()).isNull();
+    }
+
+    @Test
+    @Ignore("re-enable when we have a test app which has a shareuserid")
+    public void query_doesSpecifySharedUserId_matches() {
+        TestApp testApp = mTestAppProvider.query()
+                .whereSharedUserId().isEqualTo("com.android.bedstead")
+                .get();
+
+        assertThat(testApp.sharedUserId()).isEqualTo("com.android.bedstead");
+    }
+
+    @Test
+    public void query_specifiesNullSharedUserId_matches() {
+        TestApp testApp = mTestAppProvider.query()
+                .whereSharedUserId().isNull()
+                .get();
+
+        assertThat(testApp.sharedUserId()).isNull();
+    }
 }
diff --git a/common/device-side/bedstead/testapp/tools/index/index_testapps.py b/common/device-side/bedstead/testapp/tools/index/index_testapps.py
index b4aee78..163e35a 100644
--- a/common/device-side/bedstead/testapp/tools/index/index_testapps.py
+++ b/common/device-side/bedstead/testapp/tools/index/index_testapps.py
@@ -133,6 +133,7 @@
     android_app = AndroidApp()
     android_app.apk_name = file_name
     android_app.package_name = root.attributes["package"]
+    android_app.sharedUserId = root.attributes.get("sharedUserId", "")
 
     parse_uses_sdk(root, android_app)
     parse_permissions(root, android_app)
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 49208b1..0cbd2a9 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
@@ -465,7 +465,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) {
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/AppRestrictionsDelegateTest.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/AppRestrictionsDelegateTest.java
deleted file mode 100644
index 9c73feb..0000000
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/AppRestrictionsDelegateTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.delegate;
-
-import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
-
-import static com.android.cts.delegate.DelegateTestUtils.assertExpectException;
-
-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.Bundle;
-import android.os.Process;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.List;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test that an app given the {@link DevicePolicyManager#DELEGATION_APP_RESTRICTIONS} scope via
- * {@link DevicePolicyManager#setDelegatedScopes} can manage app restrictions.
- */
-public class AppRestrictionsDelegateTest extends BaseJUnit3TestCase  {
-
-    private static final String TAG = AppRestrictionsDelegateTest.class.getSimpleName();
-
-    private static final String APP_RESTRICTIONS_TARGET_PKG =
-            "com.android.cts.apprestrictions.targetapp";
-    private static final String APP_RESTRICTIONS_ACTIVITY_NAME =
-            APP_RESTRICTIONS_TARGET_PKG + ".ApplicationRestrictionsActivity";
-    private static final String ACTION_RESTRICTIONS_VALUE =
-            "com.android.cts.apprestrictions.targetapp.RESTRICTIONS_VALUE";
-
-    private static final Bundle BUNDLE_0 = createBundle0();
-    private static final Bundle BUNDLE_1 = createBundle1();
-
-    private static final long TIMEOUT_SECONDS = 10;
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            Log.d(TAG, "onReceive(): intent " + action + " on uid " + Process.myUid());
-            if (ACTION_RESTRICTIONS_VALUE.equals(action)) {
-                mReceivedRestrictions = intent.getBundleExtra("value");
-                mOnRestrictionsSemaphore.release();
-            }
-        }
-    };
-
-    private final Semaphore mOnRestrictionsSemaphore = new Semaphore(0);
-    private Bundle mReceivedRestrictions;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_RESTRICTIONS_VALUE));
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mContext.unregisterReceiver(mReceiver);
-        super.tearDown();
-    }
-
-    public void testCannotAccessApis() {
-        assertFalse("DelegateApp should not be an app restrictions delegate",
-                amIAppRestrictionsDelegate());
-
-        assertExpectException(SecurityException.class,
-                "Calling identity is not authorized", () -> {
-                    mDpm.setApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG, null);
-                });
-
-        assertExpectException(SecurityException.class,
-                "Calling identity is not authorized", () -> {
-                    mDpm.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG);
-                });
-    }
-
-    public void testCanAccessApis() throws InterruptedException {
-        assertTrue("DelegateApp is not an app restrictions delegate", amIAppRestrictionsDelegate());
-        try {
-            mDpm.setApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_0);
-            assertBundle0(mDpm.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG));
-
-            // Check that the target app can retrieve the same restrictions.
-            assertBundle0(waitForChangedRestriction());
-
-            // Test overwriting
-            mDpm.setApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_1);
-            assertBundle1(mDpm.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG));
-            assertBundle1(waitForChangedRestriction());
-        } finally {
-            mDpm.setApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG, new Bundle());
-            assertTrue(
-                mDpm.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG).isEmpty());
-        }
-    }
-
-    // Should be consistent with assertBundle0
-    private static Bundle createBundle0() {
-        Bundle result = new Bundle();
-        result.putString("placeholderString", "value");
-        return result;
-    }
-
-    // Should be consistent with createBundle0
-    private void assertBundle0(Bundle bundle) {
-        assertEquals(1, bundle.size());
-        assertEquals("value", bundle.getString("placeholderString"));
-    }
-
-    // Should be consistent with assertBundle1
-    private static Bundle createBundle1() {
-        Bundle result = new Bundle();
-        result.putInt("placeholderInt", 1);
-        return result;
-    }
-
-    // Should be consistent with createBundle1
-    private void assertBundle1(Bundle bundle) {
-        assertEquals(1, bundle.size());
-        assertEquals(1, bundle.getInt("placeholderInt"));
-    }
-
-    private void startTestActivity() {
-        ComponentName component = new ComponentName(
-                APP_RESTRICTIONS_TARGET_PKG, APP_RESTRICTIONS_ACTIVITY_NAME);
-        Log.d(TAG, "Starting activity " + component.flattenToShortString() + " on user "
-                + Process.myUserHandle());
-        mContext.startActivity(new Intent()
-                .setComponent(component)
-                .putExtra("admin_type",
-                        InstrumentationRegistry.getArguments().getString("admin_type"))
-                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
-    }
-
-    private Bundle waitForChangedRestriction() throws InterruptedException {
-        startTestActivity();
-        assertTrue("App restrictions target app did not respond in time",
-                mOnRestrictionsSemaphore.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS));
-        return mReceivedRestrictions;
-    }
-
-    private boolean amIAppRestrictionsDelegate() {
-        final List<String> scopes = mDpm.getDelegatedScopes(null, mContext.getPackageName());
-        return scopes.contains(DELEGATION_APP_RESTRICTIONS);
-    }
-}
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/GeneralDelegateTest.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/GeneralDelegateTest.java
index 869ff7c..b93b884 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/GeneralDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/GeneralDelegateTest.java
@@ -16,7 +16,6 @@
 package com.android.cts.delegate;
 
 import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
 import android.os.Bundle;
 import android.test.MoreAsserts;
 import android.util.Log;
@@ -58,17 +57,4 @@
                     expected.getMessage());
         }
     }
-
-    public void testSettingAdminComponentNameThrowsException() {
-        final String myPackageName = getInstrumentation().getContext().getPackageName();
-        final ComponentName myComponentName = new ComponentName(myPackageName,
-                GeneralDelegateTest.class.getName());
-
-        try {
-            mDpm.setUninstallBlocked(myComponentName, myPackageName, true);
-            fail("Expected SecurityException not thrown");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex("No active admin", expected.getMessage());
-        }
-    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java
deleted file mode 100644
index 201aeed..0000000
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.deviceandprofileowner;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.UserManager;
-import android.test.MoreAsserts;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Functionality tests for application restrictions APIs.
- *
- * <p>APIs are executed locally to assert that what you set can later be retrieved via the getter.
- * It also fires up an external activity to observe an application's view of its restrictions.
- *
- * <p>Finally, it checks that the {@link Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED} broadcast
- * is sent whenever app restrictions are modified for a given package.
- */
-public class ApplicationRestrictionsTest extends BaseDeviceAdminTest {
-
-    private static final String APP_RESTRICTIONS_TARGET_PKG =
-            "com.android.cts.apprestrictions.targetapp";
-    private static final String APP_RESTRICTIONS_ACTIVITY_NAME =
-            APP_RESTRICTIONS_TARGET_PKG + ".ApplicationRestrictionsActivity";
-    private static final String ACTION_RESTRICTIONS_VALUE =
-            "com.android.cts.apprestrictions.targetapp.RESTRICTIONS_VALUE";
-
-    private static final String OTHER_PACKAGE = APP_RESTRICTIONS_TARGET_PKG + "placeholder";
-
-    private static final String[] TEST_STRINGS = new String[] {
-            "<bad/>",
-            ">worse!\"£$%^&*()'<",
-            "<JSON>\"{ \\\"One\\\": { \\\"OneOne\\\": \\\"11\\\", \\\""
-                    + "OneTwo\\\": \\\"12\\\" }, \\\"Two\\\": \\\"2\\\" } <JSON/>\""
-    };
-
-    private static final Bundle BUNDLE_0 = createBundle0();
-    private static final Bundle BUNDLE_1 = createBundle1();
-
-    private static final long RESTRICTIONS_TIMEOUT_SECONDS = 10;
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (ACTION_RESTRICTIONS_VALUE.equals(action)) {
-                mReceivedRestrictions = intent.getBundleExtra("value");
-                mOnRestrictionsReceivedFromAppSemaphore.release();
-            } else if (Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED.equals(action)) {
-                mOnAppRestrictionsChangedSemahore.release();
-            }
-        }
-    };
-
-    private final Semaphore mOnAppRestrictionsChangedSemahore = new Semaphore(0);
-    private final Semaphore mOnRestrictionsReceivedFromAppSemaphore = new Semaphore(0);
-    private Bundle mReceivedRestrictions;
-    private UserManager mUserManager;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_RESTRICTIONS_VALUE);
-        filter.addAction(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-        mContext.registerReceiver(mReceiver, filter);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mContext.unregisterReceiver(mReceiver);
-
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, new Bundle());
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE, new Bundle());
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, mContext.getPackageName(), new Bundle());
-
-        super.tearDown();
-    }
-
-    public void testNullComponentThrowsException() {
-        try {
-            mDevicePolicyManager.setApplicationRestrictions(
-                    null, APP_RESTRICTIONS_TARGET_PKG, null);
-            fail("Expected SecurityException not thrown");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "Calling identity is not authorized",
-                    expected.getMessage());
-        }
-        try {
-            mDevicePolicyManager.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG);
-            fail("Expected SecurityException not thrown");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "Calling identity is not authorized",
-                    expected.getMessage());
-        }
-    }
-
-    public void testSetApplicationRestrictions() {
-        // Test setting restrictions
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_0);
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE, BUNDLE_1);
-
-        // Retrieve restrictions locally and make sure they are what we put in.
-        assertBundle0(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG));
-        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE));
-
-        // Check that the target app can retrieve the same restrictions.
-        assertBundle0(waitForRestrictionsValueFromTestActivity());
-
-        // Test overwriting
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_1);
-        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG));
-        assertBundle1(waitForRestrictionsValueFromTestActivity());
-
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, new Bundle());
-        assertTrue(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG).isEmpty());
-        assertTrue(waitForRestrictionsValueFromTestActivity().isEmpty());
-
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE, null);
-        assertTrue(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE).isEmpty());
-    }
-
-    public void testCanRetrieveOwnRestrictionsViaUserManager() {
-        final String packageName = mContext.getPackageName();
-
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, packageName, BUNDLE_0);
-        assertBundle0(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, packageName));
-
-        // Check that we got the restrictions changed callback.
-        assertBundle0(waitForRestrictionsChangedBroadcast());
-
-        mDevicePolicyManager.setApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, packageName, BUNDLE_1);
-        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, packageName));
-        assertBundle1(waitForRestrictionsChangedBroadcast());
-    }
-
-    public void testCannotRetrieveOtherPackageRestrictionsViaUserManager() {
-        try {
-            mUserManager.getApplicationRestrictions(OTHER_PACKAGE);
-            fail("Expected SecurityException not thrown");
-        } catch (SecurityException expected) {
-        }
-    }
-
-    public void testSetApplicationRestrictionsManagingPackage() throws NameNotFoundException {
-        final String previousValue = mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
-                ADMIN_RECEIVER_COMPONENT);
-        try {
-            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
-                    ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG);
-            assertEquals(APP_RESTRICTIONS_TARGET_PKG,
-                    mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
-                            ADMIN_RECEIVER_COMPONENT));
-            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
-                    ADMIN_RECEIVER_COMPONENT, null);
-            assertNull(mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
-                    ADMIN_RECEIVER_COMPONENT));
-        } finally {
-            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
-                    ADMIN_RECEIVER_COMPONENT, previousValue);
-            assertEquals(previousValue,
-                    mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
-                            ADMIN_RECEIVER_COMPONENT));
-        }
-    }
-
-    public void testSetApplicationRestrictionsManagingPackageForNotInstalledPackage()
-            throws NameNotFoundException {
-        try {
-            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(ADMIN_RECEIVER_COMPONENT,
-                    OTHER_PACKAGE);
-            fail("Not throwing exception for not installed package name");
-        } catch (NameNotFoundException expected) {
-            MoreAsserts.assertContainsRegex(OTHER_PACKAGE, expected.getMessage());
-        } finally {
-            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(ADMIN_RECEIVER_COMPONENT,
-                    null);
-            assertNull(mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
-                    ADMIN_RECEIVER_COMPONENT));
-        }
-    }
-
-    // Should be consistent with assertBundle0
-    private static Bundle createBundle0() {
-        Bundle result = new Bundle();
-        // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
-        // Also test for string escaping handling
-        result.putBoolean("boolean_0", false);
-        result.putBoolean("boolean_1", true);
-        result.putInt("integer", 0x7fffffff);
-        // If a null is stored, "" will be read back
-        result.putString("empty", "");
-        result.putString("string", "text");
-        result.putStringArray("string[]", TEST_STRINGS);
-
-        // Adding a bundle, which contain 2 nested restrictions - bundle_string and bundle_int
-        Bundle bundle = new Bundle();
-        bundle.putString("bundle_string", "bundle_string");
-        bundle.putInt("bundle_int", 1);
-        result.putBundle("bundle", bundle);
-
-        // Adding an array of 2 bundles
-        Bundle[] bundleArray = new Bundle[2];
-        bundleArray[0] = new Bundle();
-        bundleArray[0].putString("bundle_array_string", "bundle_array_string");
-        // Put bundle inside bundle
-        bundleArray[0].putBundle("bundle_array_bundle", bundle);
-        bundleArray[1] = new Bundle();
-        bundleArray[1].putString("bundle_array_string2", "bundle_array_string2");
-        result.putParcelableArray("bundle_array", bundleArray);
-        return result;
-    }
-
-    // Should be consistent with createBundle0
-    private void assertBundle0(Bundle bundle) {
-        assertEquals(8, bundle.size());
-        assertEquals(false, bundle.getBoolean("boolean_0"));
-        assertEquals(true, bundle.getBoolean("boolean_1"));
-        assertEquals(0x7fffffff, bundle.getInt("integer"));
-        assertEquals("", bundle.getString("empty"));
-        assertEquals("text", bundle.getString("string"));
-
-        String[] strings = bundle.getStringArray("string[]");
-        assertTrue(strings != null && strings.length == TEST_STRINGS.length);
-        for (int i = 0; i < strings.length; i++) {
-            assertEquals(strings[i], TEST_STRINGS[i]);
-        }
-
-        Bundle childBundle = bundle.getBundle("bundle");
-        assertEquals("bundle_string", childBundle.getString("bundle_string"));
-        assertEquals(1, childBundle.getInt("bundle_int"));
-
-        Parcelable[] bundleArray = bundle.getParcelableArray("bundle_array");
-        assertEquals(2, bundleArray.length);
-        // Verifying bundle_array[0]
-        Bundle bundle1 = (Bundle) bundleArray[0];
-        assertEquals("bundle_array_string", bundle1.getString("bundle_array_string"));
-        Bundle bundle1ChildBundle = bundle1.getBundle("bundle_array_bundle");
-        assertNotNull(bundle1ChildBundle);
-        assertEquals("bundle_string", bundle1ChildBundle.getString("bundle_string"));
-        assertEquals(1, bundle1ChildBundle.getInt("bundle_int"));
-        // Verifying bundle_array[1]
-        Bundle bundle2 = (Bundle) bundleArray[1];
-        assertEquals("bundle_array_string2", bundle2.getString("bundle_array_string2"));
-    }
-
-    // Should be consistent with assertBundle1
-    private static Bundle createBundle1() {
-        Bundle result = new Bundle();
-        result.putInt("placeholder", 1);
-        return result;
-    }
-
-    // Should be consistent with createBundle1
-    private void assertBundle1(Bundle bundle) {
-        assertEquals(1, bundle.size());
-        assertEquals(1, bundle.getInt("placeholder"));
-    }
-
-    private void startTestActivity() {
-        mContext.startActivity(new Intent()
-                .setComponent(new ComponentName(
-                        APP_RESTRICTIONS_TARGET_PKG, APP_RESTRICTIONS_ACTIVITY_NAME))
-                .putExtra("admin_type",
-                        InstrumentationRegistry.getArguments().getString("admin_type"))
-                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
-    }
-
-    private Bundle waitForRestrictionsValueFromTestActivity() {
-        startTestActivity();
-
-        try {
-            assertTrue(mOnRestrictionsReceivedFromAppSemaphore.tryAcquire(
-                    RESTRICTIONS_TIMEOUT_SECONDS, TimeUnit.SECONDS));
-        } catch (InterruptedException e) {
-            fail("waitForRestrictionsValueFromTestActivity() interrupted");
-        }
-
-        return mReceivedRestrictions;
-    }
-
-    private Bundle waitForRestrictionsChangedBroadcast() {
-        try {
-            assertTrue(mOnAppRestrictionsChangedSemahore.tryAcquire(
-                    RESTRICTIONS_TIMEOUT_SECONDS, TimeUnit.SECONDS));
-        } catch (InterruptedException e) {
-            fail("waitForRestrictionsChangedBroadcast() interrupted");
-        }
-
-        return mUserManager.getApplicationRestrictions(mContext.getPackageName());
-    }
-}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
index 4b8d874..eebc2bd 100755
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
@@ -422,6 +422,9 @@
      */
     // TODO(b/198408853): Migrate
     public void testCanGenerateKeyPairWithKeyAttestation() throws Exception {
+        if (!isAttestationSupported()) {
+            return;
+        }
         for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) {
             assertThat(
                     generateKeyAndCheckAttestation(
@@ -831,4 +834,8 @@
     boolean isUniqueDeviceAttestationSupported() {
         return mDevicePolicyManager.isUniqueDeviceAttestationSupported();
     }
+
+    private boolean isAttestationSupported() {
+        return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.O;
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 992f6f1..c30543b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -186,6 +186,8 @@
     /** Users we shouldn't delete in the tests */
     private ArrayList<Integer> mFixedUsers;
 
+    protected boolean mHasAttestation;
+
     private static final String VERIFY_CREDENTIAL_CONFIRMATION = "Lock credential verified";
 
     @Rule
@@ -205,6 +207,10 @@
         mFixedPackages = getDevice().getInstalledPackageNames();
         mBuildHelper = new CompatibilityBuildHelper(getBuild());
 
+        String propertyValue = getDevice().getProperty("ro.product.first_api_level");
+        if (propertyValue != null && !propertyValue.isEmpty()) {
+            mHasAttestation = Integer.parseInt(propertyValue) >= 26;
+        }
         if (hasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN)) {
             ensurePrimaryUserHasNoPassword();
         }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index bd46a07..075d422 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -234,53 +234,6 @@
             "testAssertCallerIsApplicationRestrictionsManagingPackage", mUserId);
     }
 
-    @Test
-    public void testApplicationRestrictions() throws Exception {
-        installAppAsUser(DELEGATE_APP_APK, mUserId);
-        installAppAsUser(APP_RESTRICTIONS_TARGET_APP_APK, mUserId);
-
-        try {
-            // Only the DPC can manage app restrictions by default.
-            executeDeviceTestClass(".ApplicationRestrictionsTest");
-            executeAppRestrictionsManagingPackageTest("testCannotAccessApis");
-
-            // Letting the DELEGATE_APP_PKG manage app restrictions too.
-            changeApplicationRestrictionsManagingPackage(DELEGATE_APP_PKG);
-            executeAppRestrictionsManagingPackageTest("testCanAccessApis");
-            runDeviceTestsAsUser(DELEGATE_APP_PKG, ".GeneralDelegateTest",
-                    "testSettingAdminComponentNameThrowsException", mUserId);
-
-            // The DPC should still be able to manage app restrictions normally.
-            executeDeviceTestClass(".ApplicationRestrictionsTest");
-
-            // The app shouldn't be able to manage app restrictions for other users.
-            int parentUserId = getPrimaryUser();
-            if (parentUserId != mUserId) {
-                installAppAsUser(DELEGATE_APP_APK, parentUserId);
-                installAppAsUser(APP_RESTRICTIONS_TARGET_APP_APK, parentUserId);
-                runDeviceTestsAsUser(DELEGATE_APP_PKG, ".AppRestrictionsDelegateTest",
-                        "testCannotAccessApis", parentUserId);
-            }
-
-            // Revoking the permission for DELEGATE_APP_PKG to manage restrictions.
-            changeApplicationRestrictionsManagingPackage(null);
-            executeAppRestrictionsManagingPackageTest("testCannotAccessApis");
-
-            // The DPC should still be able to manage app restrictions normally.
-            executeDeviceTestClass(".ApplicationRestrictionsTest");
-
-            assertMetricsLogged(getDevice(), () -> {
-                executeDeviceTestMethod(".ApplicationRestrictionsTest",
-                        "testSetApplicationRestrictions");
-            }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_RESTRICTIONS_VALUE)
-                    .setAdminPackageName(DEVICE_ADMIN_PKG)
-                    .setStrings(APP_RESTRICTIONS_TARGET_APP_PKG)
-                    .build());
-        } finally {
-            changeApplicationRestrictionsManagingPackage(null);
-        }
-    }
-
     /**
      * Returns a list of delegation tests that should run. Add delegations tests applicable to both
      * device owner and profile owners to this method directly. DO or PO specific tests should be
@@ -288,7 +241,6 @@
      */
     private Map<String, DevicePolicyEventWrapper[]> getDelegationTests() {
         final Map<String, DevicePolicyEventWrapper[]> result = new HashMap<>();
-        result.put(".AppRestrictionsDelegateTest", null);
         result.put(".CertInstallDelegateTest", null);
         result.put(".BlockUninstallDelegateTest", null);
         result.put(".PermissionGrantDelegateTest", null);
@@ -753,6 +705,8 @@
             reason = "Will be migrated to new test infra")
     @Test
     public void testDelegatedCertInstallerDirectly() throws Exception {
+        assumeTrue(mHasAttestation);
+
         setUpDelegatedCertInstallerAndRunTests(() ->
             runDeviceTestsAsUser("com.android.cts.certinstaller",
                     ".DirectDelegatedCertInstallerTest", mUserId));
@@ -764,6 +718,8 @@
             reason = "Will be migrated to new test infra")
     @Test
     public void testSetKeyGrant() throws Exception {
+        assumeTrue(mHasAttestation);
+
         // Install an app
         installAppAsUser(CERT_INSTALLER_APK, mUserId);
 
@@ -1292,6 +1248,8 @@
     @Test
     // TODO(b/198408853): Migrate
     public void testGenerateKeyPairLogged() throws Exception {
+        assumeTrue(mHasAttestation);
+
         assertMetricsLogged(getDevice(), () -> {
                 executeDeviceTestMethod(
                         ".KeyManagementTest", "testCanGenerateKeyPairWithKeyAttestation");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index fc638aa..17c2d48 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -832,6 +832,7 @@
     }
 
     @Test
+    @Ignore("b/204508654")
     public void testSetUserControlDisabledPackages_multiUser_reboot_verifyPackageNotStopped()
             throws Exception {
         assumeCanCreateAdditionalUsers(1);
@@ -879,7 +880,7 @@
                 // done in tear down.
             }
         } finally {
-          setStopBgUsersOnSwitchProperty(stopBgUsersOnSwitchValue);
+            setStopBgUsersOnSwitchProperty(stopBgUsersOnSwitchValue);
         }
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 6d21042..75af1c8 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -142,14 +142,6 @@
 
     @Override
     @Test
-    @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "189268544",
-            reason = "Will be migrated to new test infra")
-    public void testApplicationRestrictions() throws Exception {
-        super.testApplicationRestrictions();
-    }
-
-    @Override
-    @Test
     @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "197909577",
             reason = "Will be migrated to new test infra")
     public void testAccountManagement_userRestrictionAddAccount() throws Exception {
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt
index 3038849..955a078 100644
--- a/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt
@@ -38,7 +38,7 @@
 
 internal fun DeviceState.getWorkDevicePolicyManager() =
     profileOwner(workProfile(DeviceState.UserType.PRIMARY_USER))!!
-            .app().devicePolicyManager()
+            .devicePolicyManager()
 
 internal fun <T> withUserContext(user: UserReference, block: (context: Context) -> T) =
     TestApis.permissions()
diff --git a/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java b/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
index 4c05fd1..3203793 100644
--- a/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
+++ b/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
@@ -31,6 +31,9 @@
 import android.server.wm.WindowManagerStateHelper;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
@@ -53,6 +56,8 @@
 
     public static final int TIMEOUT_MS = 2_000;
 
+    public static final double DEADZONE_PCT = 0.2;
+
     protected Context mContext;
     protected UiDevice mDevice;
 
@@ -140,6 +145,16 @@
         boolean found = null != mDevice.wait(Until.findObject(By.text(mWorkPolicyInfoText)),
                 TIMEOUT_MS);
 
+        // Try to scroll the list to find the item
+        if (!found) {
+            UiScrollable scroller = new UiScrollable(new UiSelector().scrollable(true));
+            try {
+                // Swipe far away from the edges to avoid triggering navigation gestures
+                scroller.setSwipeDeadZonePercentage(DEADZONE_PCT);
+                found = scroller.scrollTextIntoView(mWorkPolicyInfoText);
+            } catch (UiObjectNotFoundException e) { }
+        }
+
         Log.d(TAG, "Message found: " + found);
         return found;
     }
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/AtomTestCase.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/AtomTestCase.java
index 8b628cf..d547c0d 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/AtomTestCase.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/AtomTestCase.java
@@ -79,6 +79,7 @@
 import java.util.Queue;
 import java.util.Random;
 import java.util.Set;
+import java.util.StringTokenizer;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -926,7 +927,16 @@
      */
     protected boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception {
         final String features = getDevice().executeShellCommand("pm list features");
-        boolean hasIt = features.contains(featureName);
+        StringTokenizer featureToken = new StringTokenizer(features, "\n");
+        boolean hasIt = false;
+
+        while (featureToken.hasMoreTokens()) {
+            if (("feature:" + featureName).equals(featureToken.nextToken())) {
+                 hasIt = true;
+                 break;
+            }
+        }
+
         if (hasIt != requiredAnswer) {
             LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature "
                     + featureName);
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 1adf9b4..737dfaa 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -536,7 +536,22 @@
                  android:resource="@xml/shortcuts"/>
         </activity>
 
-        <service android:name="android.app.stubs.TrimMemService"
+        <!-- Disable home activities by default or it may disturb other tests by
+                showing ResolverActivity when start home activity.
+             Set the task affinity to empty not to group with the other Activities in this app. -->
+        <activity
+            android:name="android.app.stubs.TestHomeActivity"
+            android:enabled="false"
+            android:taskAffinity=""
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+    <service android:name="android.app.stubs.TrimMemService"
             android:exported="true"
             android:isolatedProcess="true">
         </service>
diff --git a/tests/app/app/src/android/app/stubs/TestHomeActivity.java b/tests/app/app/src/android/app/stubs/TestHomeActivity.java
new file mode 100644
index 0000000..340494a
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/TestHomeActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.stubs;
+
+import android.app.Activity;
+
+public class TestHomeActivity extends Activity {
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index 428eb34..770d305 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -21,6 +21,13 @@
 import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
 import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
 import static android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 
 import static org.junit.Assert.assertArrayEquals;
 
@@ -45,8 +52,10 @@
 import android.app.stubs.MockApplicationActivity;
 import android.app.stubs.MockService;
 import android.app.stubs.ScreenOnActivity;
+import android.app.stubs.TestHomeActivity;
 import android.app.stubs.TrimMemService;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -54,6 +63,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Bundle;
@@ -144,6 +154,7 @@
     private List<Activity> mStartedActivityList;
     private int mErrorProcessID;
     private Instrumentation mInstrumentation;
+    private HomeActivitySession mTestHomeSession;
 
     @Override
     protected void setUp() throws Exception {
@@ -161,6 +172,9 @@
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
+        if (mTestHomeSession != null) {
+            mTestHomeSession.close();
+        }
         if (mIntent != null) {
             mInstrumentation.getContext().stopService(mIntent);
         }
@@ -593,6 +607,7 @@
      * Verify that the TimeTrackingAPI works properly when starting and ending an activity.
      */
     public void testTimeTrackingAPI_SimpleStartExit() throws Exception {
+        createManagedHomeActivitySession();
         launchHome();
         // Prepare to start an activity from another APK.
         Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -679,6 +694,7 @@
      * Verify that the TimeTrackingAPI works properly when switching away from the monitored task.
      */
     public void testTimeTrackingAPI_SwitchAwayTriggers() throws Exception {
+        createManagedHomeActivitySession();
         launchHome();
 
         // Prepare to start an activity from another APK.
@@ -727,6 +743,7 @@
      * and ended.
      */
     public void testTimeTrackingAPI_ChainedActivityExit() throws Exception {
+        createManagedHomeActivitySession();
         launchHome();
         // Prepare to start an activity from another APK.
         Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -1766,4 +1783,63 @@
         } while (!(result = supplier.get()) && SystemClock.uptimeMillis() < deadLine);
         return result;
     }
+
+    private void createManagedHomeActivitySession()
+            throws Exception {
+        if (noHomeScreen()) return;
+        ComponentName homeActivity = new ComponentName(
+                STUB_PACKAGE_NAME, TestHomeActivity.class.getName());
+        mTestHomeSession = new HomeActivitySession(homeActivity);
+    }
+
+    /**
+     * HomeActivitySession is used to replace the default home component, so that you can use
+     * your preferred home for testing within the session. The original default home will be
+     * restored automatically afterward.
+     */
+    private class HomeActivitySession {
+        private PackageManager mPackageManager;
+        private ComponentName mOrigHome;
+        private ComponentName mSessionHome;
+
+        HomeActivitySession(ComponentName sessionHome) throws Exception {
+            mSessionHome = sessionHome;
+            mPackageManager = mInstrumentation.getContext().getPackageManager();
+            mOrigHome = getDefaultHomeComponent();
+
+            SystemUtil.runWithShellPermissionIdentity(
+                    () -> mPackageManager.setComponentEnabledSetting(mSessionHome,
+                            COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP));
+            setDefaultHome(mSessionHome);
+        }
+
+        public void close() throws Exception {
+            SystemUtil.runWithShellPermissionIdentity(
+                    () -> mPackageManager.setComponentEnabledSetting(mSessionHome,
+                            COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP));
+            if (mOrigHome != null) {
+                setDefaultHome(mOrigHome);
+                mOrigHome = null;
+            }
+        }
+
+        private void setDefaultHome(ComponentName componentName) throws Exception {
+            executeShellCommand("cmd package set-home-activity --user "
+                    + android.os.Process.myUserHandle().getIdentifier() + " "
+                    + componentName.flattenToString());
+        }
+
+        private ComponentName getDefaultHomeComponent() {
+            final Intent intent = new Intent(ACTION_MAIN);
+            intent.addCategory(CATEGORY_HOME);
+            intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+            final ResolveInfo resolveInfo = mInstrumentation.getContext()
+                    .getPackageManager().resolveActivity(intent, MATCH_DEFAULT_ONLY);
+            if (resolveInfo == null) {
+                throw new AssertionError("Home activity not found");
+            }
+            return new ComponentName(resolveInfo.activityInfo.packageName,
+                    resolveInfo.activityInfo.name);
+        }
+    }
 }
diff --git a/tests/camera/src/android/hardware/cts/CameraGLTest.java b/tests/camera/src/android/hardware/cts/CameraGLTest.java
index 7478e79..030b5ab 100644
--- a/tests/camera/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraGLTest.java
@@ -39,6 +39,8 @@
 
 import androidx.test.rule.ActivityTestRule;
 
+import com.android.compatibility.common.util.WindowUtil;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -104,6 +106,11 @@
     public void setUp() throws Exception {
         // Start CameraCtsActivity.
         GLSurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
+        // Some of the tests run on the UI thread. In case some of the operations take a long time
+        // to complete,  wait for window to receive focus. This ensure that the focus event from
+        // input flinger has been handled, and avoids getting ANR.
+        WindowUtil.waitForFocus(ctsActivity);
+
         // Store a link to the view so we can redraw it when needed
         mGLView = ctsActivity.getView();
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
index 3ed02e8..9c5938e 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
@@ -42,7 +42,7 @@
 import com.android.bedstead.harrier.policies.AccountManagement;
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.utils.Poll;
-import com.android.bedstead.remotedpc.RemoteDpc;
+import com.android.bedstead.remotedpc.RemotePolicyManager;
 import com.android.bedstead.testapp.TestApp;
 import com.android.bedstead.testapp.TestAppInstance;
 import com.android.bedstead.testapp.TestAppProvider;
@@ -57,7 +57,7 @@
 import java.io.IOException;
 
 @RunWith(BedsteadJUnit4.class)
-public class AccountManagementTest {
+public final class AccountManagementTest {
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
@@ -85,8 +85,8 @@
 
     @Before
     public void setUp() {
-        RemoteDpc dpc = sDeviceState.dpc();
-        mAdmin = dpc.devicePolicyController().componentName();
+        RemotePolicyManager dpc = sDeviceState.dpc();
+        mAdmin = dpc.componentName();
         mDpm = dpc.devicePolicyManager();
         mAccountManager = sContext.getSystemService(AccountManager.class);
     }
@@ -100,7 +100,8 @@
 
     @Test
     @Postsubmit(reason = "new test")
-    @CannotSetPolicyTest(policy = AccountManagement.class)
+    // We don't include non device admin states as passing a null admin is a NullPointerException
+    @CannotSetPolicyTest(policy = AccountManagement.class, includeNonDeviceAdminStates = false)
     public void setAccountTypesWithManagementDisabled_invalidAdmin_throwsException() {
         assertThrows(OperationCanceledException.class, () ->
                 mDpm.setAccountManagementDisabled(
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/AdminPermissionControlParamsTests.java b/tests/devicepolicy/src/android/devicepolicy/cts/AdminPermissionControlParamsTests.java
index 22a64bf..27dbf44 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/AdminPermissionControlParamsTests.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/AdminPermissionControlParamsTests.java
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(BedsteadJUnit4.class)
-public class AdminPermissionControlParamsTests {
+public final class AdminPermissionControlParamsTests {
     private static final String PKG = "somePackage";
     private static final String PERMISSION = "somePackage";
     private static final int GRANT_STATE = DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ApplicationRestrictionsTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ApplicationRestrictionsTest.java
index da08a5c..0c054e3 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ApplicationRestrictionsTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ApplicationRestrictionsTest.java
@@ -15,12 +15,21 @@
  */
 package android.devicepolicy.cts;
 
+import static android.content.Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED;
+
+import static com.android.bedstead.metricsrecorder.truth.MetricQueryBuilderSubject.assertThat;
+import static com.android.eventlib.truth.EventLogsSubject.assertThat;
+
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.testng.Assert.assertThrows;
 
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.stats.devicepolicy.EventId;
 import android.util.Log;
 
 import com.android.bedstead.harrier.BedsteadJUnit4;
@@ -31,6 +40,8 @@
 import com.android.bedstead.harrier.annotations.enterprise.NegativePolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
 import com.android.bedstead.harrier.policies.ApplicationRestrictions;
+import com.android.bedstead.harrier.policies.ApplicationRestrictionsManagingPackage;
+import com.android.bedstead.metricsrecorder.EnterpriseMetricsRecorder;
 import com.android.bedstead.testapp.TestApp;
 import com.android.bedstead.testapp.TestAppInstance;
 import com.android.bedstead.testapp.TestAppProvider;
@@ -45,170 +56,32 @@
 @RunWith(BedsteadJUnit4.class)
 public final class ApplicationRestrictionsTest {
 
+    @ClassRule
+    @Rule
+    public static final DeviceState sDeviceState = new DeviceState();
     private static final String TAG = ApplicationRestrictionsTest.class.getSimpleName();
-
-    private static final String[] TEST_STRINGS = new String[] {
+    private static final String[] TEST_STRINGS = new String[]{
             "<bad/>",
             ">worse!\"£$%^&*()'<",
             "<JSON>\"{ \\\"One\\\": { \\\"OneOne\\\": \\\"11\\\", \\\""
                     + "OneTwo\\\": \\\"12\\\" }, \\\"Two\\\": \\\"2\\\" } <JSON/>\""
     };
-
-    private static final Bundle BUNDLE = createBundle();
-
-    @ClassRule
-    @Rule
-    public static final DeviceState sDeviceState = new DeviceState();
-
     private static final TestAppProvider sTestAppProvider = new TestAppProvider();
 
     private static final TestApp sTestApp = sTestAppProvider.any();
     private static final TestApp sDifferentTestApp = sTestAppProvider.any();
 
-    @Test
-    @Postsubmit(reason = "New test")
-    @PositivePolicyTest(policy = ApplicationRestrictions.class)
-    public void setApplicationRestrictions_applicationRestrictionsAreSet() {
-        Bundle originalApplicationRestrictions =
-                sDeviceState.dpc().devicePolicyManager()
-                        .getApplicationRestrictions(
-                                sDeviceState.dpc().componentName(), sTestApp.packageName());
-
-        try (TestAppInstance testApp = sTestApp.install()) {
-            sDeviceState.dpc().devicePolicyManager()
-                    .setApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
-                            BUNDLE);
-
-            assertEqualToBundle(
-                    testApp.userManager().getApplicationRestrictions(sTestApp.packageName()));
-        } finally {
-            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
-                    sDeviceState.dpc().componentName(),
-                    sTestApp.packageName(), originalApplicationRestrictions);
-        }
-    }
-
-    @Test
-    @Postsubmit(reason = "New test")
-    @CanSetPolicyTest(policy = ApplicationRestrictions.class)
-    public void getApplicationRestrictions_applicationRestrictionsAreSet_returnsApplicationRestrictions() {
-        Bundle originalApplicationRestrictions =
-                sDeviceState.dpc().devicePolicyManager()
-                        .getApplicationRestrictions(
-                                sDeviceState.dpc().componentName(), sTestApp.packageName());
-
-        try {
-            sDeviceState.dpc().devicePolicyManager()
-                    .setApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
-                            BUNDLE);
-
-            assertEqualToBundle(
-                    sDeviceState.dpc().devicePolicyManager().getApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName()));
-        } finally {
-            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
-                    sDeviceState.dpc().componentName(),
-                    sTestApp.packageName(), originalApplicationRestrictions);
-        }
-    }
-
-    @Test
-    @Postsubmit(reason = "New test")
-    @CanSetPolicyTest(policy = ApplicationRestrictions.class)
-    public void getApplicationRestrictions_differentPackage_throwsException() {
-        Bundle originalApplicationRestrictions =
-                sDeviceState.dpc().devicePolicyManager()
-                        .getApplicationRestrictions(
-                                sDeviceState.dpc().componentName(), sTestApp.packageName());
-
-        try (TestAppInstance differentTestApp = sDifferentTestApp.install()) {
-            sDeviceState.dpc().devicePolicyManager()
-                    .setApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
-                            BUNDLE);
-
-            assertThrows(SecurityException.class,
-                    () -> differentTestApp.userManager().getApplicationRestrictions(
-                            sTestApp.packageName()));
-        } finally {
-            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
-                    sDeviceState.dpc().componentName(),
-                    sTestApp.packageName(), originalApplicationRestrictions);
-        }
-    }
-
-    @Test
-    @Postsubmit(reason = "New test")
-    @CanSetPolicyTest(policy = ApplicationRestrictions.class)
-    public void getApplicationRestrictions_setForOtherPackage_returnsNull() {
-        Bundle originalApplicationRestrictions =
-                sDeviceState.dpc().devicePolicyManager()
-                        .getApplicationRestrictions(
-                                sDeviceState.dpc().componentName(), sTestApp.packageName());
-
-        try (TestAppInstance differentTestApp = sDifferentTestApp.install()) {
-            sDeviceState.dpc().devicePolicyManager()
-                    .setApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
-                            BUNDLE);
-
-            assertNotEqualToBundle(differentTestApp.userManager().getApplicationRestrictions(
-                            sDifferentTestApp.packageName()));
-        } finally {
-            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
-                    sDeviceState.dpc().componentName(),
-                    sTestApp.packageName(), originalApplicationRestrictions);
-        }
-    }
-
-    @Test
-    @Postsubmit(reason = "New test")
-    @NegativePolicyTest(policy = ApplicationRestrictions.class)
-    public void setApplicationRestrictions_policyDoesNotApply_applicationRestrictionsAreNotSet() {
-        Bundle originalApplicationRestrictions =
-                sDeviceState.dpc().devicePolicyManager().getApplicationRestrictions(
-                        sDeviceState.dpc().componentName(), sTestApp.packageName());
-
-        try (TestAppInstance testApp = sTestApp.install()) {
-            sDeviceState.dpc().devicePolicyManager()
-                    .setApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
-                            BUNDLE);
-
-            assertNotEqualToBundle(testApp.userManager().getApplicationRestrictions(
-                    sTestApp.packageName()));
-        } finally {
-            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
-                    sDeviceState.dpc().componentName(),
-                    sTestApp.packageName(), originalApplicationRestrictions);
-        }
-    }
-
-    @Test
-    @Postsubmit(reason = "New test")
-    @CannotSetPolicyTest(policy = ApplicationRestrictions.class)
-    public void setApplicationRestrictions_cannotSetPolicy_throwsException() {
-        assertThrows(SecurityException.class, () -> {
-            sDeviceState.dpc().devicePolicyManager()
-                    .setApplicationRestrictions(
-                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
-                            BUNDLE);
-        });
-    }
-
-    // Should be consistent with assertBundle0
-    private static Bundle createBundle() {
+    // Should be consistent with assertEqualToBundle
+    private static Bundle createBundle(String id) {
         Bundle result = new Bundle();
         // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
         // Also test for string escaping handling
         result.putBoolean("boolean_0", false);
         result.putBoolean("boolean_1", true);
-        result.putInt("integer", 0x7fffffff);
+        result.putInt("integer", 0xfffff);
         // If a null is stored, "" will be read back
         result.putString("empty", "");
-        result.putString("string", "text");
+        result.putString("string", id);
         result.putStringArray("string[]", TEST_STRINGS);
 
         // Adding a bundle, which contain 2 nested restrictions - bundle_string and bundle_int
@@ -229,15 +102,298 @@
         return result;
     }
 
-    // Should be consistent with createBundle0
-    private void assertEqualToBundle(Bundle bundle) {
+    @Test
+    @Postsubmit(reason = "New test")
+    @PositivePolicyTest(policy = ApplicationRestrictions.class)
+    public void setApplicationRestrictions_applicationRestrictionsAreSet() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("setApplicationRestrictions_applicationRestrictionsAreSet");
+
+        try (TestAppInstance testApp = sTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertEqualToBundle("setApplicationRestrictions_applicationRestrictionsAreSet",
+                    testApp.userManager().getApplicationRestrictions(sTestApp.packageName()));
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @PositivePolicyTest(policy = ApplicationRestrictions.class)
+    public void setApplicationRestrictions_applicationRestrictionsAlreadySet_setsNewRestrictions() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("setApplicationRestrictions_applicationRestrictionsAlreadySet_setsNewRestrictions");
+
+        try (TestAppInstance testApp = sTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            new Bundle());
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertEqualToBundle("setApplicationRestrictions_applicationRestrictionsAlreadySet_setsNewRestrictions",
+                    testApp.userManager().getApplicationRestrictions(sTestApp.packageName()));
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CanSetPolicyTest(policy = ApplicationRestrictions.class)
+    public void getApplicationRestrictions_applicationRestrictionsAreSet_returnsApplicationRestrictions() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("getApplicationRestrictions_applicationRestrictionsAreSet_returnsApplicationRestrictions");
+
+        try {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertEqualToBundle("getApplicationRestrictions_applicationRestrictionsAreSet_returnsApplicationRestrictions",
+                    sDeviceState.dpc().devicePolicyManager().getApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName()));
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CanSetPolicyTest(policy = ApplicationRestrictions.class)
+    public void getApplicationRestrictions_differentPackage_throwsException() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("getApplicationRestrictions_differentPackage_throwsException");
+
+        try (TestAppInstance differentTestApp = sDifferentTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertThrows(SecurityException.class,
+                    () -> differentTestApp.userManager().getApplicationRestrictions(
+                            sTestApp.packageName()));
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CanSetPolicyTest(policy = ApplicationRestrictions.class)
+    public void getApplicationRestrictions_setForOtherPackage_returnsNull() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("getApplicationRestrictions_setForOtherPackage_returnsNull");
+
+        try (TestAppInstance differentTestApp = sDifferentTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertNotEqualToBundle("getApplicationRestrictions_setForOtherPackage_returnsNull",
+                    differentTestApp.userManager().getApplicationRestrictions(
+                    sDifferentTestApp.packageName()));
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @NegativePolicyTest(policy = ApplicationRestrictions.class)
+    public void setApplicationRestrictions_policyDoesNotApply_applicationRestrictionsAreNotSet() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager().getApplicationRestrictions(
+                        sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("setApplicationRestrictions_policyDoesNotApply_applicationRestrictionsAreNotSet");
+
+        try (TestAppInstance testApp = sTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertNotEqualToBundle("setApplicationRestrictions_policyDoesNotApply_applicationRestrictionsAreNotSet",
+                    testApp.userManager().getApplicationRestrictions(sTestApp.packageName()));
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CannotSetPolicyTest(policy = ApplicationRestrictions.class)
+    public void setApplicationRestrictions_cannotSetPolicy_throwsException() {
+        Bundle bundle = createBundle("setApplicationRestrictions_cannotSetPolicy_throwsException");
+        assertThrows(SecurityException.class, () -> {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+        });
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CannotSetPolicyTest(policy = ApplicationRestrictions.class)
+    public void getApplicationRestrictions_cannotSetPolicy_throwsException() {
+        assertThrows(SecurityException.class, () -> {
+            sDeviceState.dpc().devicePolicyManager()
+                    .getApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName());
+        });
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CanSetPolicyTest(policy = ApplicationRestrictions.class, singleTestOnly = true)
+    public void setApplicationRestrictions_nullComponent_throwsException() {
+        Bundle bundle = createBundle("setApplicationRestrictions_nullComponent_throwsException");
+        assertThrows(SecurityException.class,
+                () -> sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(null,
+                        sTestApp.packageName(), bundle));
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @PositivePolicyTest(policy = ApplicationRestrictions.class)
+    public void setApplicationRestrictions_restrictionsChangedBroadcastIsReceived() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("setApplicationRestrictions_restrictionsChangedBroadcastIsReceived");
+
+        try (TestAppInstance testApp = sTestApp.install()) {
+            testApp.registerReceiver(new IntentFilter(ACTION_APPLICATION_RESTRICTIONS_CHANGED));
+
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertThat(testApp.events().broadcastReceived().whereIntent().action().isEqualTo(
+                    ACTION_APPLICATION_RESTRICTIONS_CHANGED)).eventOccurred();
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CanSetPolicyTest(policy = ApplicationRestrictionsManagingPackage.class)
+    public void setApplicationRestrictionsManagingPackage_applicationRestrictionsManagingPackageIsSet()
+            throws Exception {
+        final String originalApplicationRestrictionsManagingPackage =
+                sDeviceState.dpc().devicePolicyManager().getApplicationRestrictionsManagingPackage(
+                        sDeviceState.dpc().componentName());
+        try (TestAppInstance testApp = sTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictionsManagingPackage(
+                    sDeviceState.dpc().componentName(), sTestApp.packageName());
+
+            assertThat(sDeviceState.dpc().devicePolicyManager()
+                    .getApplicationRestrictionsManagingPackage(sDeviceState.dpc().componentName()))
+                    .isEqualTo(sTestApp.packageName());
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictionsManagingPackage(
+                    sDeviceState.dpc().componentName(),
+                    originalApplicationRestrictionsManagingPackage);
+        }
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @CanSetPolicyTest(policy = ApplicationRestrictionsManagingPackage.class)
+    public void setApplicationRestrictionsManagingPackage_appNotInstalled_throwsException() {
+        sDifferentTestApp.uninstall();
+
+        assertThrows(PackageManager.NameNotFoundException.class,
+                () -> sDeviceState.dpc().devicePolicyManager()
+                        .setApplicationRestrictionsManagingPackage(
+                                sDeviceState.dpc().componentName(),
+                                sDifferentTestApp.packageName()));
+    }
+
+    @Test
+    @Postsubmit(reason = "New test")
+    @PositivePolicyTest(policy = ApplicationRestrictions.class)
+    public void setApplicationRestrictions_logged() {
+        Bundle originalApplicationRestrictions =
+                sDeviceState.dpc().devicePolicyManager()
+                        .getApplicationRestrictions(
+                                sDeviceState.dpc().componentName(), sTestApp.packageName());
+        Bundle bundle = createBundle("setApplicationRestrictions_logged");
+
+        try (EnterpriseMetricsRecorder metrics = EnterpriseMetricsRecorder.create();
+             TestAppInstance testApp = sTestApp.install()) {
+            sDeviceState.dpc().devicePolicyManager()
+                    .setApplicationRestrictions(
+                            sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                            bundle);
+
+            assertThat(metrics.query()
+                    .whereType().isEqualTo(EventId.SET_APPLICATION_RESTRICTIONS_VALUE)
+                    .whereAdminPackageName().isEqualTo(
+                            sDeviceState.dpc().packageName())
+                    .whereStrings().contains(sTestApp.packageName())
+                    .whereStrings().size().isEqualTo(1))
+                    .wasLogged();
+        } finally {
+            sDeviceState.dpc().devicePolicyManager().setApplicationRestrictions(
+                    sDeviceState.dpc().componentName(),
+                    sTestApp.packageName(), originalApplicationRestrictions);
+        }
+    }
+
+    // Should be consistent with createBundle
+    private void assertEqualToBundle(String id, Bundle bundle) {
         assertWithMessage("bundle0 size")
                 .that(bundle.size()).isEqualTo(8);
         assertBooleanKey(bundle, "boolean_0", false);
         assertBooleanKey(bundle, "boolean_1", true);
-        assertIntKey(bundle, "integer", 0x7fffffff);
+        assertIntKey(bundle, "integer", 0xfffff);
         assertStringKey(bundle, "empty", "");
-        assertStringKey(bundle, "string", "text");
+        assertStringKey(bundle, "string", id);
         assertStringsKey(bundle, "string[]", TEST_STRINGS);
 
         Bundle childBundle = bundle.getBundle("bundle");
@@ -300,9 +456,9 @@
         return value;
     }
 
-    private void assertNotEqualToBundle(Bundle value) {
+    private void assertNotEqualToBundle(String id, Bundle value) {
         // This uses an arbitrary value from the test bundle
         assertWithMessage("Bundle should not be equal to test bundle")
-                .that(value.getInt("integer")).isNotEqualTo(0x7fffffff);
+                .that(value.getString("string")).isNotEqualTo(id);
     }
 }
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CaCertManagementTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CaCertManagementTest.java
index ada4b40..9fe4b36 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CaCertManagementTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CaCertManagementTest.java
@@ -57,7 +57,7 @@
 // the <application> element in the manifest.
 // TODO(b/205261115): Use a testapp and query for it rather than relying on the Manifest content
 @RunWith(BedsteadJUnit4.class)
-public class CaCertManagementTest {
+public final class CaCertManagementTest {
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
index 86d2857..cbeeaad 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
@@ -55,7 +55,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 @RunWith(BedsteadJUnit4.class)
-public class CameraPolicyTest {
+public final class CameraPolicyTest {
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CreateAndManageUserTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CreateAndManageUserTest.java
index b329718..0da2988 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CreateAndManageUserTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CreateAndManageUserTest.java
@@ -43,7 +43,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(BedsteadJUnit4.class)
-public class CreateAndManageUserTest {
+public final class CreateAndManageUserTest {
 
     private static final String TAG = "CreateAndManageUserTest";
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
index baabdc7..ced2600 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
@@ -23,9 +23,7 @@
 import static com.android.bedstead.nene.appops.AppOps.AppOpsMode.DEFAULT;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.testng.Assert.assertThrows;
 
@@ -66,14 +64,16 @@
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(BedsteadJUnit4.class)
-public class CredentialManagementAppTest {
+public final class CredentialManagementAppTest {
 
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
 
+    private static final int KEYCHAIN_CALLBACK_TIMEOUT_SECONDS = 300;
     private static final PrivateKey PRIVATE_KEY =
             getPrivateKey(FakeKeys.FAKE_RSA_1.privateKey, "RSA");
     private static final Certificate CERTIFICATE =
@@ -291,7 +291,8 @@
                             /* keyTypes= */ null, /* issuers= */ null, URI, /* alias = */ null)
             );
 
-            assertThat(callback.await()).isEqualTo(ALIAS);
+            assertThat(callback.await(KEYCHAIN_CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+                    .isEqualTo(ALIAS);
         } finally {
             // Remove keypair as credential management app
             sDevicePolicyManager.removeKeyPair(/* admin = */ null, ALIAS);
@@ -304,14 +305,14 @@
             throws Exception {
         removeCredentialManagementApp();
 
-        assertFalse(KeyChain.isCredentialManagementApp(sContext));
+        assertThat(KeyChain.isCredentialManagementApp(sContext)).isFalse();
     }
 
     @Test
     public void isCredentialManagementApp_isCredentialManagementApp_returnTrue() throws Exception {
         setCredentialManagementApp();
         try {
-            assertTrue(KeyChain.isCredentialManagementApp(sContext));
+            assertThat(KeyChain.isCredentialManagementApp(sContext)).isTrue();
         } finally {
             removeCredentialManagementApp();
         }
@@ -343,9 +344,10 @@
         setCredentialManagementApp();
 
         try {
-            assertTrue(KeyChain.removeCredentialManagementApp(sContext));
+            boolean wasRemoved = KeyChain.removeCredentialManagementApp(sContext);
 
-            assertFalse(KeyChain.isCredentialManagementApp(sContext));
+            assertThat(wasRemoved).isTrue();
+            assertThat(KeyChain.isCredentialManagementApp(sContext)).isFalse();
         } catch (Exception e) {
             removeCredentialManagementApp();
         }
@@ -355,12 +357,13 @@
     private void setCredentialManagementApp() {
         try (PermissionContext p = TestApis.permissions().withPermission(
                 MANAGE_CREDENTIAL_MANAGEMENT_APP)) {
+            boolean wasSet = KeyChain.setCredentialManagementApp(
+                    sContext, sContext.getPackageName(), AUTHENTICATION_POLICY);
 
-            assertTrue("Unable to set credential management app",
-                    KeyChain.setCredentialManagementApp(sContext, sContext.getPackageName(),
-                            AUTHENTICATION_POLICY));
+            assertWithMessage("Unable to set credential management app").that(wasSet).isTrue();
         }
 
+        assertThat(KeyChain.isCredentialManagementApp(sContext)).isTrue();
         assertThat(TestApis.packages().instrumented().appOps().get(MANAGE_CREDENTIALS))
                 .isEqualTo(ALLOWED);
     }
@@ -369,8 +372,9 @@
     private void removeCredentialManagementApp() {
         try (PermissionContext p = TestApis.permissions().withPermission(
                 MANAGE_CREDENTIAL_MANAGEMENT_APP)) {
-            assertTrue("Unable to remove credential management app",
-                    KeyChain.removeCredentialManagementApp(sContext));
+            boolean wasRemoved = KeyChain.removeCredentialManagementApp(sContext);
+            assertWithMessage("Unable to remove credential management app")
+                    .that(wasRemoved).isTrue();
         }
     }
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
index 72e33c0..3aaee38 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
@@ -58,7 +58,7 @@
 import java.util.List;
 
 @RunWith(BedsteadJUnit4.class)
-public class CrossProfileSharingTest {
+public final class CrossProfileSharingTest {
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java
index 473c60e..99c539c 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java
@@ -37,7 +37,7 @@
 import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
 import com.android.bedstead.harrier.policies.DefaultSmsApplication;
 import com.android.bedstead.nene.TestApis;
-import com.android.bedstead.remotedpc.RemoteDpc;
+import com.android.bedstead.remotedpc.RemotePolicyManager;
 import com.android.bedstead.testapp.TestApp;
 import com.android.bedstead.testapp.TestAppInstance;
 import com.android.bedstead.testapp.TestAppProvider;
@@ -50,7 +50,7 @@
 
 // TODO(b/198442101): Add tests for the COPE case when we can sideload system apps
 @RunWith(BedsteadJUnit4.class)
-public class DefaultSmsApplicationTest {
+public final class DefaultSmsApplicationTest {
     @ClassRule
     @Rule
     public static DeviceState sDeviceState = new DeviceState();
@@ -71,8 +71,8 @@
 
     @Before
     public void setUp() {
-        RemoteDpc dpc = sDeviceState.dpc();
-        mAdmin = dpc.devicePolicyController().componentName();
+        RemotePolicyManager dpc = sDeviceState.dpc();
+        mAdmin = dpc.componentName();
         mDpm = dpc.devicePolicyManager();
         mTelephonyManager = sContext.getSystemService(TelephonyManager.class);
     }
@@ -85,9 +85,9 @@
         assumeTrue(mTelephonyManager.isSmsCapable());
         String previousSmsAppName = getDefaultSmsPackage();
         try (TestAppInstance smsApp = sSmsApp.install()) {
-            mDpm.setDefaultSmsApplication(mAdmin, smsApp.testApp().packageName());
+            mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName());
 
-            assertThat(getDefaultSmsPackage()).isEqualTo(smsApp.testApp().packageName());
+            assertThat(getDefaultSmsPackage()).isEqualTo(smsApp.packageName());
         } finally {
             mDpm.setDefaultSmsApplication(mAdmin, previousSmsAppName);
         }
@@ -101,7 +101,7 @@
         assumeTrue(mTelephonyManager.isSmsCapable());
         String previousSmsAppName = getDefaultSmsPackage();
         try (TestAppInstance smsApp = sSmsApp.install()) {
-            mDpm.setDefaultSmsApplication(mAdmin, smsApp.testApp().packageName());
+            mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName());
 
             assertThat(getDefaultSmsPackage()).isEqualTo(previousSmsAppName);
         } finally {
@@ -134,7 +134,7 @@
 
             assertThrows(NullPointerException.class, () ->
                     mDpm.setDefaultSmsApplication(
-                            /* admin= */ null, smsApp.testApp().packageName()));
+                            /* admin= */ null, smsApp.packageName()));
         }
     }
 
@@ -146,7 +146,7 @@
         assumeTrue(!mTelephonyManager.isSmsCapable());
         String previousSmsAppName = getDefaultSmsPackage();
         try (TestAppInstance smsApp = sSmsApp.install()) {
-            mDpm.setDefaultSmsApplication(mAdmin, smsApp.testApp().packageName());
+            mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName());
 
             assertThat(getDefaultSmsPackage()).isEqualTo(previousSmsAppName);
         } finally {
@@ -156,12 +156,13 @@
 
     @Test
     @Postsubmit(reason = "new test")
-    @CannotSetPolicyTest(policy = DefaultSmsApplication.class)
+    // We don't include non device admin states as passing a null admin is a NullPointerException
+    @CannotSetPolicyTest(policy = DefaultSmsApplication.class, includeNonDeviceAdminStates = false)
     public void setDefaultSmsApplication_invalidAdmin_throwsException() {
         try (TestAppInstance smsApp = sSmsApp.install()) {
 
             assertThrows(SecurityException.class, () ->
-                    mDpm.setDefaultSmsApplication(mAdmin, smsApp.testApp().packageName()));
+                    mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName()));
         }
     }
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DelegationScopesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DelegationScopesTest.java
index 68e9558..77530c8 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DelegationScopesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DelegationScopesTest.java
@@ -24,8 +24,6 @@
 import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
 import static android.app.admin.DevicePolicyManager.EXTRA_DELEGATION_SCOPES;
 
-import static com.android.queryable.queries.StringQuery.string;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
@@ -39,6 +37,7 @@
 import com.android.bedstead.harrier.annotations.Postsubmit;
 import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDelegate;
 import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
 import com.android.bedstead.harrier.policies.Delegation;
 import com.android.bedstead.harrier.policies.NetworkLoggingDelegation;
@@ -68,7 +67,8 @@
 @Postsubmit(reason = "new test")
 // TODO(b/198584060): clean up the TestApp install API surface and remove the
 //  requirement to manually uninstall or use the 'try' block.
-public class DelegationScopesTest {
+@EnsureHasNoDelegate
+public final class DelegationScopesTest {
 
     private static final String TEST_SCOPE = DELEGATION_CERT_INSTALL;
     private static final String TEST_SCOPE_2 = DELEGATION_APP_RESTRICTIONS;
@@ -79,14 +79,15 @@
     private static final TestApis sTestApis = new TestApis();
     private static final UserReference sUser = sTestApis.users().instrumented();
 
-    private static final TestApp sTestAppProvider =
-            new TestAppProvider().query().whereActivities().isNotEmpty().get();
-    private static final TestApp sTestAppProvider2 = new TestAppProvider().any();
+    private static final TestAppProvider sTestAppProvider = new TestAppProvider();
+    private static final TestApp sTestApp = sTestAppProvider
+            .query().whereActivities().isNotEmpty().get();
+    private static final TestApp sTestApp2 = sTestAppProvider.any();
 
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void getDelegatedScopes_returnsFromSetDelegatedScopes() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
                         sDeviceState.dpc().componentName(),
@@ -104,9 +105,9 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = Delegation.class)
+    @CannotSetPolicyTest(policy = Delegation.class, includeNonDeviceAdminStates = false)
     public void setDelegatedScopes_invalidAdmin_throwsSecurityException() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             assertThrows(SecurityException.class, () ->
                     sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
                             sDeviceState.dpc().componentName(),
@@ -118,7 +119,7 @@
     @Test
     @CannotSetPolicyTest(policy = Delegation.class)
     public void getDelegatedScopes_invalidAdmin_throwsSecurityException() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             assertThrows(SecurityException.class, () ->
                     sDeviceState.dpc().devicePolicyManager().getDelegatedScopes(
                             sDeviceState.dpc().componentName(), testApp.testApp().packageName()));
@@ -128,7 +129,7 @@
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void getDelegatedScopes_returnsLatestFromSetDelegatedScopes() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -171,7 +172,7 @@
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void getDelegatePackages_oneApp_twoScopes_returnsFromSetDelegatedScopes() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -193,9 +194,9 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = Delegation.class)
+    @CannotSetPolicyTest(policy = Delegation.class, includeNonDeviceAdminStates = false)
     public void getDelegatePackages_invalidAdmin_throwsSecurityException() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             assertThrows(SecurityException.class, () ->
                     sDeviceState.dpc().devicePolicyManager().getDelegatePackages(
                             sDeviceState.dpc().componentName(), testApp.testApp().packageName()));
@@ -205,8 +206,8 @@
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void getDelegatePackages_twoApps_differentScopes_returnsFromSetDelegatedScopes() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser);
-                TestAppInstance testApp2 = sTestAppProvider2.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser);
+             TestAppInstance testApp2 = sTestApp2.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -235,8 +236,8 @@
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void getDelegatePackages_twoApps_sameScope_returnsFromSetDelegatedScopes() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser);
-             TestAppInstance testApp2 = sTestAppProvider2.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser);
+             TestAppInstance testApp2 = sTestApp2.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -264,7 +265,7 @@
     @Test
     @PositivePolicyTest(policy = NetworkLoggingDelegation.class)
     public void setDelegatedScopes_networkLogging_validAdminType_noException() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
                         sDeviceState.dpc().componentName(),
@@ -277,9 +278,10 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = NetworkLoggingDelegation.class)
+    @CannotSetPolicyTest(
+            policy = NetworkLoggingDelegation.class, includeNonDeviceAdminStates = false)
     public void setDelegatedScopes_networkLogging_invalidAdminType_throwsSecurityException() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             try {
                 assertThrows(SecurityException.class, () ->
                         sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -297,7 +299,7 @@
     // TODO(b/198774281): add a negative policy test (in line with all the others here) once we can
     //  correctly mark security logging delegation as possible for COPE profile POs.
     public void setDelegatedScopes_securityLogging_validAdminType_noException() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
                         sDeviceState.dpc().componentName(),
@@ -312,8 +314,8 @@
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void setDelegatedScopes_certSelection_settingSecondApp_revokesFirstApp() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser);
-             TestAppInstance testApp2 = sTestAppProvider2.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser);
+             TestAppInstance testApp2 = sTestApp2.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -346,8 +348,8 @@
     @Test
     @PositivePolicyTest(policy = NetworkLoggingDelegation.class)
     public void setDelegatedScopes_networkLogging_settingSecondApp_revokesFirstApp() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser);
-             TestAppInstance testApp2 = sTestAppProvider2.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser);
+             TestAppInstance testApp2 = sTestApp2.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -380,8 +382,8 @@
     @Test
     @PositivePolicyTest(policy = SecurityLoggingDelegation.class)
     public void setDelegatedScopes_securityLogging_settingSecondApp_revokesFirstApp() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser);
-             TestAppInstance testApp2 = sTestAppProvider2.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser);
+             TestAppInstance testApp2 = sTestApp2.install(sUser)) {
 
             try {
                 sDeviceState.dpc().devicePolicyManager().setDelegatedScopes(
@@ -414,7 +416,7 @@
     @Test
     @PositivePolicyTest(policy = Delegation.class)
     public void setDelegatedScopes_delegatedPackageReceivesScopesFromBroadcast() {
-        try (TestAppInstance testApp = sTestAppProvider.install(sUser)) {
+        try (TestAppInstance testApp = sTestApp.install(sUser)) {
             // TODO(b/198769413): we should not need to start (or query for) an activity, but the
             //  event is not received for some reason without it.
             testApp.activities().any().start();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DeprecatedPasswordAPIsTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DeprecatedPasswordAPIsTest.java
deleted file mode 100644
index 2579f14..0000000
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DeprecatedPasswordAPIsTest.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.devicepolicy.cts;
-
-import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.app.admin.DevicePolicyManager;
-import android.app.admin.RemoteDevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-
-import com.android.bedstead.harrier.BedsteadJUnit4;
-import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.Postsubmit;
-import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature;
-import com.android.bedstead.harrier.annotations.RequireFeature;
-import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
-import com.android.bedstead.harrier.policies.DeprecatedPasswordAPIs;
-import com.android.bedstead.nene.TestApis;
-import com.android.bedstead.remotedpc.RemoteDpc;
-
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test cases for password management APIs that are deprecated and not supported in some platforms.
- */
-@RunWith(BedsteadJUnit4.class)
-public final class DeprecatedPasswordAPIsTest {
-
-    @ClassRule
-    @Rule
-    public static final DeviceState sDeviceState = new DeviceState();
-
-    private static final Context sContext = TestApis.context().instrumentedContext();
-
-    private ComponentName mAdmin;
-    private RemoteDevicePolicyManager mDpm;
-
-    @Before
-    public void setUp() {
-        RemoteDpc dpc = sDeviceState.dpc();
-        mAdmin = dpc.devicePolicyController().componentName();
-        mDpm = dpc.devicePolicyManager();
-    }
-
-    @Test
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    public void testPasswordQuality_featureUnsupported_returnsUnspecified() {
-        mDpm.setPasswordQuality(mAdmin, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
-
-        assertWithMessage("getPasswordQuality()").that(mDpm.getPasswordQuality(mAdmin))
-                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordQuality_featureSupported_returnsValueSet() {
-        mDpm.setPasswordQuality(mAdmin, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
-
-        assertWithMessage("getPasswordQuality()").that(mDpm.getPasswordQuality(mAdmin))
-                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumLength_featureUnsupported_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumLength(mAdmin);
-
-        mDpm.setPasswordMinimumLength(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumLength()")
-                .that(mDpm.getPasswordMinimumLength(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumLength_featureSupported_returnsValueSet() {
-        mDpm.setPasswordMinimumLength(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumLength()")
-                .that(mDpm.getPasswordMinimumLength(mAdmin))
-                .isEqualTo(42);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumNumeric_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumNumeric(mAdmin);
-
-        mDpm.setPasswordMinimumNumeric(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumNumeric()")
-                .that(mDpm.getPasswordMinimumNumeric(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumNumeric_returnsValueSet() {
-        mDpm.setPasswordMinimumNumeric(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumNumeric()")
-                .that(mDpm.getPasswordMinimumNumeric(mAdmin))
-                .isEqualTo(42);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumLowerCase_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumLowerCase(mAdmin);
-
-        mDpm.setPasswordMinimumLowerCase(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumLowerCase()")
-                .that(mDpm.getPasswordMinimumLowerCase(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumLowerCase_returnsValueSet() {
-        mDpm.setPasswordMinimumLowerCase(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumLowerCase()")
-                .that(mDpm.getPasswordMinimumLowerCase(mAdmin))
-                .isEqualTo(42);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumUpperCase_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumUpperCase(mAdmin);
-
-        mDpm.setPasswordMinimumUpperCase(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumUpperCase()")
-                .that(mDpm.getPasswordMinimumUpperCase(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumUpperCase_returnsValueSet() {
-        mDpm.setPasswordMinimumUpperCase(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumUpperCase()")
-                .that(mDpm.getPasswordMinimumUpperCase(mAdmin))
-                .isEqualTo(42);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumLetters_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumLetters(mAdmin);
-
-        mDpm.setPasswordMinimumLetters(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumLetters()")
-                .that(mDpm.getPasswordMinimumLetters(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumLetters_returnsValueSet() {
-        mDpm.setPasswordMinimumLetters(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumLetters()")
-                .that(mDpm.getPasswordMinimumLetters(mAdmin))
-                .isEqualTo(42);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumSymbols_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumSymbols(mAdmin);
-
-        mDpm.setPasswordMinimumSymbols(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumSymbols()")
-                .that(mDpm.getPasswordMinimumSymbols(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumSymbols_returnsValueSet() {
-        mDpm.setPasswordMinimumSymbols(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumSymbols()")
-                .that(mDpm.getPasswordMinimumSymbols(mAdmin))
-                .isEqualTo(42);
-    }
-
-    @Test
-    @RequireFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumNonLetter_ignored() {
-        int valueBefore = mDpm.getPasswordMinimumNonLetter(mAdmin);
-
-        mDpm.setPasswordMinimumNonLetter(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumNonLetter()")
-                .that(mDpm.getPasswordMinimumNonLetter(mAdmin))
-                .isEqualTo(valueBefore);
-    }
-
-    @Test
-    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
-    @PositivePolicyTest(policy = DeprecatedPasswordAPIs.class)
-    @Postsubmit(reason = "new test")
-    public void testPasswordMinimumNonLetter_returnsValueSet() {
-        mDpm.setPasswordMinimumNonLetter(mAdmin, 42);
-
-        assertWithMessage("getPasswordMinimumNonLetter()")
-                .that(mDpm.getPasswordMinimumNonLetter(mAdmin))
-                .isEqualTo(42);
-    }
-}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
index 00aea6f..264dd12 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
@@ -48,7 +48,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(BedsteadJUnit4.class)
-public class DeviceOwnerPrerequisitesTest {
+public final class DeviceOwnerPrerequisitesTest {
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/KeyManagementTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/KeyManagementTest.java
index 0b65080..16a7733 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/KeyManagementTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/KeyManagementTest.java
@@ -62,6 +62,7 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test that a DPC can manage keys and certificate on a device by installing, generating and
@@ -69,11 +70,12 @@
  * keys by requesting access to them and retrieving them via KeyChain APIs.
  */
 @RunWith(BedsteadJUnit4.class)
-public class KeyManagementTest {
+public final class KeyManagementTest {
 
     @ClassRule
     @Rule
     public static final DeviceState sDeviceState = new DeviceState();
+    private static final int KEYCHAIN_CALLBACK_TIMEOUT_SECONDS = 600;
     private static final String RSA = "RSA";
     private static final String RSA_ALIAS = "com.android.test.valid-rsa-key-1";
     private static final PrivateKey PRIVATE_KEY =
@@ -279,7 +281,8 @@
 
             choosePrivateKeyAlias(callback, RSA_ALIAS);
 
-            assertThat(callback.await()).isEqualTo(RSA_ALIAS);
+            assertThat(callback.await(KEYCHAIN_CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+                    .isEqualTo(RSA_ALIAS);
         } finally {
             // Remove keypair
             sDeviceState.dpc().devicePolicyManager().removeKeyPair(DPC_COMPONENT_NAME, RSA_ALIAS);
@@ -299,7 +302,8 @@
 
             choosePrivateKeyAlias(callback, RSA_ALIAS);
 
-            assertThat(callback.await()).isEqualTo(RSA_ALIAS);
+            assertThat(callback.await(KEYCHAIN_CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+                    .isEqualTo(RSA_ALIAS);
         } finally {
             // Remove keypair
             sDeviceState.dpc().devicePolicyManager().removeKeyPair(DPC_COMPONENT_NAME, RSA_ALIAS);
@@ -317,7 +321,7 @@
             // Grant alias via {@code KeyChain.choosePrivateKeyAlias}
             KeyChainAliasCallback callback = new KeyChainAliasCallback();
             choosePrivateKeyAlias(callback, RSA_ALIAS);
-            callback.await();
+            callback.await(KEYCHAIN_CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 
             // Get private key for the granted alias
             final PrivateKey privateKey =
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
index 758d794..6f9128d 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
@@ -79,7 +79,7 @@
 import java.util.Set;
 
 @RunWith(BedsteadJUnit4.class)
-public class LockTaskTest {
+public final class LockTaskTest {
 
     @ClassRule
     @Rule
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java
index 7b3ff7e..d3b7087 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java
@@ -43,7 +43,7 @@
  */
 @SmallTest
 @RunWith(BedsteadJUnit4.class)
-public class NegativeCallAuthorizationTest {
+public final class NegativeCallAuthorizationTest {
     private static final String ALIAS = "some-alias";
     private static final Context sContext = ApplicationProvider.getApplicationContext();
     private static final DevicePolicyManager sDpm =
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NetworkResetTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NetworkResetTest.java
index 1e10db7..5bad1c7 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NetworkResetTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NetworkResetTest.java
@@ -49,7 +49,7 @@
 
 // TODO(b/189280629): Move this test to to net test folder to live with other network reset tests.
 @RunWith(BedsteadJUnit4.class)
-public class NetworkResetTest {
+public final class NetworkResetTest {
     @ClassRule @Rule
     public static final DeviceState sDeviceState = new DeviceState();
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NonExportedActivity.java b/tests/devicepolicy/src/android/devicepolicy/cts/NonExportedActivity.java
index a76f4ee..25ba6f7 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NonExportedActivity.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NonExportedActivity.java
@@ -19,5 +19,5 @@
 import android.app.Activity;
 
 /** Activity used for Cross Profile Apps Tests */
-public class NonExportedActivity extends Activity {
+public final class NonExportedActivity extends Activity {
 }
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NonMainActivity.java b/tests/devicepolicy/src/android/devicepolicy/cts/NonMainActivity.java
index 7ef5f8a..98b8b11 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NonMainActivity.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NonMainActivity.java
@@ -19,5 +19,5 @@
 import android.app.Activity;
 
 /** Activity used for Cross Profile Apps Tests */
-public class NonMainActivity extends Activity {
+public final class NonMainActivity extends Activity {
 }
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/PermissionGrantTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/PermissionGrantTest.java
index a56b9ac..4545f94 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/PermissionGrantTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/PermissionGrantTest.java
@@ -34,6 +34,8 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.testng.Assert.assertThrows;
+
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.AfterClass;
@@ -61,7 +63,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(BedsteadJUnit4.class)
-public class PermissionGrantTest {
+public final class PermissionGrantTest {
 
     @ClassRule
     @Rule
@@ -557,7 +559,7 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = SetSmsPermissionGranted.class)
+    @CannotSetPolicyTest(policy = SetSmsPermissionGranted.class, includeNonDeviceAdminStates = false)
     public void grantSmsPermission_cannotBeApplied_returnsTrueButDoesNotSetGrantState() {
         int existingGrantState = sDeviceState.dpc().devicePolicyManager()
                 .getPermissionGrantState(sDeviceState.dpc().componentName(),
@@ -583,6 +585,15 @@
     }
 
     @Test
+    @CannotSetPolicyTest(policy = SetSmsPermissionGranted.class, includeDeviceAdminStates = false)
+    public void grantSmsPermission_nonDeviceAdmin_throwsException() {
+        assertThrows(SecurityException.class,
+                () -> sDeviceState.dpc().devicePolicyManager().setPermissionGrantState(
+                        sDeviceState.dpc().componentName(), sTestApp.packageName(),
+                        READ_SMS, PERMISSION_GRANT_STATE_GRANTED));
+    }
+
+    @Test
     @CannotSetPolicyTest(policy = SetSensorPermissionGranted.class)
     public void grantSensorPermission_cannotBeApplied_returnsTrueButDoesNotSetGrantState() {
         // TODO(b/188893663): Replace with parameterization
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/PreferentialNetworkServiceTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/PreferentialNetworkServiceTest.java
index 3d5f7ef..51f672c 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/PreferentialNetworkServiceTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/PreferentialNetworkServiceTest.java
@@ -55,7 +55,7 @@
 
 // TODO(b/190797743): Move this test to to net test folder.
 @RunWith(BedsteadJUnit4.class)
-public class PreferentialNetworkServiceTest {
+public final class PreferentialNetworkServiceTest {
     @ClassRule @Rule
     public static final DeviceState sDeviceState = new DeviceState();
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
index 5614255..4a27ae9 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
@@ -26,11 +26,13 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 
 import static com.android.bedstead.metricsrecorder.truth.MetricQueryBuilderSubject.assertThat;
 import static com.android.bedstead.remotedpc.RemoteDpc.DPC_COMPONENT_NAME;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assume.assumeTrue;
 
@@ -42,6 +44,8 @@
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature;
+import com.android.bedstead.harrier.annotations.RequireFeature;
 import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
 import com.android.bedstead.harrier.policies.ResetPasswordWithToken;
@@ -55,7 +59,7 @@
 
 // TODO(b/191640667): Parameterize the length limit tests with multiple limits
 @RunWith(BedsteadJUnit4.class)
-public class ResetPasswordWithTokenTest {
+public final class ResetPasswordWithTokenTest {
 
     private static final String NOT_COMPLEX_PASSWORD = "1234";
     private static final String VALID_PASSWORD = NOT_COMPLEX_PASSWORD;
@@ -154,6 +158,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void resetPasswordWithToken_passwordDoesNotSatisfyRestriction_failure() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -180,6 +186,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void resetPasswordWithToken_passwordSatisfiesRestriction_success() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -225,6 +233,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void isActivePasswordSufficient_passwordDoesNotSatisfyRestriction_false() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -253,6 +263,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void isActivePasswordSufficient_passwordSatisfiesRestriction_true() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -281,6 +293,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void isActivePasswordSufficient_passwordNoLongerSatisfiesRestriction_false() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -311,6 +325,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @CanSetPolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_success() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -327,6 +343,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_something_passwordWithAMinLengthOfFourRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -347,6 +365,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_numeric_passwordWithAtLeastOneNumberOrLetterRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -366,6 +386,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_alphabetic_passwordWithAtLeastOneLetterRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -384,6 +406,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_alphanumeric_passwordWithBothALetterAndANumberRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -402,6 +426,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_complex_passwordWithAMinLengthOfFourRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -427,6 +453,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @CanSetPolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordMinimumLength is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumLength_success() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -444,6 +472,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumLength_six_passwordWithAMinLengthOfSixRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -468,6 +498,8 @@
 
     @Test
     @Postsubmit(reason = "new test")
+    // setPasswordMinimumUpperCase is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     @CanSetPolicyTest(policy = ResetPasswordWithToken.class)
     public void setPasswordMinimumUpperCase_success() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
@@ -486,6 +518,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumUpperCase_one_passwordWithAtLeastOneUpperCaseLetterRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -510,6 +544,8 @@
 
     @Test
     @Postsubmit(reason = "new test")
+    // setPasswordMinimumLowerCase is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     @CanSetPolicyTest(policy = ResetPasswordWithToken.class)
     public void setPasswordMinimumLowerCase_success() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
@@ -528,6 +564,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumLowerCase_one_passwordWithAtLeaseOneLowerCaseLetterRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -570,6 +608,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumLetters_one_passwordWithAtLeastOneLetterRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -612,6 +652,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumNumeric_one_passwordWithAtLeastOneNumberRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -654,6 +696,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumSymbols_one_passwordWithAtLeastOneSymbolRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -679,6 +723,8 @@
 
     @Test
     @Postsubmit(reason = "new test")
+    // setPasswordMinimumNonLetter is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     @CanSetPolicyTest(policy = ResetPasswordWithToken.class)
     public void setPasswordMinimumNonLetter_success() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
@@ -697,6 +743,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordMinimumNonLetter_one_passwordWithAtLeastOneNonLetterRequired() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -723,6 +771,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setRequiredPasswordComplexity_passwordQualityAlreadySet_clearsPasswordQuality() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -743,6 +793,8 @@
     @Test
     @Postsubmit(reason = "new test")
     @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    // setPasswordQuality is unsupported on automotive
+    @RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
     public void setPasswordQuality_passwordComplexityAlreadySet_clearsPasswordComplexity() {
         assumeTrue(RESET_PASSWORD_TOKEN_DISABLED, canSetResetPasswordToken(TOKEN));
         try {
@@ -894,6 +946,121 @@
         }
     }
 
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumLength_featureUnsupported_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumLength(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(DPC_COMPONENT_NAME, 42);
+
+        assertWithMessage("getPasswordMinimumLength()")
+                .that(sDeviceState.dpc().devicePolicyManager()
+                        .getPasswordMinimumLength(DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumNumeric_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumNumeric(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNumeric(DPC_COMPONENT_NAME, 42);
+
+        assertWithMessage("getPasswordMinimumNumeric()")
+                .that(sDeviceState.dpc().devicePolicyManager().getPasswordMinimumNumeric(
+                        DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumLowerCase_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumLowerCase(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLowerCase(DPC_COMPONENT_NAME,
+                42);
+
+        assertWithMessage("getPasswordMinimumLowerCase()")
+                .that(sDeviceState.dpc().devicePolicyManager().getPasswordMinimumLowerCase(
+                        DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumUpperCase_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumUpperCase(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumUpperCase(DPC_COMPONENT_NAME,
+                42);
+
+        assertWithMessage("getPasswordMinimumUpperCase()")
+                .that(sDeviceState.dpc().devicePolicyManager().getPasswordMinimumUpperCase(
+                        DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumLetters_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumLetters(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLetters(DPC_COMPONENT_NAME, 42);
+
+        assertWithMessage("getPasswordMinimumLetters()")
+                .that(sDeviceState.dpc().devicePolicyManager().getPasswordMinimumLetters(
+                        DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumSymbols_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumSymbols(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(DPC_COMPONENT_NAME, 42);
+
+        assertWithMessage("getPasswordMinimumSymbols()")
+                .that(sDeviceState.dpc().devicePolicyManager().getPasswordMinimumSymbols(
+                        DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
+    @Test
+    @RequireFeature(FEATURE_AUTOMOTIVE)
+    @PositivePolicyTest(policy = ResetPasswordWithToken.class)
+    @Postsubmit(reason = "new test")
+    public void testPasswordMinimumNonLetter_ignored() {
+        int valueBefore = sDeviceState.dpc().devicePolicyManager().getPasswordMinimumNonLetter(
+                DPC_COMPONENT_NAME);
+
+        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNonLetter(DPC_COMPONENT_NAME,
+                42);
+
+        assertWithMessage("getPasswordMinimumNonLetter()")
+                .that(sDeviceState.dpc().devicePolicyManager().getPasswordMinimumNonLetter(
+                        DPC_COMPONENT_NAME))
+                .isEqualTo(valueBefore);
+    }
+
     private void assertPasswordSucceeds(String password) {
         assertThat(sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(
                 DPC_COMPONENT_NAME, password, TOKEN, /* flags = */ 0)).isTrue();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/RingtoneTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/RingtoneTest.java
index f7ef7b2..f49fd9a 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/RingtoneTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/RingtoneTest.java
@@ -41,7 +41,7 @@
 
 @RunWith(BedsteadJUnit4.class)
 @Postsubmit(reason = "New tests")
-public class RingtoneTest {
+public final class RingtoneTest {
 
     @ClassRule @Rule
     public static final DeviceState sDeviceState = new DeviceState();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
index 37c9bdb..c6558e8 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
@@ -34,6 +34,7 @@
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.annotations.SlowApiTest;
 import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.NegativePolicyTest;
 import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
@@ -55,7 +56,7 @@
 
 
 @RunWith(BedsteadJUnit4.class)
-public class ScreenCaptureDisabledTest {
+public final class ScreenCaptureDisabledTest {
 
     @ClassRule
     @Rule
@@ -141,6 +142,7 @@
     @Test
     @PositivePolicyTest(policy = ScreenCaptureDisabled.class)
     @Postsubmit(reason = "new test")
+    @SlowApiTest("Screenshot policy can take minutes to propagate")
     public void setScreenCaptureDisabled_true_screenCaptureFails() {
         mDevicePolicyManager.setScreenCaptureDisabled(mAdmin, true);
 
@@ -188,7 +190,7 @@
         try (TestAppInstance testApp = sTestApp.install()) {
             testApp.activities().any().start();
             return Poll.forValue(mUiAutomation::takeScreenshot)
-                    .timeout(Duration.ofSeconds(60))
+                    .timeout(Duration.ofMinutes(5))
                     .toBeNull()
                     .await();
         }
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java
index c6fcd6b..5d8c421 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java
@@ -43,6 +43,7 @@
 import com.android.bedstead.harrier.annotations.Postsubmit;
 import com.android.bedstead.harrier.annotations.RequireFeature;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
+import com.android.bedstead.harrier.annotations.SlowApiTest;
 import com.android.bedstead.nene.users.UserReference;
 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 
@@ -60,6 +61,8 @@
     private static final ActivityManager sActivityManager =
             sContext.getSystemService(ActivityManager.class);
 
+    private static final int START_PROFILE_BROADCAST_TIMEOUT = 480_000; // 8 minutes
+
     @ClassRule @Rule
     public static final DeviceState sDeviceState = new DeviceState();
 
@@ -83,6 +86,7 @@
     @RequireRunOnPrimaryUser
     @EnsureHasWorkProfile
     @EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
+    @SlowApiTest("Start profile broadcasts can take a long time")
     public void startProfile_broadcastIsReceived_profileIsStarted() {
         sDeviceState.workProfile().stop();
         BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
@@ -90,7 +94,7 @@
                 userIsEqual(sDeviceState.workProfile()));
         sActivityManager.startProfile(sDeviceState.workProfile().userHandle());
 
-        broadcastReceiver.awaitForBroadcastOrFail();
+        broadcastReceiver.awaitForBroadcastOrFail(START_PROFILE_BROADCAST_TIMEOUT);
 
         assertThat(sUserManager.isUserRunning(sDeviceState.workProfile().userHandle())).isTrue();
     }
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/SupportMessageTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/SupportMessageTest.java
index f3873bb..3610ac4 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/SupportMessageTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/SupportMessageTest.java
@@ -31,7 +31,7 @@
 import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
 import com.android.bedstead.harrier.policies.SupportMessage;
 import com.android.bedstead.metricsrecorder.EnterpriseMetricsRecorder;
-import com.android.bedstead.remotedpc.RemoteDpc;
+import com.android.bedstead.remotedpc.RemotePolicyManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -67,8 +67,8 @@
 
     @Before
     public void setUp() {
-        RemoteDpc dpc = sDeviceState.dpc();
-        mAdmin = dpc.devicePolicyController().componentName();
+        RemotePolicyManager dpc = sDeviceState.dpc();
+        mAdmin = dpc.componentName();
         mDevicePolicyManager = dpc.devicePolicyManager();
     }
 
@@ -195,7 +195,8 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = SupportMessage.class)
+    // We don't include non device admin states as passing a null admin is a NullPointerException
+    @CannotSetPolicyTest(policy = SupportMessage.class, includeNonDeviceAdminStates = false)
     @Postsubmit(reason = "new test")
     public void getLongSupportMessage_invalidAdmin_fails() {
         assertThrows(SecurityException.class, () ->
@@ -203,7 +204,8 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = SupportMessage.class)
+    // We don't include non device admin states as passing a null admin is a NullPointerException
+    @CannotSetPolicyTest(policy = SupportMessage.class, includeNonDeviceAdminStates = false)
     @Postsubmit(reason = "new test")
     public void setLongSupportMessage_invalidAdmin_fails() {
         assertThrows(SecurityException.class, () ->
@@ -211,7 +213,8 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = SupportMessage.class)
+    // We don't include non device admin states as passing a null admin is a NullPointerException
+    @CannotSetPolicyTest(policy = SupportMessage.class, includeNonDeviceAdminStates = false)
     @Postsubmit(reason = "new test")
     public void getShortSupportMessage_invalidAdmin_fails() {
         assertThrows(SecurityException.class, () ->
@@ -219,7 +222,8 @@
     }
 
     @Test
-    @CannotSetPolicyTest(policy = SupportMessage.class)
+    // We don't include non device admin states as passing a null admin is a NullPointerException
+    @CannotSetPolicyTest(policy = SupportMessage.class, includeNonDeviceAdminStates = false)
     @Postsubmit(reason = "new test")
     public void setShortSupportMessage_invalidAdmin_fails() {
         assertThrows(SecurityException.class, () ->
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java
index c636a4f..131f7d0 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java
@@ -59,7 +59,7 @@
 import java.util.List;
 
 @RunWith(BedsteadJUnit4.class)
-public class UserControlDisabledPackagesTest {
+public final class UserControlDisabledPackagesTest {
     private static final String TAG = "UserControlDisabledPackagesTest";
 
     private static final TestAppProvider sTestAppProvider = new TestAppProvider();
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 35f50d9..14856b3 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -518,6 +518,18 @@
                   android:exported="true"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
                   android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+
+        <activity android:name="android.server.wm.HostActivity"
+                  android:exported="true">
+               <intent-filter>
+                 <action android:name="android.server.wm.app.HostActivity"/>
+               </intent-filter>
+               <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"
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 36909cc..fbbbbca 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -573,14 +573,9 @@
            </intent-filter>
         </service>
 
-        <activity android:name=".HostActivity"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.server.wm.app.HostActivity"/>
-            </intent-filter>
-        </activity>
         <service android:name=".RenderService"
-             android:process=".render_process"/>
+             android:process=".render_process"
+             android:exported="true"/>
         <activity android:name=".ClickableToastActivity"
              android:exported="true"/>
         <activity android:name=".MinimalPostProcessingActivity"
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 1dfb795..79499f7 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -579,6 +579,12 @@
 
     public static class RenderService {
         public static final String PROCESS_NAME = ".render_process";
+        public static final String EXTRAS_BUNDLE = "EXTRAS_BUNDLE";
+        public static final String EXTRAS_DISPLAY_ID = "EXTRAS_DISPLAY_ID";
+        public static final String EXTRAS_HOST_TOKEN = "EXTRAS_HOST_TOKEN";
+        public static final String BROADCAST_EMBED_CONTENT =
+                "android.server.wm.app.RenderService.EMBED_CONTENT";
+        public static final String EXTRAS_SURFACE_PACKAGE = "surfacePackage";
     }
 
     /**
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/RenderService.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/RenderService.java
index f781d2e..9c611ea 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/RenderService.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/RenderService.java
@@ -16,13 +16,16 @@
 
 package android.server.wm.app;
 
+import static android.server.wm.app.Components.RenderService.BROADCAST_EMBED_CONTENT;
+import static android.server.wm.app.Components.RenderService.EXTRAS_BUNDLE;
+import static android.server.wm.app.Components.RenderService.EXTRAS_DISPLAY_ID;
+import static android.server.wm.app.Components.RenderService.EXTRAS_HOST_TOKEN;
+import static android.server.wm.app.Components.RenderService.EXTRAS_SURFACE_PACKAGE;
 import static android.server.wm.app.Components.UnresponsiveActivity.EXTRA_ON_MOTIONEVENT_DELAY_MS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.PixelFormat;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -32,18 +35,13 @@
 import android.view.MotionEvent;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
-import android.view.WindowManager;
 import android.widget.Button;
 
-public class RenderService extends Service {
-    static final String EXTRAS_BUNDLE = "INTENT_BUNDLE";
-    static final String EXTRAS_DISPLAY_ID = "hostDisplayId";
-    static final String EXTRAS_HOST_TOKEN = "hostInputToken";
-    static final String BROADCAST_EMBED_CONTENT
-            = "android.server.wm.app.RenderService.EMBED_CONTENT";
-    static final String EXTRAS_SURFACE_PACKAGE = "surfacePackage";
+import java.util.concurrent.CountDownLatch;
 
+public class RenderService extends Service {
     private int mOnMotionEventDelayMs;
+    CountDownLatch mViewDrawnCallbackLatch = new CountDownLatch(1);
 
     private boolean onTouch(View v, MotionEvent event) {
         SystemClock.sleep(mOnMotionEventDelayMs);
@@ -60,7 +58,7 @@
 
         SurfaceControlViewHost surfaceControlViewHost = getSurfaceControlViewHost(hostToken,
                 hostDisplayId);
-        sendSurfacePackage(surfaceControlViewHost.getSurfacePackage());
+        new Thread(()-> sendSurfacePackage(surfaceControlViewHost.getSurfacePackage())).start();
         return null;
     }
 
@@ -71,6 +69,8 @@
 
         View embeddedView = new Button(this);
         embeddedView.setOnTouchListener(this::onTouch);
+        embeddedView.getViewTreeObserver().registerFrameCommitCallback(
+                mViewDrawnCallbackLatch::countDown);
         DisplayMetrics metrics = new DisplayMetrics();
         displayContext.getDisplay().getMetrics(metrics);
         surfaceControlViewHost.setView(embeddedView, metrics.widthPixels, metrics.heightPixels);
@@ -84,8 +84,12 @@
     }
 
     private void sendSurfacePackage(SurfaceControlViewHost.SurfacePackage surfacePackage) {
-        Intent broadcast = new Intent();
-        broadcast.setAction(BROADCAST_EMBED_CONTENT);
+        try {
+            // wait until we commit a frame from the embedded viewrootimpl
+            mViewDrawnCallbackLatch.await();
+        } catch (InterruptedException ignored) {
+        }
+        Intent broadcast = new Intent(BROADCAST_EMBED_CONTENT);
         broadcast.putExtra(EXTRAS_SURFACE_PACKAGE, surfacePackage);
         sendBroadcast(broadcast);
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
index 2b425c9..401ebaf 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
@@ -337,6 +337,44 @@
         mWmState.assertHomeActivityVisible(true);
     }
 
+    /**
+     * This test case tests behavior of activity launched with FLAG_ACTIVITY_TASK_ON_HOME in lock
+     * task mode. The home task do not move to the front of the launched task if the home task
+     * is violated with the lock-task mode.
+     */
+    @Test
+    public void testLaunchTaskOnHomeInLockTaskMode() {
+        if (!hasHomeScreen()) {
+            return;
+        }
+        // Start LaunchingActivity and BroadcastReceiverActivity in two separate tasks.
+        getLaunchActivityBuilder().setTargetActivity(BROADCAST_RECEIVER_ACTIVITY)
+                .setIntentFlags(FLAG_ACTIVITY_NEW_TASK).execute();
+        waitAndAssertResumedActivity(BROADCAST_RECEIVER_ACTIVITY,"Activity must be resumed");
+        final int taskId1 = mWmState.getTaskByActivity(LAUNCHING_ACTIVITY).mTaskId;
+        final int taskId2 = mWmState.getTaskByActivity(BROADCAST_RECEIVER_ACTIVITY).mTaskId;
+
+        try {
+            runWithShellPermission(() -> {
+                mAtm.startSystemLockTaskMode(taskId1);
+                mAtm.startSystemLockTaskMode(taskId2);
+            });
+            getLaunchActivityBuilder()
+                    .setUseInstrumentation()
+                    .setTargetActivity(BROADCAST_RECEIVER_ACTIVITY)
+                    .setIntentFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME).execute();
+            waitAndAssertResumedActivity(BROADCAST_RECEIVER_ACTIVITY,"Activity must be resumed");
+            mBroadcastActionTrigger.finishBroadcastReceiverActivity();
+            mWmState.waitAndAssertActivityRemoved(BROADCAST_RECEIVER_ACTIVITY);
+
+            mWmState.assertHomeActivityVisible(false);
+        } finally {
+            runWithShellPermission(() -> {
+                mAtm.stopSystemLockTaskMode();
+            });
+        }
+    }
+
     @Test
     public void testFinishActivityWithMoveTaskToBackAfterPause() {
         performFinishActivityWithMoveTaskToBack(FINISH_POINT_ON_PAUSE);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AnrTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AnrTests.java
index 312fb4c..91b42dc 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AnrTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AnrTests.java
@@ -23,17 +23,15 @@
 import static android.server.wm.app.Components.UnresponsiveActivity.EXTRA_ON_CREATE_DELAY_MS;
 import static android.server.wm.app.Components.UnresponsiveActivity.EXTRA_ON_KEYDOWN_DELAY_MS;
 import static android.server.wm.app.Components.UnresponsiveActivity.EXTRA_ON_MOTIONEVENT_DELAY_MS;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
-import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-import android.server.wm.app.Components.RenderService;
 import android.provider.Settings;
+import android.server.wm.app.Components.RenderService;
 import android.server.wm.settings.SettingsSession;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
@@ -43,14 +41,16 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.List;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.platform.app.InstrumentationRegistry;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * Test scenarios that lead to ANR dialog being shown.
@@ -123,15 +123,11 @@
         startUnresponsiveActivity(EXTRA_ON_MOTIONEVENT_DELAY_MS, true /* waitForCompletion */,
                 UNRESPONSIVE_ACTIVITY);
 
-        // TODO(b/143566069) investigate why we need multiple taps on display to trigger anr.
         mWmState.computeState();
         // Tap on the UnresponsiveActivity
         final WindowManagerState.Task unresponsiveActivityTask =
                 mWmState.getTaskByActivity(UNRESPONSIVE_ACTIVITY);
-        tapOnTaskCenter(unresponsiveActivityTask);
-        SystemClock.sleep(1000);
-        tapOnTaskCenter(unresponsiveActivityTask);
-
+        mTouchHelper.tapOnTaskCenterAsync(unresponsiveActivityTask);
         clickCloseAppOnAnrDialog();
         assertEventLogsContainsAnr(UnresponsiveActivity.PROCESS_NAME);
     }
@@ -141,19 +137,19 @@
      */
     @Test
     public void embeddedWindowTriggersAnr() {
-        startUnresponsiveActivity(EXTRA_ON_MOTIONEVENT_DELAY_MS, true  /* waitForCompletion */,
-                HOST_ACTIVITY);
-
-        // TODO(b/143566069) investigate why we need multiple taps on display to trigger anr.
-        mWmState.computeState();
-        // Tap on the HostActivity
-        final WindowManagerState.Task hostActivityTask =
-                mWmState.getTaskByActivity(HOST_ACTIVITY);
-        tapOnTaskCenter(hostActivityTask);
-        SystemClock.sleep(1000);
-        tapOnTaskCenter(hostActivityTask);
-
-        clickCloseAppOnAnrDialog();
+        try (ActivityScenario<HostActivity> scenario =
+                     ActivityScenario.launch(HostActivity.class)) {
+            CountDownLatch[] latch = new CountDownLatch[1];
+            scenario.onActivity(activity -> latch[0] = activity.mEmbeddedViewAttachedLatch);
+            latch[0].await();
+            mWmState.computeState();
+            final WindowManagerState.Task hostActivityTask =
+                    mWmState.getTaskByActivity(new ComponentName("android.server.wm.cts",
+                            "android.server.wm.HostActivity"));
+            mTouchHelper.tapOnTaskCenterAsync(hostActivityTask);
+            clickCloseAppOnAnrDialog();
+        } catch (InterruptedException ignored) {
+        }
         assertEventLogsContainsAnr(RenderService.PROCESS_NAME);
     }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
index e948001..ad1d0e3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
@@ -45,7 +45,6 @@
 import android.util.Size;
 
 import androidx.annotation.Nullable;
-import androidx.test.filters.FlakyTest;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.java
index 4f9b023..7dd3d2b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.java
@@ -24,6 +24,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.WindowManagerState.DisplayArea;
 import android.server.wm.WindowManagerState.DisplayContent;
+import android.view.Display;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,6 +53,10 @@
         mDisplays = mWmState.getDisplays();
     }
 
+    private boolean isTrustedDisplay(DisplayContent displayContent) {
+        return (displayContent.getFlags() & Display.FLAG_TRUSTED) != 0;
+    }
+
     /**
      * DisplayContent should have feature id of FEATURE_ROOT. It should be organized.
      */
@@ -59,7 +64,10 @@
     public void testDisplayContent() {
         for (DisplayContent displayContent : mDisplays) {
             assertThat(displayContent.getFeatureId()).isEqualTo(FEATURE_ROOT);
-            assertThat(displayContent.isOrganized()).isTrue();
+            // DisplayAreaOrganizerController registers the organizer for the trusted displays only.
+            if (isTrustedDisplay(displayContent)) {
+                assertThat(displayContent.isOrganized()).isTrue();
+            }
         }
     }
 
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/HostActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/HostActivity.java
similarity index 65%
rename from tests/framework/base/windowmanager/app/src/android/server/wm/app/HostActivity.java
rename to tests/framework/base/windowmanager/src/android/server/wm/HostActivity.java
index 4a365a3..318e1e6 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/HostActivity.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/HostActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.server.wm.app;
+package android.server.wm;
 
+import static android.server.wm.app.Components.RenderService.BROADCAST_EMBED_CONTENT;
+import static android.server.wm.app.Components.RenderService.EXTRAS_BUNDLE;
+import static android.server.wm.app.Components.RenderService.EXTRAS_DISPLAY_ID;
+import static android.server.wm.app.Components.RenderService.EXTRAS_HOST_TOKEN;
+import static android.server.wm.app.Components.RenderService.EXTRAS_SURFACE_PACKAGE;
 import static android.server.wm.app.Components.UnresponsiveActivity.EXTRA_ON_MOTIONEVENT_DELAY_MS;
-import static android.server.wm.app.RenderService.BROADCAST_EMBED_CONTENT;
-import static android.server.wm.app.RenderService.EXTRAS_BUNDLE;
-import static android.server.wm.app.RenderService.EXTRAS_DISPLAY_ID;
-import static android.server.wm.app.RenderService.EXTRAS_HOST_TOKEN;
-import static android.server.wm.app.RenderService.EXTRAS_SURFACE_PACKAGE;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -32,15 +32,18 @@
 import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.view.SurfaceControlViewHost.SurfacePackage;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
 import android.widget.RelativeLayout;
 
+import java.util.concurrent.CountDownLatch;
+
+
 public class HostActivity extends Activity implements SurfaceHolder.Callback{
     private SurfaceView mSurfaceView;
-
+    public CountDownLatch mEmbeddedViewAttachedLatch =  new CountDownLatch(1);
     private ServiceConnection mConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {}
@@ -49,13 +52,14 @@
         public void onServiceDisconnected(ComponentName className) {}
     };
 
-    private BroadcastReceiver receiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            SurfacePackage surfacePackage =
+            SurfaceControlViewHost.SurfacePackage surfacePackage =
                     intent.getParcelableExtra(EXTRAS_SURFACE_PACKAGE);
             if (surfacePackage != null) {
                 mSurfaceView.setChildSurfacePackage(surfacePackage);
+                mEmbeddedViewAttachedLatch.countDown();
             }
         }
     };
@@ -64,9 +68,8 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BROADCAST_EMBED_CONTENT);
-        registerReceiver(receiver, filter);
+        IntentFilter filter = new IntentFilter(BROADCAST_EMBED_CONTENT);
+        registerReceiver(mReceiver, filter);
 
         final RelativeLayout content = new RelativeLayout(this);
         mSurfaceView = new SurfaceView(this);
@@ -81,20 +84,21 @@
 
     @Override
     protected void onPause() {
-        unregisterReceiver(receiver);
+        unregisterReceiver(mReceiver);
         super.onPause();
     }
 
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
-        Intent mIntent =  new Intent(this, RenderService.class);
-        Bundle b = new Bundle();
-        b.putBinder(EXTRAS_HOST_TOKEN, mSurfaceView.getHostToken());
-        b.putInt(EXTRAS_DISPLAY_ID, getDisplay().getDisplayId());
-        b.putInt(EXTRA_ON_MOTIONEVENT_DELAY_MS,
-                getIntent().getIntExtra(EXTRA_ON_MOTIONEVENT_DELAY_MS, 2000));
-        mIntent.putExtra(EXTRAS_BUNDLE, b);
-        bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE|Context.BIND_IMPORTANT);
+        Intent mIntent = new Intent();
+        mIntent.setComponent(new ComponentName(
+                "android.server.wm.app", "android.server.wm.app.RenderService"));
+        Bundle bundle = new Bundle();
+        bundle.putBinder(EXTRAS_HOST_TOKEN, mSurfaceView.getHostToken());
+        bundle.putInt(EXTRAS_DISPLAY_ID, getDisplay().getDisplayId());
+        bundle.putInt(EXTRA_ON_MOTIONEVENT_DELAY_MS, 10000);
+        mIntent.putExtra(EXTRAS_BUNDLE, bundle);
+        bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
     }
 
     @Override
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index aa3689f..4a4f6b4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -1300,6 +1300,19 @@
     }
 
     @Test
+    public void testAutoPipOnLaunchingAnotherActivity() {
+        // Launch the PIP activity and set its pip params to allow auto-pip.
+        launchActivity(PIP_ACTIVITY, extraString(EXTRA_ALLOW_AUTO_PIP, "true"));
+        assertPinnedStackDoesNotExist();
+
+        // Launch another and ensure that there is a pinned stack.
+        launchActivity(TEST_ACTIVITY);
+        waitForEnterPip(PIP_ACTIVITY);
+        assertPinnedStackExists();
+        waitAndAssertActivityState(PIP_ACTIVITY, STATE_PAUSED, "activity must be paused");
+    }
+
+    @Test
     public void testMaxNumberOfActions() {
         final int maxNumberActions = ActivityTaskManager.getMaxNumPictureInPictureActions(mContext);
         assertThat(maxNumberActions, greaterThanOrEqualTo(3));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java
index 3d6d97c..49d2dba 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java
@@ -21,6 +21,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 import static android.server.wm.RoundedCornerTests.TestActivity.EXTRA_ORIENTATION;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -31,9 +32,11 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 
 import android.app.Activity;
 import android.app.AppOpsManager;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -56,7 +59,7 @@
 
 @Presubmit
 @RunWith(Parameterized.class)
-public class PrivacyIndicatorBoundsTests {
+public class PrivacyIndicatorBoundsTests extends ActivityManagerTestBase {
 
     private static final String TAG = PrivacyIndicatorBoundsTests.class.getSimpleName();
     private static final long TIMEOUT_MS = 1000;
@@ -85,6 +88,10 @@
 
     @Test
     public void testStaticBoundsAreNotNull() {
+        // TODO(b/187757919): Allow Automotive to skip this test until privacy chip is implemented
+        // in immersive mode
+        assumeFalse(isCar());
+
         final PrivacyIndicatorBoundsTests.TestActivity activity = mTestActivity.launchActivity(
                 new Intent().putExtra(EXTRA_ORIENTATION, orientation));
         getInstrumentation().runOnMainSync(() -> {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
index 8a68de1..34a16a5 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
@@ -30,7 +30,6 @@
 import android.os.IBinder;
 import android.server.wm.WindowManagerState.WindowContainer;
 import android.util.ArrayMap;
-import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentCreationParams;
 import android.window.TaskFragmentInfo;
 import android.window.TaskFragmentOrganizer;
@@ -259,11 +258,9 @@
         }
 
         @Override
-        public void onTaskFragmentAppeared(
-                @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
-            super.onTaskFragmentAppeared(taskFragmentAppearedInfo);
-            final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
-            mInfos.put(info.getFragmentToken(), info);
+        public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+            super.onTaskFragmentAppeared(taskFragmentInfo);
+            mInfos.put(taskFragmentInfo.getFragmentToken(), taskFragmentInfo);
             mAppearedLatch.countDown();
         }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java b/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java
index de323ac..6b8be2d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java
@@ -165,9 +165,13 @@
         mWmState.waitAndAssertAppFocus(Components.UNRESPONSIVE_ACTIVITY.getPackageName(),
                 2_000 /* waitTime */);
         // queue up enough key events to trigger an ANR
-        for (int i = 0; i < 14; i++) {
+        for (int i = 0; i < 20; i++) {
             injectKey(KeyEvent.KEYCODE_TAB, false /* longPress */, false /* sync */);
             SystemClock.sleep(500);
+            mWmState.computeState();
+            if (!mWmState.isActivityVisible(Components.UNRESPONSIVE_ACTIVITY)) {
+                break;
+            }
         }
         ensureNoCrashDialog(Components.UNRESPONSIVE_ACTIVITY);
         ensureActivityNotFocused(Components.UNRESPONSIVE_ACTIVITY);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index 2b133f4..054da4c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -607,8 +607,10 @@
 
     @Test
     public void testInsetsDispatch() throws Exception {
-        // Start an activity which hides system bars.
-        final TestHideOnCreateActivity activity = startActivity(TestHideOnCreateActivity.class);
+        // Start an activity which hides system bars in fullscreen mode,
+        // otherwise, it might not be able to hide system bars in other windowing modes.
+        final TestHideOnCreateActivity activity = startActivityInWindowingModeFullScreen(
+                TestHideOnCreateActivity.class);
         final View rootView = activity.getWindow().getDecorView();
         ANIMATION_CALLBACK.waitForFinishing();
         PollingCheck.waitFor(TIMEOUT,
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index e37f284..1c2fdf9 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -1308,7 +1308,7 @@
      */
     protected static class DisableScreenDozeRule implements TestRule {
 
-        /** Copied from android.provider.Settings.Secure since these keys are hiden. */
+        /** Copied from android.provider.Settings.Secure since these keys are hidden. */
         private static final String[] DOZE_SETTINGS = {
                 "doze_enabled",
                 "doze_always_on",
@@ -1317,7 +1317,8 @@
                 "doze_pulse_on_double_tap",
                 "doze_wake_screen_gesture",
                 "doze_wake_display_gesture",
-                "doze_tap_gesture"
+                "doze_tap_gesture",
+                "doze_quick_pickup_gesture"
         };
 
         private String get(String key) {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/TouchHelper.java b/tests/framework/base/windowmanager/util/src/android/server/wm/TouchHelper.java
index 39afb54..54c9b3e 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/TouchHelper.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/TouchHelper.java
@@ -163,4 +163,11 @@
                 KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
         getInstrumentation().getUiAutomation().injectInputEvent(upEvent, sync);
     }
+
+    public void tapOnTaskCenterAsync(WindowManagerState.Task task) {
+        final Rect bounds = task.getBounds();
+        final int x = bounds.left + bounds.width() / 2;
+        final int y = bounds.top + bounds.height() / 2;
+        tapOnDisplay(x, y, task.mDisplayId, false /* sync*/);
+    }
 }
diff --git a/tests/inputmethod/AndroidManifest.xml b/tests/inputmethod/AndroidManifest.xml
index 8ce099b..18c6ebf 100644
--- a/tests/inputmethod/AndroidManifest.xml
+++ b/tests/inputmethod/AndroidManifest.xml
@@ -29,7 +29,7 @@
         <activity android:name="android.view.inputmethod.cts.util.TestActivity"
              android:theme="@style/no_starting_window"
              android:label="TestActivity"
-             android:configChanges="fontScale"
+             android:configChanges="fontScale|smallestScreenSize|screenSize|screenLayout"
              android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/tests/location/common/src/android/location/cts/common/OpActiveChangedCapture.java b/tests/location/common/src/android/location/cts/common/OpActiveChangedCapture.java
new file mode 100644
index 0000000..bc69bcb
--- /dev/null
+++ b/tests/location/common/src/android/location/cts/common/OpActiveChangedCapture.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts.common;
+
+import android.app.AppOpsManager;
+import android.os.Looper;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class OpActiveChangedCapture implements AppOpsManager.OnOpActiveChangedListener,
+        AutoCloseable {
+
+    private final AppOpsManager mAppOps;
+    private final String mOp;
+    private final LinkedBlockingQueue<Boolean> mActives;
+
+    public OpActiveChangedCapture(AppOpsManager appOps, String op) {
+        mAppOps = appOps;
+        mOp = op;
+        mActives = new LinkedBlockingQueue<>();
+    }
+
+    public Boolean getNextActive(long timeoutMs) throws InterruptedException {
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            throw new AssertionError("getNextActive() called from main thread");
+        }
+
+        return mActives.poll(timeoutMs, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public void close() {
+        mAppOps.stopWatchingActive(this);
+    }
+
+    @Override
+    public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
+        if (op.equals(mOp)) {
+            mActives.add(active);
+        }
+    }
+}
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 1be37c3..fffe877 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -17,6 +17,8 @@
 package android.location.cts.fine;
 
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.app.AppOpsManager.OPSTR_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OPSTR_MONITOR_LOCATION;
 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED;
@@ -27,6 +29,7 @@
 import static android.location.LocationManager.PASSIVE_PROVIDER;
 import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
 import static android.location.LocationRequest.PASSIVE_INTERVAL;
+import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY;
 import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
 import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
 import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
@@ -52,7 +55,6 @@
 import android.app.UiAutomation;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.location.Criteria;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
@@ -67,6 +69,7 @@
 import android.location.cts.common.GetCurrentLocationCapture;
 import android.location.cts.common.LocationListenerCapture;
 import android.location.cts.common.LocationPendingIntentCapture;
+import android.location.cts.common.OpActiveChangedCapture;
 import android.location.cts.common.ProviderRequestChangedListenerCapture;
 import android.location.cts.common.gnss.GnssAntennaInfoCapture;
 import android.location.cts.common.gnss.GnssMeasurementsCapture;
@@ -144,15 +147,11 @@
         }
 
         mManager.addTestProvider(TEST_PROVIDER,
-                true,
-                false,
-                true,
-                false,
-                false,
-                false,
-                false,
-                Criteria.POWER_MEDIUM,
-                Criteria.ACCURACY_FINE);
+                new ProviderProperties.Builder()
+                        .setHasNetworkRequirement(true)
+                        .setHasCellRequirement(true)
+                        .setPowerUsage(ProviderProperties.POWER_USAGE_HIGH)
+                        .setAccuracy(ProviderProperties.ACCURACY_FINE).build());
         mManager.setTestProviderEnabled(TEST_PROVIDER, true);
     }
 
@@ -844,6 +843,57 @@
     }
 
     @Test
+    public void testMonitoring() throws Exception {
+        AppOpsManager appOps = Objects.requireNonNull(
+                mContext.getSystemService(AppOpsManager.class));
+
+        try (OpActiveChangedCapture opCapture = new OpActiveChangedCapture(appOps,
+                OPSTR_MONITOR_LOCATION);
+             OpActiveChangedCapture opHighPowerCapture = new OpActiveChangedCapture(appOps,
+                     OPSTR_MONITOR_HIGH_POWER_LOCATION);
+             LocationListenerCapture capture1 = new LocationListenerCapture(mContext);
+             LocationListenerCapture capture2 = new LocationListenerCapture(mContext);
+             LocationListenerCapture capture3 = new LocationListenerCapture(mContext)) {
+            appOps.startWatchingActive(new String[]{OPSTR_MONITOR_LOCATION}, Runnable::run,
+                    opCapture);
+            appOps.startWatchingActive(new String[]{OPSTR_MONITOR_HIGH_POWER_LOCATION},
+                    Runnable::run, opHighPowerCapture);
+
+            mManager.requestLocationUpdates(TEST_PROVIDER,
+                    new LocationRequest.Builder(Long.MAX_VALUE - 1).build(),
+                    Executors.newSingleThreadExecutor(), capture1);
+            assertThat(opCapture.getNextActive(TIMEOUT_MS)).isTrue();
+            assertThat(opHighPowerCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+
+            mManager.requestLocationUpdates(TEST_PROVIDER, new LocationRequest.Builder(
+                            0).setQuality(
+                            QUALITY_HIGH_ACCURACY).build(),
+                    Executors.newSingleThreadExecutor(), capture2);
+            assertThat(opCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+            assertThat(opHighPowerCapture.getNextActive(TIMEOUT_MS)).isTrue();
+
+            mManager.requestLocationUpdates(TEST_PROVIDER, new LocationRequest.Builder(
+                            0).setQuality(
+                            QUALITY_HIGH_ACCURACY).build(),
+                    Executors.newSingleThreadExecutor(), capture3);
+            assertThat(opCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+            assertThat(opHighPowerCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+
+            mManager.removeUpdates(capture2);
+            assertThat(opCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+            assertThat(opHighPowerCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+
+            mManager.removeUpdates(capture3);
+            assertThat(opCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+            assertThat(opHighPowerCapture.getNextActive(TIMEOUT_MS)).isFalse();
+
+            mManager.removeUpdates(capture1);
+            assertThat(opCapture.getNextActive(TIMEOUT_MS)).isFalse();
+            assertThat(opHighPowerCapture.getNextActive(FAILURE_TIMEOUT_MS)).isNull();
+        }
+    }
+
+    @Test
     public void testAddProviderRequestListener() throws Exception {
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity(Manifest.permission.LOCATION_HARDWARE);
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index bc813aa..b6ee78c 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -1086,7 +1086,8 @@
         try {
             // Get all active subscriptions.
             List<SubscriptionInfo> activeSubInfos =
-                    mSubscriptionManager.getActiveSubscriptionInfoList();
+                    ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
+                    (sm) -> sm.getActiveSubscriptionInfoList());
 
             List<Integer> activeSubGroup = getSubscriptionIdList(activeSubInfos);
             activeSubGroup.removeIf(id -> id == subId);
@@ -1262,13 +1263,17 @@
     }
 
     private void removeSubscriptionsFromGroup(ParcelUuid uuid) {
-        List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
+        List<SubscriptionInfo> infoList = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mSubscriptionManager,
+                (sm) -> (sm.getSubscriptionsInGroup(uuid)));
         if (!infoList.isEmpty()) {
             List<Integer> subscriptionIdList = getSubscriptionIdList(infoList);
             ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSubscriptionManager,
                     (sm) -> sm.removeSubscriptionsFromGroup(subscriptionIdList, uuid));
         }
-        infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
+        infoList = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mSubscriptionManager,
+                (sm) -> (sm.getSubscriptionsInGroup(uuid)));
         assertThat(infoList).isEmpty();
     }
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
index ba34269..6149732 100644
--- a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
@@ -26,6 +26,7 @@
 import android.graphics.cts.utils.Cam;
 import android.util.Pair;
 
+
 import androidx.annotation.ColorInt;
 import androidx.core.graphics.ColorUtils;
 import androidx.test.filters.SmallTest;
@@ -35,15 +36,18 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SystemPaletteTest {
 
     // Hue goes from 0 to 360
-    private static final int MAX_HUE_DISTANCE = 30;
+    private static final int MAX_HUE_DISTANCE = 12;
 
     @Test
     public void testShades0and1000() {
@@ -72,16 +76,56 @@
                 getAllAccent2Colors(context), getAllAccent3Colors(context),
                 getAllNeutral1Colors(context), getAllNeutral2Colors(context));
 
+        final float[] tones = {100, 99, 95, 90, 80, 70, 60, 49, 40, 30, 20, 10, 0};
         for (int[] palette : allPalettes) {
-            for (int i = 3; i < palette.length - 1; i++) {
-                assertWithMessage("Color " + Integer.toHexString((palette[i - 1]))
-                        + " has different hue compared to " + Integer.toHexString(palette[i])
-                        + " for palette: " + Arrays.toString(palette))
-                        .that(similarHue(palette[i - 1], palette[i])).isTrue();
+            // Determine the median hue of the palette. Each color in the palette colors will have
+            // its hue measured against the median hue. If the difference is too large, the test
+            // fails.
+            List<Float> hues = new ArrayList<>();
+            for (int i = 0; i < palette.length - 1; i++) {
+                // Avoid measuring hue of colors above 90 or below 10 in tone.
+                //
+                // Converting from HCT to sRGB from display quantizes colors - i.e. not every
+                // HCT color can be expressed in sRGB. As colors approach the extreme tones, white
+                // at 100 and black at 0, hues begin overlapping overlay - made up example: hues
+                // 110 to 128 at tone 95, when mapped to sRGB for display, all end up being measured
+                // as hue 114.
+                final float tone = tones[i];
+                if (tone < 10.0 || tone > 90.0) {
+                    continue;
+                }
+                final Cam cam = Cam.fromInt(palette[i]);
+                hues.add(cam.getHue());
+            }
+            Collections.sort(hues);
+            final float medianHue = hues.get(hues.size() / 2);
+
+            // Measure the hue of each color in the palette against the median hue.
+            for (int i = 0; i < palette.length - 1; i++) {
+                final float tone = tones[i];
+                // Skip testing hue of extreme tones, due to overlap due to quantization that occurs
+                // when converting from HCT to sRGB for display.
+                if (tone < 10.0 || tone > 90.0) {
+                    continue;
+                }
+                final Cam cam = Cam.fromInt(palette[i]);
+                final float hue = cam.getHue();
+                final boolean hueWithinTolerance = deltaHueWithinTolerance(hue, medianHue);
+                assertWithMessage("Color " + toHctString(cam)
+                        + " has different hue compared to median hue " + Math.round(medianHue)
+                        + " of palette: " + Arrays.toString(palette))
+                        .that(hueWithinTolerance).isTrue();
             }
         }
     }
 
+    private static String toHctString(Cam cam) {
+        final double[] labColor = new double[3];
+        ColorUtils.colorToLAB(cam.viewedInSrgb(), labColor);
+        return "H" + Math.round(cam.getHue()) + " C" + Math.round(cam.getChroma()) + " T"
+                + Math.round(labColor[0]);
+    }
+
     /**
      * Compare if color A and B have similar hue, in gCAM space.
      *
@@ -89,12 +133,10 @@
      * @param colorB Color 2
      * @return True when colors have similar hue.
      */
-    private boolean similarHue(@ColorInt int colorA, @ColorInt int colorB) {
-        final Cam camA = Cam.fromInt(colorA);
-        final Cam camB = Cam.fromInt(colorB);
+    private boolean deltaHueWithinTolerance(float hueA, float hueB) {
 
-        float hue1 = Math.max(camA.getHue(), camB.getHue());
-        float hue2 = Math.min(camA.getHue(), camB.getHue());
+        float hue1 = Math.max(hueA, hueB);
+        float hue2 = Math.min(hueA, hueB);
 
         float diffDegrees = 180.0f - Math.abs(Math.abs(hue1 - hue2) - 180.0f);
         return diffDegrees < MAX_HUE_DISTANCE;
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
index 010773f..9b414ff 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.PackageManager;
 import android.security.KeyPairGeneratorSpec;
+import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
 import android.test.AndroidTestCase;
@@ -25,6 +26,8 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
+import org.junit.Assert;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
@@ -61,6 +64,7 @@
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
 import javax.crypto.Mac;
 import javax.crypto.SecretKey;
 import javax.security.auth.x500.X500Principal;
@@ -1775,6 +1779,43 @@
         mKeyStore.setEntry(TEST_ALIAS_1, entry, null);
     }
 
+    /*
+     * Replacing an existing secret key with itself should be a no-op.
+     */
+    public void testKeyStore_SetKeyEntry_ReplacedWithSameGeneratedSecretKey()
+            throws Exception {
+        final String plaintext = "My awesome plaintext message!";
+        final String algorithm = "AES/GCM/NoPadding";
+
+        final KeyGenerator generator = KeyGenerator.getInstance("AES", "AndroidKeyStore");
+        final KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(TEST_ALIAS_1,
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setKeySize(256)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .build();
+        generator.init(spec);
+        final SecretKey key = generator.generateKey();
+
+        Cipher cipher = Cipher.getInstance(algorithm);
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        AlgorithmParameters params = cipher.getParameters();
+        final byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
+
+        mKeyStore.load(null, null);
+
+        // This should succeed.
+        mKeyStore.setKeyEntry(TEST_ALIAS_1, key, null, null);
+        // And it should not change the key under TEST_ALIAS_1. And what better way to test
+        // then to use it on some cipher text generated with that key.
+        final Key key2 = mKeyStore.getKey(TEST_ALIAS_1, null);
+        cipher = Cipher.getInstance(algorithm);
+        cipher.init(Cipher.DECRYPT_MODE, key2, params);
+        byte[] plaintext2 = cipher.doFinal(ciphertext);
+        Assert.assertArrayEquals("The plaintext2 should match the original plaintext.",
+                plaintext2, plaintext.getBytes());
+    }
+
     public void testKeyStore_Size_Unencrypted_Success() throws Exception {
         mKeyStore.load(null, null);
 
diff --git a/tests/tests/keystore/src/android/keystore/cts/AttestationPerformanceTest.java b/tests/tests/keystore/src/android/keystore/cts/AttestationPerformanceTest.java
index 9101f3b..caa18a0 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AttestationPerformanceTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AttestationPerformanceTest.java
@@ -35,6 +35,10 @@
     };
 
     public void testRsaKeyAttestation() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         for (byte[] challenge : ATTESTATION_CHALLENGES) {
             for (int keySize : RSA_KEY_SIZES) {
                 measure(new KeystoreAttestationMeasurable(
@@ -45,6 +49,10 @@
     }
 
     public void testEcKeyAttestation() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         for (byte[] challenge : ATTESTATION_CHALLENGES) {
             for (int curve : EC_CURVES) {
                 measure(new KeystoreAttestationMeasurable(
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 5a3b712..85de847 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -159,6 +159,10 @@
 
     @RequiresDevice
     public void testEcAttestation() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
             return;
 
@@ -212,6 +216,10 @@
     }
 
     public void testEcAttestation_TooLargeChallenge() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         boolean[] devicePropertiesAttestationValues = {true, false};
         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
             try {
@@ -266,6 +274,10 @@
     @RestrictedBuildTest
     @RequiresDevice
     public void testEcAttestation_DeviceLocked() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
             return;
 
@@ -426,6 +438,10 @@
 
     @RequiresDevice
     public void testRsaAttestation() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
             return;
 
@@ -488,6 +504,10 @@
     }
 
     public void testRsaAttestation_TooLargeChallenge() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         boolean[] devicePropertiesAttestationValues = {true, false};
         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
             try {
@@ -542,6 +562,10 @@
     @RestrictedBuildTest
     @RequiresDevice  // Emulators have no place to store the needed key
     public void testRsaAttestation_DeviceLocked() throws Exception {
+        if (!TestUtils.isAttestationSupported()) {
+            return;
+        }
+
         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
             return;
 
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index aeb3ad6..4efa171 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.FeatureInfo;
+import android.os.Build;
 import android.os.SystemProperties;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyInfo;
@@ -1080,4 +1081,8 @@
         new SecureRandom().nextBytes(message);
         return message;
     }
+
+    public static boolean isAttestationSupported() {
+        return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.O;
+    }
 }
diff --git a/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java b/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
index 634ec56..178b426 100644
--- a/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
+++ b/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
@@ -21,6 +21,7 @@
 import android.support.test.uiautomator.UiDevice;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.CddTest;
 import com.google.common.base.Strings;
 
 import org.junit.After;
@@ -126,6 +127,7 @@
      *
      * @throws Exception
      */
+    @CddTest(requirement="7.3.6")
     @Test
     public void testGetThermalHeadroom() throws Exception {
         final String failureMessage = nativeTestGetThermalHeadroom();
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index d179c5a..8065633 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -3367,6 +3367,122 @@
         }
     }
 
+    /**
+     * Test AudioTrack Builder error handling.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testAudioTrackBuilderError() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final AudioTrack[] audioTrack = new AudioTrack[1]; // pointer to audio track.
+        final int BIGNUM = Integer.MAX_VALUE; // large value that should be invalid.
+        final int INVALID_SESSION_ID = 1024;  // can never occur (wrong type in 3 lsbs)
+        final int INVALID_CHANNEL_MASK = -1;
+
+        try {
+            // NOTE:
+            // Tuner Configuration builder error tested in testTunerConfiguration (same file).
+            // AudioAttributes tested in AudioAttributesTest#testAudioAttributesBuilderError.
+            // AudioFormat tested in AudioFormatTest#testAudioFormatBuilderError.
+
+            // We must be able to create the AudioTrack.
+            audioTrack[0] = new AudioTrack.Builder().build();
+            audioTrack[0].release();
+
+            // Out of bounds buffer size.  A large size will fail in AudioTrack creation.
+            assertThrows(UnsupportedOperationException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setBufferSizeInBytes(BIGNUM)
+                        .build();
+            });
+
+            // 0 and negative buffer size throw IllegalArgumentException
+            for (int bufferSize : new int[] {-BIGNUM, -1, 0}) {
+                assertThrows(IllegalArgumentException.class, () -> {
+                    audioTrack[0] = new AudioTrack.Builder()
+                            .setBufferSizeInBytes(bufferSize)
+                            .build();
+                });
+            }
+
+            assertThrows(IllegalArgumentException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setEncapsulationMode(BIGNUM)
+                        .build();
+            });
+
+            assertThrows(IllegalArgumentException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setPerformanceMode(BIGNUM)
+                        .build();
+            });
+
+            // Invalid session id that is positive.
+            // (logcat error message vague)
+            assertThrows(UnsupportedOperationException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setSessionId(INVALID_SESSION_ID)
+                        .build();
+            });
+
+            assertThrows(IllegalArgumentException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setTransferMode(BIGNUM)
+                        .build();
+            });
+
+            // Specialty AudioTrack build errors.
+
+            // Bad audio encoding DRA expected unsupported.
+            try {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setAudioFormat(new AudioFormat.Builder()
+                                .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
+                                .setEncoding(AudioFormat.ENCODING_DRA)
+                                .build())
+                        .build();
+                // Don't throw an exception, maybe it is supported somehow, but warn.
+                // Note: often specialty audio formats are offloaded (see setOffloadedPlayback).
+                // AudioTrackSurroundTest and AudioTrackOffloadedTest can be used as examples.
+                Log.w(TAG, "ENCODING_DRA is expected to be unsupported");
+                audioTrack[0].release();
+                audioTrack[0] = null;
+            } catch (UnsupportedOperationException e) {
+                ; // OK expected
+            }
+
+            // Sample rate out of bounds.
+            // System levels caught on AudioFormat.
+            assertThrows(IllegalArgumentException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setAudioFormat(new AudioFormat.Builder()
+                                .setSampleRate(BIGNUM)
+                                .build())
+                        .build();
+            });
+
+            // Invalid channel mask - caught here on use.
+            assertThrows(IllegalArgumentException.class, () -> {
+                audioTrack[0] = new AudioTrack.Builder()
+                        .setAudioFormat(new AudioFormat.Builder()
+                                .setChannelMask(INVALID_CHANNEL_MASK)
+                                .build())
+                        .build();
+            });
+        } finally {
+            // Did we successfully complete for some reason but did not
+            // release?
+            if (audioTrack[0] != null) {
+                audioTrack[0].release();
+                audioTrack[0] = null;
+            }
+        }
+    }
+
 /* Do not run in JB-MR1. will be re-opened in the next platform release.
     public void testResourceLeakage() throws Exception {
         final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/mediatranscoding/OWNERS b/tests/tests/mediatranscoding/OWNERS
index e653979..a4393a7 100644
--- a/tests/tests/mediatranscoding/OWNERS
+++ b/tests/tests/mediatranscoding/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 761430
-hkuang@google.com
-chz@google.com
-lnilsson@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
index 932cbaf..598996c 100644
--- a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
+++ b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
@@ -32,6 +32,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.ThermalUtils;
 
 import org.junit.After;
@@ -126,6 +127,12 @@
                 .times(1)).onThermalStatusChanged(status);
     }
 
+    /**
+     * Test that getThermalHeadroom works
+     *
+     * @throws Exception
+     */
+    @CddTest(requirement="7.3.6")
     @Test
     public void testGetThermalHeadroom() throws Exception {
         float headroom = mPowerManager.getThermalHeadroom(0);
diff --git a/tests/tests/settings/Android.bp b/tests/tests/settings/Android.bp
index 246e3fd..b1a443e 100644
--- a/tests/tests/settings/Android.bp
+++ b/tests/tests/settings/Android.bp
@@ -17,12 +17,20 @@
     static_libs: [
         "androidx.slice_slice-core",
         "androidx.slice_slice-view",
+        "androidx.test.core",
         "ctstestrunner-axt",
         "junit",
+        "kotlin-stdlib",
         "truth-prebuilt",
+        "ctsWindowExtLib",
     ],
 
     srcs: ["src/**/*.java"],
 
     sdk_version: "test_current",
 }
+
+android_library_import {
+    name: "ctsWindowExtLib",
+    aars: ["libs/cts_window_ext_lib.aar"],
+}
diff --git a/tests/tests/settings/AndroidManifest.xml b/tests/tests/settings/AndroidManifest.xml
index 9225b6a..007b36d 100644
--- a/tests/tests/settings/AndroidManifest.xml
+++ b/tests/tests/settings/AndroidManifest.xml
@@ -20,9 +20,20 @@
 
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK"/>
 
     <application>
         <uses-library android:name="android.test.runner"/>
+        <uses-library android:name="androidx.window.extensions" android:required="false"/>
+        <uses-library android:name="androidx.window.sidecar" android:required="false"/>
+
+        <activity android:name=".RightPaneActivity"
+                android:exported="true">
+            <intent-filter>
+                <action android:name="android.settings.cts.LAUNCH_RIGHT_PANE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/settings/libs/cts_window_ext_lib.aar b/tests/tests/settings/libs/cts_window_ext_lib.aar
new file mode 100644
index 0000000..ca58b36
--- /dev/null
+++ b/tests/tests/settings/libs/cts_window_ext_lib.aar
Binary files differ
diff --git a/tests/tests/settings/src/android/settings/cts/RightPaneActivity.java b/tests/tests/settings/src/android/settings/cts/RightPaneActivity.java
new file mode 100644
index 0000000..86391e7
--- /dev/null
+++ b/tests/tests/settings/src/android/settings/cts/RightPaneActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.settings.cts;
+
+import android.app.Activity;
+import android.provider.Settings;
+
+/**
+ * For SettingsMultiPaneDeepLinkTest to test if
+ * {@link Settings#ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY}
+ */
+public class RightPaneActivity extends Activity {
+}
diff --git a/tests/tests/settings/src/android/settings/cts/SettingsMultiPaneDeepLinkTest.java b/tests/tests/settings/src/android/settings/cts/SettingsMultiPaneDeepLinkTest.java
new file mode 100644
index 0000000..b6103c8
--- /dev/null
+++ b/tests/tests/settings/src/android/settings/cts/SettingsMultiPaneDeepLinkTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.settings.cts;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.Instrumentation.ActivityMonitor;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.window.embedding.SplitController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to ensure the Activity to handle
+ * {@link Settings#ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY}
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsMultiPaneDeepLinkTest {
+
+    private static final String DEEP_LINK_PERMISSION =
+            "android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK";
+
+    boolean mIsSplitSupported;
+    ResolveInfo mDeepLinkIntentResolveInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        // runOnMainSync or SplitController#isSplitSupported will return wrong value for large
+        // screen devices.
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mIsSplitSupported = SplitController.getInstance().isSplitSupported();
+            }
+        });
+        mDeepLinkIntentResolveInfo = InstrumentationRegistry.getInstrumentation().getContext()
+                .getPackageManager().resolveActivity(
+                new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY),
+                /* flags= */ PackageManager.MATCH_DEFAULT_ONLY);
+
+        assumeFalse("Skipping test: The device does not support Activity embedding",
+                !mIsSplitSupported && mDeepLinkIntentResolveInfo == null);
+    }
+
+    @Test
+    public void deepLinkHomeActivity_protectedWithPermission() throws Exception {
+        assertTrue("The Activity to handle the Intent ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY must"
+                + " be protected by " + DEEP_LINK_PERMISSION,
+                DEEP_LINK_PERMISSION.equals(mDeepLinkIntentResolveInfo.activityInfo.permission));
+    }
+
+    @Test
+    public void deepLinkHomeActivity_splitSupported_deepLinkHomeEnabled() throws Exception {
+        assumeTrue(mIsSplitSupported);
+
+        assertTrue("The Activity to handle the Intent ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY must"
+                + " be enabled when the device supports Activity embedding",
+                mDeepLinkIntentResolveInfo != null);
+    }
+
+    @Test
+    public void deepLinkHomeActivity_splitNotSupported_deepLinkHomeDisabled() throws Exception {
+        assumeFalse(mIsSplitSupported);
+
+        assertTrue("The Activity to handle the Intent ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY must"
+                + " be disabled when the device does NOT support Activity embedding",
+                mDeepLinkIntentResolveInfo == null);
+    }
+
+    @Test
+    public void deepLinkHomeActivity_receiveMultiPaneDeepLinkIntent_shouldStartActivity()
+                throws Exception {
+        Intent intent = new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
+        intent.putExtra(Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
+                new Intent("android.settings.cts.LAUNCH_RIGHT_PANE")
+                .toUri(Intent.URI_INTENT_SCHEME));
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        ActivityMonitor am = instrumentation.addMonitor(RightPaneActivity.class.getName(),
+                /* result */ null, /* block */ false);
+
+        // Take the Shell UID permission identity because Shell app has the permission
+        // android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK.
+        instrumentation.getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            instrumentation.getContext()
+                    .startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        } finally {
+            // Drop the Shell UID permission identity.
+            instrumentation.getUiAutomation().dropShellPermissionIdentity();
+        }
+
+        Activity rightPaneActivity = am.waitForActivityWithTimeout(5000);
+        assertNotNull("The Activity to handle the Intent ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY"
+                + " must start Activity for EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI",
+                rightPaneActivity);
+        rightPaneActivity.finish();
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index a594afa..722cdae 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -3333,6 +3333,19 @@
                 TelephonyManager.SIM_STATE_NOT_READY,
                 TelephonyManager.SIM_STATE_PERM_DISABLED,
                 TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState));
+
+        for (int i = 0; i <= mTelephonyManager.getPhoneCount(); i++) {
+            final int slotId = i;
+            simApplicationState = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.getSimApplicationState(slotId));
+            assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+                    TelephonyManager.SIM_STATE_PIN_REQUIRED,
+                    TelephonyManager.SIM_STATE_PUK_REQUIRED,
+                    TelephonyManager.SIM_STATE_NETWORK_LOCKED,
+                    TelephonyManager.SIM_STATE_NOT_READY,
+                    TelephonyManager.SIM_STATE_PERM_DISABLED,
+                    TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState));
+        }
     }
 
     @Test
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
index 4863f5b..d4386ff 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
@@ -818,6 +818,8 @@
             } finally {
                 errorQueue.clear();
                 retryAfterQueue.clear();
+                removeTestContactFromEab();
+                removeUceRequestDisallowedStatus();
             }
 
             requestAvailability(uceAdapter, sTestNumberUri, callback);
@@ -831,6 +833,8 @@
             } finally {
                 errorQueue.clear();
                 retryAfterQueue.clear();
+                removeTestContactFromEab();
+                removeUceRequestDisallowedStatus();
             }
 
             /*
@@ -854,6 +858,8 @@
             } finally {
                 errorQueue.clear();
                 retryAfterQueue.clear();
+                removeTestContactFromEab();
+                removeUceRequestDisallowedStatus();
             }
 
             requestAvailability(uceAdapter, sTestNumberUri, callback);
@@ -867,6 +873,8 @@
             } finally {
                 errorQueue.clear();
                 retryAfterQueue.clear();
+                removeTestContactFromEab();
+                removeUceRequestDisallowedStatus();
             }
         });
 
@@ -888,6 +896,8 @@
         } finally {
             errorQueue.clear();
             retryAfterQueue.clear();
+            removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
         }
 
         requestCapabilities(uceAdapter, numbers, callback);
@@ -902,6 +912,8 @@
         } finally {
             errorQueue.clear();
             retryAfterQueue.clear();
+            removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
         }
 
         overrideCarrierConfig(null);
@@ -975,22 +987,38 @@
         });
 
         requestCapabilities(uceAdapter, contacts, callback);
+        List<RcsContactUceCapability> resultCapList = new ArrayList<>();
 
         // Verify that all the three contact's capabilities are received
         RcsContactUceCapability capability = waitForResult(capabilityQueue);
-        assertNotNull("Capabilities were not received for contact: " + contact1, capability);
-        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
-                true, true);
+        assertNotNull("Capabilities were not received for contact", capability);
+        resultCapList.add(capability);
 
         capability = waitForResult(capabilityQueue);
-        assertNotNull("Capabilities were not received for contact: " + contact2, capability);
-        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
-                true, false);
+        assertNotNull("Capabilities were not received for contact", capability);
+        resultCapList.add(capability);
 
         capability = waitForResult(capabilityQueue);
-        assertNotNull("Capabilities were not received for contact: " + contact3, capability);
-        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
-                false, false);
+        assertNotNull("Capabilities were not received for contact", capability);
+        resultCapList.add(capability);
+
+        // Verify the first contact capabilities from the received capabilities list
+        RcsContactUceCapability resultCapability = getContactCapability(resultCapList, contact1);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, true);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact2);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, false);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact3);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
@@ -999,6 +1027,7 @@
         errorRetryQueue.clear();
         completeQueue.clear();
         capabilityQueue.clear();
+        resultCapList.clear();
         removeTestContactFromEab();
 
         // Setup the callback that some of the contacts are terminated.
@@ -1010,16 +1039,34 @@
 
         // Verify the contacts are not found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
+
+        // Verify the first contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact1);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact2);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact3);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
@@ -1028,6 +1075,7 @@
         errorRetryQueue.clear();
         completeQueue.clear();
         capabilityQueue.clear();
+        resultCapList.clear();
         removeTestContactFromEab();
 
         // Setup the callback that some of the contacts are terminated.
@@ -1051,17 +1099,35 @@
 
         // Verify the first contact is found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
-                true, true);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
 
         // Verify the reset contacts are not found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                true, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
+
+        // Verify the first contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact1);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, true);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact2);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact3);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
@@ -1962,60 +2028,17 @@
                 } else if (sipCode == sipCodeBadEvent) {
                     assertTrue(retryAfterMillis > 0L);
                 }
-
-                // Verify the ImsService received the capabilities request.
-                assertEquals(1, subscribeRequestCount.get());
             } catch (Exception e) {
                 fail("testForbiddenResponseToCapabilitiesRequest with command error failed: " + e);
             } finally {
                 errorQueue.clear();
                 retryAfterQueue.clear();
+                capabilityQueue.clear();
+                completeQueue.clear();
                 subscribeRequestCount.set(0);
+                removeTestContactFromEab();
+                removeUceRequestDisallowedStatus();
             }
-
-            // Override the network response with the sip code 503 Service Unavailable
-            capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
-                subscribeRequestCount.incrementAndGet();
-                cb.onNetworkResponse(503, "Service Unavailable");
-            });
-
-            try {
-                // Request contact uce capabilities again.
-                requestCapabilities(uceAdapter, contacts, callback);
-
-                if (sipCode == sipCodeForbidden) {
-                    // Verify that device can still send the subscribe request. The callback
-                    // "onError" is called with the Server Unavailable error.
-                    assertEquals(RcsUceAdapter.ERROR_SERVER_UNAVAILABLE,
-                            waitForIntResult(errorQueue));
-                    // Verify the retryAfter value
-                    assertEquals(0L, waitForLongResult(retryAfterQueue));
-                    // Verify that the capabilities is not forbidden. The ImsService received the
-                    // request from the framework.
-                    assertEquals(1, subscribeRequestCount.get());
-                } else if (sipCode == sipCodeBadEvent) {
-                    // When carrier config Bad Event flag is enabled and the device has received
-                    // the sip code 489 (bad event) before, the uce request will be forbidden.
-
-                    // Verify that the callback "onError" is called with the error code FORBIDDEN
-                    assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
-                    // Verify the retryAfter value
-                    long retryAfterMillis = waitForLongResult(retryAfterQueue);
-                    assertTrue(retryAfterMillis > 0L);
-                    // Verify that the capabilities won't be send to the ImsService because the
-                    // uce request is forbidden.
-                    assertEquals(0, subscribeRequestCount.get());
-                }
-            } catch (Exception e) {
-                fail("testForbiddenResponseToCapabilitiesRequest with command error failed: " + e);
-            } finally {
-                errorQueue.clear();
-                retryAfterQueue.clear();
-                subscribeRequestCount.set(0);
-            }
-
-            // Reset the device status
-            removeUceRequestDisallowedStatus();
         });
 
         overrideCarrierConfig(null);
@@ -2284,6 +2307,7 @@
             completeQueue.clear();
             capabilityQueue.clear();
             removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
             setCapabilitiesRequestTimeout(-1L);
         }
 
@@ -2309,12 +2333,115 @@
             completeQueue.clear();
             capabilityQueue.clear();
             removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
             setCapabilitiesRequestTimeout(-1L);
         }
 
         overrideCarrierConfig(null);
     }
 
+    @Test
+    public void testRequestTimeoutWithPresenceMechanism() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Prepare the test contact
+        Collection<Uri> contacts = new ArrayList<>();
+        contacts.add(sTestNumberUri);
+
+        // Setup the ImsService doesn't trigger any callbacks.
+        AtomicInteger subscribeRequestCount = new AtomicInteger(0);
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            subscribeRequestCount.incrementAndGet();
+            // It won't trigger any callbacks to the framework.
+        });
+
+        // Set the timeout for 3 seconds
+        setCapabilitiesRequestTimeout(3000L);
+
+        // Request capabilities
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        try {
+            // Verify the error code REQUEST_TIMEOUT is received
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, waitForLongResult(errorRetryQueue));
+
+            // Verify the capabilities can still be received.
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            completeQueue.clear();
+            capabilityQueue.clear();
+            subscribeRequestCount.set(0);
+        }
+
+        // Request the capabilities with the same contact again.
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        try {
+            // Verify that the caller can received the capabilities callback.
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+
+            // Verify the complete callback will be called.
+            waitForResult(completeQueue);
+
+            // Verify that the ImsService didn't received the request because the capabilities
+            // should be retrieved from the cache.
+            assertEquals(0, subscribeRequestCount.get());
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            completeQueue.clear();
+            capabilityQueue.clear();
+            removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
+            subscribeRequestCount.set(0);
+            setCapabilitiesRequestTimeout(-1L);
+        }
+
+        overrideCarrierConfig(null);
+    }
 
     @Test
     public void testTimeoutToRequestCapabilitiesWithOptionsMechanism() throws Exception {
@@ -2457,22 +2584,40 @@
 
         requestCapabilities(uceAdapter, contacts, callback);
 
+        List<RcsContactUceCapability> resultCapList = new ArrayList<>();
+
         // Verify that all the three contact's capabilities are received
         RcsContactUceCapability capability = waitForResult(capabilityQueue);
-        assertNotNull("Capabilities were not received for contact: " + contact1SipScheme,
-                capability);
-        verifyCapabilityResult(capability, contact1SipScheme, SOURCE_TYPE_NETWORK,
+        assertNotNull("Capabilities were not received for contact", capability);
+        resultCapList.add(capability);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact", capability);
+        resultCapList.add(capability);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact", capability);
+        resultCapList.add(capability);
+
+
+        // Verify the first contact capabilities from the received capabilities list
+        RcsContactUceCapability resultCapability =
+                getContactCapability(resultCapList, contact1SipScheme);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact1SipScheme, SOURCE_TYPE_NETWORK,
                 REQUEST_RESULT_FOUND, true, true);
 
-        capability = waitForResult(capabilityQueue);
-        assertNotNull("Capabilities were not received for contact: " + contact2, capability);
-        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
-                true, false);
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact2);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, false);
 
-        capability = waitForResult(capabilityQueue);
-        assertNotNull("Capabilities were not received for contact: " + contact3, capability);
-        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
-                false, false);
+        // Verify the third contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact3);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
@@ -2481,6 +2626,7 @@
         errorRetryQueue.clear();
         completeQueue.clear();
         capabilityQueue.clear();
+        resultCapList.clear();
         removeTestContactFromEab();
 
         // Setup the callback that some of the contacts are terminated.
@@ -2492,16 +2638,34 @@
 
         // Verify the contacts are not found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact1TelScheme, SOURCE_TYPE_NETWORK,
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
+
+        // Verify the first contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact1TelScheme);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact1TelScheme, SOURCE_TYPE_NETWORK,
                 REQUEST_RESULT_NOT_FOUND, false, false);
 
-        capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact2);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
 
-        capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact3);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
@@ -2510,6 +2674,7 @@
         errorRetryQueue.clear();
         completeQueue.clear();
         capabilityQueue.clear();
+        resultCapList.clear();
         removeTestContactFromEab();
 
         // Setup the callback that some of the contacts are terminated.
@@ -2533,17 +2698,35 @@
 
         // Verify the first contact is found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact1SipScheme, SOURCE_TYPE_NETWORK,
-                REQUEST_RESULT_FOUND, true, true);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
 
         // Verify the reset contacts are not found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                true, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
-                false, false);
+        assertNotNull("Capabilities were not received", capability);
+        resultCapList.add(capability);
+
+        // Verify the first contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact1SipScheme);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact1SipScheme, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, true);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact2);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, true, false);
+
+        // Verify the second contact capabilities from the received capabilities list
+        resultCapability = getContactCapability(resultCapList, contact3);
+        assertNotNull("Cannot find the contact", resultCapability);
+        verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_NOT_FOUND, false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
@@ -2678,6 +2861,352 @@
         overrideCarrierConfig(null);
     }
 
+    @Test
+    public void testContactInThrottlingState() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+        // Reset the UCE device state.
+        removeUceRequestDisallowedStatus();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* options */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Prepare the test contact
+        Collection<Uri> numbers = new ArrayList<>(1);
+        numbers.add(sTestNumberUri);
+
+        // Setup the network response is 408 Request Timeout.
+        int networkRespCode = 408;
+        String networkRespReason = "Request Timeout";
+        AtomicInteger subscribeRequestCount = new AtomicInteger(0);
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            subscribeRequestCount.incrementAndGet();
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+        });
+
+        // Request contact capabilities
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        // Verify that the callback "onError" is called with the expected error code.
+        try {
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, waitForLongResult(errorRetryQueue));
+            // Verify the caller can received the capabilities callback.
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+            // Verity the ImsService received the request.
+            assertTrue(subscribeRequestCount.get() > 0);
+        } catch (Exception e) {
+            fail("testContactsInThrottlingState with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            capabilityQueue.clear();
+            completeQueue.clear();
+            subscribeRequestCount.set(0);
+        }
+
+        // Request the capabilities again with the same contact.
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        // Verify that the result.
+        try {
+            // Verify that the caller can received the capabilities callback.
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_CACHED,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+            // Verify the complete callback will be called.
+            waitForResult(completeQueue);
+            // Verify that the ImsService didn't received the request because the capabilities
+            // should be retrieved from the cache.
+            assertEquals(0, subscribeRequestCount.get());
+        } catch (Exception e) {
+            fail("testContactsInThrottlingState with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            subscribeRequestCount.set(0);
+            // reset the cache and throttling list
+            removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
+        }
+
+        // Request availability.
+        requestAvailability(uceAdapter, sTestNumberUri, callback);
+
+        // Verify that the callback "onError" is called with the expected error code.
+        try {
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, waitForLongResult(errorRetryQueue));
+            // Verify the caller can received the capabilities callback.
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+            // Verity the ImsService received the request.
+            assertTrue(subscribeRequestCount.get() > 0);
+        } catch (Exception e) {
+            fail("requestAvailability with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            capabilityQueue.clear();
+            completeQueue.clear();
+            subscribeRequestCount.set(0);
+        }
+
+        // Request availability again with the same contact.
+        requestAvailability(uceAdapter, sTestNumberUri, callback);
+
+        // Verify that the callback "onError" is called with the expected error code.
+        try {
+            // Verify that the caller can received the capabilities callback.
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_CACHED,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+            // Verify the complete callback will be called.
+            waitForResult(completeQueue);
+            // Verify that the ImsService didn't received the request because the capabilities
+            // should be retrieved from the cache.
+            assertEquals(0, subscribeRequestCount.get());
+        } catch (Exception e) {
+            fail("testContactsInThrottlingState with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            subscribeRequestCount.set(0);
+            removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
+        }
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testRequestResultInconclusive() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+        // Reset the UCE device state.
+        removeUceRequestDisallowedStatus();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* options */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // In the first round, prepare the test account
+        Collection<Uri> numbers = new ArrayList<>();
+        numbers.add(sTestNumberUri);
+
+        ArrayList<String> pidfXmlList = new ArrayList<>();
+        pidfXmlList.add(getPidfXmlData(sTestNumberUri, true, true));
+
+        // Setup the network response is 200 OK for the first request
+        final int networkRespCode200 = 200;
+        final String networkRespReasonOK = "OK";
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            cb.onNetworkResponse(networkRespCode200, networkRespReasonOK);
+            cb.onNotifyCapabilitiesUpdate(pidfXmlList);
+            cb.onTerminated("", 0L);
+        });
+
+        // Request contact capabilities
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        // Verify that the contact capability is received and the onCompleted is called.
+        try {
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_FOUND, true, true);
+            waitForResult(completeQueue);
+        } catch (Exception e) {
+            fail("testRequestResultInconclusive with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            capabilityQueue.clear();
+            completeQueue.clear();
+            numbers.clear();
+            pidfXmlList.clear();
+        }
+
+        // Request the second contacts and this time, the network respons is 408 Request Timeout
+        numbers.add(sTestContact2Uri);
+
+        final int networkRespCode408 = 408;
+        final String networkRespReasonTimeout = "Request Timeout";
+        AtomicInteger subscribeRequestCount = new AtomicInteger(0);
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            subscribeRequestCount.incrementAndGet();
+            cb.onNetworkResponse(networkRespCode408, networkRespReasonTimeout);
+        });
+
+        // Request contact capabilities again with different contact
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        // Verify that the callback "onError" is called with the expected error code.
+        try {
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, waitForLongResult(errorRetryQueue));
+            RcsContactUceCapability capability = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability);
+            verifyCapabilityResult(capability, sTestContact2Uri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+            assertTrue(subscribeRequestCount.get() > 0);
+        } catch (Exception e) {
+            fail("testRequestResultInconclusive with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            capabilityQueue.clear();
+            completeQueue.clear();
+            numbers.clear();
+            pidfXmlList.clear();
+            subscribeRequestCount.set(0);
+        }
+
+        // Request three contacts at a time in the third round.
+        numbers.add(sTestNumberUri);
+        numbers.add(sTestContact2Uri);
+        numbers.add(sTestContact3Uri);
+
+        // The first two contact capabilities can be retrieved from the cache. However, the third
+        // contact capabilities will be provided by the ImsService
+        pidfXmlList.add(getPidfXmlData(sTestContact3Uri, true, true));
+
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            subscribeRequestCount.incrementAndGet();
+            assertNotNull("The uris of capabilities request cannot be null", uris);
+            List<Uri> uriList = new ArrayList(uris);
+            // Verify that only uri need to be queried from the network
+            assertEquals(1, uriList.size());
+            assertEquals(sTestContact3Uri, uriList.get(0));
+            cb.onNetworkResponse(networkRespCode200, networkRespReasonOK);
+            cb.onNotifyCapabilitiesUpdate(pidfXmlList);
+            cb.onTerminated("", 0L);
+        });
+
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        List<RcsContactUceCapability> resultCapList = new ArrayList<>();
+
+        // Verify that the contact capability is received and the onCompleted is called.
+        try {
+            RcsContactUceCapability capability1 = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability1);
+            resultCapList.add(capability1);
+
+            RcsContactUceCapability capability2 = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability2);
+            resultCapList.add(capability2);
+
+            RcsContactUceCapability capability3 = waitForResult(capabilityQueue);
+            assertNotNull("Capabilities were not received", capability3);
+            resultCapList.add(capability3);
+
+            // Verify contact1's capabilities from the received capabilities list
+            RcsContactUceCapability resultCapability =
+                    getContactCapability(resultCapList, sTestNumberUri);
+            assertNotNull("Cannot find the contact", resultCapability);
+            verifyCapabilityResult(resultCapability, sTestNumberUri, SOURCE_TYPE_CACHED,
+                    REQUEST_RESULT_FOUND, true, true);
+
+            // Verify contact2's capabilities from the received capabilities list
+            resultCapability = getContactCapability(resultCapList, sTestContact2Uri);
+            assertNotNull("Cannot find the contact", resultCapability);
+            verifyCapabilityResult(resultCapability, sTestContact2Uri, SOURCE_TYPE_CACHED,
+                    REQUEST_RESULT_NOT_FOUND, false, false);
+
+            // Verify contact3's capabilities from the received capabilities list
+            resultCapability = getContactCapability(resultCapList, sTestContact3Uri);
+            assertNotNull("Cannot find the contact", sTestContact3Uri);
+            verifyCapabilityResult(resultCapability, sTestContact3Uri, SOURCE_TYPE_NETWORK,
+                    REQUEST_RESULT_FOUND, true, true);
+
+            // Verify the onCompleted is called
+            waitForResult(completeQueue);
+
+        } catch (Exception e) {
+            fail("testRequestResultInconclusive with command error failed: " + e);
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            capabilityQueue.clear();
+            completeQueue.clear();
+            numbers.clear();
+            pidfXmlList.clear();
+            removeTestContactFromEab();
+            removeUceRequestDisallowedStatus();
+        }
+
+        overrideCarrierConfig(null);
+    }
+
     private void setupTestImsService(RcsUceAdapter uceAdapter, boolean presencePublishEnabled,
             boolean presenceCapExchangeEnabled, boolean sipOptionsEnabled) throws Exception {
         // Trigger carrier config changed
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
index 33df595..9670611 100644
--- a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
@@ -442,8 +442,10 @@
 
         // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED) before
         // waiting for VCN Network.
-        final NetworkRequest cellNetworkReq =
-                new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+        final NetworkRequest cellNetworkReq = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build();
         final VcnTestNetworkCallback cellNetworkCb = new VcnTestNetworkCallback();
         mConnectivityManager.requestNetwork(cellNetworkReq, cellNetworkCb);
         final Network cellNetwork = cellNetworkCb.waitForAvailable();
@@ -563,8 +565,10 @@
 
         // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED) before
         // waiting for VCN Network.
-        final NetworkRequest cellNetworkReq =
-                new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+        final NetworkRequest cellNetworkReq = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build();
         final VcnTestNetworkCallback cellNetworkCb = new VcnTestNetworkCallback();
         mConnectivityManager.requestNetwork(cellNetworkReq, cellNetworkCb);
         final Network cellNetwork = cellNetworkCb.waitForAvailable();
@@ -662,8 +666,10 @@
 
         // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED) before
         // waiting for VCN Network.
-        final NetworkRequest cellNetworkReq =
-                new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+        final NetworkRequest cellNetworkReq = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build();
         final VcnTestNetworkCallback cellNetworkCb = new VcnTestNetworkCallback();
         mConnectivityManager.requestNetwork(cellNetworkReq, cellNetworkCb);
         final Network cellNetwork = cellNetworkCb.waitForAvailable();
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
index ecff1a2..2fa4399 100644
--- a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
@@ -249,14 +249,7 @@
         }
         Log.i(TAG, "Retrieved dialog description " + dialogDescription);
 
-        if (isCar()) {
-            // Make sure Voice recognizer's app label is present in dialog.
-            assertWithMessage(
-                    "Voice recognition service can blame the calling app name " + APP_LABEL
-                            + ", but does not find it.")
-                    .that(dialogDescription)
-                    .contains(APP_LABEL);
-        } else if (trustVoiceService) {
+        if (trustVoiceService) {
             // Check trust recognizer can blame calling apmic permission
             assertWithMessage(
                     "Trusted voice recognition service can blame the calling app name " + APP_LABEL
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java
index ad351a7..ec76300 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThrows;
@@ -36,6 +37,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SizeF;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
@@ -58,13 +61,20 @@
 
 import com.android.compatibility.common.util.WidgetTestUtils;
 
+import com.google.common.collect.Lists;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -116,11 +126,7 @@
                 .addItem(5 /* id */, new RemoteViews(PACKAGE_NAME, R.layout.textview_gravity))
                 .build();
 
-        Parcel parcel = Parcel.obtain();
-        items.writeToParcel(parcel, 0 /* flags */);
-        parcel.setDataPosition(0);
-
-        RemoteCollectionItems unparceled = RemoteCollectionItems.CREATOR.createFromParcel(parcel);
+        RemoteCollectionItems unparceled = parcelAndUnparcel(items);
         assertEquals(2, unparceled.getItemCount());
         assertEquals(3, unparceled.getItemId(0));
         assertEquals(5, unparceled.getItemId(1));
@@ -128,8 +134,78 @@
         assertEquals(R.layout.textview_gravity, unparceled.getItemView(1).getLayoutId());
         assertTrue(unparceled.hasStableIds());
         assertEquals(10, unparceled.getViewTypeCount());
+        assertNotNull(unparceled.getItemView(0).mApplication);
+        assertSame(
+                unparceled.getItemView(0).mApplication, unparceled.getItemView(1).mApplication);
 
-        parcel.recycle();
+        // Parcel and unparcel the RemoteViews and test again to ensure that the parent child
+        // relationship is correctly established from the Parcel constructor.
+        unparceled = parcelAndUnparcel(unparceled);
+        assertEquals(2, unparceled.getItemCount());
+        assertEquals(3, unparceled.getItemId(0));
+        assertEquals(5, unparceled.getItemId(1));
+        assertEquals(R.layout.textview_singleline, unparceled.getItemView(0).getLayoutId());
+        assertEquals(R.layout.textview_gravity, unparceled.getItemView(1).getLayoutId());
+        assertTrue(unparceled.hasStableIds());
+        assertEquals(10, unparceled.getViewTypeCount());
+        assertNotNull(unparceled.getItemView(0).mApplication);
+        assertSame(
+                unparceled.getItemView(0).mApplication, unparceled.getItemView(1).mApplication);
+    }
+
+    @Test
+    public void testParcelingAndUnparceling_afterAttaching() {
+        RemoteCollectionItems items = new RemoteCollectionItems.Builder()
+                .setHasStableIds(true)
+                .setViewTypeCount(10)
+                .addItem(3 /* id */, new RemoteViews(PACKAGE_NAME, R.layout.textview_singleline))
+                .addItem(5 /* id */, new RemoteViews(PACKAGE_NAME, R.layout.textview_gravity))
+                .build();
+
+        RemoteViews parent = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+        parent.setRemoteAdapter(R.id.listview_default, items);
+
+        RemoteCollectionItems unparceled = parcelAndUnparcel(items);
+        assertEquals(2, unparceled.getItemCount());
+        assertEquals(3, unparceled.getItemId(0));
+        assertEquals(5, unparceled.getItemId(1));
+        assertEquals(R.layout.textview_singleline, unparceled.getItemView(0).getLayoutId());
+        assertEquals(R.layout.textview_gravity, unparceled.getItemView(1).getLayoutId());
+        assertTrue(unparceled.hasStableIds());
+        assertEquals(10, unparceled.getViewTypeCount());
+        assertNotNull(unparceled.getItemView(0).mApplication);
+        assertSame(
+                unparceled.getItemView(0).mApplication, unparceled.getItemView(1).mApplication);
+    }
+
+    @Test
+    public void testParcelingAndUnparceling_multiplePackages() {
+        Optional<String> otherPackageName = getAnotherPackageName();
+        if (!otherPackageName.isPresent()) return;
+        RemoteCollectionItems items = new RemoteCollectionItems.Builder()
+                .setHasStableIds(true)
+                .setViewTypeCount(10)
+                .addItem(3 /* id */, new RemoteViews(PACKAGE_NAME, R.layout.textview_singleline))
+                .addItem(
+                    5 /* id */,
+                    new RemoteViews(otherPackageName.get(), R.layout.textview_gravity))
+                .build();
+
+        RemoteViews parent = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+        parent.setRemoteAdapter(R.id.listview_default, items);
+
+        RemoteCollectionItems unparceled = parcelAndUnparcel(items);
+        assertEquals(2, unparceled.getItemCount());
+        assertEquals(3, unparceled.getItemId(0));
+        assertEquals(5, unparceled.getItemId(1));
+        assertEquals(R.layout.textview_singleline, unparceled.getItemView(0).getLayoutId());
+        assertEquals(R.layout.textview_gravity, unparceled.getItemView(1).getLayoutId());
+        assertTrue(unparceled.hasStableIds());
+        assertEquals(10, unparceled.getViewTypeCount());
+        assertNotNull(unparceled.getItemView(0).mApplication);
+        assertNotNull(unparceled.getItemView(1).mApplication);
+        assertEquals(PACKAGE_NAME, unparceled.getItemView(0).mApplication.packageName);
+        assertEquals(otherPackageName.get(), unparceled.getItemView(1).mApplication.packageName);
     }
 
     @Test
@@ -234,6 +310,129 @@
     }
 
     @Test
+    public void testSerializationSize_largeCollection() {
+        RemoteCollectionItems items = createSampleCollectionItems(/* size= */ 100);
+
+        int dataSize = parcelAndRun(items, Parcel::dataSize);
+
+        // 7,408 when test was written.
+        assertLessThan(10_000, dataSize);
+    }
+
+    @Test
+    public void testSerializationSize_largeCollection_multiPackage() {
+        Optional<String> otherPackageName = getAnotherPackageName();
+        if (!otherPackageName.isPresent()) return;
+        RemoteCollectionItems items =
+                createSampleMultiPackageCollectionItems(/* size= */ 100, otherPackageName.get());
+
+        int dataSize = parcelAndRun(items, Parcel::dataSize);
+
+        // 9,140 when test was written.
+        assertLessThan(12_000, dataSize);
+    }
+
+    @Test
+    public void testSerializationSize_extraLargeCollection() {
+        RemoteCollectionItems items = createSampleCollectionItems(/* size= */ 1000);
+
+        int dataSize = parcelAndRun(items, Parcel::dataSize);
+
+        // 50,608 when test was written.
+        assertLessThan(70_000, dataSize);
+    }
+
+    @Test
+    public void testSerializationSize_largeCollectionInLandPortRemoteViews() {
+        RemoteViews landscape = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+        landscape.setRemoteAdapter(
+                R.id.listview_default,
+                createSampleCollectionItems(/* size= */ 100));
+        RemoteViews portrait = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+        landscape.setRemoteAdapter(
+                R.id.listview_default,
+                createSampleCollectionItems(/* size= */ 100));
+
+        RemoteViews joinedRemoteViews = new RemoteViews(landscape, portrait);
+
+        int dataSize = parcelAndRun(joinedRemoteViews, Parcel::dataSize);
+
+        // 12,336 when test was written.
+        assertLessThan(15_000, dataSize);
+    }
+
+    @Test
+    public void testSerializationSize_largeCollectionInLandPortRemoteViews_multiPackage() {
+        Optional<String> otherPackage = getAnotherPackageName();
+        if (!otherPackage.isPresent()) return;
+        RemoteViews landscape = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+        landscape.setRemoteAdapter(
+                R.id.listview_default,
+                createSampleMultiPackageCollectionItems(/* size= */ 100, otherPackage.get()));
+        RemoteViews portrait = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+        landscape.setRemoteAdapter(
+                R.id.listview_default,
+                createSampleMultiPackageCollectionItems(/* size= */ 100, otherPackage.get()));
+
+        RemoteViews joinedRemoteViews = new RemoteViews(landscape, portrait);
+
+        int dataSize = parcelAndRun(joinedRemoteViews, Parcel::dataSize);
+
+        // 14,068 when test was written.
+        assertLessThan(20_000, dataSize);
+    }
+
+    @Test
+    public void testSerializationSize_largeCollectionInSizedRemoteViews() {
+        List<SizeF> sizes =
+                Lists.newArrayList(
+                        new SizeF(50, 50),
+                        new SizeF(50, 100),
+                        new SizeF(100, 100),
+                        new SizeF(200, 100));
+        Map<SizeF, RemoteViews> sizeToRemoteViews =
+                sizes.stream().collect(Collectors.toMap(Function.identity(), ignored -> {
+                    RemoteCollectionItems items = createSampleCollectionItems(/* size= */ 100);
+                    RemoteViews views = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+                    views.setRemoteAdapter(R.id.listview_default, items);
+                    return views;
+                }));
+        RemoteViews joinedRemoteViews = new RemoteViews(sizeToRemoteViews);
+
+        int dataSize = parcelAndRun(joinedRemoteViews, Parcel::dataSize);
+
+        // 22,100 when test was written.
+        assertLessThan(30_000, dataSize);
+    }
+
+    @Test
+    public void testSerializationSize_largeCollectionInSizedRemoteViews_multiPackage() {
+        Optional<String> otherPackage = getAnotherPackageName();
+        if (!otherPackage.isPresent()) return;
+        List<SizeF> sizes =
+                Lists.newArrayList(
+                        new SizeF(50, 50),
+                        new SizeF(50, 100),
+                        new SizeF(100, 100),
+                        new SizeF(200, 100));
+        Map<SizeF, RemoteViews> sizeToRemoteViews =
+                sizes.stream().collect(Collectors.toMap(Function.identity(), ignored -> {
+                    RemoteCollectionItems items =
+                            createSampleMultiPackageCollectionItems(
+                                    /* size= */ 100, otherPackage.get());
+                    RemoteViews views = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
+                    views.setRemoteAdapter(R.id.listview_default, items);
+                    return views;
+                }));
+        RemoteViews joinedRemoteViews = new RemoteViews(sizeToRemoteViews);
+
+        int dataSize = parcelAndRun(joinedRemoteViews, Parcel::dataSize);
+
+        // 23,832 when test was written.
+        assertLessThan(30_000, dataSize);
+    }
+
+    @Test
     public void testSetRemoteAdapter_emptyCollection() {
         RemoteCollectionItems items = new RemoteCollectionItems.Builder().build();
         mRemoteViews.setRemoteAdapter(R.id.remoteView_list, items);
@@ -316,7 +515,7 @@
                 .addItem(12 /* id= */, item2)
                 .build();
 
-        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, items);
+        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, parcelAndUnparcel(items));
         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule,
                 () -> mRemoteViews.reapply(mActivity, mView), true);
 
@@ -458,7 +657,7 @@
                 .setViewTypeCount(2)
                 .build();
 
-        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, items);
+        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, parcelAndUnparcel(items));
         runOnMainAndDrawSync(mActivityRule, listView, () -> mRemoteViews.reapply(mActivity, mView));
 
         Adapter initialAdapter = listView.getAdapter();
@@ -469,7 +668,7 @@
                 .addItem(10 /* id= */, new RemoteViews(PACKAGE_NAME, R.layout.listitemfixed_layout))
                 .setViewTypeCount(2)
                 .build();
-        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, items);
+        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, parcelAndUnparcel(items));
         runOnMainAndDrawSync(mActivityRule, listView, () -> mRemoteViews.reapply(mActivity, mView));
 
         // The adapter should have been kept, and the second item should have maintained its view
@@ -489,7 +688,7 @@
                 .addItem(10 /* id= */, new RemoteViews(PACKAGE_NAME, R.layout.listitemfixed_layout))
                 .build();
 
-        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, items);
+        mRemoteViews.setRemoteAdapter(R.id.remoteView_list, parcelAndUnparcel(items));
         runOnMainAndDrawSync(mActivityRule, listView, () -> mRemoteViews.reapply(mActivity, mView));
 
         Adapter initialAdapter = listView.getAdapter();
@@ -540,7 +739,7 @@
                 mGridView, () -> {
                     mListView.setVisibility(View.GONE);
                     mGridView.setVisibility(View.VISIBLE);
-                    mRemoteViews.setRemoteAdapter(R.id.remoteView_grid, items);
+                    mRemoteViews.setRemoteAdapter(R.id.remoteView_grid, parcelAndUnparcel(items));
                     mRemoteViews.reapply(mActivity, mView);
                 });
 
@@ -624,6 +823,27 @@
         assertEquals(11, adapter.getItemId(1));
     }
 
+    private static RemoteCollectionItems parcelAndUnparcel(RemoteCollectionItems items) {
+        return parcelAndRun(items, RemoteCollectionItems.CREATOR::createFromParcel);
+    }
+
+    private static <T> T parcelAndRun(Parcelable parcelable, Function<Parcel, T> function) {
+        Parcel parcel = Parcel.obtain();
+        parcelable.writeToParcel(parcel, /* flags= */ 0);
+        parcel.setDataPosition(0);
+        try {
+            return function.apply(parcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    private static void assertLessThan(int expected, int actual) {
+        if (actual >= expected) {
+            fail("Expected to be less than " + expected + ", but was " + actual);
+        }
+    }
+
     private static final class MockBroadcastReceiver extends BroadcastReceiver {
 
         Intent mIntent;
@@ -666,4 +886,35 @@
         }
     }
 
+    private static RemoteCollectionItems createSampleCollectionItems(int size) {
+        RemoteCollectionItems.Builder builder = new RemoteCollectionItems.Builder();
+        for (int i = 0; i < size; i++) {
+            builder.addItem(i,
+                    new RemoteViews(PACKAGE_NAME, R.layout.textview_singleline));
+        }
+        return builder.build();
+    }
+
+    private static RemoteCollectionItems createSampleMultiPackageCollectionItems(
+            int size, String otherPackage) {
+        RemoteCollectionItems.Builder builder = new RemoteCollectionItems.Builder();
+        for (int i = 0; i < size; i++) {
+            String packageName = i % 2 == 0 ? PACKAGE_NAME : otherPackage;
+            builder.addItem(i, new RemoteViews(packageName, R.layout.textview_singleline));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Returns a different package on the device that can be used for testing multi-package
+     * collections.
+     */
+    private Optional<String> getAnotherPackageName() {
+        return mActivity.getPackageManager()
+                .getInstalledApplications(/* flags= */ 0)
+                .stream()
+                .filter(info -> !PACKAGE_NAME.equals(info.packageName))
+                .findFirst()
+                .map(info -> info.packageName);
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
index fdbd6b4..0cadd73 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
@@ -55,8 +55,11 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.DisplayMetrics;
+import android.util.SizeF;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
@@ -101,6 +104,7 @@
 import com.android.compatibility.common.util.WidgetTestUtils;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -112,6 +116,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
 
 /**
  * Test {@link RemoteViews}.
@@ -499,12 +507,9 @@
     public void testWriteToParcel() {
         mRemoteViews.setTextViewText(R.id.remoteView_text, "This is content");
         mRemoteViews.setViewVisibility(R.id.remoteView_frame, View.GONE);
-        Parcel p = Parcel.obtain();
-        mRemoteViews.writeToParcel(p, 0);
-        p.setDataPosition(0);
 
         // the package and layout are successfully written into parcel
-        mRemoteViews = RemoteViews.CREATOR.createFromParcel(p);
+        mRemoteViews = parcelAndUnparcel(mRemoteViews);
         View result = mRemoteViews.apply(mContext, null);
         assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
         assertEquals(R.layout.remoteviews_good, mRemoteViews.getLayoutId());
@@ -512,12 +517,8 @@
                 .getText().toString());
         assertEquals(View.GONE, result.findViewById(R.id.remoteView_frame).getVisibility());
 
-        p = Parcel.obtain();
-
         // currently the flag is not used
-        mRemoteViews.writeToParcel(p, -1);
-
-        p.recycle();
+        parcelAndUnparcel(mRemoteViews, /* flags= */ -1);
 
         RemoteViews[] remote = RemoteViews.CREATOR.newArray(1);
         assertNotNull(remote);
@@ -529,6 +530,51 @@
         mRemoteViews.writeToParcel(null, 0);
     }
 
+    @Test
+    public void testWriteToParcel_landscapePortrait() {
+        RemoteViews landscape = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
+        landscape.setTextViewText(R.id.remoteView_text, "Hello world");
+        RemoteViews portrait = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
+        portrait.setTextViewText(R.id.remoteView_text, "Hello world");
+        int landscapeParcelledSize = getParcelledSize(landscape);
+        mRemoteViews = new RemoteViews(landscape, portrait);
+
+        mRemoteViews = parcelAndUnparcel(mRemoteViews);
+        assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
+        View result = mRemoteViews.apply(mContext, null);
+        assertEquals("Hello world", ((TextView) result.findViewById(R.id.remoteView_text))
+                .getText().toString());
+
+        // The ApplicationInfo should only have been written once, so this should be much smaller
+        // than twice the size of parcelling one RemoteViews.
+        assertLessThan(landscapeParcelledSize * 2, getParcelledSize(mRemoteViews));
+    }
+
+    @Test
+    public void testWriteToParcel_sizeMap() {
+        List<SizeF> sizes =
+                Arrays.asList(new SizeF(50, 50), new SizeF(100, 100), new SizeF(100, 200));
+        Map<SizeF, RemoteViews> sizeMap = new ArrayMap<>();
+        int singelParcelledSize = 0;
+        for (SizeF size : sizes) {
+            RemoteViews views = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
+            views.setTextViewText(R.id.remoteView_text, "Hello world");
+            sizeMap.put(size, views);
+            singelParcelledSize = getParcelledSize(views);
+        }
+        mRemoteViews = new RemoteViews(sizeMap);
+
+        mRemoteViews = parcelAndUnparcel(mRemoteViews);
+        assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
+        View result = mRemoteViews.apply(mContext, null);
+        assertEquals("Hello world", ((TextView) result.findViewById(R.id.remoteView_text))
+                .getText().toString());
+
+        // The ApplicationInfo should only have been written once, so this should be much smaller
+        // than thrice the size of parcelling one RemoteViews.
+        assertLessThan(singelParcelledSize * 3, getParcelledSize(mRemoteViews));
+    }
+
     @Test(expected=NegativeArraySizeException.class)
     public void testCreateNegativeSizedArray() {
         RemoteViews.CREATOR.newArray(-1);
@@ -1940,4 +1986,41 @@
         runShellCommand("cmd uimode night " + (nightMode ? "yes" : "no"));
         return previousMode;
     }
+
+    private static RemoteViews parcelAndUnparcel(RemoteViews views) {
+        return parcelAndUnparcel(views, /* flags= */ 0);
+    }
+
+    /**
+     * Returns the result of writing {@code views} to a {@link Parcel} and then creating a new
+     * {@link RemoteViews} from the parcel.
+     */
+    private static RemoteViews parcelAndUnparcel(RemoteViews views, int flags) {
+        return parcelAndRun(views, flags, RemoteViews.CREATOR::createFromParcel);
+    }
+
+    /** Returns the data size from writing {@code parcelable} to a {@link Parcel}. */
+    private static int getParcelledSize(Parcelable parcelable) {
+        return parcelAndRun(parcelable, /* flags= */ 0, Parcel::dataSize);
+    }
+
+    private static <T> T parcelAndRun(
+            Parcelable parcelable,
+            int flags,
+            Function<Parcel, T> function) {
+        Parcel parcel = Parcel.obtain();
+        parcelable.writeToParcel(parcel, flags);
+        parcel.setDataPosition(0);
+        try {
+            return function.apply(parcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    private static void assertLessThan(int expected, int actual) {
+        if (actual >= expected) {
+            Assert.fail("Expected to be less than " + expected + ", but was " + actual);
+        }
+    }
 }
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index 30d07ff..d4f548c 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -95,4 +95,8 @@
 
     <!-- b/183653612: CtsTransitionTestCases -->
     <option name="compatibility:exclude-filter" value="CtsTransitionTestCases" />
+
+    <!-- b/198226244 -->
+    <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.CodecEncoderPerformanceTest#testPerformanceOfHardwareVideoEncoders" />
+
 </configuration>