Merge "CTS Tests for GnssMetrics"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e09cccd..7b9c427 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -37,6 +37,7 @@
                       tests/tests/widget/
                       common/device-side/util/
                       hostsidetests/stagedinstall/
+                      hostsidetests/userspacereboot/
                       tests/tests/packageinstaller/atomicinstall/
 
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
diff --git a/apps/CameraITS/tests/scene2_e/test_continuous_picture.py b/apps/CameraITS/tests/scene2_e/test_continuous_picture.py
new file mode 100644
index 0000000..3f64e3a
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_e/test_continuous_picture.py
@@ -0,0 +1,98 @@
+# Copyright 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.
+
+import os.path
+import its.caps
+import its.device
+import its.image
+import its.objects
+
+CONTINUOUS_PICTURE_MODE = 4
+CONVERGED_3A = [[2, 2, 2], [2, 5, 2], [2, 6, 2]]  # [AE, AF, AWB]
+# AE_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED,
+#             4: FLASH_REQ, 5: PRECAPTURE}
+# AF_STATES: {0: INACTIVE, 1: PASSIVE_SCAN, 2: PASSIVE_FOCUSED,
+#             3: PASSIVE_UNFOCUSED, 4: ACTIVE_SCAN, 5: FOCUS_LOCKED,
+#             6: NOT_FOCUSED_LOCKED}
+# AWB_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED}
+NAME = os.path.basename(__file__).split('.')[0]
+NUM_FRAMES = 50
+W, H = 640, 480
+
+
+def capture_frames(cam, debug):
+    """Capture frames."""
+    cap_data_list = []
+    req = its.objects.auto_capture_request()
+    req['android.control.afMode'] = CONTINUOUS_PICTURE_MODE
+    fmt = {'format': 'yuv', 'width': W, 'height': H}
+    caps = cam.do_capture([req]*NUM_FRAMES, fmt)
+
+    # extract frame metadata and frame
+    for i, cap in enumerate(caps):
+        cap_data = {}
+        md = cap['metadata']
+        exp = md['android.sensor.exposureTime']
+        iso = md['android.sensor.sensitivity']
+        fd = md['android.lens.focalLength']
+        ae_state = md['android.control.aeState']
+        af_state = md['android.control.afState']
+        awb_state = md['android.control.awbState']
+        fd_str = 'infinity'
+        if fd != 0.0:
+            fd_str = str(round(1.0E2/fd, 2)) + 'cm'
+        img = its.image.convert_capture_to_rgb_image(cap)
+        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+        g = its.image.compute_image_means(tile)[1]
+        print '%d, iso: %d, exp: %.2fms, fd: %s, avg: %.3f' % (
+                i, iso, exp*1E-6, fd_str, g),
+        print '[ae,af,awb]: [%d,%d,%d]' % (ae_state, af_state, awb_state)
+        cap_data['exp'] = exp
+        cap_data['iso'] = iso
+        cap_data['fd'] = fd
+        cap_data['3a_state'] = [ae_state, af_state, awb_state]
+        cap_data['avg'] = g
+        cap_data_list.append(cap_data)
+        if debug:
+            its.image.write_image(img, '%s_%d.jpg' % (NAME, i))
+    return cap_data_list
+
+
+def main():
+    """Test 3A converges in CONTINUOUS_PICTURE mode.
+
+    Set camera into CONTINUOUS_PICTURE mode and do NUM_FRAMES capture.
+    By the end of NUM_FRAMES capture, 3A should be in converged state.
+    """
+
+    # check for skip conditions and do 3a up front
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+        props = cam.override_with_hidden_physical_camera_props(props)
+        its.caps.skip_unless(its.caps.continuous_picture(props) and
+                             its.caps.read_3a(props))
+        debug = its.caps.debug_mode()
+        cam.do_3a()
+
+        # ensure 3a settles in CONTINUOUS_PICTURE mode with no scene change
+        cap_data = capture_frames(cam, debug)
+        final_3a = cap_data[NUM_FRAMES-1]['3a_state']
+        msg = '\n Last frame [ae, af, awb] state: [%d, %d, %d]' % (
+                final_3a[0], final_3a[1], final_3a[2])
+        msg += '\n Converged states:' + str(CONVERGED_3A)
+        assert final_3a in CONVERGED_3A, msg
+
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
index 976bee0..75b7a5d 100644
--- a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
+++ b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
@@ -30,6 +30,7 @@
 ALIGN_TOL = 0.01  # multiplied by sensor diagonal to convert to pixels
 CIRCLE_RTOL = 0.1
 GYRO_REFERENCE = 1
+LENS_FACING_BACK = 1  # 0: FRONT, 1: BACK, 2: EXTERNAL
 UNDEFINED_REFERENCE = 2
 NAME = os.path.basename(__file__).split('.')[0]
 TRANS_REF_MATRIX = np.array([0, 0, 0])
@@ -333,7 +334,7 @@
                              its.caps.logical_multi_camera(props))
 
         # Convert chart_distance for lens facing back
-        if props['android.lens.facing']:
+        if props['android.lens.facing'] == LENS_FACING_BACK:
             # API spec defines +z is pointing out from screen
             print 'lens facing BACK'
             chart_distance *= -1
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 20fd98b..021a324 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -113,7 +113,8 @@
                 ['test_num_faces', 30]
         ],
         'scene2_e': [
-                ['test_num_faces', 30]
+                ['test_num_faces', 30],
+                ['test_continuous_picture', 30]
         ],
         'scene3': [],
         'scene4': [],
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
index 272a9fc..a0a715f 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
@@ -32,6 +32,8 @@
             android:orientation="vertical"
         >
 
+            <include layout="@layout/audio_refmic_layout"/>
+
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
index 435f5a7..a9aeafa 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
@@ -30,70 +30,72 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:scrollbars="vertical"
-            android:gravity="bottom"
-            android:id="@+id/info_text"
-            android:text="@string/audio_frequency_speaker_instructions"/>
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-            <Button
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:id="@+id/audio_frequency_speaker_mic_ready_btn"
-                android:text="@string/audio_frequency_speaker_mic_ready_btn"
-                android:nextFocusForward="@+id/audio_frequency_speaker_test_btn"
-                android:nextFocusDown="@+id/audio_frequency_speaker_test_btn"
-                android:nextFocusRight="@+id/audio_frequency_speaker_test_btn" />
+            <include layout="@layout/audio_refmic_layout"/>
 
             <TextView
-                android:layout_width="wrap_content"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/audio_frequency_speaker_usb_status"
-                android:id="@+id/audio_frequency_speaker_usb_status"/>
+                android:scrollbars="vertical"
+                android:gravity="bottom"
+                android:id="@+id/info_text"
+                android:text="@string/audio_frequency_speaker_instructions"/>
 
             <LinearLayout
-                android:orientation="vertical"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent">
-
-                <LinearLayout
-                    android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+                <Button
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:orientation="horizontal"
-                    android:id="@+id/audio_frequency_speaker_layout">
-                    <Button
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:text="@string/audio_frequency_speaker_test_btn"
-                        android:id="@+id/audio_frequency_speaker_test_btn"
-                        android:nextFocusForward="@+id/pass_button"
-                        android:nextFocusUp="@+id/audio_frequency_speaker_mic_ready_btn"
-                        android:nextFocusDown="@+id/pass_button"
-                        android:nextFocusLeft="@+id/audio_frequency_speaker_mic_ready_btn"
-                        android:nextFocusRight="@+id/pass_button" />
-
-                    <ProgressBar
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:id="@+id/audio_frequency_speaker_progress_bar"/>
-                </LinearLayout>
+                    android:id="@+id/audio_frequency_speaker_mic_ready_btn"
+                    android:text="@string/audio_frequency_speaker_mic_ready_btn"
+                    android:nextFocusForward="@+id/audio_frequency_speaker_test_btn"
+                    android:nextFocusDown="@+id/audio_frequency_speaker_test_btn"
+                    android:nextFocusRight="@+id/audio_frequency_speaker_test_btn" />
 
                 <TextView
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/audio_frequency_speaker_results_text"
-                    android:id="@+id/audio_frequency_speaker_results_text"/>
+                    android:text="@string/audio_frequency_speaker_usb_status"
+                    android:id="@+id/audio_frequency_speaker_usb_status"/>
 
+                <LinearLayout
+                    android:orientation="vertical"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="horizontal"
+                        android:id="@+id/audio_frequency_speaker_layout">
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/audio_frequency_speaker_test_btn"
+                            android:id="@+id/audio_frequency_speaker_test_btn"
+                            android:nextFocusForward="@+id/pass_button"
+                            android:nextFocusUp="@+id/audio_frequency_speaker_mic_ready_btn"
+                            android:nextFocusDown="@+id/pass_button"
+                            android:nextFocusLeft="@+id/audio_frequency_speaker_mic_ready_btn"
+                            android:nextFocusRight="@+id/pass_button" />
+
+                        <ProgressBar
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:id="@+id/audio_frequency_speaker_progress_bar"/>
+                    </LinearLayout>
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/audio_frequency_speaker_results_text"
+                        android:id="@+id/audio_frequency_speaker_results_text"/>
+
+                </LinearLayout>
             </LinearLayout>
-        </LinearLayout>
 
-        <include layout="@layout/pass_fail_buttons"/>
+            <include layout="@layout/pass_fail_buttons"/>
         </LinearLayout>
       </ScrollView>
 
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
index d02ef0b..8032f93 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
@@ -23,6 +23,8 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
+            <include layout="@layout/audio_refmic_layout"/>
+
             <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml
index c6cd0cd..1ccd947 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_voice_recognition_activity.xml
@@ -31,6 +31,8 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
+            <include layout="@layout/audio_refmic_layout"/>
+
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_refmic_layout.xml b/apps/CtsVerifier/res/layout/audio_refmic_layout.xml
new file mode 100644
index 0000000..bd806a1
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_refmic_layout.xml
@@ -0,0 +1,38 @@
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/refmic_test_question"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:soundEffectsEnabled="false"
+            android:text="@string/refmic_test_yes"
+            android:id="@+id/refmic_tests_yes_btn" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:soundEffectsEnabled="false"
+            android:text="@string/refmic_test_no"
+            android:id="@+id/refmic_tests_no_btn" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:soundEffectsEnabled="false"
+            android:text="@string/refmic_test_info"
+            android:id="@+id/refmic_test_info_btn" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/uap_attribs_panel.xml b/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
index 6c33d9f..b9c5d3e 100644
--- a/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
@@ -9,6 +9,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
+    <include layout="@layout/uap_usb_confirm"/>
+
     <include layout="@layout/uap_profile_header"/>
 
     <LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_buttons_panel.xml b/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
index 4fdc757..355bfb6 100644
--- a/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
@@ -9,6 +9,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
+    <include layout="@layout/uap_usb_confirm"/>
+
     <include layout="@layout/uap_profile_header"/>
 
     <LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_play_panel.xml b/apps/CtsVerifier/res/layout/uap_play_panel.xml
index 2faa50c..30ef884 100644
--- a/apps/CtsVerifier/res/layout/uap_play_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_play_panel.xml
@@ -9,6 +9,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
+    <include layout="@layout/uap_usb_confirm"/>
+
     <include layout="@layout/uap_profile_header"/>
 
     <LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_record_panel.xml b/apps/CtsVerifier/res/layout/uap_record_panel.xml
index 2deb738..a5bfcfc 100644
--- a/apps/CtsVerifier/res/layout/uap_record_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_record_panel.xml
@@ -9,6 +9,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
+    <include layout="@layout/uap_usb_confirm"/>
+
     <include layout="@layout/uap_profile_header"/>
 
     <LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_usb_confirm.xml b/apps/CtsVerifier/res/layout/uap_usb_confirm.xml
new file mode 100644
index 0000000..ea73b88
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/uap_usb_confirm.xml
@@ -0,0 +1,38 @@
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/uap_test_question"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:soundEffectsEnabled="false"
+            android:text="@string/uap_test_yes"
+            android:id="@+id/uap_tests_yes_btn" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:soundEffectsEnabled="false"
+            android:text="@string/uap_test_no"
+            android:id="@+id/uap_tests_no_btn" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:soundEffectsEnabled="false"
+            android:text="@string/uap_test_info"
+            android:id="@+id/uap_test_info_btn" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 3df427f..4f9cc0a9 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2125,8 +2125,6 @@
     <string name="nls_unblock_app">Please unblock the linked application and return here.</string>
     <string name="nls_block_channel">Please block the linked notification channel and return here.</string>
     <string name="nls_block_group">Please block the linked notification channel group and return here.</string>
-    <string name="nls_cannot_enable_service">Please make sure you cannot enable
-        \"Notification Listener for CTS Verifier\" and return here.</string>
     <string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\"
         under Apps > Gear Icon > Special Access > Notification Access and return here.</string>
     <string name="nls_start_settings">Launch Settings</string>
@@ -2158,11 +2156,10 @@
     <string name="nas_note_missed_enqueued">Check that notification was not enqueued.</string>
     <string name="cp_test">Condition Provider test</string>
     <string name="cp_service_name">Condition Provider for CTS Verifier</string>
-    <string name="cp_info">This test checks that on non-low ram a ConditionProviderService can be enabled
+    <string name="cp_info">This test checks that ConditionProviderService can be enabled
         and disabled, and that once enabled the service is able to create, query, edit, and delete
-        automatic zen rules. On low ram devices condition providers should not be bound.
+        automatic zen rules.
     </string>
-    <string name="cp_cannot_enable_service">Please make sure you cannot enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
     <string name="cp_enable_service">Please enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
     <string name="cp_disable_service">Please disable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
     <string name="cp_start_settings">Launch Settings</string>
@@ -5413,4 +5410,30 @@
     6. Verify there is an action allowing user to clear/delete app. \n\n
     7. Click Pass button if checks in step 5 and 6 passed, otherwise click Fail button.
     </string>
+
+    <string name="uap_test_no">No</string>
+    <string name="uap_test_yes">Yes</string>
+    <string name="uap_test_info">Info</string>
+    <string name="uap_test_question">Does this device allow for the connectiono of a USB reference microphone?</string>
+    <string name="uap_mic_dlg_caption">USB Host Mode Audio Required</string>
+    <string name="uap_mic_dlg_text">This test requires a USB audio peripheral to be connected to the device.
+    If the device under test does not support USB Host Mode Audio (either because it does not have a
+    USB port, or USB Host Mode Audio has been removed from the OS) you can be granted a provisional
+    pass on this test by pressing the \"No\" button and indicating \"Test Pass\" at the bottom.\n
+    Note: Handheld devices supporting USB host mode MUST support USB audio class (CDD 7.7 .2/H-1-1)\n
+    Note: Devices declaring feature android.hardware.audio.pro MUST implement USB host mode (CDD 5.10 C-1-3) and if they omit a 4 conductor 3.5mm audio jack MUST support USB audio class (CDD 5.10 C-3-1)
+    </string>
+
+    <string name="refmic_test_no">No</string>
+    <string name="refmic_test_yes">Yes</string>
+    <string name="refmic_test_info">Info</string>
+    <string name="refmic_test_question">Does this device allow for the connection of a USB reference microphone?</string>
+    <string name="ref_mic_dlg_caption">Reference Mic Required</string>
+    <string name="ref_mic_dlg_text">This test requires a USB Reference Mic to be connected to the device.
+    If the device under test does not support USB Host Mode Audio (either because it does not have a
+    USB port, or USB Host Mode Audio has been removed from the OS) you can be granted a provisional
+    pass on this test by pressing the \"No\" button and indicating \"Test Pass\" at the bottom.\n
+    Note: Handheld devices supporting USB host mode MUST support USB audio class (CDD 7.7 .2/H-1-1)\n
+    Note: Devices declaring feature android.hardware.audio.pro MUST implement USB host mode (CDD 5.10 C-1-3) and if they omit a 4 conductor 3.5mm audio jack MUST support USB audio class (CDD 5.10 C-3-1)
+    </string>
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
index 3b97e37..ffc5f41 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
@@ -53,6 +53,65 @@
 
     public int mMaxLevel = 0;
 
+    private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+    //
+    // Common UI Handling
+    protected void connectRefMicUI() {
+        findViewById(R.id.refmic_tests_yes_btn).setOnClickListener(mBtnClickListener);
+        findViewById(R.id.refmic_tests_no_btn).setOnClickListener(mBtnClickListener);
+        findViewById(R.id.refmic_test_info_btn).setOnClickListener(mBtnClickListener);
+
+        enableTestUI(false);
+    }
+
+    private void showRefMicInfoDialog() {
+        new AlertDialog.Builder(this)
+                .setTitle(R.string.ref_mic_dlg_caption)
+                .setMessage(R.string.ref_mic_dlg_text)
+                .setPositiveButton(R.string.audio_general_ok, null)
+                .show();
+    }
+
+    private class OnBtnClickListener implements OnClickListener {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.refmic_tests_yes_btn:
+                    recordRefMicStatus(true);
+                    enableTestUI(true);
+                    // disable test button so that they will now run the test(s)
+                    getPassButton().setEnabled(false);
+                    break;
+
+                case R.id.refmic_tests_no_btn:
+                    recordRefMicStatus(false);
+                    enableTestUI(false);
+                    // Allow the user to "pass" the test.
+                    getPassButton().setEnabled(true);
+                    break;
+
+                case R.id.refmic_test_info_btn:
+                    showRefMicInfoDialog();
+                    break;
+            }
+        }
+    }
+
+    private void recordRefMicStatus(boolean has) {
+        getReportLog().addValue(
+                "User reported ref mic availability: ",
+                has ? 1.0 : 0,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+    }
+
+    //
+    // Overrides
+    //
+    void enableTestUI(boolean enable) {
+
+    }
+
     public void setMaxLevel() {
         AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
         mMaxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index 129fb72..e296f86 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -238,7 +238,22 @@
                 5.0, -50.0,      /* start top,bottom value */
                 5.0, -50.0      /* stop top,bottom value */);
 
+        connectRefMicUI();
     }
+
+    //
+    // Overrides
+    //
+    void enableTestUI(boolean enable) {
+        mButtonTestNoise.setEnabled(enable);
+        mButtonPlayNoise.setEnabled(enable);
+
+        mButtonTestUsbBackground.setEnabled(enable);
+
+        mButtonTestUsbNoise.setEnabled(enable);
+        mButtonPlayUsbNoise.setEnabled(enable);
+    }
+
     private void playerToggleButton(int buttonId) {
         if (playerIsPlaying()) {
             playerStopAll();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
index 294e48d..f954b0c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
@@ -205,6 +205,15 @@
                 5.0, -50.0,      /* start top,bottom value */
                 5.0, -50.0      /* stop top,bottom value */);
 
+        connectRefMicUI();
+    }
+
+    //
+    // Overrides
+    //
+    void enableTestUI(boolean enable) {
+        mLoopbackPlugReady.setEnabled(enable);
+        mTestButton.setEnabled(enable);
     }
 
     /**
@@ -212,8 +221,8 @@
      */
     private void enableLayout(boolean enable) {
         for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
-            View view = mLinearLayout.getChildAt(i);
-            view.setEnabled(enable);
+            mLoopbackPlugReady.setEnabled(enable);
+            mTestButton.setEnabled(enable);
         }
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
index 009dd58..fb8460b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -292,6 +292,24 @@
         mResultsMic =  new Results("mic_response", mBands);
         mResultsTone = new Results("tone_response", mBandsTone);
         mResultsBack = new Results("background_response", mBandsBack);
+
+        connectRefMicUI();
+    }
+
+    //
+    // Overrides
+    //
+    void enableTestUI(boolean enable) {
+        mButtonTestTone.setEnabled(enable);
+        mButtonPlayTone.setEnabled(enable);
+
+        mButtonTestNoise.setEnabled(enable);
+        mButtonPlayNoise.setEnabled(enable);
+
+        mButtonTestUsbBackground.setEnabled(enable);
+
+        mButtonTestUsbNoise.setEnabled(enable);
+        mButtonPlayUsbNoise.setEnabled(enable);
     }
 
     private void playerToggleButton(int buttonId, int sourceId) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
index 4017973..efe666a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
@@ -329,6 +329,23 @@
         mResultsMic = new Results("mic_response", BANDS_MIC);
         mResultsTone = new Results("tone_response", BANDS_TONE);
         mResultsBack = new Results("background_response", BANDS_BACKGROUND);
+        connectRefMicUI();
+    }
+
+    //
+    // Overrides
+    //
+    void enableTestUI(boolean enable) {
+        mButtonTestTone.setEnabled(enable);
+        mButtonPlayTone.setEnabled(enable);
+
+        mButtonTestNoise.setEnabled(enable);
+        mButtonPlayNoise.setEnabled(enable);
+
+        mButtonTestUsbBackground.setEnabled(enable);
+
+        mButtonTestUsbNoise.setEnabled(enable);
+        mButtonPlayUsbNoise.setEnabled(enable);
     }
 
     private void playerToggleButton(int buttonId, int sourceId) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
index fdb57fc..ebd2e6f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
@@ -108,10 +108,6 @@
     @Override
     protected List<InteractiveTestCase> createTestItems() {
         List<InteractiveTestCase> tests = new ArrayList<>();
-        if (supportsConditionProviders()) {
-            tests.add(new PassTest());
-            return tests;
-        }
         tests.add(new SetModeAllTest());
         tests.add(new SetModePriorityTest());
         tests.add(new TestAccessRingerModeDndOn());
@@ -136,12 +132,6 @@
         return tests;
     }
 
-    private boolean supportsConditionProviders() {
-        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        return !am.isLowRamDevice()
-                || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
-    }
-
     private int getVolumeDelta(int volume) {
         return 1;
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
index 140757d..8f0a9b0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
@@ -16,12 +16,18 @@
 
 package com.android.cts.verifier.audio;
 
+import android.app.AlertDialog;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
 import android.widget.TextView;
 
 import com.android.cts.verifier.audio.peripheralprofile.PeripheralProfile;
@@ -55,6 +61,68 @@
 
     private TextView mPeripheralNameTx;
 
+    private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+    //
+    // Common UI Handling
+    //
+    protected void connectUSBPeripheralUI() {
+        findViewById(R.id.uap_tests_yes_btn).setOnClickListener(mBtnClickListener);
+        findViewById(R.id.uap_tests_no_btn).setOnClickListener(mBtnClickListener);
+        findViewById(R.id.uap_test_info_btn).setOnClickListener(mBtnClickListener);
+
+        // Leave the default state in tact
+        // enableTestUI(false);
+    }
+
+    private void showUAPInfoDialog() {
+        new AlertDialog.Builder(this)
+                .setTitle(R.string.uap_mic_dlg_caption)
+                .setMessage(R.string.uap_mic_dlg_text)
+                .setPositiveButton(R.string.audio_general_ok, null)
+                .show();
+    }
+
+    private class OnBtnClickListener implements OnClickListener {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.uap_tests_yes_btn:
+                    recordUSBAudioStatus(true);
+                    enableTestUI(true);
+                    // disable test button so that they will now run the test(s)
+                    getPassButton().setEnabled(false);
+                    break;
+
+                case R.id.uap_tests_no_btn:
+                    recordUSBAudioStatus(false);
+                    enableTestUI(false);
+                    // Allow the user to "pass" the test.
+                    getPassButton().setEnabled(true);
+                    break;
+
+                case R.id.uap_test_info_btn:
+                    showUAPInfoDialog();
+                    break;
+            }
+        }
+    }
+
+    private void recordUSBAudioStatus(boolean has) {
+        getReportLog().addValue(
+                "User reported USB Host Audio Support: ",
+                has ? 1.0 : 0,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+    }
+
+    //
+    // Overrides
+    //
+    void enableTestUI(boolean enable) {
+
+    }
+
     public USBAudioPeripheralActivity(boolean mandatedRequired) {
         super();
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
index 5029160..23ed91a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
@@ -47,6 +47,8 @@
 
         setPassFailButtonClickListeners();
         setInfoResources(R.string.usbaudio_attribs_test, R.string.usbaudio_attribs_info, -1);
+
+        connectUSBPeripheralUI();
     }
 
     //
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
index de3ce7f..823f326 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
@@ -83,6 +83,8 @@
         setInfoResources(R.string.usbaudio_buttons_test, R.string.usbaudio_buttons_info, -1);
 
         showDisableAssistantDialog();
+
+        connectUSBPeripheralUI();
     }
 
     private void showButtonsState() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
index de9016a..4ae5ec3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
@@ -50,11 +50,17 @@
 
         setPassFailButtonClickListeners();
         setInfoResources(R.string.usbaudio_play_test, R.string.usbaudio_play_info, -1);
+
+        connectUSBPeripheralUI();
     }
 
     //
     // USBAudioPeripheralActivity
     // Headset not publicly available, violates CTS Verifier additional equipment guidelines.
+    void enableTestUI(boolean enable) {
+        mPlayBtn.setEnabled(enable);
+    }
+
     public void updateConnectStatus() {
         mPlayBtn.setEnabled(mIsPeripheralAttached);
         getPassButton().setEnabled(mIsPeripheralAttached);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
index 268201c..d51eac3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
@@ -139,11 +139,18 @@
 
         setPassFailButtonClickListeners();
         setInfoResources(R.string.usbaudio_record_test, R.string.usbaudio_record_info, -1);
+
+        connectUSBPeripheralUI();
     }
 
     //
     // USBAudioPeripheralActivity
     //
+    void enableTestUI(boolean enable) {
+        mRecordBtn.setEnabled(enable);
+        mRecordLoopbackBtn.setEnabled(enable);
+    }
+
     public void updateConnectStatus() {
         mRecordBtn.setEnabled(mIsPeripheralAttached);
         mRecordLoopbackBtn.setEnabled(mIsPeripheralAttached);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java
index 7730b17..473579f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/CredentialEnrolledTests.java
@@ -59,10 +59,18 @@
             final BiometricManager bm = getSystemService(BiometricManager.class);
 
             final int biometricResult = bm.canAuthenticate(Authenticators.BIOMETRIC_WEAK);
-            if (biometricResult != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
-                showToastAndLog("Unexpected result: " + biometricResult +
-                        ". Please make sure the device does not have a biometric enrolled");
-                return;
+            switch (biometricResult) {
+                case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
+                case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
+                    // OK
+                    break;
+                case BiometricManager.BIOMETRIC_SUCCESS:
+                    showToastAndLog("Unexpected result: " + biometricResult +
+                            ". Please make sure the device does not have a biometric enrolled");
+                    return;
+                default:
+                    showToastAndLog("Unexpected result: " + biometricResult);
+                    return;
             }
 
             final int credentialResult = bm.canAuthenticate(Authenticators.DEVICE_CREDENTIAL);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 8764dbe..8391816 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -20,12 +20,10 @@
 import static com.android.cts.verifier.notifications.MockListener.JSON_MATCHES_ZEN_FILTER;
 import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
 
-import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.ContentProviderOperation;
-import android.content.Context;
 import android.content.OperationApplicationException;
 import android.database.Cursor;
 import android.media.AudioAttributes;
@@ -94,37 +92,30 @@
 
     @Override
     protected List<InteractiveTestCase> createTestItems() {
-
         List<InteractiveTestCase> tests = new ArrayList<>(17);
-        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        if (am.isLowRamDevice()) {
-            tests.add(new CannotBeEnabledTest());
-            tests.add(new ServiceStoppedTest());
-        } else {
-            tests.add(new IsEnabledTest());
-            tests.add(new ServiceStartedTest());
-            tests.add(new InsertContactsTest());
-            tests.add(new NoneInterceptsAllMessagesTest());
-            tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
-            tests.add(new PriorityInterceptsSomeMessagesTest());
+        tests.add(new IsEnabledTest());
+        tests.add(new ServiceStartedTest());
+        tests.add(new InsertContactsTest());
+        tests.add(new NoneInterceptsAllMessagesTest());
+        tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
+        tests.add(new PriorityInterceptsSomeMessagesTest());
 
-            if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
-                // Tests targeting P and above:
-                tests.add(new PriorityInterceptsAlarmsTest());
-                tests.add(new PriorityInterceptsMediaSystemOtherTest());
-            }
-
-            tests.add(new AllInterceptsNothingMessagesTest());
-            tests.add(new AllInterceptsNothingDiffCategoriesTest());
-            tests.add(new DefaultOrderTest());
-            tests.add(new PriorityOrderTest());
-            tests.add(new InterruptionOrderTest());
-            tests.add(new AmbientBitsTest());
-            tests.add(new LookupUriOrderTest());
-            tests.add(new EmailOrderTest());
-            tests.add(new PhoneOrderTest());
-            tests.add(new DeleteContactsTest());
+        if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
+            // Tests targeting P and above:
+            tests.add(new PriorityInterceptsAlarmsTest());
+            tests.add(new PriorityInterceptsMediaSystemOtherTest());
         }
+
+        tests.add(new AllInterceptsNothingMessagesTest());
+        tests.add(new AllInterceptsNothingDiffCategoriesTest());
+        tests.add(new DefaultOrderTest());
+        tests.add(new PriorityOrderTest());
+        tests.add(new InterruptionOrderTest());
+        tests.add(new AmbientBitsTest());
+        tests.add(new LookupUriOrderTest());
+        tests.add(new EmailOrderTest());
+        tests.add(new PhoneOrderTest());
+        tests.add(new DeleteContactsTest());
         return tests;
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
index bad4639..2260f6d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -21,9 +21,7 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-import static android.provider.Settings.EXTRA_APP_PACKAGE;
 
-import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.content.ComponentName;
@@ -71,32 +69,26 @@
     @Override
     protected List<InteractiveTestCase> createTestItems() {
         List<InteractiveTestCase> tests = new ArrayList<>(9);
-        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        if (am.isLowRamDevice()) {
-            tests.add(new CannotBeEnabledTest());
-            tests.add(new ServiceStoppedTest());
-        } else {
-            tests.add(new IsEnabledTest());
-            tests.add(new ServiceStartedTest());
-            tests.add(new CreateAutomaticZenRuleTest());
-            tests.add(new CreateAutomaticZenRuleWithZenPolicyTest());
-            tests.add(new UpdateAutomaticZenRuleTest());
-            tests.add(new UpdateAutomaticZenRuleWithZenPolicyTest());
-            tests.add(new GetAutomaticZenRuleTest());
-            tests.add(new GetAutomaticZenRulesTest());
-            tests.add(new VerifyRulesIntent());
-            tests.add(new VerifyRulesAvailableToUsers());
-            tests.add(new ReceiveRuleDisableNoticeTest());
-            tests.add(new ReceiveRuleEnabledNoticeTest());
-            tests.add(new ReceiveRuleDeletedNoticeTest());
-            tests.add(new SubscribeAutomaticZenRuleTest());
-            tests.add(new DeleteAutomaticZenRuleTest());
-            tests.add(new UnsubscribeAutomaticZenRuleTest());
-            tests.add(new RequestUnbindTest());
-            tests.add(new RequestBindTest());
-            tests.add(new IsDisabledTest());
-            tests.add(new ServiceStoppedTest());
-        }
+        tests.add(new IsEnabledTest());
+        tests.add(new ServiceStartedTest());
+        tests.add(new CreateAutomaticZenRuleTest());
+        tests.add(new CreateAutomaticZenRuleWithZenPolicyTest());
+        tests.add(new UpdateAutomaticZenRuleTest());
+        tests.add(new UpdateAutomaticZenRuleWithZenPolicyTest());
+        tests.add(new GetAutomaticZenRuleTest());
+        tests.add(new GetAutomaticZenRulesTest());
+        tests.add(new VerifyRulesIntent());
+        tests.add(new VerifyRulesAvailableToUsers());
+        tests.add(new ReceiveRuleDisableNoticeTest());
+        tests.add(new ReceiveRuleEnabledNoticeTest());
+        tests.add(new ReceiveRuleDeletedNoticeTest());
+        tests.add(new SubscribeAutomaticZenRuleTest());
+        tests.add(new DeleteAutomaticZenRuleTest());
+        tests.add(new UnsubscribeAutomaticZenRuleTest());
+        tests.add(new RequestUnbindTest());
+        tests.add(new RequestBindTest());
+        tests.add(new IsDisabledTest());
+        tests.add(new ServiceStoppedTest());
         return tests;
     }
 
@@ -138,45 +130,6 @@
         }
     }
 
-    protected class CannotBeEnabledTest extends InteractiveTestCase {
-        @Override
-        protected View inflate(ViewGroup parent) {
-            return createNlsSettingsItem(parent, R.string.cp_cannot_enable_service);
-        }
-
-        @Override
-        boolean autoStart() {
-            return true;
-        }
-
-        @Override
-        protected void test() {
-            mNm.cancelAll();
-            Intent settings = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
-            if (settings.resolveActivity(mPackageManager) == null) {
-                logFail("no settings activity");
-                status = FAIL;
-            } else {
-                if (mNm.isNotificationPolicyAccessGranted()) {
-                    status = FAIL;
-                } else {
-                    status = PASS;
-                }
-                next();
-            }
-        }
-
-        protected void tearDown() {
-            // wait for the service to start
-            delay();
-        }
-
-        @Override
-        protected Intent getIntent() {
-            return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
-        }
-    }
-
     protected class ServiceStartedTest extends InteractiveTestCase {
         int mRetries = 5;
         @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index 296df5d..aa5e9c0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -36,6 +36,7 @@
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
+
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
@@ -524,47 +525,6 @@
         }
     }
 
-    protected class CannotBeEnabledTest extends InteractiveTestCase {
-        @Override
-        protected View inflate(ViewGroup parent) {
-            return createNlsSettingsItem(parent, R.string.nls_cannot_enable_service);
-        }
-
-        @Override
-        boolean autoStart() {
-            return true;
-        }
-
-        @Override
-        protected void test() {
-            mNm.cancelAll();
-            Intent settings = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
-            if (settings.resolveActivity(mPackageManager) == null) {
-                logFail("no settings activity");
-                status = FAIL;
-            } else {
-                String listeners = Secure.getString(getContentResolver(),
-                        ENABLED_NOTIFICATION_LISTENERS);
-                if (listeners != null && listeners.contains(LISTENER_PATH)) {
-                    status = FAIL;
-                } else {
-                    status = PASS;
-                }
-                next();
-            }
-        }
-
-        protected void tearDown() {
-            // wait for the service to start
-            delay();
-        }
-
-        @Override
-        protected Intent getIntent() {
-            return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
-        }
-    }
-
     protected class ServiceStartedTest extends InteractiveTestCase {
         @Override
         protected View inflate(ViewGroup parent) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 7297dbe..348c7a3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -35,7 +35,6 @@
 import static com.android.cts.verifier.notifications.MockListener.REASON_LISTENER_CANCEL;
 
 import android.annotation.SuppressLint;
-import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -108,39 +107,32 @@
     @Override
     protected List<InteractiveTestCase> createTestItems() {
         List<InteractiveTestCase> tests = new ArrayList<>(17);
-        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        if (am.isLowRamDevice()) {
-            tests.add(new CannotBeEnabledTest());
-            tests.add(new ServiceStoppedTest());
-            tests.add(new NotificationNotReceivedTest());
-        } else {
-            tests.add(new IsEnabledTest());
-            tests.add(new ServiceStartedTest());
-            tests.add(new NotificationReceivedTest());
-            tests.add(new DataIntactTest());
-            tests.add(new AudiblyAlertedTest());
-            tests.add(new DismissOneTest());
-            tests.add(new DismissOneWithReasonTest());
-            tests.add(new DismissOneWithStatsTest());
-            tests.add(new DismissAllTest());
-            tests.add(new SnoozeNotificationForTimeTest());
-            tests.add(new SnoozeNotificationForTimeCancelTest());
-            tests.add(new GetSnoozedNotificationTest());
-            tests.add(new EnableHintsTest());
-            tests.add(new ReceiveAppBlockNoticeTest());
-            tests.add(new ReceiveAppUnblockNoticeTest());
-            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
-                tests.add(new ReceiveChannelBlockNoticeTest());
-                tests.add(new ReceiveGroupBlockNoticeTest());
-            }
-            tests.add(new RequestUnbindTest());
-            tests.add(new RequestBindTest());
-            tests.add(new MessageBundleTest());
-            tests.add(new EnableHintsTest());
-            tests.add(new IsDisabledTest());
-            tests.add(new ServiceStoppedTest());
-            tests.add(new NotificationNotReceivedTest());
+        tests.add(new IsEnabledTest());
+        tests.add(new ServiceStartedTest());
+        tests.add(new NotificationReceivedTest());
+        tests.add(new DataIntactTest());
+        tests.add(new AudiblyAlertedTest());
+        tests.add(new DismissOneTest());
+        tests.add(new DismissOneWithReasonTest());
+        tests.add(new DismissOneWithStatsTest());
+        tests.add(new DismissAllTest());
+        tests.add(new SnoozeNotificationForTimeTest());
+        tests.add(new SnoozeNotificationForTimeCancelTest());
+        tests.add(new GetSnoozedNotificationTest());
+        tests.add(new EnableHintsTest());
+        tests.add(new ReceiveAppBlockNoticeTest());
+        tests.add(new ReceiveAppUnblockNoticeTest());
+        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            tests.add(new ReceiveChannelBlockNoticeTest());
+            tests.add(new ReceiveGroupBlockNoticeTest());
         }
+        tests.add(new RequestUnbindTest());
+        tests.add(new RequestBindTest());
+        tests.add(new MessageBundleTest());
+        tests.add(new EnableHintsTest());
+        tests.add(new IsDisabledTest());
+        tests.add(new ServiceStoppedTest());
+        tests.add(new NotificationNotReceivedTest());
         return tests;
     }
 
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java
index ca0e3e2..a24239c 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/RetryRule.java
@@ -31,16 +31,8 @@
 
     private static final String TAG = "RetryRule";
 
-    /**
-     * An interface is used to clean up testing objects between the retries to make sure
-     * the testing environment is clean.
-     */
-    public interface RetryCleaner {
-        void clean();
-    }
-
     private final int mMaxAttempts;
-    private final RetryCleaner mCleaner;
+    private final Runnable mCleaner;
 
     /**
      * Retries the underlying test when it catches a {@link RetryableException}.
@@ -57,12 +49,12 @@
      * Retries the underlying test when it catches a {@link RetryableException}.
      *
      * @param retries number of retries. Use {@code 0} to disable rule.
-     * @param cleaner a {@link RetryCleaner} to clean up the objects generated by the testing
+     * @param cleaner a {@link Runnable} to clean up the objects generated by the testing
      *               between retries
      *
      * @throws IllegalArgumentException if {@code retries} is less than {@code 0}.
      */
-    public RetryRule(int retries, @Nullable RetryCleaner cleaner) {
+    public RetryRule(int retries, @Nullable Runnable cleaner) {
         if (retries < 0) {
             throw new IllegalArgumentException("retries must be more than 0");
         }
@@ -105,7 +97,7 @@
                         }
                         caught = e;
                         if (i != mMaxAttempts && mCleaner != null) {
-                            mCleaner.clean();
+                            mCleaner.run();
                         }
                     }
                     Log.w(TAG, "Arrrr! " + name + " failed at attempt " + i + "/" + mMaxAttempts
diff --git a/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java b/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
index 644d95f..571a1b8 100644
--- a/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
+++ b/common/device-side/util-axt/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
@@ -19,11 +19,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.testng.Assert.assertThrows;
 import static org.testng.Assert.expectThrows;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Test;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
@@ -43,15 +47,22 @@
         private final int mNumberFailures;
         private int mNumberCalls;
         private final T mException;
+        private final Runnable mCleaner;
 
         RetryableStatement(int numberFailures, T exception) {
+            this(numberFailures, exception, null);
+        }
+
+        RetryableStatement(int numberFailures, T exception, @Nullable Runnable cleaner) {
             mNumberFailures = numberFailures;
             mException = exception;
+            mCleaner = cleaner;
         }
 
         @Override
         public void evaluate() throws Throwable {
             mNumberCalls++;
+            verifyCleaner();
             if (mNumberCalls <= mNumberFailures) {
                 throw mException;
             }
@@ -61,6 +72,13 @@
         public String toString() {
             return "RetryableStatement: failures=" + mNumberFailures + ", calls=" + mNumberCalls;
         }
+
+        private void verifyCleaner() {
+            if (mCleaner == null) {
+                return;
+            }
+            verify(mCleaner, times(mNumberCalls - 1)).run();
+        }
     }
 
     private @Mock Statement mMockStatement;
@@ -68,6 +86,8 @@
     @Test
     public void testInvalidConstructor() throws Throwable {
         assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1));
+        assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1, null));
+        assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1, () -> {}));
     }
 
     @Test
@@ -78,6 +98,57 @@
     }
 
     @Test
+    public void testDoCleanOnRetryableException() throws Throwable {
+        final Runnable cleaner = mock(Runnable.class);
+        final RetryRule rule = new RetryRule(2, cleaner);
+
+        rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException, cleaner),
+                mDescription).evaluate();
+
+        verify(cleaner, times(2)).run();
+    }
+
+    @Test
+    public void testKeepLastStatusWhenFailOnRetryableException() throws Throwable {
+        final Runnable cleaner = mock(Runnable.class);
+        final RetryRule rule = new RetryRule(2, cleaner);
+
+        final RetryableException actualException = expectThrows(RetryableException.class,
+                () -> rule.apply(
+                        new RetryableStatement<RetryableException>(3, sRetryableException, cleaner),
+                        mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(sRetryableException);
+        verify(cleaner, times(2)).run();
+    }
+
+    @Test
+    public void testNeverCleanWhenStatementPass() throws Throwable {
+        final Runnable cleaner = mock(Runnable.class);
+        final RetryRule rule = new RetryRule(2, cleaner);
+
+        rule.apply(mMockStatement, mDescription).evaluate();
+
+        verify(mMockStatement, times(1)).evaluate();
+        verify(cleaner, never()).run();
+    }
+
+    @Test
+    public void testNeverCleanWhenDisabledAndStatementThrowsRetryableException() throws Throwable {
+        final RetryableException exception = new RetryableException("Y U NO?");
+        final Runnable cleaner = mock(Runnable.class);
+        final RetryRule rule = new RetryRule(0, cleaner);
+        doThrow(exception).when(mMockStatement).evaluate();
+
+        final RetryableException actualException = expectThrows(RetryableException.class,
+                () -> rule.apply(mMockStatement, mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(exception);
+        verify(mMockStatement, times(1)).evaluate();
+        verify(cleaner, never()).run();
+    }
+
+    @Test
     public void testPassOnRetryableExceptionWithTimeout() throws Throwable {
         final Timeout timeout = new Timeout("YOUR TIME IS GONE", 1, 2, 10);
         final RetryableException exception = new RetryableException(timeout, "Y U NO?");
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml
index 1231874..a6f7b1d 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/AndroidManifest.xml
@@ -16,7 +16,7 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.encryptionapp">
-    <application android:label="EncryptionApp">
+    <application android:label="EncryptionApp" android:forceQueryable="true">
         <activity android:name=".UnawareActivity"
                 android:exported="true">
             <intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
index 71d13ae..04ed8f4 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
-    <application android:label="SplitApp" android:multiArch="true">
+    <application android:label="SplitApp" android:multiArch="true" android:forceQueryable="true">
         <activity android:name=".MyActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/blobstore/Android.bp b/hostsidetests/blobstore/Android.bp
index 398aff8..aab448e 100644
--- a/hostsidetests/blobstore/Android.bp
+++ b/hostsidetests/blobstore/Android.bp
@@ -31,8 +31,8 @@
 }
 
 android_test_helper_app {
-  name: "CtsBlobStoreHelperApp",
-  srcs:  ["test-apps/BlobStoreHelperApp/src/**/*.java"],
+  name: "CtsBlobStoreHostTestHelper",
+  srcs:  ["test-apps/BlobStoreHostTestHelper/src/**/*.java"],
   static_libs: [
       "compatibility-device-util-axt",
       "androidx.test.ext.junit",
@@ -41,7 +41,7 @@
       "truth-prebuilt",
       "testng",
   ],
-  manifest : "test-apps/BlobStoreHelperApp/AndroidManifest.xml",
+  manifest : "test-apps/BlobStoreHostTestHelper/AndroidManifest.xml",
   platform_apis: true,
   // Tag this module as a cts test artifact
   test_suites: [
diff --git a/hostsidetests/blobstore/AndroidTest.xml b/hostsidetests/blobstore/AndroidTest.xml
index eaa301d..b1b6d63 100644
--- a/hostsidetests/blobstore/AndroidTest.xml
+++ b/hostsidetests/blobstore/AndroidTest.xml
@@ -24,7 +24,7 @@
         <option name="jar" value="CtsBlobStoreHostTestCases.jar" />
     </test>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="test-file-name" value="CtsBlobStoreHelperApp.apk" />
+        <option name="test-file-name" value="CtsBlobStoreHostTestHelper.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
 </configuration>
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java
index 9e3701d..7d25f5d 100644
--- a/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java
@@ -27,6 +27,9 @@
 import java.util.stream.Collectors;
 
 abstract class BaseBlobStoreHostTest extends BaseHostJUnit4Test {
+    protected static final String TARGET_APK = "CtsBlobStoreHostTestHelper.apk";
+    protected static final String TARGET_PKG = "com.android.cts.device.blob";
+
     private static final long TIMEOUT_BOOT_COMPLETE_MS = 120_000;
 
     protected static final String KEY_SESSION_ID = "session";
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java
index 9f58c3e..d8cc1a0 100644
--- a/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java
@@ -31,8 +31,6 @@
 
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class BlobStoreMultiUserTest extends BaseBlobStoreHostTest {
-    private static final String TARGET_APK = "CtsBlobStoreHelperApp.apk";
-    private static final String TARGET_PKG = "com.android.cts.blob.helper";
     private static final String TEST_CLASS = TARGET_PKG + ".DataCleanupTest";
 
     private int mPrimaryUserId;
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java
index 8a3efc8..291bc7c 100644
--- a/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java
@@ -24,8 +24,6 @@
 
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class DataCleanupTest extends BaseBlobStoreHostTest {
-    private static final String TARGET_APK = "CtsBlobStoreHelperApp.apk";
-    private static final String TARGET_PKG = "com.android.cts.blob.helper";
     private static final String TEST_CLASS = TARGET_PKG + ".DataCleanupTest";
 
     @Test
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java
index 39f9b32..973ca8b 100644
--- a/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java
@@ -22,8 +22,6 @@
 
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class DataPersistenceTest extends BaseBlobStoreHostTest {
-    private static final String TARGET_APK = "CtsBlobStoreHelperApp.apk";
-    private static final String TARGET_PKG = "com.android.cts.blob.helper";
     private static final String TEST_CLASS = TARGET_PKG + ".DataPersistenceTest";
 
     @Test
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/AndroidManifest.xml
similarity index 88%
rename from hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
rename to hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/AndroidManifest.xml
index a649209..36e0a9b 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/AndroidManifest.xml
@@ -15,12 +15,12 @@
  * limitations under the License.
  -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.cts.blob.helper">
+          package="com.android.cts.device.blob">
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation
             android:name="androidx.test.runner.AndroidJUnitRunner"
-            android:targetPackage="com.android.cts.blob.helper"  />
+            android:targetPackage="com.android.cts.device.blob"  />
 </manifest>
\ No newline at end of file
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataCleanupTest.java b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/DataCleanupTest.java
similarity index 99%
rename from hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataCleanupTest.java
rename to hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/DataCleanupTest.java
index 00467cd..5bb82b9 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataCleanupTest.java
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/DataCleanupTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.blob.helper;
+package com.android.cts.device.blob;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/DataPersistenceTest.java
similarity index 96%
rename from hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
rename to hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/DataPersistenceTest.java
index a280997..b4226a6 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/DataPersistenceTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.blob.helper;
+package com.android.cts.device.blob;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -149,7 +149,7 @@
     }
 
     private SharedPreferences getSharedPreferences() {
-        return mContext.getSharedPreferences(mContext.getPackageName(),
-                Context.MODE_PRIVATE);
+        return mContext.createDeviceProtectedStorageContext().getSharedPreferences(
+                mContext.getPackageName(), Context.MODE_PRIVATE);
     }
 }
diff --git a/hostsidetests/bootstats/Android.bp b/hostsidetests/bootstats/Android.bp
index ca1e292..07f9c84 100644
--- a/hostsidetests/bootstats/Android.bp
+++ b/hostsidetests/bootstats/Android.bp
@@ -17,12 +17,12 @@
     defaults: ["cts_defaults"],
     // Only compile source java files in this apk.
     srcs: ["src/**/*.java"],
-    static_libs: ["framework-protos"],
     libs: [
         "cts-tradefed",
         "tradefed",
         "compatibility-host-util",
         "libprotobuf-java-full",
+        "platformprotos",
     ],
     // tag this module as a cts test artifact
     test_suites: [
diff --git a/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java b/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
index 9ec3dad..e5eb36c 100644
--- a/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
+++ b/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
@@ -16,7 +16,7 @@
 
 package android.bootstats.cts;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.os.AtomsProto.Atom;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -56,37 +56,53 @@
         Thread.sleep(10000);
 
         // find logs and parse them
-        // ex: sysui_multi_action: [757,804,799,ota_boot_complete,801,85,802,1]
-        // ex: 757,804,799,counter_name,801,bucket_value,802,increment_value
-        final String bucketTag = Integer.toString(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
-        final String counterNameTag = Integer.toString(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME);
-        final String counterNamePattern = counterNameTag + ",boot_complete,";
-        final String multiActionPattern = "sysui_multi_action: [";
+        // ex: Atom 239->10
+        // ex: Atom 240->9
+         final String bootTimeEventDurationReported =
+                Integer.toString(Atom.BOOT_TIME_EVENT_DURATION_REPORTED_FIELD_NUMBER);
+        final String bootTimeEventDurationReportedPattern = "Atom "
+                + bootTimeEventDurationReported + "->";
+        final String bootTimeEventElapsedTimeReported =
+                Integer.toString(Atom.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED_FIELD_NUMBER);
+        final String bootTimeEventElapsedTimeReportedPattern = "Atom "
+                + bootTimeEventElapsedTimeReported + "->";
 
-        final String log = getDevice().executeShellCommand("logcat --buffer=events -d");
+        final String log = getDevice().executeShellCommand("cmd stats print-stats");
 
-        int counterNameIndex = log.indexOf(counterNamePattern);
-        Assert.assertTrue("did not find boot logs", counterNameIndex != -1);
+        int bootTimeEventDurationReportedIndex =
+                log.indexOf(bootTimeEventDurationReportedPattern);
+        Assert.assertTrue("did not find boot duration logs",
+                bootTimeEventDurationReportedIndex != -1);
+        // extract the number after ->, e.g., 10 inside 239->10
+        int valueIndex = bootTimeEventDurationReportedIndex +
+                bootTimeEventDurationReportedPattern.length();
+        int value = getIntValue(log, valueIndex);
+        Assert.assertTrue("boot duration time smaller than 1", value > 1);
 
-        int multiLogStart = log.lastIndexOf(multiActionPattern, counterNameIndex);
-        multiLogStart += multiActionPattern.length();
-        int multiLogEnd = log.indexOf("]", multiLogStart);
-        String[] multiLogDataStrings = log.substring(multiLogStart, multiLogEnd).split(",");
+        int bootTimeEventElapsedTimeReportedIndex =
+                log.indexOf(bootTimeEventElapsedTimeReportedPattern);
+        Assert.assertTrue("did not find boot elapsed time logs",
+                bootTimeEventElapsedTimeReportedIndex != -1);
 
-        boolean foundBucket = false;
-        int bootTime = 0;
-        for (int i = 0; i < multiLogDataStrings.length; i += 2) {
-            if (bucketTag.equals(multiLogDataStrings[i])) {
-                foundBucket = true;
-                Assert.assertTrue("histogram data was truncated",
-                        (i + 1) < multiLogDataStrings.length);
-                bootTime = Integer.valueOf(multiLogDataStrings[i + 1]);
+        // extract the number after ->, e.g., 9 inside Atom 240->9
+        valueIndex = bootTimeEventElapsedTimeReportedIndex +
+                bootTimeEventElapsedTimeReportedPattern.length();
+        value = getIntValue(log, valueIndex);
+        Assert.assertTrue("boot elapsed time smaller than 1", value > 1);
+    }
+
+    // extract the value from the string starting from index till EOL
+    private int getIntValue(String str, int index) throws Exception {
+        int lastIndex = index;
+        for (int i = index; i < str.length(); i++) {
+            if (str.charAt(i) == '\n') {
+                lastIndex = i;
+                break;
             }
         }
-        Assert.assertTrue("log line did not contain a tag " + bucketTag, foundBucket);
-        Assert.assertTrue("reported boot time must be less than observed boot time",
-                bootTime < upperBoundSeconds);
-        Assert.assertTrue("reported boot time must be non-zero", bootTime > 0);
+        String valueStr = str.substring(index, lastIndex);
+        int value = Integer.valueOf(valueStr);
+        return value;
     }
 
     private boolean isBootCompleted() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
index 02b480a..5564c3a 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
@@ -42,6 +42,8 @@
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
+
+        <activity android:name=".CrossProfileSameTaskLauncherActivity" android:exported="true"/>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml
new file mode 100644
index 0000000..8d9cf94
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="CrossProfileSameTaskLauncherActivity"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
index 87878a1..be5c4b1 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -36,7 +37,6 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -104,7 +104,7 @@
         intent.setComponent(MainActivity.getComponentName(mContext));
         ShellIdentityUtils.dropShellPermissionIdentity();
 
-        mCrossProfileApps.startActivity(intent, mTargetUser);
+        mCrossProfileApps.startActivity(intent, mTargetUser, /* callingActivity= */ null);
     }
 
     @Test
@@ -113,8 +113,11 @@
         intent.setComponent(MainActivity.getComponentName(mContext));
         ShellIdentityUtils.dropShellPermissionIdentity();
 
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
-                crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_PROFILES);
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mCrossProfileApps,
+                crossProfileApps -> crossProfileApps.startActivity(
+                        intent, mTargetUser, /* callingActivity= */ null),
+                INTERACT_ACROSS_PROFILES);
 
         // Look for the text view to verify that MainActivity is started.
         UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -130,8 +133,11 @@
         intent.setComponent(MainActivity.getComponentName(mContext));
         ShellIdentityUtils.dropShellPermissionIdentity();
 
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
-                crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_USERS);
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mCrossProfileApps,
+                crossProfileApps -> crossProfileApps.startActivity(
+                        intent, mTargetUser, /* callingActivity= */ null),
+                INTERACT_ACROSS_USERS);
 
         // Look for the text view to verify that MainActivity is started.
         UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -147,8 +153,11 @@
         intent.setComponent(MainActivity.getComponentName(mContext));
         ShellIdentityUtils.dropShellPermissionIdentity();
 
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
-                crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_USERS_FULL);
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mCrossProfileApps,
+                crossProfileApps -> crossProfileApps.startActivity(
+                        intent, mTargetUser, /* callingActivity= */ null),
+                INTERACT_ACROSS_USERS_FULL);
 
         // Look for the text view to verify that MainActivity is started.
         UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -164,9 +173,10 @@
         Intent nonMainActivityImplicitIntent = new Intent();
         nonMainActivityImplicitIntent.setAction(Intent.ACTION_VIEW);
 
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mCrossProfileApps,
                 crossProfileApps -> crossProfileApps.startActivity(
-                        nonMainActivityImplicitIntent, mTargetUser));
+                        nonMainActivityImplicitIntent, mTargetUser, /* callingActivity= */ null));
     }
 
     @Test
@@ -175,9 +185,10 @@
         mainActivityIntent.setComponent(MainActivity.getComponentName(mContext));
 
         try {
-            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                    mCrossProfileApps,
                     crossProfileApps -> mCrossProfileApps.startActivity(
-                            mainActivityIntent, mTargetUser));
+                            mainActivityIntent, mTargetUser, /* callingActivity= */ null));
 
             // Look for the text view to verify that MainActivity is started.
             UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -196,9 +207,10 @@
         nonMainActivityIntent.setComponent(NonMainActivity.getComponentName(mContext));
 
         try {
-            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                    mCrossProfileApps,
                     crossProfileApps -> mCrossProfileApps.startActivity(
-                            nonMainActivityIntent, mTargetUser));
+                            nonMainActivityIntent, mTargetUser, /* callingActivity= */ null));
 
             // Look for the text view to verify that NonMainActivity is started.
             UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
@@ -212,7 +224,33 @@
     }
 
     /**
-     * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle)}. This can then be used by
+     * Starts an activity in the same task in the target user. Asserts that the activity is
+     * correctly started in the correct user, but the host-side test should verify that the tasks
+     * are the same using the log messages printed by each activity.
+     */
+    @Test
+    public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+        try {
+            final Intent crossProfileSameTaskCheckerIntent = new Intent();
+            crossProfileSameTaskCheckerIntent.setComponent(
+                    CrossProfileSameTaskLauncherActivity.getComponentName(mContext));
+            crossProfileSameTaskCheckerIntent.putExtra(
+                    CrossProfileSameTaskLauncherActivity.TARGET_USER_EXTRA, mTargetUser);
+            mContext.startActivity(crossProfileSameTaskCheckerIntent);
+
+            // Look for the text view to verify that NonMainActivity is started.
+            UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
+                    TIMEOUT_WAIT_UI);
+            assertNotNull("Failed to start non-main activity in target user", textView);
+            assertEquals("Non-Main Activity is started in wrong user",
+                    String.valueOf(mUserSerialNumber), textView.getText());
+        } catch (Exception e) {
+            fail("unable to start cross-profile activity in the same task: " + e);
+        }
+    }
+
+    /**
+     * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle, Activity)}. This can then be used by
      * host-side tests.
      */
     @Test
@@ -222,8 +260,8 @@
 
         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
                 mCrossProfileApps,
-                crossProfileApps ->
-                        mCrossProfileApps.startActivity(nonMainActivityIntent, mTargetUser));
+                crossProfileApps -> mCrossProfileApps.startActivity(
+                        nonMainActivityIntent, mTargetUser, /* callingActivity= */ null));
     }
 
     @Test
@@ -267,8 +305,10 @@
         Intent nonExportedActivityIntent = new Intent();
         nonExportedActivityIntent.setComponent(NonExportedActivity.getComponentName(mContext));
 
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
-                crossProfileApps -> mCrossProfileApps.startActivity(nonExportedActivityIntent, mTargetUser));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mCrossProfileApps,
+                crossProfileApps -> mCrossProfileApps.startActivity(
+                        nonExportedActivityIntent, mTargetUser, /* callingActivity= */ null));
 
         // Look for the text view to verify that NonExportedActivity is started.
         UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
@@ -290,9 +330,8 @@
         otherPackageIntent.setComponent(new ComponentName(
                 "com.android.cts.launcherapps.simpleapp",
                 "com.android.cts.launcherapps.simpleapp.SimpleActivity"));
-        mCrossProfileApps.startActivity(otherPackageIntent,
-                mTargetUser
-        );
+        mCrossProfileApps.startActivity(
+                otherPackageIntent, mTargetUser, /* callingActivity= */ null);
     }
 
     @Test(expected = SecurityException.class)
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java
new file mode 100644
index 0000000..9d1f209
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+/**
+ * An activity that launches the {@link NonMainActivity} in a different user within the same task.
+ *
+ * <p>Logs its task ID with the following format:
+ * "CrossProfileSameTaskLauncherActivity#taskId#[taskId]#", where [taskId] is the actual task ID,
+ * such as NonMainActivity#taskId#4#.
+ */
+public class CrossProfileSameTaskLauncherActivity extends Activity {
+    static final String TARGET_USER_EXTRA = "TARGET_USER";
+
+    private static final String LOG_TAG = "CrossProfileSameTaskChe"; // 23 chars max
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Intent intent = getIntent();
+        if (!intent.hasExtra(TARGET_USER_EXTRA)) {
+            throw new IllegalStateException(
+                    "ActivityForwarder started without the extra: " + TARGET_USER_EXTRA);
+        }
+        setContentView(R.layout.cross_profile_same_task_launcher);
+        Log.w(LOG_TAG, "CrossProfileSameTaskLauncherActivity#taskId#" + getTaskId() + "#");
+        final UserHandle targetUser = intent.getParcelableExtra(TARGET_USER_EXTRA);
+        final Intent nonMainActivityIntent = new Intent();
+        nonMainActivityIntent.setComponent(NonMainActivity.getComponentName(this));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                getSystemService(CrossProfileApps.class),
+                crossProfileApps
+                        -> crossProfileApps.startActivity(nonMainActivityIntent, targetUser, this));
+    }
+
+    static ComponentName getComponentName(Context context) {
+        return new ComponentName(context, CrossProfileSameTaskLauncherActivity.class);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
index 4131a98..c2ad97c 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
@@ -21,14 +21,23 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.os.UserManager;
+import android.util.Log;
 import android.widget.TextView;
 
+/**
+ * An activity that is not the main activity of the application.
+ *
+ * <p>Logs its task ID with the following format: "NonMainActivity#taskId#[taskId]#", where [taskId]
+ * is the actual task ID, such as NonMainActivity#taskId#4#.
+ */
 public class NonMainActivity extends Activity {
+    private static final String LOG_TAG = "NonMainActivity";
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         setContentView(R.layout.non_main);
+        Log.w(LOG_TAG, "NonMainActivity#taskId#" + getTaskId() + "#");
     }
 
     @Override
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
index 212d20f..889e91a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
@@ -69,6 +69,7 @@
     protected DevicePolicyManager mDevicePolicyManager;
     protected UserManager mUserManager;
     protected Context mContext;
+    protected boolean mHasSecureLockScreen;
     static CountDownLatch mOnPasswordExpiryTimeoutCalled;
 
     private final String mTag = getClass().getSimpleName();
@@ -84,6 +85,9 @@
         mUserManager = mContext.getSystemService(UserManager.class);
         assertNotNull(mUserManager);
 
+        mHasSecureLockScreen = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SECURE_LOCK_SCREEN);
+
         assertTrue(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
         assertTrue("App is neither device nor profile owner",
                 mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME) ||
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java
index 23c62fc..304b67a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyguardDisabledFeaturesTest.java
@@ -18,8 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.assertThrows;
-
 import android.app.admin.DevicePolicyManager;
 
 public class KeyguardDisabledFeaturesTest extends BaseDeviceAdminTest {
@@ -32,9 +30,19 @@
         assertThat(mDevicePolicyManager.getKeyguardDisabledFeatures(
                 ADMIN_RECEIVER_COMPONENT)).isEqualTo(
                 DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+        removeKeyguardDisableFeatures(mDevicePolicyManager);
+        mDevicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+        // Check if the admin has disabled notifications specifically for the keyguard
+        assertThat(mDevicePolicyManager.getKeyguardDisabledFeatures(
+                ADMIN_RECEIVER_COMPONENT)).isEqualTo(
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+        removeKeyguardDisableFeatures(mDevicePolicyManager);
     }
 
-    public void testSetKeyguardDisabledFeatures_onParentSilentIgnore() {
+    public void testSetKeyguardDisabledFeatures_onParentSilentIgnoreWhenCallerIsNotOrgOwnedPO() {
         DevicePolicyManager parentDevicePolicyManager =
                 mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
 
@@ -56,5 +64,20 @@
         assertThat(parentDevicePolicyManager.getKeyguardDisabledFeatures(
                 ADMIN_RECEIVER_COMPONENT)).isEqualTo(
                 DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+        removeKeyguardDisableFeatures(parentDevicePolicyManager);
+        parentDevicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+        // Check if the admin has disabled notifications specifically for the keyguard
+        assertThat(parentDevicePolicyManager.getKeyguardDisabledFeatures(
+                ADMIN_RECEIVER_COMPONENT)).isEqualTo(
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+        removeKeyguardDisableFeatures(parentDevicePolicyManager);
+    }
+
+    private void removeKeyguardDisableFeatures(DevicePolicyManager devicePolicyManager) {
+        devicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+                DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE);
     }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
index 2f9bc37..78b5b2e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.deviceandprofileowner;
 
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
@@ -35,7 +36,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
 import android.util.Log;
+import android.view.KeyEvent;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -134,11 +139,13 @@
     private final Object mReceiverActivityRunningLock = new Object();
 
     private Context mContext;
+    private UiDevice mUiDevice;
     private ActivityManager mActivityManager;
     private DevicePolicyManager mDevicePolicyManager;
 
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
 
         mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
         mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
@@ -302,6 +309,43 @@
         stopAndFinish(UTILITY_ACTIVITY);
     }
 
+    // When LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is set, un-whitelisted apps cannot
+    // be launched in the same task.
+    public void testStartActivity_withinTask_blockInTask() throws Exception {
+        mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+        mDevicePolicyManager.setLockTaskFeatures(
+                ADMIN_COMPONENT, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK);
+        startLockTask(UTILITY_ACTIVITY);
+        waitForResume();
+
+        mIsReceiverActivityRunning = false;
+        Intent launchIntent = createReceiverActivityIntent(false /*newTask*/, false /*shouldWait*/);
+        Intent lockTaskUtility = getLockTaskUtility(UTILITY_ACTIVITY);
+        lockTaskUtility.putExtra(LockTaskUtilityActivity.START_ACTIVITY, launchIntent);
+        mContext.startActivity(lockTaskUtility);
+
+        synchronized (mReceiverActivityRunningLock) {
+            mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+            assertFalse(mIsReceiverActivityRunning);
+        }
+
+        Log.d(TAG, "Waiting for the system dialog");
+        mUiDevice.waitForIdle();
+        assertTrue(mUiDevice.wait(
+                Until.hasObject(By.textContains(RECEIVER_ACTIVITY_PACKAGE_NAME)),
+                ACTIVITY_RESUMED_TIMEOUT_MILLIS));
+        Log.d(TAG, "dialog found");
+
+        // Dismiss the system dialog
+        mUiDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER);
+        mUiDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER);
+        assertFalse(mUiDevice.wait(
+                Until.hasObject(By.textContains(RECEIVER_ACTIVITY_PACKAGE_NAME)),
+                ACTIVITY_RESUMED_TIMEOUT_MILLIS));
+
+        mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, 0);
+    }
+
     // This launches a whitelisted activity that is not part of the current task.
     // This should be permitted as a part of lock task.
     public void testStartActivity_outsideTaskWhitelisted() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java
index 9f0d3fb..5de41ab 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PersonalAppsSuspensionTest.java
@@ -50,4 +50,15 @@
         assertThat(mDpm.getPersonalAppsSuspendedReasons(ADMIN))
                 .isEqualTo(DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED);
     }
+
+    @Test
+    public void testSetManagedProfileMaximumTimeOff1Sec() {
+        mDpm.setManagedProfileMaximumTimeOff(ADMIN, 1000);
+    }
+
+    @Test
+    public void testPersonalAppsSuspendedByTimeout() {
+        assertThat(mDpm.getPersonalAppsSuspendedReasons(ADMIN))
+                .isEqualTo(DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT);
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
similarity index 83%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
index ed4c19f..58a377e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner;
 
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
@@ -62,16 +62,21 @@
 import android.content.SharedPreferences;
 import android.os.Parcel;
 import android.os.Process;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
+import junit.framework.AssertionFailedError;
+
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.KeyStore;
@@ -85,7 +90,8 @@
 
 import javax.crypto.spec.SecretKeySpec;
 
-public class SecurityLoggingTest extends BaseDeviceOwnerTest {
+public class SecurityLoggingTest extends BaseDeviceAdminTest {
+    private static final String TAG = "SecurityLoggingTest";
     private static final String ARG_BATCH_NUMBER = "batchNumber";
     private static final String PREF_KEY_PREFIX = "batch-last-id-";
     private static final String PREF_NAME = "batchIds";
@@ -123,8 +129,8 @@
                     .put(TAG_KEY_GENERATED, of(I, S, I))
                     .put(TAG_KEY_IMPORT, of(I, S, I))
                     .put(TAG_KEY_DESTRUCTION, of(I, S, I))
-                    .put(TAG_CERT_AUTHORITY_INSTALLED, of(I, S))
-                    .put(TAG_CERT_AUTHORITY_REMOVED, of(I, S))
+                    .put(TAG_CERT_AUTHORITY_INSTALLED, of(I, S, I))
+                    .put(TAG_CERT_AUTHORITY_REMOVED, of(I, S, I))
                     .put(TAG_USER_RESTRICTION_ADDED, of(S, I, S))
                     .put(TAG_USER_RESTRICTION_REMOVED, of(S, I, S))
                     .put(TAG_CRYPTO_SELF_TEST_COMPLETED, of(I))
@@ -170,6 +176,7 @@
     private static final int SUCCESS_INDEX = 0;
     private static final int ALIAS_INDEX = 1;
     private static final int UID_INDEX = 2;
+    private static final int USERID_INDEX = 2;
     private static final int SUBJECT_INDEX = 1;
     private static final int ADMIN_PKG_INDEX = 0;
     private static final int ADMIN_USER_INDEX = 1;
@@ -208,7 +215,7 @@
      */
     public void testRetrievingSecurityLogsThrowsSecurityException() {
         try {
-            mDevicePolicyManager.retrieveSecurityLogs(getWho());
+            mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT);
             fail("did not throw expected SecurityException");
         } catch (SecurityException expected) {
         }
@@ -220,7 +227,7 @@
      */
     public void testRetrievingPreviousSecurityLogsThrowsSecurityException() {
         try {
-            mDevicePolicyManager.retrievePreRebootSecurityLogs(getWho());
+            mDevicePolicyManager.retrievePreRebootSecurityLogs(ADMIN_RECEIVER_COMPONENT);
             fail("did not throw expected SecurityException");
         } catch (SecurityException expected) {
         }
@@ -236,6 +243,10 @@
         verifyKeystoreEventsPresent(events);
         verifyKeyChainEventsPresent(events);
         verifyAdminEventsPresent(events);
+        verifyAdbShellCommand(events); // Event generated from host side logic
+        if (mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+            verifyEventsRedacted(events);
+        }
     }
 
     private void verifyAutomaticEventsPresent(List<SecurityEvent> events) {
@@ -264,6 +275,39 @@
         verifyUserRestrictionEventsPresent(events);
         verifyCameraPolicyEvents(events);
     }
+    private void verifyAdbShellCommand(List<SecurityEvent> events) {
+        // Won't be able to locate the command on org-owned devices, as it will be redacted.
+        if (!mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+            findEvent("adb command", events,
+                    e -> e.getTag() == TAG_ADB_SHELL_CMD &&
+                            e.getData().equals("whoami"));
+
+        }
+    }
+
+    private void verifyEventsRedacted(List<SecurityEvent> events) {
+        final int userId = Process.myUserHandle().getIdentifier();
+        for (SecurityEvent event : events) {
+            switch (event.getTag()) {
+                case TAG_ADB_SHELL_CMD:
+                    assertTrue(TextUtils.isEmpty((String) event.getData()));
+                    break;
+                case TAG_APP_PROCESS_START:
+                case TAG_KEY_GENERATED:
+                case TAG_KEY_IMPORT:
+                case TAG_KEY_DESTRUCTION:
+                    assertEquals(userId, UserHandle.getUserId(getInt(event, UID_INDEX)));
+                    break;
+                case TAG_CERT_AUTHORITY_INSTALLED:
+                case TAG_CERT_AUTHORITY_REMOVED:
+                    assertEquals(userId, getInt(event, USERID_INDEX));
+                    break;
+                case TAG_KEY_INTEGRITY_VIOLATION:
+                    assertEquals(userId, UserHandle.getUserId(getInt(event, 1)));
+                    break;
+            }
+        }
+    }
 
     /**
      * Generates events for positive test cases.
@@ -303,15 +347,26 @@
         // Retry once after seeping for 1 second, in case "dpm force-security-logs" hasn't taken
         // effect just yet.
         for (int i = 0; i < 2 && events == null; i++) {
-            events = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+            events = mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT);
             if (events == null) Thread.sleep(1000);
         }
-
-        verifySecurityLogs(events);
+        try {
+            verifySecurityLogs(events);
+        } catch (AssertionFailedError e) {
+            dumpSecurityLogs(events);
+            throw e;
+        }
 
         return events;
     }
 
+    private void dumpSecurityLogs(List<SecurityEvent> events) {
+        Log.d(TAG, "Security events dump:");
+        for (SecurityEvent event : events) {
+            Log.d(TAG, event.toString());
+        }
+    }
+
     /**
      * Test: check that there are no gaps between ids in two consecutive batches. Shared preference
      * is used to store these numbers between test invocations.
@@ -470,9 +525,9 @@
      * security logging is enabled after its execution.
      */
     public void testEnablingSecurityLogging() {
-        assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
-        mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), true);
-        assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
+        assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT));
+        mDevicePolicyManager.setSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT, true);
+        assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT));
     }
 
     /**
@@ -480,11 +535,11 @@
      * disabled after its execution.
      */
     public void testDisablingSecurityLogging() {
-        mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), false);
-        assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
+        mDevicePolicyManager.setSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT, false);
+        assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT));
 
         // Verify that logs are actually not available.
-        assertNull(mDevicePolicyManager.retrieveSecurityLogs(getWho()));
+        assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT));
     }
 
     /**
@@ -492,11 +547,12 @@
      * null.
      */
     public void testSecurityLoggingRetrievalRateLimited() {
-        final List<SecurityEvent> logs = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+        final List<SecurityEvent> logs = mDevicePolicyManager.retrieveSecurityLogs(
+                ADMIN_RECEIVER_COMPONENT);
         // if logs is null it means that that attempt was rate limited => test PASS
         if (logs != null) {
-            assertNull(mDevicePolicyManager.retrieveSecurityLogs(getWho()));
-            assertNull(mDevicePolicyManager.retrieveSecurityLogs(getWho()));
+            assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT));
+            assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT));
         }
     }
 
@@ -546,7 +602,8 @@
     }
 
     private void installCaCert() {
-        mDevicePolicyManager.installCaCert(getWho(), TEST_CA.getBytes());
+        assertTrue(
+                mDevicePolicyManager.installCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes()));
     }
 
     private void verifyCertInstalledEventPresent(List<SecurityEvent> events) {
@@ -557,7 +614,7 @@
     }
 
     private void uninstallCaCert() {
-        mDevicePolicyManager.uninstallCaCert(getWho(), TEST_CA.getBytes());
+        mDevicePolicyManager.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
     }
 
     private void verifyCertUninstalledEventPresent(List<SecurityEvent> events) {
@@ -568,21 +625,21 @@
     }
 
     private void generatePasswordComplexityEvents() {
-        mDevicePolicyManager.setPasswordQuality(getWho(), PASSWORD_QUALITY_COMPLEX);
-        mDevicePolicyManager.setPasswordMinimumLength(getWho(), TEST_PWD_LENGTH);
-        mDevicePolicyManager.setPasswordMinimumLetters(getWho(), TEST_PWD_CHARS);
-        mDevicePolicyManager.setPasswordMinimumNonLetter(getWho(), TEST_PWD_CHARS);
-        mDevicePolicyManager.setPasswordMinimumUpperCase(getWho(), TEST_PWD_CHARS);
-        mDevicePolicyManager.setPasswordMinimumLowerCase(getWho(), TEST_PWD_CHARS);
-        mDevicePolicyManager.setPasswordMinimumNumeric(getWho(), TEST_PWD_CHARS);
-        mDevicePolicyManager.setPasswordMinimumSymbols(getWho(), TEST_PWD_CHARS);
+        mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_PWD_LENGTH);
+        mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+        mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+        mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+        mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+        mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
+        mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS);
     }
 
     private void verifyPasswordComplexityEventsPresent(List<SecurityEvent> events) {
         final int userId = Process.myUserHandle().getIdentifier();
         // This reflects default values for password complexity event payload fields.
         final Object[] expectedPayload = new Object[] {
-                getWho().getPackageName(), // admin package
+                ADMIN_RECEIVER_COMPONENT.getPackageName(), // admin package
                 userId,                    // admin user
                 userId,                    // target user
                 0,                         // default password length
@@ -617,37 +674,40 @@
 
     private void generateLockingPolicyEvents() {
         if (mHasSecureLockScreen) {
-            mDevicePolicyManager.setPasswordExpirationTimeout(getWho(),
+            mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT,
                     TEST_PWD_EXPIRATION_TIMEOUT);
-            mDevicePolicyManager.setPasswordHistoryLength(getWho(), TEST_PWD_HISTORY_LENGTH);
-            mDevicePolicyManager.setMaximumFailedPasswordsForWipe(getWho(), TEST_PWD_MAX_ATTEMPTS);
+            mDevicePolicyManager.setPasswordHistoryLength(ADMIN_RECEIVER_COMPONENT,
+                    TEST_PWD_HISTORY_LENGTH);
+            mDevicePolicyManager.setMaximumFailedPasswordsForWipe(ADMIN_RECEIVER_COMPONENT,
+                    TEST_PWD_MAX_ATTEMPTS);
         }
-        mDevicePolicyManager.setKeyguardDisabledFeatures(getWho(), KEYGUARD_DISABLE_FINGERPRINT);
-        mDevicePolicyManager.setMaximumTimeToLock(getWho(), TEST_MAX_TIME_TO_LOCK);
+        mDevicePolicyManager.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT,
+                KEYGUARD_DISABLE_FINGERPRINT);
+        mDevicePolicyManager.setMaximumTimeToLock(ADMIN_RECEIVER_COMPONENT, TEST_MAX_TIME_TO_LOCK);
         mDevicePolicyManager.lockNow();
     }
 
     private void verifyLockingPolicyEventsPresent(List<SecurityEvent> events) {
         final int userId = Process.myUserHandle().getIdentifier();
-
+        final String packageName = ADMIN_RECEIVER_COMPONENT.getPackageName();
         if (mHasSecureLockScreen) {
             findEvent("set password expiration", events,
                     e -> e.getTag() == TAG_PASSWORD_EXPIRATION_SET &&
-                            getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                            getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
                             getInt(e, ADMIN_USER_INDEX) == userId &&
                             getInt(e, TARGET_USER_INDEX) == userId &&
                             getLong(e, PWD_EXPIRATION_INDEX) == TEST_PWD_EXPIRATION_TIMEOUT);
 
             findEvent("set password history length", events,
                     e -> e.getTag() == TAG_PASSWORD_HISTORY_LENGTH_SET &&
-                            getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                            getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
                             getInt(e, ADMIN_USER_INDEX) == userId &&
                             getInt(e, TARGET_USER_INDEX) == userId &&
                             getInt(e, PWD_HIST_LEN_INDEX) == TEST_PWD_HISTORY_LENGTH);
 
             findEvent("set password attempts", events,
                     e -> e.getTag() == TAG_MAX_PASSWORD_ATTEMPTS_SET &&
-                            getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                            getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
                             getInt(e, ADMIN_USER_INDEX) == userId &&
                             getInt(e, TARGET_USER_INDEX) == userId &&
                             getInt(e, MAX_PWD_ATTEMPTS_INDEX) == TEST_PWD_MAX_ATTEMPTS);
@@ -655,21 +715,21 @@
 
         findEvent("set keyguard disabled features", events,
                 e -> e.getTag() == TAG_KEYGUARD_DISABLED_FEATURES_SET &&
-                        getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                        getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
                         getInt(e, ADMIN_USER_INDEX) == userId &&
                         getInt(e, TARGET_USER_INDEX) == userId &&
                         getInt(e, KEYGUARD_FEATURES_INDEX) == KEYGUARD_DISABLE_FINGERPRINT);
 
         findEvent("set screen lock timeout", events,
                 e -> e.getTag() == TAG_MAX_SCREEN_LOCK_TIMEOUT_SET &&
-                        getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                        getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
                         getInt(e, ADMIN_USER_INDEX) == userId &&
                         getInt(e, TARGET_USER_INDEX) == userId &&
                         getLong(e, MAX_SCREEN_TIMEOUT_INDEX) == TEST_MAX_TIME_TO_LOCK);
 
         findEvent("set screen lock timeout", events,
                 e -> e.getTag() == TAG_REMOTE_LOCK &&
-                        getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                        getString(e, ADMIN_PKG_INDEX).equals(packageName) &&
                         getInt(e, ADMIN_USER_INDEX) == userId);
     }
 
@@ -681,8 +741,10 @@
     }
 
     private void generateUserRestrictionEvents() {
-        mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_FUN);
-        mDevicePolicyManager.clearUserRestriction(getWho(), UserManager.DISALLOW_FUN);
+        mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+                UserManager.DISALLOW_PRINTING);
+        mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+                UserManager.DISALLOW_PRINTING);
     }
 
     private void verifyUserRestrictionEventsPresent(List<SecurityEvent> events) {
@@ -694,14 +756,15 @@
         final int userId = Process.myUserHandle().getIdentifier();
         findEvent(description, events,
                 e -> e.getTag() == tag &&
-                        getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                        getString(e, ADMIN_PKG_INDEX).equals(
+                                ADMIN_RECEIVER_COMPONENT.getPackageName()) &&
                         getInt(e, ADMIN_USER_INDEX) == userId &&
-                        UserManager.DISALLOW_FUN.equals(getString(e, USER_RESTRICTION_INDEX)));
+                        UserManager.DISALLOW_PRINTING.equals(getString(e, USER_RESTRICTION_INDEX)));
     }
 
     private void generateCameraPolicyEvents() {
-        mDevicePolicyManager.setCameraDisabled(getWho(), true);
-        mDevicePolicyManager.setCameraDisabled(getWho(), false);
+        mDevicePolicyManager.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, true);
+        mDevicePolicyManager.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, false);
     }
 
     private void verifyCameraPolicyEvents(List<SecurityEvent> events) {
@@ -709,14 +772,16 @@
 
         findEvent("set camera disabled", events,
                 e -> e.getTag() == TAG_CAMERA_POLICY_SET &&
-                        getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                        getString(e, ADMIN_PKG_INDEX).equals(
+                                ADMIN_RECEIVER_COMPONENT.getPackageName()) &&
                         getInt(e, ADMIN_USER_INDEX) == userId &&
                         getInt(e, TARGET_USER_INDEX) == userId &&
                         getInt(e, CAMERA_DISABLED_INDEX) == 1);
 
         findEvent("set camera enabled", events,
                 e -> e.getTag() == TAG_CAMERA_POLICY_SET &&
-                        getString(e, ADMIN_PKG_INDEX).equals(getWho().getPackageName()) &&
+                        getString(e, ADMIN_PKG_INDEX).equals(
+                                ADMIN_RECEIVER_COMPONENT.getPackageName()) &&
                         getInt(e, ADMIN_USER_INDEX) == userId &&
                         getInt(e, TARGET_USER_INDEX) == userId &&
                         getInt(e, CAMERA_DISABLED_INDEX) == 0);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RequestSetLocationProviderAllowedTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RequestSetLocationProviderAllowedTest.java
deleted file mode 100644
index 6b30207..0000000
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RequestSetLocationProviderAllowedTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.deviceowner;
-
-import static android.location.LocationManager.EXTRA_PROVIDER_NAME;
-import static android.location.LocationManager.MODE_CHANGED_ACTION;
-import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.location.Criteria;
-import android.location.LocationManager;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.LocationUtils;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test {@link DevicePolicyManager#requestSetLocationProviderAllowed}.
- */
-public class RequestSetLocationProviderAllowedTest extends BaseDeviceOwnerTest {
-
-    private static final long TIMEOUT_MS = 5000;
-
-    private static final String TEST_PROVIDER = "test_provider";
-
-    private LocationManager mLocationManager;
-
-    private boolean mLocationEnabled;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        // allow us to add mock location providers
-        LocationUtils.registerMockLocationProvider(mInstrumentation, /*enable=*/true);
-
-        mLocationManager = mContext.getSystemService(LocationManager.class);
-
-        mLocationEnabled = mLocationManager.isLocationEnabled();
-        if (!mLocationEnabled) {
-            setLocationEnabledAndAssert(true);
-        }
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        if (!mLocationEnabled) {
-            setLocationEnabledAndAssert(false);
-        }
-
-        LocationUtils.registerMockLocationProvider(mInstrumentation, /*enable=*/false);
-        super.tearDown();
-    }
-
-    public void testRequestSetLocationProviderAllowedTest() throws Exception {
-        mLocationManager.addTestProvider(TEST_PROVIDER,
-                /* requiresNetwork=*/ true,
-                /* requiresSatellite=*/ false,
-                /* requiresCell=*/ true,
-                /* hasMonetaryCost=*/ false,
-                /* supportsAltitude=*/ false,
-                /* supportsSpeed=*/ false,
-                /* supportsBearing=*/ false,
-                Criteria.POWER_MEDIUM,
-                Criteria.ACCURACY_FINE);
-        try {
-            mLocationManager.setTestProviderEnabled(TEST_PROVIDER, false);
-
-            requestProviderAllowedAndAssert(TEST_PROVIDER, true);
-            requestProviderAllowedAndAssert(TEST_PROVIDER, false);
-        } finally {
-            mLocationManager.removeTestProvider(TEST_PROVIDER);
-        }
-    }
-
-    private void requestProviderAllowedAndAssert(String provider, boolean allowed) throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (provider.equals(intent.getStringExtra(EXTRA_PROVIDER_NAME))) {
-                    latch.countDown();
-                }
-            }
-        };
-        mContext.registerReceiver(receiver, new IntentFilter(PROVIDERS_CHANGED_ACTION));
-
-        try {
-            mDevicePolicyManager.requestSetLocationProviderAllowed(getWho(), provider, allowed);
-            assertTrue("timed out waiting for provider change broadcast",
-                    latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            assertEquals(allowed, mLocationManager.isProviderEnabled(provider));
-        } finally {
-            mContext.unregisterReceiver(receiver);
-        }
-    }
-
-    private void setLocationEnabledAndAssert(boolean enabled) throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-            }
-        };
-        mContext.registerReceiver(receiver, new IntentFilter(MODE_CHANGED_ACTION));
-
-        try {
-            mDevicePolicyManager.setLocationEnabled(getWho(), enabled);
-            assertTrue("timed out waiting for location mode change broadcast",
-                    latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        } finally {
-            mContext.unregisterReceiver(receiver);
-        }
-    }
-}
diff --git a/hostsidetests/devicepolicy/app/DummyLauncher/Android.bp b/hostsidetests/devicepolicy/app/DummyLauncher/Android.bp
new file mode 100644
index 0000000..dd0ab0a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DummyLauncher/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+android_test_helper_app {
+    name: "DummyLauncher",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "cts-devicepolicy-suspensionchecker",
+    ],
+    test_suites: [
+        "arcts",
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
diff --git a/hostsidetests/devicepolicy/app/DummyLauncher/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DummyLauncher/AndroidManifest.xml
new file mode 100644
index 0000000..a80e9b4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DummyLauncher/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<!--
+/*
+ * Copyright 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.
+ */
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.dummylauncher">
+    <application android:label="Dummy Launcher">
+        <activity android:name="android.app.Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.HOME"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".QuietModeToggleActivity"
+                  android:exported="true"/>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.dummylauncher">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/DummyLauncher/src/com/android/cts/dummylauncher/QuietModeToggleActivity.java b/hostsidetests/devicepolicy/app/DummyLauncher/src/com/android/cts/dummylauncher/QuietModeToggleActivity.java
new file mode 100644
index 0000000..e659f15
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DummyLauncher/src/com/android/cts/dummylauncher/QuietModeToggleActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.dummylauncher;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.List;
+
+public class QuietModeToggleActivity extends Activity {
+    private static final String TAG = "QuietModeToggleActivity";
+    private static final String EXTRA_QUIET_MODE_STATE =
+            "com.android.cts.dummyactivity.QUIET_MODE_STATE";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        toggleQuietMode();
+        finish();
+    }
+
+    private void toggleQuietMode() {
+        final boolean quietModeState = getIntent().getBooleanExtra(EXTRA_QUIET_MODE_STATE, false);
+        final UserManager userManager = UserManager.get(this);
+
+        final List<UserHandle> users = userManager.getUserProfiles();
+        if (users.size() != 2) {
+            Log.e(TAG, "Unexpected number of profiles: " + users.size());
+            return;
+        }
+
+        final UserHandle profileHandle =
+                users.get(0).equals(Process.myUserHandle()) ? users.get(1) : users.get(0);
+
+        final String quietModeStateString = quietModeState ? "enabled" : "disabled";
+        if (userManager.isQuietModeEnabled(profileHandle) == quietModeState) {
+            Log.w(TAG, "Quiet mode is already " + quietModeStateString);
+            return;
+        }
+
+        userManager.requestQuietModeEnabled(quietModeState, profileHandle);
+        Log.i(TAG, "Quiet mode for user " + profileHandle + " was set to " + quietModeStateString);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileIntentFilterTest.java
similarity index 68%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileIntentFilterTest.java
index 91f2046..15123d4 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileIntentFilterTest.java
@@ -16,29 +16,33 @@
 package com.android.cts.managedprofile;
 
 import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.test.ActivityInstrumentationTestCase2;
 
 import androidx.test.InstrumentationRegistry;
 
+import java.util.List;
+
 /**
  * Test for {@link DevicePolicyManager#addCrossProfileIntentFilter} API.
  *
- * Note that it expects that there is an activity responding to {@code PrimaryUserActivity.ACTION}
- * in the primary profile, one to {@code ManagedProfileActivity.ACTION} in the secondary profile,
- * and one to {@code AllUsersActivity.ACTION} in both profiles.
+ * <p>Note that it expects that there is an activity responding to {@code PrimaryUserActivity
+ * .ACTION} in the primary profile, one to {@code ManagedProfileActivity.ACTION} in the secondary
+ * profile, and one to {@code AllUsersActivity.ACTION} in both profiles.
  */
-public class ManagedProfileTest extends ActivityInstrumentationTestCase2<TestActivity> {
+public class CrossProfileIntentFilterTest extends ActivityInstrumentationTestCase2<TestActivity> {
 
     private PackageManager mPackageManager;
     private DevicePolicyManager mDevicePolicyManager;
 
-    public ManagedProfileTest() {
+    public CrossProfileIntentFilterTest() {
         super(TestActivity.class);
     }
 
@@ -65,8 +69,11 @@
         testIntentFilter.addAction(PrimaryUserActivity.ACTION);
         mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
                 testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
-        assertEquals(1, mPackageManager.queryIntentActivities(
-                new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0).size());
+        final List<ResolveInfo> activities =
+                mPackageManager.queryIntentActivities(
+                        new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0);
+        assertThat(activities).hasSize(1);
+        assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isTrue();
 
         mDevicePolicyManager.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
 
@@ -85,8 +92,11 @@
         mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
                 testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
 
-        assertEquals(1, mPackageManager.queryIntentActivities(
-                new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0).size());
+        final List<ResolveInfo> activities =
+                mPackageManager.queryIntentActivities(
+                        new Intent(PrimaryUserActivity.ACTION), /* flags = */ 0);
+        assertThat(activities).hasSize(1);
+        assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isTrue();
         getActivity().startActivity(PrimaryUserActivity.ACTION);
         assertTrue(getActivity().checkActivityStarted());
     }
@@ -100,13 +110,16 @@
         mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
                 testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
 
-        assertEquals(2, mPackageManager.queryIntentActivities(
-                new Intent(AllUsersActivity.ACTION), /* flags = */ 0).size());
+        final List<ResolveInfo> activities =
+                mPackageManager.queryIntentActivities(
+                        new Intent(AllUsersActivity.ACTION), /* flags = */ 0);
+        assertThat(activities).hasSize(2);
+        assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isTrue();
         // If we used startActivity(), the user would have a disambiguation dialog presented which
         // requires human intervention, so we won't be testing like that
     }
 
-    public void testAddCrossProfileIntentFilter_managed() {
+    public void testAddCrossProfileIntentFilter_work() {
         assertEquals(1, mPackageManager.queryIntentActivities(
                 new Intent(ManagedProfileActivity.ACTION), /* flags = */ 0).size());
 
@@ -116,9 +129,22 @@
                 testIntentFilter, DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
 
         // We should still be resolving in the profile
-        assertEquals(1, mPackageManager.queryIntentActivities(
-                new Intent(ManagedProfileActivity.ACTION), /* flags = */ 0).size());
+        final List<ResolveInfo> activities =
+                mPackageManager.queryIntentActivities(
+                        new Intent(ManagedProfileActivity.ACTION), /* flags = */ 0);
+        assertThat(activities).hasSize(1);
+        assertThat(activitiesIncludeCrossProfileIntentForwarderActivity(activities)).isFalse();
         getActivity().startActivity(ManagedProfileActivity.ACTION);
         assertTrue(getActivity().checkActivityStarted());
     }
+
+    private boolean activitiesIncludeCrossProfileIntentForwarderActivity(
+            List<ResolveInfo> activities) {
+        for (ResolveInfo activity : activities) {
+            if (activity.isCrossProfileIntentForwarderActivity()) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java b/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java
index 8b6e354..55a361e 100644
--- a/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java
+++ b/hostsidetests/devicepolicy/app/SuspensionChecker/src/com/android/cts/suspensionchecker/ActivityLaunchTest.java
@@ -18,12 +18,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+
+import static org.junit.Assert.fail;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Class to test whether activities from this package can be launched and not suspended.
  */
@@ -40,4 +45,17 @@
         assertThat(LaunchCheckingActivity.checkLaunch(InstrumentationRegistry.getContext()))
                 .isFalse();
     }
+
+    @Test
+    public void testWaitForActivityNotLaunchable() throws Exception {
+        // Wait up to 1 minute checking if the app is not launchable anymore.
+        final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(1);
+        while (System.nanoTime() < deadline) {
+            if (!LaunchCheckingActivity.checkLaunch(InstrumentationRegistry.getContext())) {
+                return;
+            }
+            Thread.sleep(1000);
+        }
+        fail("Activity is still launchable");
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 54e9497..a6b92a6 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -74,6 +74,9 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public abstract class BaseDevicePolicyTest extends BaseHostJUnit4Test {
 
+    //The maximum time to wait for user to be unlocked.
+    private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
+    private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
     @Option(
             name = "skip-device-admin-feature-check",
             description = "Flag that allows to skip the check for android.software.device_admin "
@@ -227,6 +230,18 @@
         executeShellCommand("input keyevent KEYCODE_HOME");
     }
 
+    void waitForUserUnlock(int userId) throws Exception {
+        final String command = String.format("am get-started-user-state %d", userId);
+        final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
+        while (System.nanoTime() <= deadline) {
+            if (getDevice().executeShellCommand(command).startsWith(USER_STATE_UNLOCKED)) {
+                return;
+            }
+            Thread.sleep(1000);
+        }
+        fail("User is not unlocked.");
+    }
+
     @After
     public void tearDown() throws Exception {
         // reset the package verifier setting to its original value
@@ -985,4 +1000,15 @@
         getDevice().pushFile(file, TEST_UPDATE_LOCATION + "/" + fileName);
         file.delete();
     }
+
+    boolean hasService(String service) {
+        String command = "service check " + service;
+        try {
+            String commandOutput = getDevice().executeShellCommand(command);
+            return !commandOutput.contains("not found");
+        } catch (Exception e) {
+            CLog.w("Exception running '" + command + "': " + e);
+            return false;
+        }
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
index 2a82338..f9cf486 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
@@ -41,9 +41,6 @@
     private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
     private static final String NOTIFICATION_PKG =
             "com.android.cts.managedprofiletests.notificationsender";
-    //The maximum time to wait for user to be unlocked.
-    private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
-    private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
     protected int mParentUserId;
     // ID of the profile we'll create. This will always be a profile of the parent.
     protected int mProfileUserId;
@@ -70,22 +67,10 @@
             installAppAsUser(MANAGED_PROFILE_APK, USER_ALL);
             setProfileOwnerOrFail(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
                     mProfileUserId);
-            waitForUserUnlock();
+            waitForUserUnlock(mProfileUserId);
         }
     }
 
-    void waitForUserUnlock() throws Exception {
-        final String command = String.format("am get-started-user-state %d", mProfileUserId);
-        final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
-        while (System.nanoTime() <= deadline) {
-            if (getDevice().executeShellCommand(command).startsWith(USER_STATE_UNLOCKED)) {
-                return;
-            }
-            Thread.sleep(100);
-        }
-        fail("Profile user is not unlocked.");
-    }
-
     @Override
     public void tearDown() throws Exception {
         if (mHasFeature) {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index eff3ce7..d427645 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -7,17 +7,25 @@
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
 import android.platform.test.annotations.FlakyTest;
 import android.platform.test.annotations.LargeTest;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.util.StreamUtil;
 
 import org.junit.Test;
 
 import java.io.FileNotFoundException;
 import java.util.Collections;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
 
@@ -158,6 +166,33 @@
 
     @LargeTest
     @Test
+    public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+        if (!mHasManagedUserFeature) {
+            return;
+        }
+        getDevice().clearLogcat();
+        verifyCrossProfileAppsApi(
+                mProfileId,
+                mPrimaryUserId,
+                START_ACTIVITY_TEST_CLASS,
+                "testStartActivityIntent_sameTaskByDefault");
+        assertThat(findTaskId("CrossProfileSameTaskLauncherActivity"))
+                .isEqualTo(findTaskId("NonMainActivity"));
+    }
+
+    private int findTaskId(String className) throws Exception {
+        final Matcher matcher =
+                Pattern.compile(className + "#taskId#" + "(.*?)" + "#").matcher(readLogcat());
+        boolean isFound = matcher.find();
+        if (!isFound) {
+            fail("Task not found for " + className);
+            return -1;
+        }
+        return Integer.parseInt(matcher.group(1));
+    }
+
+    @LargeTest
+    @Test
     public void testPrimaryUserToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser) {
             return;
@@ -257,4 +292,14 @@
         return Collections.singletonMap(PARAM_TARGET_USER,
                 Integer.toString(getUserSerialNumber(targetUserId)));
     }
+
+    private String readLogcat() throws Exception {
+        getDevice().stopLogcat();
+        final String logcat;
+        try (InputStreamSource logcatStream = getDevice().getLogcat()) {
+            logcat = StreamUtil.getStringFromSource(logcatStream);
+        }
+        getDevice().startLogcat();
+        return logcat;
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 3a41d0f..09c05aa 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -2095,6 +2095,11 @@
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, testName, mUserId);
     }
 
+    protected void executeDeviceTestMethod(String className, String testName,
+            Map<String, String> params) throws Exception {
+        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, testName, mUserId, params);
+    }
+
     private void installAppPermissionAppAsUser()
             throws FileNotFoundException, DeviceNotAvailableException {
         installAppAsUser(PERMISSIONS_APP_APK, false, mUserId);
@@ -2312,19 +2317,4 @@
         getDevice().executeShellCommand(
                 restricted ? RESTRICT_BACKGROUND_ON_CMD : RESTRICT_BACKGROUND_OFF_CMD);
     }
-
-    // TODO: copied from RequiredServiceRule, which is on compatibility-device-util
-    // (and we use compatibility-host-util)
-    public boolean hasService(String service) {
-        // TODO: ideally should call SystemServiceManager directly, but we would need to open
-        // some @Testing APIs for that.
-        String command = "service check " + service;
-        try {
-            String commandOutput = getDevice().executeShellCommand(command);
-            return !commandOutput.contains("not found");
-        } catch (Exception e) {
-            CLog.w("Exception running '" + command + "': " + e);
-            return false;
-        }
-    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index a4e8877..e45e647 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -78,9 +78,6 @@
     private static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts";
     private static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/";
 
-    private static final String ARG_SECURITY_LOGGING_BATCH_NUMBER = "batchNumber";
-    private static final int SECURITY_EVENTS_BATCH_SIZE = 100;
-
     private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
 
     private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
@@ -456,97 +453,6 @@
     }
 
     @Test
-    public void testSecurityLoggingWithTwoUsers() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
-            return;
-        }
-
-        final int userId = createUser();
-        try {
-            // The feature can be enabled, but in a "paused" state. Attempting to retrieve logs
-            // should throw security exception.
-            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
-            executeDeviceTestMethod(".SecurityLoggingTest",
-                    "testRetrievingSecurityLogsThrowsSecurityException");
-            executeDeviceTestMethod(".SecurityLoggingTest",
-                    "testRetrievingPreviousSecurityLogsThrowsSecurityException");
-        } finally {
-            removeUser(userId);
-            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
-        }
-    }
-
-    @FlakyTest(bugId = 137093665)
-    @Test
-    public void testSecurityLoggingWithSingleUser() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        // Backup stay awake setting because testGenerateLogs() will turn it off.
-        final String stayAwake = getDevice().getSetting("global", "stay_on_while_plugged_in");
-        try {
-            // Turn logging on.
-            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
-            // Reboot to ensure ro.device_owner is set to true in logd and logging is on.
-            rebootAndWaitUntilReady();
-
-            // Generate various types of events on device side and check that they are logged.
-            executeDeviceTestMethod(".SecurityLoggingTest", "testGenerateLogs");
-            getDevice().executeShellCommand("dpm force-security-logs");
-            executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyGeneratedLogs");
-
-            // Reboot the device, so the security event ids are reset.
-            rebootAndWaitUntilReady();
-
-            // Verify event ids are consistent across a consecutive batch.
-            for (int batchNumber = 0; batchNumber < 3; batchNumber++) {
-                generateDummySecurityLogs();
-                getDevice().executeShellCommand("dpm force-security-logs");
-                executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyLogIds",
-                        Collections.singletonMap(ARG_SECURITY_LOGGING_BATCH_NUMBER,
-                                Integer.toString(batchNumber)));
-            }
-
-            // Immediately attempting to fetch events again should fail.
-            executeDeviceTestMethod(".SecurityLoggingTest",
-                    "testSecurityLoggingRetrievalRateLimited");
-            // Turn logging off.
-            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
-        } finally {
-            // Restore stay awake setting.
-            if (stayAwake != null) {
-                getDevice().setSetting("global", "stay_on_while_plugged_in", stayAwake);
-            }
-        }
-    }
-
-    @Test
-    public void testSecurityLoggingEnabledLogged() throws Exception {
-        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
-            return;
-        }
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
-            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
-                .setAdminPackageName(DEVICE_OWNER_PKG)
-                .setBoolean(true)
-                .build(),
-            new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
-                    .setAdminPackageName(DEVICE_OWNER_PKG)
-                    .setBoolean(false)
-                    .build());
-
-    }
-
-    private void generateDummySecurityLogs() throws DeviceNotAvailableException {
-        // Trigger security events of type TAG_ADB_SHELL_CMD.
-        for (int i = 0; i < SECURITY_EVENTS_BATCH_SIZE; i++) {
-            getDevice().executeShellCommand("echo just_testing_" + i);
-        }
-    }
-
-    @Test
     public void testNetworkLoggingWithTwoUsers() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -789,14 +695,6 @@
     }
 
     @Test
-    public void testRequestSetLocationProviderAllowed() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        executeDeviceOwnerTest("RequestSetLocationProviderAllowedTest");
-    }
-
-    @Test
     public void testDeviceOwnerProvisioning() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
index 6e7477b..450dc44 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -73,7 +73,7 @@
         if (isStatsdEnabled(getDevice())) {
             assertMetricsLogged(getDevice(), () -> {
                 runDeviceTestsAsUser(
-                        MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ManagedProfileTest",
+                        MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".CrossProfileIntentFilterTest",
                         "testAddCrossProfileIntentFilter_all", mProfileUserId);
             }, new DevicePolicyEventWrapper.Builder(ADD_CROSS_PROFILE_INTENT_FILTER_VALUE)
                     .setAdminPackageName(MANAGED_PROFILE_PKG)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
index edafb01..736b15d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -180,7 +180,7 @@
         try {
             rebootAndWaitUntilReady();
             verifyUserCredential(password, mPrimaryUserId);
-            waitForUserUnlock();
+            waitForUserUnlock(mProfileUserId);
             installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
             runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
         } finally {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index d28ecd9..794cb06 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -37,6 +37,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,6 +50,9 @@
 
     private static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
 
+    private static final String ARG_SECURITY_LOGGING_BATCH_NUMBER = "batchNumber";
+    private static final int SECURITY_EVENTS_BATCH_SIZE = 100;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -302,6 +306,97 @@
                     .build());
     }
 
+    @FlakyTest(bugId = 137093665)
+    @Test
+    public void testSecurityLoggingWithSingleUser() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Backup stay awake setting because testGenerateLogs() will turn it off.
+        final String stayAwake = getDevice().getSetting("global", "stay_on_while_plugged_in");
+        try {
+            // Turn logging on.
+            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+            // Reboot to ensure ro.device_owner is set to true in logd and logging is on.
+            rebootAndWaitUntilReady();
+            waitForUserUnlock(mUserId);
+
+            // Generate various types of events on device side and check that they are logged.
+            executeDeviceTestMethod(".SecurityLoggingTest", "testGenerateLogs");
+            getDevice().executeShellCommand("whoami"); // Generate adb command securty event
+            getDevice().executeShellCommand("dpm force-security-logs");
+            executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyGeneratedLogs");
+
+            // Reboot the device, so the security event ids are reset.
+            rebootAndWaitUntilReady();
+
+            // Verify event ids are consistent across a consecutive batch.
+            for (int batchNumber = 0; batchNumber < 3; batchNumber++) {
+                generateDummySecurityLogs();
+                getDevice().executeShellCommand("dpm force-security-logs");
+                executeDeviceTestMethod(".SecurityLoggingTest", "testVerifyLogIds",
+                        Collections.singletonMap(ARG_SECURITY_LOGGING_BATCH_NUMBER,
+                                Integer.toString(batchNumber)));
+            }
+
+            // Immediately attempting to fetch events again should fail.
+            executeDeviceTestMethod(".SecurityLoggingTest",
+                    "testSecurityLoggingRetrievalRateLimited");
+        } finally {
+            // Turn logging off.
+            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+            // Restore stay awake setting.
+            if (stayAwake != null) {
+                getDevice().setSetting("global", "stay_on_while_plugged_in", stayAwake);
+            }
+        }
+    }
+
+    @Test
+    public void testSecurityLoggingEnabledLogged() throws Exception {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
+            return;
+        }
+        assertMetricsLogged(getDevice(), () -> {
+            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+        }, new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
+                .setAdminPackageName(DEVICE_ADMIN_PKG)
+                .setBoolean(true)
+                .build(),
+            new DevicePolicyEventWrapper.Builder(EventId.SET_SECURITY_LOGGING_ENABLED_VALUE)
+                    .setAdminPackageName(DEVICE_ADMIN_PKG)
+                    .setBoolean(false)
+                    .build());
+    }
+
+    @Test
+    public void testSecurityLoggingWithTwoUsers() throws Exception {
+        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+
+        final int userId = createUser();
+        try {
+            // The feature can be enabled, but in a "paused" state. Attempting to retrieve logs
+            // should throw security exception.
+            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+            executeDeviceTestMethod(".SecurityLoggingTest",
+                    "testRetrievingSecurityLogsThrowsSecurityException");
+            executeDeviceTestMethod(".SecurityLoggingTest",
+                    "testRetrievingPreviousSecurityLogsThrowsSecurityException");
+        } finally {
+            removeUser(userId);
+            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+        }
+    }
+
+    private void generateDummySecurityLogs() throws Exception {
+        // Trigger security events of type TAG_ADB_SHELL_CMD.
+        for (int i = 0; i < SECURITY_EVENTS_BATCH_SIZE; i++) {
+            getDevice().executeShellCommand("echo just_testing_" + i);
+        }
+    }
     private int createSecondaryUserAsProfileOwner() throws Exception {
         final int userId = createUser();
         installAppAsUser(INTENT_RECEIVER_APK, userId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index d6e5a9e..bb75420 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -273,7 +273,8 @@
             return;
         }
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".KeyguardDisabledFeaturesTest",
-                "testSetKeyguardDisabledFeatures_onParentSilentIgnore", mUserId);
+                "testSetKeyguardDisabledFeatures_onParentSilentIgnoreWhenCallerIsNotOrgOwnedPO",
+                mUserId);
     }
 
     @FlakyTest
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 512d29b..9973fac 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -52,8 +52,16 @@
     private static final String DUMMY_IME_APK = "DummyIme.apk";
     private static final String DUMMY_IME_PKG = "com.android.cts.dummyime";
     private static final String DUMMY_IME_COMPONENT = DUMMY_IME_PKG + "/.DummyIme";
+    private static final String DUMMY_LAUNCHER_APK = "DummyLauncher.apk";
+    private static final String DUMMY_LAUNCHER_COMPONENT =
+            "com.android.cts.dummylauncher/android.app.Activity";
+    private static final String QUIET_MODE_TOGGLE_ACTIVITY =
+            "com.android.cts.dummylauncher/.QuietModeToggleActivity";
+    private static final String EXTRA_QUIET_MODE_STATE =
+            "com.android.cts.dummyactivity.QUIET_MODE_STATE";
+    public static final String SUSPENSION_CHECKER_CLASS =
+            "com.android.cts.suspensionchecker.ActivityLaunchTest";
 
-    private int mParentUserId = -1;
     protected int mUserId;
     private boolean mHasProfileToRemove = true;
     private boolean mHasSecondaryProfileToRemove = false;
@@ -69,14 +77,13 @@
 
         if (mHasFeature) {
             removeTestUsers();
-            mParentUserId = mPrimaryUserId;
             createManagedProfile();
         }
     }
 
     private void createManagedProfile() throws Exception {
-        mUserId = createManagedProfile(mParentUserId);
-        switchUser(mParentUserId);
+        mUserId = createManagedProfile(mPrimaryUserId);
+        switchUser(mPrimaryUserId);
         startUserAndWait(mUserId);
 
         installAppAsUser(DEVICE_ADMIN_APK, mUserId);
@@ -125,14 +132,14 @@
         mHasProfileToRemove = false;
 
         try {
-            installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
+            installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
             assertTrue(setDeviceOwner(DEVICE_ADMIN_COMPONENT_FLATTENED,
-                    mParentUserId, /*expectFailure*/false));
+                    mPrimaryUserId, /*expectFailure*/false));
             mHasSecondaryProfileToRemove = true;
             runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".LockScreenInfoTest", "testLockInfoIsNull",
-                    mParentUserId);
+                    mPrimaryUserId);
         } finally {
-            removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mParentUserId);
+            removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mPrimaryUserId);
             getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
         }
     }
@@ -266,6 +273,44 @@
                         .build());
     }
 
+    @FlakyTest(bugId = 137093665)
+    @Test
+    public void testSecurityLogging() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Backup stay awake setting because testGenerateLogs() will turn it off.
+        final String stayAwake = getDevice().getSetting("global", "stay_on_while_plugged_in");
+        try {
+            // Turn logging on.
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+                    "testEnablingSecurityLogging", mUserId);
+            // Reboot to ensure ro.device_owner is set to true in logd and logging is on.
+            rebootAndWaitUntilReady();
+            waitForUserUnlock(mUserId);
+
+            // Generate various types of events on device side and check that they are logged.
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG,".SecurityLoggingTest",
+                    "testGenerateLogs", mUserId);
+            getDevice().executeShellCommand("whoami"); // Generate adb command securty event
+            getDevice().executeShellCommand("dpm force-security-logs");
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+                    "testVerifyGeneratedLogs", mUserId);
+
+            // Immediately attempting to fetch events again should fail.
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+                    "testSecurityLoggingRetrievalRateLimited", mUserId);
+        } finally {
+            // Turn logging off.
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".SecurityLoggingTest",
+                    "testDisablingSecurityLogging", mUserId);
+            // Restore stay awake setting.
+            if (stayAwake != null) {
+                getDevice().setSetting("global", "stay_on_while_plugged_in", stayAwake);
+            }
+        }
+    }
+
     private void failToCreateUser() throws Exception {
         String command ="pm create-user " + "TestUser_" + System.currentTimeMillis();
         String commandOutput = getDevice().executeShellCommand(command);
@@ -275,25 +320,6 @@
         assertEquals("Error:", tokens[0]);
     }
 
-    protected int createUser() throws Exception {
-        String command ="pm create-user " + "TestUser_" + System.currentTimeMillis();
-        String commandOutput = getDevice().executeShellCommand(command);
-
-        String[] tokens = commandOutput.split("\\s+");
-        assertTrue(tokens.length > 0);
-        assertEquals("Success:", tokens[0]);
-        int userId = Integer.parseInt(tokens[tokens.length-1]);
-        startUser(userId);
-        return userId;
-    }
-
-    protected void removeUser(int userId) throws Exception  {
-        if (listUsers().contains(userId) && userId != USER_SYSTEM) {
-            String command = "am stop-user -w -f " + userId;
-            getDevice().executeShellCommand(command);
-        }
-    }
-
     @Test
     public void testSetTime() throws Exception {
         if (!mHasFeature) {
@@ -390,7 +416,7 @@
     }
 
     @Test
-    public void testApplicationHidden() throws Exception {
+    public void testApplicationHiddenParent() throws Exception {
         if (!mHasFeature) {
             return;
         }
@@ -458,14 +484,14 @@
         }
 
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AccountManagementParentTest",
-                "testCanSetAccountManagementRestriction", mUserId);
+                "testSetAccountManagementDisabledOnParent", mUserId);
         installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
         try {
             runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AccountManagementParentTest",
-                    "testAccountRestricted", mPrimaryUserId);
+                    "testAccountManagementDisabled", mPrimaryUserId);
         } finally {
             runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AccountManagementParentTest",
-                    "testCanRemoveAccountManagementRestriction", mUserId);
+                    "testEnableAccountManagement", mUserId);
         }
     }
 
@@ -491,7 +517,7 @@
     private void assertCanStartPersonalApp(String packageName, boolean canStart)
             throws DeviceNotAvailableException {
         runDeviceTestsAsUser(packageName, "com.android.cts.suspensionchecker.ActivityLaunchTest",
-                canStart ? "testCanStartActivity" : "testCannotStartActivity", mParentUserId);
+                canStart ? "testCanStartActivity" : "testCannotStartActivity", mPrimaryUserId);
     }
 
     @Test
@@ -525,17 +551,6 @@
                         userId, /* expectFailure */ false));
     }
 
-    private boolean hasService(String service) {
-        String command = "service check " + service;
-        try {
-            String commandOutput = getDevice().executeShellCommand(command);
-            return !commandOutput.contains("not found");
-        } catch (Exception e) {
-            LogUtil.CLog.w("Exception running '" + command + "': " + e);
-            return false;
-        }
-    }
-
     @Test
     public void testSetPersonalAppsSuspendedLogged() throws Exception {
         if (!mHasFeature|| !isStatsdEnabled(getDevice())) {
@@ -573,4 +588,47 @@
                         .setTimePeriod(0)
                         .build());
     }
+
+    @Test
+    public void testWorkProfileMaximumTimeOff() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
+        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".PersonalAppsSuspensionTest",
+                "testSetManagedProfileMaximumTimeOff1Sec", mUserId);
+
+        final String defaultLauncher = getDefaultLauncher();
+        try {
+            installAppAsUser(DUMMY_LAUNCHER_APK, true, true, mPrimaryUserId);
+            setAndStartLauncher(DUMMY_LAUNCHER_COMPONENT);
+            toggleQuietMode(true);
+            // Verify that at some point personal app becomes impossible to launch.
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, SUSPENSION_CHECKER_CLASS,
+                    "testWaitForActivityNotLaunchable", mPrimaryUserId);
+            toggleQuietMode(false);
+            // Ensure the profile is properly started before wipe broadcast is sent in teardown.
+            waitForUserUnlock(mUserId);
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".PersonalAppsSuspensionTest",
+                    "testPersonalAppsSuspendedByTimeout", mUserId);
+        } finally {
+            setAndStartLauncher(defaultLauncher);
+        }
+    }
+
+    private void toggleQuietMode(boolean quietModeEnable) throws Exception {
+        final String str = String.format("am start-activity -n %s --ez %s %s",
+                QUIET_MODE_TOGGLE_ACTIVITY, EXTRA_QUIET_MODE_STATE, quietModeEnable);
+        executeShellCommand(str);
+    }
+
+    private void setAndStartLauncher(String component) throws Exception {
+        String output = getDevice().executeShellCommand(String.format(
+                "cmd package set-home-activity --user %d %s", mPrimaryUserId, component));
+        assertTrue("failed to set home activity", output.contains("Success"));
+        output = getDevice().executeShellCommand(
+                String.format("cmd shortcut clear-default-launcher --user %d", mPrimaryUserId));
+        assertTrue("failed to clear default launcher", output.contains("Success"));
+        executeShellCommand("am start -W -n " + component);
+    }
 }
diff --git a/hostsidetests/hdmicec/OWNERS b/hostsidetests/hdmicec/OWNERS
index 254c58d..e7f5c32 100644
--- a/hostsidetests/hdmicec/OWNERS
+++ b/hostsidetests/hdmicec/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 141606867
+# Bug component: 114946
 nchalko@google.com
 amyjojo@google.com
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index a4bacf4..f9b1899 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -186,7 +186,6 @@
      */
     public void sendUserControlPress(CecDevice source, CecDevice destination,
             int keycode, boolean holdKey) throws Exception {
-        checkCecClient();
         String key = String.format("%02x", keycode);
         String command = "tx " + source + destination + ":" +
                 CecMessage.USER_CONTROL_PRESSED + ":" + key;
@@ -208,6 +207,19 @@
         mOutputConsole.flush();
     }
 
+    /**
+     * Sends a series of <UCP> [firstKeycode] from source to destination through the output console
+     * of the cec-communication channel immediately followed by <UCP> [secondKeycode]. No <UCR>
+     *  message is sent.
+     */
+    public void sendUserControlInterruptedPressAndHold(CecDevice source, CecDevice destination,
+            int firstKeycode, int secondKeycode, boolean holdKey) throws Exception {
+        sendUserControlPress(source, destination, firstKeycode, holdKey);
+        /* Sleep less than 200ms between press and release */
+        TimeUnit.MILLISECONDS.sleep(100);
+        sendUserControlPress(source, destination, secondKeycode, false);
+    }
+
     /** Sends a message to the output console of the cec-client */
     public void sendConsoleMessage(String message) throws Exception {
         checkCecClient();
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
index a90b09f..9931e26 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
@@ -174,4 +174,45 @@
                 HdmiCecConstants.CEC_CONTROL_BACK, true);
         lookForLog("Long press KEYCODE_BACK");
     }
+
+    /**
+     * Test 11.2.13-4
+     * Tests that the device responds correctly to a <User Control Pressed> [firstKeycode] press
+     * and hold operation when interrupted by a <User Control Pressed> [secondKeycode] before a
+     * <User Control Released> [firstKeycode] is sent.
+     */
+    @Test
+    public void cect_11_2_13_4_UserControlInterruptedPressAndHoldWithNoRelease() throws Exception {
+        ITestDevice device = getDevice();
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND);
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_UP,
+                HdmiCecConstants.CEC_CONTROL_BACK, true);
+        lookForLog("Long press KEYCODE_DPAD_UP");
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_DOWN,
+                HdmiCecConstants.CEC_CONTROL_UP, true);
+        lookForLog("Long press KEYCODE_DPAD_DOWN");
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_LEFT,
+                HdmiCecConstants.CEC_CONTROL_DOWN, true);
+        lookForLog("Long press KEYCODE_DPAD_LEFT");
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_RIGHT,
+                HdmiCecConstants.CEC_CONTROL_LEFT, true);
+        lookForLog("Long press KEYCODE_DPAD_RIGHT");
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_SELECT,
+                HdmiCecConstants.CEC_CONTROL_RIGHT, true);
+        lookForLog("Long press KEYCODE_DPAD_CENTER");
+        hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
+                CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_BACK,
+                HdmiCecConstants.CEC_CONTROL_SELECT, true);
+        lookForLog("Long press KEYCODE_BACK");
+    }
 }
diff --git a/hostsidetests/jvmti/base/jni/Android.bp b/hostsidetests/jvmti/base/jni/Android.bp
index 3567373..4940df3 100644
--- a/hostsidetests/jvmti/base/jni/Android.bp
+++ b/hostsidetests/jvmti/base/jni/Android.bp
@@ -17,6 +17,7 @@
 
     srcs: [
         "cts_agent.cpp",
+        "cts_logging.cpp",
         "tagging.cpp",
         "tracking.cpp",
         "redefine.cpp",
diff --git a/hostsidetests/jvmti/base/jni/cts_agent.cpp b/hostsidetests/jvmti/base/jni/cts_agent.cpp
index 3f3a836..55f2471 100644
--- a/hostsidetests/jvmti/base/jni/cts_agent.cpp
+++ b/hostsidetests/jvmti/base/jni/cts_agent.cpp
@@ -29,6 +29,7 @@
 extern void register_android_jvmti_cts_JvmtiRedefineClassesTest(jvmtiEnv*, JNIEnv*);
 extern void register_android_jvmti_cts_JvmtiTaggingTest(jvmtiEnv*, JNIEnv*);
 extern void register_android_jvmti_cts_JvmtiTrackingTest(jvmtiEnv*, JNIEnv*);
+extern void register_android_jvmti_cts_JvmtiRunTestBasedTest(jvmtiEnv*, JNIEnv*);
 
 static void InformMainAttach(jvmtiEnv* jenv,
                              JNIEnv* env,
@@ -42,6 +43,7 @@
   register_android_jvmti_cts_JvmtiRedefineClassesTest(jenv, env);
   register_android_jvmti_cts_JvmtiTaggingTest(jenv, env);
   register_android_jvmti_cts_JvmtiTrackingTest(jenv, env);
+  register_android_jvmti_cts_JvmtiRunTestBasedTest(jenv, env);
 
   // Use JNI to load the class.
   ScopedLocalRef<jclass> klass(env, GetClass(jenv, env, class_name, nullptr));
diff --git a/hostsidetests/jvmti/base/jni/cts_logging.cpp b/hostsidetests/jvmti/base/jni/cts_logging.cpp
new file mode 100644
index 0000000..b37e1f9
--- /dev/null
+++ b/hostsidetests/jvmti/base/jni/cts_logging.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <jvmti.h>
+
+#include "android-base/logging.h"
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_jvmti_cts_JvmtiRunTestBasedTest_setupExtraLogging(JNIEnv* env, jclass) {
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->SetVerboseFlag(JVMTI_VERBOSE_OTHER, true));
+}
+
+static JNINativeMethod gMethods[] = {
+  { "setupExtraLogging", "()V",
+          (void*)Java_android_jvmti_cts_JvmtiRunTestBasedTest_setupExtraLogging },
+};
+void register_android_jvmti_cts_JvmtiRunTestBasedTest(jvmtiEnv* jenv, JNIEnv* env) {
+  ScopedLocalRef<jclass> klass(env, GetClass(jenv, env,
+          "android/jvmti/cts/JvmtiRunTestBasedTest", nullptr));
+  if (klass.get() == nullptr) {
+    env->ExceptionClear();
+    return;
+  }
+
+  env->RegisterNatives(klass.get(), gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+    LOG(ERROR) << "Could not register natives for JvmtiRedefineClassesTest class";
+  }
+}
+
+}  // namespace art
diff --git a/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java b/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java
index fcb718a..a59fd3f 100644
--- a/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java
+++ b/hostsidetests/jvmti/base/run-test-based-app/src/android/jvmti/cts/JvmtiRunTestBasedTest.java
@@ -18,11 +18,14 @@
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
 
 import android.content.pm.PackageManager;
+import android.util.Log;
 
 import org.junit.After;
 import org.junit.Before;
@@ -36,13 +39,49 @@
     private PrintStream oldOut, oldErr;
     private ByteArrayOutputStream bufferedOut, bufferedErr;
 
+    private class TeeLogcatOutputStream extends OutputStream {
+        private OutputStream os;
+        private ByteArrayOutputStream lc_os;
+        public TeeLogcatOutputStream(OutputStream os) {
+            this.lc_os = new ByteArrayOutputStream();
+            this.os = os;
+        }
+        public void write(int b) throws IOException {
+            os.write(b);
+            if (b == (int)'\n') {
+              lc_os.flush();
+              Log.i(mActivity.getPackageName(), "Test Output: " +  lc_os.toString());
+              lc_os.reset();
+            } else {
+              lc_os.write(b);
+            }
+        }
+        public void close() throws IOException {
+            flush();
+            os.close();
+            lc_os.close();
+        }
+        public void flush() throws IOException {
+            os.flush();
+            lc_os.flush();
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         oldOut = System.out;
         oldErr = System.err;
+        bufferedOut = new ByteArrayOutputStream();
+        bufferedErr = new ByteArrayOutputStream();
 
-        System.setOut(new PrintStream(bufferedOut = new ByteArrayOutputStream(), true));
-        System.setErr(new PrintStream(bufferedErr = new ByteArrayOutputStream(), true));
+        if (doExtraLogging()) {
+            setupExtraLogging();
+            System.setOut(new PrintStream(new TeeLogcatOutputStream(bufferedOut), true));
+            System.setErr(new PrintStream(new TeeLogcatOutputStream(bufferedErr), true));
+        } else {
+            System.setOut(new PrintStream(bufferedOut, true));
+            System.setErr(new PrintStream(bufferedErr, true));
+        }
     }
 
     @After
@@ -51,6 +90,16 @@
         System.setErr(oldErr);
     }
 
+    private native void setupExtraLogging();
+
+    protected boolean doExtraLogging() throws Exception {
+        return mActivity
+            .getPackageManager()
+            .getApplicationInfo(mActivity.getPackageName(), PackageManager.GET_META_DATA)
+            .metaData
+            .getBoolean("android.jvmti.cts.run_test.extra_logging", /*defaultValue*/false);
+    }
+
     protected int getTestNumber() throws Exception {
         return mActivity.getPackageManager().getApplicationInfo(mActivity.getPackageName(),
                 PackageManager.GET_META_DATA).metaData.getInt("android.jvmti.cts.run_test_nr");
diff --git a/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
index 4388209..176ef69 100644
--- a/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1924" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
index b3fab4f..9b8f768 100644
--- a/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1925" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
index ee5da05..a07a28f 100644
--- a/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1926" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
index 57cf5cd..d51b1b6 100644
--- a/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1936" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
index 1bed7f1..1d29560 100644
--- a/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1982" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
index 6f1d556..b04fa7b 100644
--- a/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1995" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
index c5ecaec..673d7d0 100644
--- a/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="2004" />
+        <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
+        <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
         <activity android:name="android.jvmti.JvmtiActivity" >
         </activity>
     </application>
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java
index f8e5576..944e258 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4ClassRunnerWithParameters.java
@@ -18,12 +18,15 @@
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.testtype.HostTest;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.ISetOptionReceiver;
+import com.android.tradefed.testtype.ITestInformationReceiver;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -41,7 +44,7 @@
  * Custom JUnit4 parameterized test runner that also accommodate {@link IDeviceTest}.
  */
 public class DeviceJUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters
-        implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver {
+        implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver, ITestInformationReceiver {
 
     @Option(
         name = HostTest.SET_OPTION_NAME,
@@ -49,6 +52,7 @@
     )
     private Set<String> mKeyValueOptions = new HashSet<>();
 
+    private TestInformation mTestInformation;
     private ITestDevice mDevice;
     private IBuildInfo mBuildInfo;
     private IAbi mAbi;
@@ -78,6 +82,15 @@
         return mDevice;
     }
 
+    @Override
+    public void setTestInformation(TestInformation testInformation) {
+        mTestInformation = testInformation;
+    }
+
+    @Override
+    public TestInformation getTestInformation() {
+        return mTestInformation;
+    }
 
     @Override
     public Description getDescription() {
@@ -122,6 +135,9 @@
         if (testObj instanceof IAbiReceiver) {
             ((IAbiReceiver) testObj).setAbi(mAbi);
         }
+        if (testObj instanceof ITestInformationReceiver) {
+            ((ITestInformationReceiver) testObj).setTestInformation(mTestInformation);
+        }
         HostTest.setOptionToLoadedObject(testObj, new ArrayList<String>(mKeyValueOptions));
         return testObj;
     }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java
index e28d81d..ea7ce7f 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/DeviceJUnit4Parameterized.java
@@ -20,6 +20,7 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.HostTest;
 import com.android.tradefed.testtype.IAbi;
@@ -27,20 +28,21 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.ISetOptionReceiver;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.junit.runner.Description;
+import com.android.tradefed.testtype.ITestInformationReceiver;
+
 import org.junit.runner.Runner;
 import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.Parameterized;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 /**
  * Custom JUnit4 parameterized test runner that also accommodate {@link IDeviceTest}.
  */
 public class DeviceJUnit4Parameterized extends Parameterized
-        implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver {
+        implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver, ITestInformationReceiver {
 
     @Option(
         name = HostTest.SET_OPTION_NAME,
@@ -49,6 +51,7 @@
     private Set<String> mKeyValueOptions = new HashSet<>();
 
     private ITestDevice mDevice;
+    private TestInformation mTestInformation;
     private List<Runner> mRunners;
 
     public DeviceJUnit4Parameterized(Class<?> klass) throws Throwable {
@@ -89,13 +92,27 @@
         return mDevice;
     }
 
-
     @Override
     protected List<Runner> getChildren() {
         return mRunners;
     }
 
     @Override
+    public void setTestInformation(TestInformation testInformation) {
+        mTestInformation = testInformation;
+        for (Runner runner : mRunners) {
+            if (runner instanceof ITestInformationReceiver) {
+                ((ITestInformationReceiver) runner).setTestInformation(mTestInformation);
+            }
+        }
+    }
+
+    @Override
+    public TestInformation getTestInformation() {
+        return mTestInformation;
+    }
+
+    @Override
     protected void runChild(Runner runner, RunNotifier notifier) {
         try {
             OptionSetter setter = new OptionSetter(runner);
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
index 24a96a7..69a7d9c 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
@@ -25,12 +25,22 @@
 import com.android.tradefed.config.OptionClass;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.ITestInformationReceiver;
 import com.android.tradefed.util.FileUtil;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileFilter;
@@ -53,18 +63,12 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
 
 /**
  * Test that verifies video bitstreams decode pixel perfectly
  */
 @OptionClass(alias="media-bitstreams-test")
-public abstract class MediaBitstreamsTest implements IDeviceTest, IBuildReceiver, IAbiReceiver {
+public abstract class MediaBitstreamsTest implements IDeviceTest, IBuildReceiver, IAbiReceiver, ITestInformationReceiver {
 
     @Option(name = MediaBitstreams.OPT_HOST_BITSTREAMS_PATH,
             description = "Absolute path of Ittiam bitstreams (host)",
@@ -132,6 +136,7 @@
 
     private IAbi mAbi;
     private ITestDevice mDevice;
+    private TestInformation mTestInfo;
 
     static File getDefaultBitstreamsDir() {
         File mediaDir = MediaPreparer.getDefaultMediaDir();
@@ -239,6 +244,16 @@
         return mDevice;
     }
 
+    @Override
+    public void setTestInformation(TestInformation testInformation) {
+        mTestInfo = testInformation;
+    }
+
+    @Override
+    public TestInformation getTestInformation() {
+        return mTestInfo;
+    }
+
     /*
      * Returns true if all necessary media files exist on the device, and false otherwise.
      *
@@ -453,6 +468,7 @@
     public void testGetBitstreamsFormats() throws DeviceNotAvailableException, IOException {
         ReportProcessor processor = new ProcessBitstreamsFormats();
         processor.processDeviceReport(
+                getTestInformation(),
                 getDevice(),
                 getCurrentMethod(),
                 MediaBitstreams.KEY_BITSTREAMS_FORMATS_XML);
@@ -497,6 +513,7 @@
         SupportedBitstreamsProcessor preparer;
         preparer = new SupportedBitstreamsProcessor(prefix, mDebugTargetDevice);
         preparer.processDeviceReport(
+                getTestInformation(),
                 device,
                 MediaBitstreams.K_TEST_GET_SUPPORTED_BITSTREAMS,
                 MediaBitstreams.KEY_SUPPORTED_BITSTREAMS_TXT);
@@ -547,6 +564,7 @@
                 ReportProcessor processor;
                 processor = new ProcessBitstreamsValidation(toPush, curMethod);
                 processor.processDeviceReport(
+                        getTestInformation(),
                         device,
                         curMethod,
                         MediaBitstreams.KEY_BITSTREAMS_VALIDATION_TXT);
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
index b334b0b..c924cbc 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
@@ -19,6 +19,7 @@
 import com.android.tradefed.config.Configuration;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -139,6 +140,7 @@
     }
 
     private boolean runDeviceTest(
+            TestInformation testInfo,
             ITestDevice device, String method, String reportKey, int testTimeout,
             long shellTimeout)
             throws DeviceNotAvailableException {
@@ -160,7 +162,7 @@
         // AndroidJUnitTest requires a IConfiguration to work properly, add a stub to this
         // implementation to avoid an NPE.
         instrTest.setConfiguration(new Configuration("stub", "stub"));
-        instrTest.run(new MediaBitstreamsListener());
+        instrTest.run(testInfo, new MediaBitstreamsListener());
 
         return checkFile(reportKey);
 
@@ -178,12 +180,12 @@
         return true;
     }
 
-    void processDeviceReport(
+    void processDeviceReport(TestInformation testInfo,
             ITestDevice device, String method, String reportKey)
             throws DeviceNotAvailableException, IOException {
         try {
             setUp(device);
-            while (!runDeviceTest(device, method, reportKey, 0, 0)) {
+            while (!runDeviceTest(testInfo, device, method, reportKey, 0, 0)) {
                 if (!recover(device, mMetrics.get(reportKey))) {
                     return;
                 }
diff --git a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
index 07fa856..750e98c 100644
--- a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
+++ b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
@@ -24,7 +24,6 @@
 
 import android.media.cts.BaseMultiUserTest;
 import android.media.cts.MediaSessionTestHelperConstants;
-
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.AppModeInstant;
 import android.platform.test.annotations.RequiresDevice;
@@ -61,8 +60,6 @@
 
     private final List<Integer> mNotificationListeners = new ArrayList<>();
 
-    private boolean mNotificationListenerDisabled;
-
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -70,7 +67,6 @@
         // Ensure that the previously running media session test helper app doesn't exist.
         getDevice().uninstallPackage(MEDIA_SESSION_TEST_HELPER_PKG);
         mNotificationListeners.clear();
-        mNotificationListenerDisabled = "true".equals(getDevice().getProperty("ro.config.low_ram"));
     }
 
     @Override
@@ -101,11 +97,6 @@
     }
 
     private void testGetActiveSessions_primaryUser(boolean instant) throws Exception {
-        if (mNotificationListenerDisabled) {
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "NotificationListener is disabled. Test won't run.");
-            return;
-        }
         int primaryUserId = getDevice().getPrimaryUserId();
 
         setAllowGetActiveSessionForTest(true, primaryUserId);
@@ -144,11 +135,6 @@
                     "Cannot create a new user. Skipping multi-user test cases.");
             return;
         }
-        if (mNotificationListenerDisabled) {
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "NotificationListener is disabled. Test won't run.");
-            return;
-        }
 
         // Test if another user can get the session.
         int newUser = createAndStartUser();
@@ -183,11 +169,6 @@
                     "Cannot create a new user. Skipping multi-user test cases.");
             return;
         }
-        if (mNotificationListenerDisabled) {
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "NotificationListener is disabled. Test won't run.");
-            return;
-        }
 
         // Test if another restricted profile can get the session.
         // Remove the created user first not to exceed system's user number limit.
@@ -223,11 +204,6 @@
                     "Device doesn't support managed profiles. Test won't run.");
             return;
         }
-        if (mNotificationListenerDisabled) {
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "NotificationListener is disabled. Test won't run.");
-            return;
-        }
 
         // Test if another managed profile can get the session.
         // Remove the created user first not to exceed system's user number limit.
@@ -241,12 +217,6 @@
     @AppModeFull
     @RequiresDevice
     public void testGetActiveSessions_noSession2() throws Exception {
-        if (mNotificationListenerDisabled) {
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "NotificationListener is disabled. Test won't run.");
-            return;
-        }
-
         int primaryUserId = getDevice().getPrimaryUserId();
 
         setAllowGetActiveSessionForTest(true, primaryUserId);
@@ -264,12 +234,6 @@
     @AppModeFull
     @RequiresDevice
     public void testGetActiveSessions_withSession2() throws Exception {
-        if (mNotificationListenerDisabled) {
-            CLog.logAndDisplay(LogLevel.INFO,
-                    "NotificationListener is disabled. Test won't run.");
-            return;
-        }
-
         int primaryUserId = getDevice().getPrimaryUserId();
 
         setAllowGetActiveSessionForTest(true, primaryUserId);
diff --git a/hostsidetests/net/AndroidTest.xml b/hostsidetests/net/AndroidTest.xml
index 0452fc6..7cc0dd1 100644
--- a/hostsidetests/net/AndroidTest.xml
+++ b/hostsidetests/net/AndroidTest.xml
@@ -25,6 +25,7 @@
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="teardown-command" value="cmd power set-mode 0" />
         <option name="teardown-command" value="cmd battery reset" />
+        <option name="teardown-command" value="cmd netpolicy stop-watching" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 529b640..69dd2ad 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -79,7 +79,6 @@
     private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
 
     private static final int SLEEP_TIME_SEC = 1;
-    private static final boolean DEBUG = true;
 
     // Constants below must match values defined on app2's Common.java
     private static final String MANIFEST_RECEIVER = "ManifestReceiver";
@@ -159,6 +158,7 @@
         if (!mIsLocationOn) {
             enableLocation();
         }
+        executeShellCommand("cmd netpolicy start-watching " + mUid);
         setAppIdle(false);
 
         Log.i(TAG, "Apps status:\n"
@@ -167,6 +167,7 @@
     }
 
     protected void tearDown() throws Exception {
+        executeShellCommand("cmd netpolicy stop-watching");
         if (!mIsLocationOn) {
             disableLocation();
         }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
index cedd62a..5ecb399 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -16,6 +16,7 @@
 package com.android.cts.net.hostside;
 
 import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_APP2_PKG;
 import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
 
 import android.os.Environment;
@@ -58,7 +59,7 @@
             for (String cmd : new String[] {
                     "dumpsys netpolicy",
                     "dumpsys network_management",
-                    "dumpsys usagestats " + TEST_PKG,
+                    "dumpsys usagestats " + TEST_PKG + " " + TEST_APP2_PKG,
                     "dumpsys usagestats appstandby",
             }) {
                 dumpCommandOutput(out, cmd);
diff --git a/hostsidetests/packagemanager/dynamicmime/Android.bp b/hostsidetests/packagemanager/dynamicmime/Android.bp
new file mode 100644
index 0000000..e23c99a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+java_test_host {
+    name: "CtsDynamicMimeHostTestCases",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    libs: [
+        "cts-tradefed",
+        "tradefed",
+        "compatibility-host-util",
+        "truth-prebuilt",
+    ],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
new file mode 100644
index 0000000..56ab6ca
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<configuration description="Config for the CTS dynamic MIME tests">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <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" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="CtsDynamicMimeHelperApp.apk" />
+        <option name="test-file-name" value="CtsDynamicMimeTestApp.apk" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/dynamic-mime-test" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/dynamic-mime-test"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsDynamicMimeUpdateAppFirstGroup.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppFirstGroup.apk"/>
+        <option name="push" value="CtsDynamicMimeUpdateAppSecondGroup.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppSecondGroup.apk"/>
+        <option name="push" value="CtsDynamicMimeUpdateAppBothGroups.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppBothGroups.apk"/>
+        <option name="push" value="CtsDynamicMimePreferredApp.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimePreferredApp.apk"/>
+    </target_preparer>
+
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsDynamicMimeHostTestCases.jar" />
+    </test>
+
+</configuration>
diff --git a/hostsidetests/packagemanager/dynamicmime/OWNERS b/hostsidetests/packagemanager/dynamicmime/OWNERS
new file mode 100644
index 0000000..af9eee9
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136635677
+tantoshchuk@google.com
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/app/Android.bp b/hostsidetests/packagemanager/dynamicmime/app/Android.bp
new file mode 100644
index 0000000..1e983e1
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/Android.bp
@@ -0,0 +1,88 @@
+// 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.
+
+android_test_helper_app {
+    name: "CtsDynamicMimeHelperApp",
+    defaults: ["cts_support_defaults"],
+    static_libs: ["CtsDynamicMimeCommon"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+    manifest: "manifests/AndroidManifest_helper.xml",
+}
+
+android_test_helper_app {
+    name: "CtsDynamicMimeUpdateAppFirstGroup",
+    defaults: ["cts_support_defaults"],
+    static_libs: ["CtsDynamicMimeCommon"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+    manifest: "manifests/AndroidManifest_update_firstGroup.xml",
+}
+
+android_test_helper_app {
+    name: "CtsDynamicMimeUpdateAppSecondGroup",
+    defaults: ["cts_support_defaults"],
+    static_libs: ["CtsDynamicMimeCommon"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+    manifest: "manifests/AndroidManifest_update_secondGroup.xml",
+}
+
+android_test_helper_app {
+    name: "CtsDynamicMimeUpdateAppBothGroups",
+    defaults: ["cts_support_defaults"],
+    static_libs: ["CtsDynamicMimeCommon"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+    manifest: "manifests/AndroidManifest_update_bothGroups.xml",
+}
+
+android_test_helper_app {
+    name: "CtsDynamicMimePreferredApp",
+    defaults: ["cts_support_defaults"],
+    static_libs: ["CtsDynamicMimeCommon"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+    manifest: "manifests/AndroidManifest_preferred.xml",
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml
new file mode 100644
index 0000000..1373430
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.dynamicmime.helper">
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.helper.FILTER_INFO_HOOK_group_first"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_first"/>
+            </intent-filter>
+        </activity>
+        <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.helper.FILTER_INFO_HOOK_group_second"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.dynamicmime.helper" >
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
new file mode 100644
index 0000000..83cdf0e
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.dynamicmime.preferred">
+    <application android:testOnly="true"
+                 android:label="TestApp.Application">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+            <intent-filter android:label="TestApp.FirstActivity">
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_first"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_first"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
+            <intent-filter android:label="TestApp.TwoGroupsActivity">
+                <action android:name="android.intent.action.SEND"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_both"/>
+                <data android:mimeGroup="group_third"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.dynamicmime.preferred">
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml
new file mode 100644
index 0000000..802d13c
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.dynamicmime.update">
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_first"/>
+            </intent-filter>
+        </activity>
+        <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.dynamicmime.update" >
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml
new file mode 100644
index 0000000..adc93cf
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.dynamicmime.update">
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_first"/>
+            </intent-filter>
+        </activity>
+        <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.dynamicmime.update" >
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml
new file mode 100644
index 0000000..e93a3d5
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.dynamicmime.update">
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+        <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+            </intent-filter>
+        </receiver>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.dynamicmime.update" >
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java b/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java
new file mode 100644
index 0000000..7c79fef
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.app;
+
+import static android.dynamicmime.common.Constants.ACTION_REQUEST;
+import static android.dynamicmime.common.Constants.ACTION_RESPONSE;
+import static android.dynamicmime.common.Constants.EXTRA_GROUP;
+import static android.dynamicmime.common.Constants.EXTRA_MIMES;
+import static android.dynamicmime.common.Constants.EXTRA_REQUEST;
+import static android.dynamicmime.common.Constants.EXTRA_RESPONSE;
+import static android.dynamicmime.common.Constants.REQUEST_CLEAR;
+import static android.dynamicmime.common.Constants.REQUEST_GET;
+import static android.dynamicmime.common.Constants.REQUEST_SET;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Receiver of {@link android.dynamicmime.testapp.util.AppMimeGroups} commands
+ */
+public class AppMimeGroupsReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!ACTION_REQUEST.equals(intent.getAction())) {
+            return;
+        }
+
+        String mimeGroup = intent.getStringExtra(EXTRA_GROUP);
+        String[] mimeTypes = intent.getStringArrayExtra(EXTRA_MIMES);
+        int requestCode = intent.getIntExtra(EXTRA_REQUEST, -1);
+
+        Intent response = new Intent(ACTION_RESPONSE);
+        switch (requestCode) {
+            case REQUEST_SET:
+                context.getPackageManager().setMimeGroup(mimeGroup, new ArraySet<>(mimeTypes));
+                break;
+            case REQUEST_CLEAR:
+                context.getPackageManager().clearMimeGroup(mimeGroup);
+                break;
+            case REQUEST_GET:
+                response.putExtra(EXTRA_RESPONSE, getMimeGroup(context, mimeGroup));
+                break;
+            default:
+                //do not respond with broadcast to indicate that something is wrong
+                return;
+        }
+
+        context.sendBroadcast(response);
+    }
+
+    private String[] getMimeGroup(Context context, String mimeGroup) {
+        Set<String> mimeTypes = context.getPackageManager().getMimeGroup(mimeGroup);
+        return mimeTypes != null ? mimeTypes.toArray(new String[0]) : null;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/common/Android.bp b/hostsidetests/packagemanager/dynamicmime/common/Android.bp
new file mode 100644
index 0000000..a80aba7
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+    name: "CtsDynamicMimeCommon",
+    defaults: ["cts_defaults"],
+    sdk_version: "test_current",
+    srcs: ["src/**/*.java"],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java
new file mode 100644
index 0000000..e3289ce
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.common;
+
+public class Constants {
+    public static final String ACTION_REQUEST = "android.dynamicmime.UPDATE_MIME_GROUP_REQUEST";
+    public static final String ACTION_RESPONSE = "android.dynamicmime.UPDATE_MIME_GROUP_RESPONSE";
+
+    public static final String EXTRA_GROUP = "EXTRA_GROUP";
+    public static final String EXTRA_MIMES = "EXTRA_MIME";
+    public static final String EXTRA_REQUEST = "EXTRA_REQUEST";
+    public static final String EXTRA_RESPONSE = "EXTRA_RESPONSE";
+
+    public static final String GROUP_FIRST = "group_first";
+    public static final String GROUP_SECOND = "group_second";
+    public static final String GROUP_THIRD = "group_third";
+    public static final String GROUP_UNDEFINED = "undefined";
+
+    public static final String ALIAS_BOTH_GROUPS = "group_both";
+    public static final String ALIAS_BOTH_GROUPS_AND_TYPE = "groups_and_type";
+
+    public static final String[] GROUPS = {
+            GROUP_FIRST, GROUP_SECOND, GROUP_UNDEFINED
+    };
+
+    public static final int REQUEST_CLEAR = 1;
+    public static final int REQUEST_SET = 2;
+    public static final int REQUEST_GET = 3;
+
+    public static final String MIME_TEXT_PLAIN = "text/plain";
+    public static final String MIME_TEXT_XML = "text/xml";
+    public static final String MIME_TEXT_ANY = "text/*";
+    public static final String MIME_IMAGE_PNG = "image/png";
+    public static final String MIME_IMAGE_JPEG = "image/jpeg";
+    public static final String MIME_IMAGE_ANY = "image/*";
+    public static final String MIME_ANY = "*/*";
+
+    public static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+    public static final String PACKAGE_ACTIVITIES = "android.dynamicmime.common.activity";
+    public static final String PACKAGE_HELPER_APP = "android.dynamicmime.helper";
+    public static final String PACKAGE_UPDATE_APP = "android.dynamicmime.update";
+    public static final String PACKAGE_PREFERRED_APP = "android.dynamicmime.preferred";
+
+    public static final String ACTIVITY_FIRST = ".FirstActivity";
+    public static final String ACTIVITY_SECOND = ".SecondActivity";
+    public static final String ACTIVITY_BOTH = ".TwoGroupsActivity";
+    public static final String ACTIVITY_BOTH_AND_TYPE = ".TwoGroupsAndTypeActivity";
+
+    public static final String DATA_DIR = "/data/local/tmp/dynamic-mime-test/";
+
+    public static final String APK_PREFERRED_APP = DATA_DIR + "CtsDynamicMimePreferredApp.apk";
+    public static final String APK_BOTH_GROUPS = DATA_DIR + "CtsDynamicMimeUpdateAppBothGroups.apk";
+    public static final String APK_FIRST_GROUP = DATA_DIR + "CtsDynamicMimeUpdateAppFirstGroup.apk";
+    public static final String APK_SECOND_GROUP = DATA_DIR +
+            "CtsDynamicMimeUpdateAppSecondGroup.apk";
+
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java
new file mode 100644
index 0000000..94aeb8f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class FirstActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java
new file mode 100644
index 0000000..3ce0ff3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class SecondActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java
new file mode 100644
index 0000000..9171f0b
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class TwoGroupsActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java
new file mode 100644
index 0000000..0a2f04d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class TwoGroupsAndTypeActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java
new file mode 100644
index 0000000..25ac458
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Preferred Activities test cases
+ *
+ * Verifies that preferred activity is removed after any change to any MIME group
+ * declared by preferred activity package
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PreferredActivitiesTestCases extends BaseHostJUnit4Test {
+    private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+
+    @Before
+    public void setUp() throws DeviceNotAvailableException {
+        // wake up and unlock device
+        getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        getDevice().disableKeyguard();
+    }
+
+    @Test
+    public void testRemoveFromGroup() throws DeviceNotAvailableException {
+        runDeviceTest("testRemoveFromGroup");
+    }
+
+    @Test
+    public void testAddToGroup() throws DeviceNotAvailableException {
+        runDeviceTest("testAddToGroup");
+    }
+
+    @Test
+    public void testClearGroup() throws DeviceNotAvailableException {
+        runDeviceTest("testClearGroup");
+    }
+
+    @Test
+    public void testModifyGroupWithoutActualGroupChanges() throws DeviceNotAvailableException {
+        runDeviceTest("testModifyGroupWithoutActualGroupChanges");
+    }
+
+    @Test
+    public void testModifyGroupWithoutActualIntentFilterChanges()
+            throws DeviceNotAvailableException {
+        runDeviceTest("testModifyGroupWithoutActualIntentFilterChanges");
+    }
+
+    private void runDeviceTest(String testMethodName) throws DeviceNotAvailableException {
+        runDeviceTests(PACKAGE_TEST_APP, PACKAGE_TEST_APP + ".preferred.PreferredActivitiesTest",
+                testMethodName);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
new file mode 100644
index 0000000..26ac19f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Reboot test cases
+ *
+ * Reuses existing test cases from {@link android.dynamicmime.testapp.ComplexFilterTest}
+ * and {@link android.dynamicmime.testapp.SingleAppTest}
+ * by "inserting" device reboot between setup part (MIME group commands) and verification part
+ * (MIME group assertions) in each test case
+ *
+ * @see android.dynamicmime.testapp.reboot.PreRebootSingleAppTest
+ * @see android.dynamicmime.testapp.reboot.PostRebootSingleAppTest
+ * @see #runTestWithReboot(String, String)
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class RebootTestCases extends BaseHostJUnit4Test {
+    private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+    private static final String PACKAGE_REBOOT_TESTS = PACKAGE_TEST_APP + ".reboot";
+
+    @Test
+    public void testGroupWithExactType() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testGroupWithExactType");
+    }
+
+    @Test
+    public void testGroupWithWildcardType() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testGroupWithWildcardType");
+    }
+
+    @Test
+    public void testGroupWithAnyType() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testGroupWithAnyType");
+    }
+
+    @Test
+    public void testAddSimilarTypes() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testAddSimilarTypes");
+    }
+
+    @Test
+    public void testAddDifferentTypes() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testAddDifferentTypes");
+    }
+
+    @Test
+    public void testRemoveExactType() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testRemoveExactType");
+    }
+
+    @Test
+    public void testRemoveDifferentType() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testRemoveDifferentType");
+    }
+
+    @Test
+    public void testRemoveSameBaseType_keepSpecific() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testRemoveSameBaseType_keepSpecific");
+    }
+
+    @Test
+    public void testRemoveSameBaseType_keepWildcard() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testRemoveSameBaseType_keepWildcard");
+    }
+
+    @Test
+    public void testRemoveAnyType_keepSpecific() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testRemoveAnyType_keepSpecific");
+    }
+
+    @Test
+    public void testRemoveAnyType_keepWildcard() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testRemoveAnyType_keepWildcard");
+    }
+
+    @Test
+    public void testResetWithoutIntersection() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testResetWithoutIntersection");
+    }
+
+    @Test
+    public void testResetWithIntersection() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testResetWithIntersection");
+    }
+
+    @Test
+    public void testResetToEmpty() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testResetToEmpty");
+    }
+
+    @Test
+    public void testClear() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testClear");
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterAdd() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterAdd");
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterSet() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterSet");
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterRemove() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterRemove");
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterClear() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterClear");
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterRemove() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterRemove");
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterClear() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterClear");
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterSetEmpty() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterSetEmpty");
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterSetNull() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterSetNull");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentAdd() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentAdd");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentAddToBoth() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentAddToBoth");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentRemove() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentRemove");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentClear() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentClear");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentClearBoth() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentClearBoth");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentSet() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentSet");
+    }
+
+    @Test
+    public void testMimeGroupsIndependentSetBoth() throws DeviceNotAvailableException {
+        runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentSetBoth");
+    }
+
+    @Test
+    public void testMimeGroupsNotIntersect() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testMimeGroupsNotIntersect");
+    }
+
+    @Test
+    public void testMimeGroupsIntersect() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testMimeGroupsIntersect");
+    }
+
+    @Test
+    public void testRemoveTypeFromIntersection() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testRemoveTypeFromIntersection");
+    }
+
+    @Test
+    public void testRemoveIntersectionFromBothGroups() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testRemoveIntersectionFromBothGroups");
+    }
+
+    @Test
+    public void testClearGroupWithoutIntersection() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testClearGroupWithoutIntersection");
+    }
+
+    @Test
+    public void testClearGroupWithIntersection() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testClearGroupWithIntersection");
+    }
+
+    @Test
+    public void testMimeGroupNotIntersectWithStaticType() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testMimeGroupNotIntersectWithStaticType");
+    }
+
+    @Test
+    public void testMimeGroupIntersectWithStaticType() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testMimeGroupIntersectWithStaticType");
+    }
+
+    @Test
+    public void testRemoveStaticTypeFromMimeGroup() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testRemoveStaticTypeFromMimeGroup");
+    }
+
+    @Test
+    public void testClearGroupContainsStaticType() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testClearGroupContainsStaticType");
+    }
+
+    @Test
+    public void testClearGroupNotContainStaticType() throws DeviceNotAvailableException {
+        runTestWithReboot("ComplexFilterTest", "testClearGroupNotContainStaticType");
+    }
+
+    private void runTestWithReboot(String testClassName, String testMethodName)
+            throws DeviceNotAvailableException {
+        runPreReboot(testClassName, testMethodName);
+        getDevice().reboot();
+        runPostReboot(testClassName, testMethodName);
+    }
+
+    private void runPostReboot(String testClassName, String testMethodName)
+        throws DeviceNotAvailableException {
+        runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PostReboot" + testClassName,
+            testMethodName);
+    }
+
+    private void runPreReboot(String testClassName, String testMethodName)
+        throws DeviceNotAvailableException {
+        runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PreReboot" + testClassName,
+            testMethodName);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java
new file mode 100644
index 0000000..d3a2e89
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Invokes device-side tests as is, no need for any host-side setup
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class TestCases extends BaseHostJUnit4Test {
+    private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+
+    @Test
+    public void testDynamicMimeWithSingleApp() throws DeviceNotAvailableException {
+        runDeviceTests("SingleAppTest");
+    }
+
+    @Test
+    public void testDynamicMimeWithMultipleApps() throws DeviceNotAvailableException {
+        runDeviceTests("MultipleAppsTest");
+    }
+
+    @Test
+    public void testDynamicMimeWithMultipleGroupsPerFilter() throws DeviceNotAvailableException {
+        runDeviceTests("ComplexFilterTest");
+    }
+
+    @Test
+    public void testDynamicMimeWithAppUpdate() throws DeviceNotAvailableException {
+        runDeviceTests("update.SameGroupsTest");
+    }
+
+    @Test
+    public void testDynamicMimeWithChangedGroupsAppUpdate() throws DeviceNotAvailableException {
+        runDeviceTests("update.ChangedGroupsTest");
+    }
+
+    private void runDeviceTests(String className)
+            throws DeviceNotAvailableException {
+        runDeviceTests(PACKAGE_TEST_APP, PACKAGE_TEST_APP + "." + className);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/Android.bp b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
new file mode 100644
index 0000000..207f050
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+android_test_helper_app {
+    name: "CtsDynamicMimeTestApp",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "compatibility-device-util-axt",
+        "CtsDynamicMimeCommon",
+        "hamcrest-library",
+        "android-support-test",
+        "androidx.test.uiautomator_uiautomator",
+    ],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+    platform_apis: true,
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml b/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml
new file mode 100644
index 0000000..d31dcf3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.dynamicmime.testapp">
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_first"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_first"/>
+            </intent-filter>
+        </activity>
+        <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_second"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_both"/>
+                <data android:mimeGroup="group_first"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.dynamicmime.common.activity.TwoGroupsAndTypeActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_groups_and_type"/>
+                <data android:mimeType="text/plain"/>
+                <data android:mimeGroup="group_first"/>
+                <data android:mimeGroup="group_second"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.dynamicmime.testapp">
+    </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java
new file mode 100644
index 0000000..ab16c74
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.MimeGroupOperations;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+
+/**
+ * Base class for dynamic MIME handlers feature test cases
+ */
+public abstract class BaseDynamicMimeTest extends MimeGroupOperations {
+    public BaseDynamicMimeTest(MimeGroupCommands commands, MimeGroupAssertions assertions) {
+        super(commands, assertions);
+    }
+
+    protected static Context context() {
+        return instrumentation().getContext();
+    }
+
+    protected static Context targetContext() {
+        return instrumentation().getTargetContext();
+    }
+
+    protected static Instrumentation instrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+
+    @After
+    public void tearDown() {
+        clearGroups();
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java
new file mode 100644
index 0000000..0ad981c
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS_AND_TYPE;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import android.dynamicmime.testapp.assertions.AssertionsByIntentResolution;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import org.junit.Test;
+
+/**
+ * Test cases for:
+ *  - multiple MIME groups per intent filter
+ *  - MIME groups and android:mimeType attribute in one filter
+ */
+public class ComplexFilterTest extends BaseDynamicMimeTest {
+    public ComplexFilterTest() {
+        super(MimeGroupCommands.testApp(context()),
+                AssertionsByIntentResolution.testApp(context()));
+    }
+
+    @Test
+    public void testMimeGroupsNotIntersect() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+        setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+        assertBothGroups(MIME_IMAGE_PNG, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupsIntersect() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+        setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+
+        assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_PNG);
+    }
+
+    @Test
+    public void testRemoveTypeFromIntersection() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+        setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+
+        assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_PNG);
+    }
+
+    @Test
+    public void testRemoveIntersectionFromBothGroups() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+        setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+        removeMimeTypeFromGroup(GROUP_SECOND, MIME_IMAGE_PNG);
+
+        assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testClearGroupWithoutIntersection() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertBothGroups(MIME_IMAGE_PNG);
+    }
+
+    @Test
+    public void testClearGroupWithIntersection() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+        setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertBothGroups(MIME_IMAGE_PNG, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupNotIntersectWithStaticType() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+
+        assertGroupsAndType(MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testMimeGroupIntersectWithStaticType() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        assertGroupsAndType(MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testRemoveStaticTypeFromMimeGroup() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        assertGroupsAndType(MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testClearGroupContainsStaticType() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertGroupsAndType(MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testClearGroupNotContainStaticType() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertGroupsAndType(MIME_TEXT_PLAIN);
+    }
+
+    private void assertGroupsAndType(String... expectedTypes) {
+        assertMimeGroup(ALIAS_BOTH_GROUPS_AND_TYPE, expectedTypes);
+    }
+
+    private void assertBothGroups(String... expectedTypes) {
+        assertMimeGroup(ALIAS_BOTH_GROUPS, expectedTypes);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java
new file mode 100644
index 0000000..fa5876d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.MimeGroupOperations;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases that verify independence of MIME groups from different apps
+ */
+@RunWith(AndroidJUnit4.class)
+public class MultipleAppsTest extends BaseDynamicMimeTest {
+    public MultipleAppsTest() {
+        super(MimeGroupCommands.testApp(context()), MimeGroupAssertions.testApp(context()));
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        helperApp().clearGroups();
+        super.tearDown();
+    }
+
+    @Test
+    public void testMimeGroupsIndependentSet() {
+        helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+        helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+        helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentReset() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+        helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        helperApp().removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+        helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentClear() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+        helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+        helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        helperApp().clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        helperApp().assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    /**
+     * {@link MimeGroupOperations} implementation to interact with sample app MIME groups
+     */
+    private MimeGroupOperations helperApp() {
+        return new MimeGroupOperations(MimeGroupCommands.helperApp(context()),
+                MimeGroupAssertions.helperApp(context()));
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java
new file mode 100644
index 0000000..84eab63
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_ANY;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_ANY;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_JPEG;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+import static android.dynamicmime.common.Constants.MIME_TEXT_XML;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for single app and one MIME group per intent filter scenario
+ */
+@RunWith(AndroidJUnit4.class)
+public class SingleAppTest extends BaseDynamicMimeTest {
+    public SingleAppTest() {
+        super(MimeGroupCommands.testApp(context()), MimeGroupAssertions.testApp(context()));
+    }
+
+    @Test
+    public void testGroupWithExactType() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_ANY, MIME_ANY);
+        assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+    }
+
+    @Test
+    public void testGroupWithWildcardType() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_ANY);
+        assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+    }
+
+    @Test
+    public void testGroupWithAnyType() {
+        setMimeGroup(GROUP_FIRST, MIME_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_ANY);
+        assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_ANY);
+    }
+
+    @Test
+    public void testAddSimilarTypes() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        addMimeTypeToGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testAddDifferentTypes() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testRemoveExactType() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+        assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testRemoveDifferentType() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testRemoveSameBaseType_keepSpecific() {
+        testKeepAndRemoveSimilarTypes(MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testRemoveSameBaseType_keepWildcard() {
+        testKeepAndRemoveSimilarTypes(MIME_TEXT_ANY, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testRemoveAnyType_keepSpecific() {
+        testKeepAndRemoveSimilarTypes(MIME_TEXT_PLAIN, MIME_ANY);
+    }
+
+    @Test
+    public void testRemoveAnyType_keepWildcard() {
+        testKeepAndRemoveSimilarTypes(MIME_ANY, MIME_TEXT_PLAIN);
+    }
+
+    @Test
+    public void testResetWithoutIntersection() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_JPEG, MIME_IMAGE_PNG);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_JPEG, MIME_IMAGE_PNG);
+    }
+
+    @Test
+    public void testResetWithIntersection() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+    }
+
+    @Test
+    public void testResetToEmpty() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+        setMimeGroup(GROUP_FIRST, (String[]) null);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testClear() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterAdd() {
+        addMimeTypeToGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterSet() {
+        setMimeGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterRemove() {
+        removeMimeTypeFromGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Test
+    public void testUndefinedGroupIsNullAfterClear() {
+        clearMimeGroup(GROUP_UNDEFINED);
+
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterRemove() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterClear() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterSetEmpty() {
+        setMimeGroup(GROUP_FIRST, (String[]) null);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testDefinedGroupNotNullAfterSetNull() {
+        setMimeGroup(GROUP_FIRST, (String[]) null);
+        clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentAdd() {
+        addMimeTypeToGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+        assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentAddToBoth() {
+        addMimeTypeToGroup(GROUP_SECOND, MIME_TEXT_ANY);
+        addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+        assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentRemove() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+        setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+        removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        removeMimeTypeFromGroup(GROUP_SECOND, MIME_IMAGE_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+        assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentClear() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+        setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+        clearMimeGroup(GROUP_FIRST);
+
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+        assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentClearBoth() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+        setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+        clearMimeGroup(GROUP_FIRST);
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        clearMimeGroup(GROUP_SECOND);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+        assertMimeGroupIsEmpty(GROUP_SECOND);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentSet() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+        assertMimeGroupIsEmpty(GROUP_SECOND);
+    }
+
+    @Test
+    public void testMimeGroupsIndependentSetBoth() {
+        setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+        setMimeGroup(GROUP_SECOND, MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+
+        assertMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+        assertMimeGroup(GROUP_SECOND, MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+    }
+
+    private void testKeepAndRemoveSimilarTypes(String mimeTypeToKeep, String mimeTypeToRemove) {
+        addMimeTypeToGroup(GROUP_FIRST, mimeTypeToKeep, mimeTypeToRemove);
+        removeMimeTypeFromGroup(GROUP_FIRST, mimeTypeToRemove);
+
+        assertMimeGroup(GROUP_FIRST, mimeTypeToKeep);
+        assertMatchedByMimeGroup(GROUP_FIRST, mimeTypeToRemove);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java
new file mode 100644
index 0000000..95eaa12
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+import android.dynamicmime.testapp.util.AppMimeGroups;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+public class AppAssertionsByGroupData extends AssertionsByGroupData {
+    private final AppMimeGroups mAppMimeGroups;
+
+    AppAssertionsByGroupData(Context context, String targetPackage) {
+        mAppMimeGroups = AppMimeGroups.with(context, targetPackage);
+    }
+
+    @Override
+    protected Set<String> getMimeGroup(String mimeGroup) {
+        String[] mimeTypes = mAppMimeGroups.get(mimeGroup);
+        return mimeTypes != null ? new ArraySet<>(mimeTypes) : null;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java
new file mode 100644
index 0000000..eb74b3d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.assertions;
+
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.content.Context;
+
+import org.junit.Assert;
+
+import java.util.Set;
+
+public abstract class AssertionsByGroupData extends MimeGroupAssertions {
+    @Override
+    public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+        // We cannot check if type is matched by group via group info from PackageManager
+    }
+
+    @Override
+    public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+        // We cannot check if type is matched by group via group info from PackageManager
+    }
+
+    @Override
+    public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+        Assert.assertEquals(getMimeGroup(mimeGroup), mimeTypes);
+    }
+
+    protected abstract Set<String> getMimeGroup(String mimeGroup);
+
+    public static MimeGroupAssertions testApp(Context context) {
+        return new TestAppAssertionsByGroupData(context);
+    }
+
+    public static MimeGroupAssertions helperApp(Context context) {
+        return new AppAssertionsByGroupData(context, PACKAGE_HELPER_APP);
+    }
+
+    public static MimeGroupAssertions appWithUpdates(Context context) {
+        return new AppAssertionsByGroupData(context, PACKAGE_UPDATE_APP);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java
new file mode 100644
index 0000000..13f4d4d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.assertions;
+
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH;
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH_AND_TYPE;
+import static android.dynamicmime.common.Constants.ACTIVITY_FIRST;
+import static android.dynamicmime.common.Constants.ACTIVITY_SECOND;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS_AND_TYPE;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_TEST_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+import static android.dynamicmime.testapp.util.IntentsResolutionHelper.resolve;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+public class AssertionsByIntentResolution extends MimeGroupAssertions {
+    private final Context mContext;
+    private final String mPackageName;
+
+    private AssertionsByIntentResolution(String packageName, Context context) {
+        mContext = context;
+        mPackageName = packageName;
+    }
+
+    @Override
+    public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+        resolve(mContext, intentSend(mimeType))
+                .assertMatched(mPackageName, activity(mimeGroup));
+    }
+
+    @Override
+    public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+        resolve(mContext, intentSend(mimeType))
+                .assertNotMatched(mPackageName, activity(mimeGroup));
+    }
+
+    @Override
+    public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+        if (mimeTypes == null) {
+            // We can't distinguish empty group from null group via intent resolution
+            return;
+        }
+
+        // Check MIME group types retrieved from declared intent filter
+        assertEquals("Mismatch for " + mimeGroup + " MIME types",
+                mimeTypes, getMimeGroup(mimeGroup, mimeTypes));
+
+        // Additionally we check if all types are matched by this MIME group
+        for (String mimeType: mimeTypes) {
+            assertMatchedByMimeGroup(mimeGroup, mimeType);
+        }
+    }
+
+    public static MimeGroupAssertions testApp(Context context) {
+        return new AssertionsByIntentResolution(PACKAGE_TEST_APP, context);
+    }
+
+    public static MimeGroupAssertions helperApp(Context context) {
+        return new AssertionsByIntentResolution(PACKAGE_HELPER_APP, context);
+    }
+
+    public static MimeGroupAssertions appWithUpdates(Context context) {
+        return new AssertionsByIntentResolution(PACKAGE_UPDATE_APP, context);
+    }
+
+    private Set<String> getMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+        IntentFilter filter = resolve(mContext, intentGroup(mimeGroup, mimeTypes))
+                .getAny()
+                .filter;
+
+        Set<String> actualTypes = new ArraySet<>(filter.countDataTypes());
+
+        for (int i = 0; i < filter.countDataTypes(); i++) {
+            actualTypes.add(appendWildcard(filter.getDataType(i)));
+        }
+
+        return actualTypes;
+    }
+
+    private Intent intentGroup(String mimeGroup, Set<String> mimeTypes) {
+        Intent intent = new Intent(action(mimeGroup));
+
+        if (mimeTypes != null && !mimeTypes.isEmpty()) {
+            intent.setType(mimeTypes.iterator().next());
+        }
+
+        return intent;
+    }
+
+    private static Intent intentSend(String mimeType) {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_SEND);
+        sendIntent.setType(mimeType);
+        return sendIntent;
+    }
+
+    private static String appendWildcard(String actualType) {
+        int slashpos = actualType.indexOf('/');
+
+        if (slashpos < 0) {
+            return actualType + "/*";
+        } else {
+            return actualType;
+        }
+    }
+
+    private static String activity(String mimeGroup) {
+        switch (mimeGroup) {
+            case GROUP_FIRST:
+                return ACTIVITY_FIRST;
+            case GROUP_SECOND:
+                return ACTIVITY_SECOND;
+            case ALIAS_BOTH_GROUPS:
+                return ACTIVITY_BOTH;
+            case ALIAS_BOTH_GROUPS_AND_TYPE:
+                return ACTIVITY_BOTH_AND_TYPE;
+            default:
+                throw new IllegalArgumentException("Unexpected MIME group " + mimeGroup);
+        }
+    }
+
+    private String action(String mimeGroup) {
+        return mPackageName + ".FILTER_INFO_HOOK_"+ mimeGroup;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java
new file mode 100644
index 0000000..105e6eb
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+
+public abstract class MimeGroupAssertions {
+    public abstract void assertMatchedByMimeGroup(String mimeGroup, String mimeType);
+
+    public abstract void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType);
+
+    public abstract void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes);
+
+    public final void assertMimeGroup(String mimeGroup, String... mimeTypes) {
+        assertMimeGroupInternal(mimeGroup, new ArraySet<>(mimeTypes));
+    }
+
+    public final void assertMimeGroupIsEmpty(String mimeGroup) {
+        assertMimeGroupInternal(mimeGroup, Collections.emptySet());
+    }
+
+    public final void assertMimeGroupIsNull(String mimeGroup) {
+        assertMimeGroupInternal(mimeGroup, null);
+    }
+
+    public final void assertMatchedByMimeGroup(String mimeGroup, String... mimeTypes) {
+        for (String mimeType: mimeTypes) {
+            assertMatchedByMimeGroup(mimeGroup, mimeType);
+        }
+    }
+
+    public final void assertNotMatchedByMimeGroup(String mimeGroup, String... mimeTypes) {
+        for (String mimeType: mimeTypes) {
+            assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+        }
+    }
+
+    public static MimeGroupAssertions testApp(Context context) {
+        return AssertionsByGroupData.testApp(context)
+                .mergeWith(AssertionsByIntentResolution.testApp(context));
+    }
+
+    public static MimeGroupAssertions helperApp(Context context) {
+        return AssertionsByGroupData.helperApp(context)
+                .mergeWith(AssertionsByIntentResolution.helperApp(context));
+    }
+
+    public static MimeGroupAssertions appWithUpdates(Context context) {
+        return AssertionsByGroupData.appWithUpdates(context)
+                .mergeWith(AssertionsByIntentResolution.appWithUpdates(context));
+    }
+
+    public static MimeGroupAssertions noOp() {
+        return new MimeGroupAssertions() {
+            @Override
+            public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+            }
+
+            @Override
+            public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+            }
+
+            @Override
+            public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+            }
+        };
+    }
+
+    public static MimeGroupAssertions notUsed() {
+        return new MimeGroupAssertions() {
+            @Override
+            public void assertMatchedByMimeGroup(String group, String type) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void assertNotMatchedByMimeGroup(String group, String type) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void assertMimeGroupInternal(String group, Set<String> types) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    protected final MimeGroupAssertions mergeWith(MimeGroupAssertions other) {
+        return new MimeGroupAssertions() {
+            @Override
+            public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+                other.assertMatchedByMimeGroup(mimeGroup, mimeType);
+                MimeGroupAssertions.this.assertMatchedByMimeGroup(mimeGroup, mimeType);
+            }
+
+            @Override
+            public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+                other.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+                MimeGroupAssertions.this.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+            }
+
+            @Override
+            public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+                other.assertMimeGroupInternal(mimeGroup, mimeTypes);
+                MimeGroupAssertions.this.assertMimeGroupInternal(mimeGroup, mimeTypes);
+            }
+        };
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java
new file mode 100644
index 0000000..017fa44
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+
+import java.util.Set;
+
+class TestAppAssertionsByGroupData extends AssertionsByGroupData {
+    private final Context mContext;
+
+    TestAppAssertionsByGroupData(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    protected Set<String> getMimeGroup(String mimeGroup) {
+        return mContext.getPackageManager().getMimeGroup(mimeGroup);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java
new file mode 100644
index 0000000..83400f4
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.commands;
+
+import android.content.Context;
+import android.dynamicmime.testapp.util.AppMimeGroups;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public class AppCommands implements MimeGroupCommands {
+    private final AppMimeGroups mAppMimeGroups;
+
+    AppCommands(Context context, String targetPackage) {
+        mAppMimeGroups = AppMimeGroups.with(context, targetPackage);
+    }
+
+    @Override
+    public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+        mAppMimeGroups.set(mimeGroup, mimeTypes.toArray(new String[0]));
+    }
+
+    @Override
+    public void clearMimeGroup(String mimeGroup) {
+        mAppMimeGroups.clear(mimeGroup);
+    }
+
+    @Nullable
+    @Override
+    public Set<String> getMimeGroupInternal(String mimeGroup) {
+        return new ArraySet<>(mAppMimeGroups.get(mimeGroup));
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java
new file mode 100644
index 0000000..27a9cb2
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.commands;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_THIRD;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_PREFERRED_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.content.Context;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public interface MimeGroupCommands {
+    void setMimeGroup(String mimeGroup, Set<String> mimeTypes);
+    void clearMimeGroup(String mimeGroup);
+
+    @Nullable
+    Set<String> getMimeGroupInternal(String mimeGroup);
+
+    default void addMimeTypeToGroup(String mimeGroup, String... mimeTypes) {
+        ArraySet<String> typesToAdd = new ArraySet<>(mimeTypes);
+
+        Set<String> newMimeTypes = new ArraySet<>(getMimeGroupInternal(mimeGroup));
+        newMimeTypes.addAll(typesToAdd);
+
+        setMimeGroup(mimeGroup, newMimeTypes);
+    }
+
+    default void removeMimeTypeFromGroup(String mimeGroup, String... mimeTypes) {
+        ArraySet<String> typesToRemove = new ArraySet<>(mimeTypes);
+
+        Set<String> newMimeTypes = new ArraySet<>(getMimeGroupInternal(mimeGroup));
+        newMimeTypes.removeAll(typesToRemove);
+
+        setMimeGroup(mimeGroup, newMimeTypes);
+    }
+
+    default void setMimeGroup(String mimeGroup, String... mimeTypes) {
+        setMimeGroup(mimeGroup, new ArraySet<>(mimeTypes));
+    }
+
+    default void clearGroups() {
+        clearMimeGroup(GROUP_FIRST);
+        clearMimeGroup(GROUP_SECOND);
+        clearMimeGroup(GROUP_THIRD);
+        clearMimeGroup(GROUP_UNDEFINED);
+    }
+
+    static MimeGroupCommands testApp(Context context) {
+        return new TestAppCommands(context);
+    }
+
+    static MimeGroupCommands helperApp(Context context) {
+        return new AppCommands(context, PACKAGE_HELPER_APP);
+    }
+
+    static MimeGroupCommands appWithUpdates(Context context) {
+        return new AppCommands(context, PACKAGE_UPDATE_APP);
+    }
+
+    static MimeGroupCommands preferredApp(Context context) {
+        return new AppCommands(context, PACKAGE_PREFERRED_APP);
+    }
+
+    static MimeGroupCommands noOp() {
+        return new MimeGroupCommands() {
+            @Override
+            public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+            }
+
+            @Override
+            public void clearMimeGroup(String mimeGroup) {
+            }
+
+            @Override
+            public Set<String> getMimeGroupInternal(String mimeGroup) {
+                return null;
+            }
+        };
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java
new file mode 100644
index 0000000..82dcb8853
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.commands;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public class TestAppCommands implements MimeGroupCommands {
+    private PackageManager mPM;
+
+    TestAppCommands(Context context) {
+        mPM = context.getPackageManager();
+    }
+
+    @Override
+    public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+        mPM.setMimeGroup(mimeGroup, mimeTypes);
+    }
+
+    @Override
+    public void clearMimeGroup(String mimeGroup) {
+        mPM.clearMimeGroup(mimeGroup);
+    }
+
+    @Nullable
+    @Override
+    public Set<String> getMimeGroupInternal(String mimeGroup) {
+        return mPM.getMimeGroup(mimeGroup);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
new file mode 100644
index 0000000..5ef052ef
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.preferred;
+
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH;
+import static android.dynamicmime.common.Constants.ACTIVITY_FIRST;
+import static android.dynamicmime.common.Constants.APK_PREFERRED_APP;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_THIRD;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+import static android.dynamicmime.common.Constants.PACKAGE_PREFERRED_APP;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Intent;
+import android.dynamicmime.testapp.BaseDynamicMimeTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.Utils;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PreferredActivitiesTest extends BaseDynamicMimeTest {
+    private static final BySelector BUTTON_ALWAYS = By.res("android:id/button_always");
+    private static final BySelector RESOLVER_DIALOG = By.res("android:id/contentPanel");
+
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+    private TestStrategy mTest;
+
+    public PreferredActivitiesTest() {
+        super(MimeGroupCommands.preferredApp(context()), MimeGroupAssertions.notUsed());
+    }
+
+    @Before
+    public void setUp() {
+        Utils.installApk(APK_PREFERRED_APP);
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        Utils.uninstallApp(PACKAGE_PREFERRED_APP);
+    }
+
+    @Test
+    public void testRemoveFromGroup() {
+        setStrategyAndRun(new TestStrategy() {
+            @Override
+            public void prepareMimeGroups() {
+                addMimeTypeToGroup(GROUP_FIRST, mimeType());
+            }
+
+            @Override
+            public void changeMimeGroups() {
+                removeMimeTypeFromGroup(GROUP_FIRST, mimeType());
+            }
+
+            @Override
+            public String preferredActivity() {
+                return ACTIVITY_FIRST;
+            }
+        });
+    }
+
+    @Test
+    public void testAddToGroup() {
+        setStrategyAndRun(new TestStrategy() {
+            @Override
+            public void prepareMimeGroups() {
+                addMimeTypeToGroup(GROUP_FIRST, mimeType());
+            }
+
+            @Override
+            public void changeMimeGroups() {
+                addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+            }
+
+            @Override
+            public void revertMimeGroupsChange() {
+                removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+            }
+
+            @Override
+            public String preferredActivity() {
+                return ACTIVITY_FIRST;
+            }
+        });
+    }
+
+    @Test
+    public void testClearGroup() {
+        setStrategyAndRun(new TestStrategy() {
+            @Override
+            public void prepareMimeGroups() {
+                addMimeTypeToGroup(GROUP_FIRST, mimeType());
+            }
+
+            @Override
+            public void changeMimeGroups() {
+                clearMimeGroup(GROUP_FIRST);
+            }
+
+            @Override
+            public String preferredActivity() {
+                return ACTIVITY_FIRST;
+            }
+        });
+    }
+
+    @Test
+    public void testModifyGroupWithoutActualGroupChanges() {
+        setStrategyAndRun(new TestStrategy() {
+            @Override
+            public void prepareMimeGroups() {
+                addMimeTypeToGroup(GROUP_FIRST, mimeType());
+            }
+
+            @Override
+            public void changeMimeGroups() {
+                addMimeTypeToGroup(GROUP_FIRST, mimeType());
+            }
+
+            @Override
+            public String preferredActivity() {
+                return ACTIVITY_FIRST;
+            }
+
+            @Override
+            public boolean isActivityPreferredAfterRevert() {
+                return true;
+            }
+
+            @Override
+            public boolean isActivityPreferredAfterChange() {
+                return true;
+            }
+        });
+    }
+
+    @Test
+    public void testModifyGroupWithoutActualIntentFilterChanges() {
+        setStrategyAndRun(new TestStrategy() {
+            @Override
+            public void prepareMimeGroups() {
+                addMimeTypeToGroup(GROUP_THIRD, mimeType());
+                addMimeTypeToGroup(GROUP_SECOND, mimeType());
+            }
+
+            @Override
+            public void changeMimeGroups() {
+                removeMimeTypeFromGroup(GROUP_THIRD, mimeType());
+            }
+
+            @Override
+            public void revertMimeGroupsChange() {
+                addMimeTypeToGroup(GROUP_THIRD, mimeType());
+            }
+
+            @Override
+            public String preferredActivity() {
+                return ACTIVITY_BOTH;
+            }
+
+            @Override
+            public boolean isActivityPreferredAfterRevert() {
+                return true;
+            }
+
+            @Override
+            public boolean isActivityPreferredAfterChange() {
+                return true;
+            }
+        });
+    }
+
+    private void setStrategyAndRun(TestStrategy test) {
+        mTest = test;
+        runTest();
+    }
+
+    private void runTest() {
+        mTest.prepareMimeGroups();
+        setPreferredActivity();
+
+        mTest.changeMimeGroups();
+        checkPreferredActivityAfterChange();
+
+        mTest.revertMimeGroupsChange();
+        checkPreferredActivityAfterRevert();
+
+        getUiDevice().pressHome();
+    }
+
+    private void setPreferredActivity() {
+        triggerResolutionDialog();
+
+        verifyDialogIsShown(true);
+
+        chooseActivity("TestApp" + mTest.preferredActivity());
+    }
+
+    private void checkPreferredActivityAfterChange() {
+        checkPreferredActivity(mTest.isActivityPreferredAfterChange());
+    }
+
+    private void checkPreferredActivityAfterRevert() {
+        checkPreferredActivity(mTest.isActivityPreferredAfterRevert());
+    }
+
+    private void checkPreferredActivity(boolean hasPreferredActivity) {
+        triggerResolutionDialog();
+        verifyResolutionDialog(hasPreferredActivity);
+    }
+
+    private void triggerResolutionDialog() {
+        getUiDevice().pressHome();
+        sendIntent(mTest.mimeType());
+    }
+
+    private void verifyResolutionDialog(boolean shouldLaunchActivity) {
+        verifyDialogIsShown(!shouldLaunchActivity);
+        getUiDevice().pressBack();
+    }
+
+    private void verifyDialogIsShown(boolean shouldBeShown) {
+        UiObject2 buttonAlways = getUiDevice().wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT);
+
+        if (shouldBeShown) {
+            assertNotNull(buttonAlways);
+        } else {
+            assertNull(buttonAlways);
+        }
+    }
+
+    private void chooseActivity(String label) {
+        findActivityInDialog(label).click();
+        chooseUseAlways();
+
+        getUiDevice().pressBack();
+    }
+
+    private UiObject2 findActivityInDialog(String label) {
+        getUiDevice()
+                .wait(Until.findObject(RESOLVER_DIALOG), TIMEOUT)
+                .swipe(Direction.UP, 1f);
+
+        return getUiDevice().findObject(By.text(label));
+    }
+
+    private void chooseUseAlways() {
+        getUiDevice()
+                .findObject(BUTTON_ALWAYS)
+                .click();
+    }
+
+    private interface TestStrategy {
+        void prepareMimeGroups();
+
+        void changeMimeGroups();
+
+        default void revertMimeGroupsChange() {
+            prepareMimeGroups();
+        }
+
+        default String mimeType() {
+            return MIME_TEXT_PLAIN;
+        }
+
+        String preferredActivity();
+
+        default boolean isActivityPreferredAfterChange() {
+            return false;
+        }
+
+        default boolean isActivityPreferredAfterRevert() {
+            return false;
+        }
+    }
+
+    private static UiDevice getUiDevice() {
+        return UiDevice.getInstance(instrumentation());
+    }
+
+    private static void sendIntent(String mimeType) {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_SEND);
+        sendIntent.setType(mimeType);
+        sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        targetContext().startActivity(sendIntent, null);
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java
new file mode 100644
index 0000000..19f8bab
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.ComplexFilterTest;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Post-reboot part of {@link ComplexFilterTest}
+ *
+ * {@link MimeGroupCommands} are disabled during test body execution
+ */
+@RunWith(AndroidJUnit4.class)
+public class PostRebootComplexFilterTest extends ComplexFilterTest {
+    private MimeGroupCommands mCommands;
+
+    public PostRebootComplexFilterTest() {
+        super();
+        mCommands = getCommands();
+    }
+
+    @Before
+    public void setUp() {
+        setCommands(MimeGroupCommands.noOp());
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        setCommands(mCommands);
+        super.tearDown();
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java
new file mode 100644
index 0000000..3e4c86d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.SingleAppTest;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Post-reboot part of {@link SingleAppTest}
+ *
+ * {@link MimeGroupCommands} are disabled during test body execution
+ */
+@RunWith(AndroidJUnit4.class)
+public class PostRebootSingleAppTest extends SingleAppTest {
+    private MimeGroupCommands mCommands;
+
+    public PostRebootSingleAppTest() {
+        super();
+        mCommands = getCommands();
+    }
+
+    @Before
+    public void setUp() {
+        setCommands(MimeGroupCommands.noOp());
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        setCommands(mCommands);
+        super.tearDown();
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java
new file mode 100644
index 0000000..9f290e3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.ComplexFilterTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Pre-reboot part of {@link ComplexFilterTest}
+ *
+ * {@link MimeGroupAssertions} are disabled
+ * {@link MimeGroupCommands} are disabled during tear down
+ */
+@RunWith(AndroidJUnit4.class)
+public class PreRebootComplexFilterTest extends ComplexFilterTest {
+    private MimeGroupCommands mCommands;
+
+    public PreRebootComplexFilterTest() {
+        super();
+        setAssertions(MimeGroupAssertions.noOp());
+        mCommands = getCommands();
+    }
+
+    @Before
+    public void setUp() {
+        setCommands(mCommands);
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        setCommands(MimeGroupCommands.noOp());
+        super.tearDown();
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java
new file mode 100644
index 0000000..5a65df7
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.SingleAppTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Pre-reboot part of {@link SingleAppTest}
+ *
+ * {@link MimeGroupAssertions} are disabled
+ * {@link MimeGroupCommands} are disabled during tear down
+ */
+@RunWith(AndroidJUnit4.class)
+public class PreRebootSingleAppTest extends SingleAppTest {
+    private MimeGroupCommands mCommands;
+
+    public PreRebootSingleAppTest() {
+        super();
+        setAssertions(MimeGroupAssertions.noOp());
+        mCommands = getCommands();
+    }
+
+    @Before
+    public void setUp() {
+        setCommands(mCommands);
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        setCommands(MimeGroupCommands.noOp());
+        super.tearDown();
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java
new file mode 100644
index 0000000..43f590a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.dynamicmime.testapp.BaseDynamicMimeTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.IOException;
+
+abstract class BaseUpdateTest extends BaseDynamicMimeTest {
+    BaseUpdateTest() {
+        super(MimeGroupCommands.appWithUpdates(context()),
+                MimeGroupAssertions.appWithUpdates(context()));
+    }
+
+    @Before
+    public void setUp() throws IOException {
+        Utils.installApk(installApkPath());
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        super.tearDown();
+        Utils.uninstallApp(PACKAGE_UPDATE_APP);
+    }
+
+    void updateApp() {
+        Utils.updateApp(updateApkPath());
+    }
+
+    abstract String installApkPath();
+    abstract String updateApkPath();
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java
new file mode 100644
index 0000000..b209699
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.APK_FIRST_GROUP;
+import static android.dynamicmime.common.Constants.APK_SECOND_GROUP;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for app update with MIME groups changes
+ */
+@RunWith(AndroidJUnit4.class)
+public class ChangedGroupsTest extends BaseUpdateTest {
+    @Test
+    public void testUpdateRemoveEmptyGroup() {
+        assertMimeGroupIsEmpty(GROUP_FIRST);
+        assertMimeGroupIsNull(GROUP_SECOND);
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+
+        updateApp();
+
+        assertMimeGroupIsNull(GROUP_FIRST);
+        assertMimeGroupIsEmpty(GROUP_SECOND);
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Test
+    public void testUpdateRemoveNonEmptyGroup() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        assertMimeGroupIsNull(GROUP_SECOND);
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+
+        updateApp();
+
+        assertMimeGroupIsNull(GROUP_FIRST);
+        assertMimeGroupIsEmpty(GROUP_SECOND);
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Override
+    String installApkPath() {
+        return APK_FIRST_GROUP;
+    }
+
+    @Override
+    String updateApkPath() {
+        return APK_SECOND_GROUP;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java
new file mode 100644
index 0000000..0c3349a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.APK_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for app update, without MIME groups changes
+ */
+@RunWith(AndroidJUnit4.class)
+public class SameGroupsTest extends BaseUpdateTest {
+    @Test
+    public void testUpdate() {
+        setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+        updateApp();
+
+        assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+        assertMimeGroupIsEmpty(GROUP_SECOND);
+        assertMimeGroupIsNull(GROUP_UNDEFINED);
+    }
+
+    @Override
+    String installApkPath() {
+        return APK_BOTH_GROUPS;
+    }
+
+    @Override
+    String updateApkPath() {
+        return APK_BOTH_GROUPS;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java
new file mode 100644
index 0000000..b034946
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.util;
+
+import static android.dynamicmime.common.Constants.ACTION_REQUEST;
+import static android.dynamicmime.common.Constants.ACTION_RESPONSE;
+import static android.dynamicmime.common.Constants.EXTRA_GROUP;
+import static android.dynamicmime.common.Constants.EXTRA_MIMES;
+import static android.dynamicmime.common.Constants.EXTRA_REQUEST;
+import static android.dynamicmime.common.Constants.EXTRA_RESPONSE;
+import static android.dynamicmime.common.Constants.REQUEST_CLEAR;
+import static android.dynamicmime.common.Constants.REQUEST_GET;
+import static android.dynamicmime.common.Constants.REQUEST_SET;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class that dispatches MIME groups related commands to another app
+ * Target app should register {@link android.dynamicmime.app.AppMimeGroupsReceiver}
+ * to handler requests
+ */
+public class AppMimeGroups {
+    private final Context mContext;
+    private final String mTargetPackage;
+
+    private AppMimeGroups(Context context, String targetPackage) {
+        mContext = context;
+        mTargetPackage = targetPackage;
+    }
+
+    public static AppMimeGroups with(Context context, String targetPackage) {
+        return new AppMimeGroups(context, targetPackage);
+    }
+
+    public void set(String mimeGroup, String[] mimeTypes) {
+        sendRequestAndAwait(mimeGroup, REQUEST_SET, mimeTypes);
+    }
+
+    public void clear(String mimeGroup) {
+        sendRequestAndAwait(mimeGroup, REQUEST_CLEAR);
+    }
+
+    public String[] get(String mimeGroup) {
+        return sendRequestAndAwait(mimeGroup, REQUEST_GET)
+                .getStringArrayExtra(EXTRA_RESPONSE);
+    }
+
+    private Intent sendRequestAndAwait(String mimeGroup, int requestSet) {
+        return sendRequestAndAwait(mimeGroup, requestSet, null);
+    }
+
+    private Intent sendRequestAndAwait(String mimeGroup, int request, String[] mimeTypes) {
+        BlockingBroadcastReceiver receiver =
+                new BlockingBroadcastReceiver(mContext, ACTION_RESPONSE);
+        receiver.register();
+
+        mContext.sendBroadcast(getRequestIntent(mimeGroup, mimeTypes, request));
+
+        Intent response = receiver.awaitForBroadcast(TimeUnit.SECONDS.toMillis(5L));
+        assertNotNull(response);
+
+        mContext.unregisterReceiver(receiver);
+
+        return response;
+    }
+
+    private Intent getRequestIntent(String mimeGroup, String[] mimeTypes, int request) {
+        Intent intent = new Intent(ACTION_REQUEST);
+        intent.setPackage(mTargetPackage);
+        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+
+        intent.putExtra(EXTRA_GROUP, mimeGroup);
+        intent.putExtra(EXTRA_MIMES, mimeTypes);
+        intent.putExtra(EXTRA_REQUEST, request);
+
+        return intent;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java
new file mode 100644
index 0000000..fceb48f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.util;
+
+import static android.dynamicmime.common.Constants.PACKAGE_ACTIVITIES;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+
+/**
+ * Helper class for {@link PackageManager#queryIntentActivities} results verification
+ */
+public class IntentsResolutionHelper {
+    private final List<ResolveInfo> resolvedActivities;
+    private final Intent mIntent;
+
+    public static IntentsResolutionHelper resolve(Context context, Intent intent) {
+        return new IntentsResolutionHelper(context, intent);
+    }
+
+    private IntentsResolutionHelper(Context context, Intent intent) {
+        mIntent = intent;
+        this.resolvedActivities = context.getPackageManager().queryIntentActivities(mIntent, PackageManager.GET_RESOLVED_FILTER);
+    }
+
+    public void assertMatched(String packageName, String simpleClassName) {
+        assertThat("Missing expected match for " + mIntent, resolvedActivities,
+                hasItem(activity(packageName, simpleClassName)));
+    }
+
+    public void assertNotMatched(String packageName, String simpleClassName) {
+        assertThat("Unexpected match for " + mIntent, resolvedActivities,
+                not(hasItem(activity(packageName, simpleClassName))));
+    }
+
+    public ResolveInfo getAny() {
+        assertThat("Missing match for " + mIntent, resolvedActivities, not(empty()));
+
+        return resolvedActivities
+                .stream()
+                .findAny()
+                .orElse(null);
+    }
+
+    private Matcher<ResolveInfo> activity(String packageName, String simpleClassName) {
+        return new BaseMatcher<ResolveInfo>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("packageName=" + packageName + ", simpleClassName=" + simpleClassName);
+            }
+
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) {
+                    return false;
+                }
+                ResolveInfo info = (ResolveInfo) item;
+
+                return info.activityInfo.packageName.equals(packageName)
+                        && info.activityInfo.name.equals(PACKAGE_ACTIVITIES + simpleClassName);
+            }
+        };
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java
new file mode 100644
index 0000000..ebd4cc4
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.util;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+/**
+ * Base class for commands/queries related to MIME groups
+ * Allows independent implementations for commands and assertions
+ */
+public class MimeGroupOperations extends MimeGroupAssertions implements MimeGroupCommands {
+    private MimeGroupCommands mCommands;
+    private MimeGroupAssertions mAssertions;
+
+    public MimeGroupOperations(MimeGroupCommands commands, MimeGroupAssertions assertions) {
+        mCommands = commands;
+        mAssertions = assertions;
+    }
+
+    @Override
+    public final void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+        mAssertions.assertMatchedByMimeGroup(mimeGroup, mimeType);
+    }
+
+    @Override
+    public final void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+        mAssertions.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+    }
+
+    @Override
+    public final void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+        mAssertions.assertMimeGroupInternal(mimeGroup, mimeTypes);
+    }
+
+    @Override
+    public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+        mCommands.setMimeGroup(mimeGroup, mimeTypes);
+    }
+
+    @Override
+    public final void clearMimeGroup(String mimeGroup) {
+        mCommands.clearMimeGroup(mimeGroup);
+    }
+
+    @Nullable
+    @Override
+    public Set<String> getMimeGroupInternal(String mimeGroup) {
+        return mCommands.getMimeGroupInternal(mimeGroup);
+    }
+
+    public MimeGroupCommands getCommands() {
+        return mCommands;
+    }
+
+    public MimeGroupAssertions getAssertions() {
+        return mAssertions;
+    }
+
+    public void setCommands(MimeGroupCommands commands) {
+        mCommands = commands;
+    }
+
+    public void setAssertions(MimeGroupAssertions assertions) {
+        mAssertions = assertions;
+    }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java
new file mode 100644
index 0000000..fce0c98f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.testapp.util;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class Utils {
+    public static void installApk(String pathToApk) {
+        executeShellCommand("pm install -t " + pathToApk);
+    }
+
+    public static void updateApp(String pathToApk) {
+        executeShellCommand("pm install -t -r  " + pathToApk);
+    }
+
+    public static void uninstallApp(String appPackage) {
+        executeShellCommand("pm uninstall " + appPackage);
+    }
+
+    private static void executeShellCommand(String command) {
+        ParcelFileDescriptor pfd = InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .executeShellCommand(command);
+        InputStream is = new FileInputStream(pfd.getFileDescriptor());
+
+        try {
+            readFully(is);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void readFully(InputStream in) throws IOException {
+        try {
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int count;
+            while ((count = in.read(buffer)) != -1) {
+                bytes.write(buffer, 0, count);
+            }
+
+            String result = new String(bytes.toByteArray());
+            assertTrue(result, result.isEmpty() || result.contains("Success"));
+        } finally {
+            in.close();
+        }
+    }
+}
diff --git a/hostsidetests/stagedinstall/app/AndroidManifest.xml b/hostsidetests/stagedinstall/app/AndroidManifest.xml
index 067e1cc..bc8bb0b 100644
--- a/hostsidetests/stagedinstall/app/AndroidManifest.xml
+++ b/hostsidetests/stagedinstall/app/AndroidManifest.xml
@@ -17,7 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.tests.stagedinstall" >
 
-    <uses-permission android:name="QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <application>
         <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
                   android:exported="true" />
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index d0a05f4..b3ab63c 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.Intent;
 import android.graphics.Color;
@@ -44,6 +45,7 @@
     public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
     public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
     public static final String ACTION_CRASH = "action.crash";
+    public static final String ACTION_CREATE_CHANNEL_GROUP = "action.create_channel_group";
 
     public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
     public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -81,6 +83,9 @@
             case ACTION_CRASH:
                 doCrash();
                 break;
+            case ACTION_CREATE_CHANNEL_GROUP:
+                doCreateChannelGroup();
+                break;
             default:
                 Log.e(TAG, "Intent had invalid action " + action);
                 finish();
@@ -135,15 +140,17 @@
 
     private void doShowNotification() {
         final int notificationId = R.layout.activity_main;
-        final String notificationChannel = "StatsdCtsChannel";
+        final String notificationChannelId = "StatsdCtsChannel";
 
         NotificationManager nm = getSystemService(NotificationManager.class);
-        nm.createNotificationChannel(new NotificationChannel(notificationChannel, "Statsd Cts",
-                NotificationManager.IMPORTANCE_DEFAULT));
+        NotificationChannel channel = new NotificationChannel(notificationChannelId, "Statsd Cts",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setDescription("Statsd Cts Channel");
+        nm.createNotificationChannel(channel);
 
         nm.notify(
                 notificationId,
-                new Notification.Builder(this, notificationChannel)
+                new Notification.Builder(this, notificationChannelId)
                         .setSmallIcon(android.R.drawable.stat_notify_chat)
                         .setContentTitle("StatsdCts")
                         .setContentText("StatsdCts")
@@ -152,6 +159,15 @@
         finish();
     }
 
+    private void doCreateChannelGroup() {
+        NotificationManager nm = getSystemService(NotificationManager.class);
+        NotificationChannelGroup channelGroup = new NotificationChannelGroup("StatsdCtsGroup",
+                "Statsd Cts Group");
+        channelGroup.setDescription("StatsdCtsGroup Description");
+        nm.createNotificationChannelGroup(channelGroup);
+        finish();
+    }
+
     @SuppressWarnings("ConstantOverflow")
     private void doCrash() {
         Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index af7438a..1d4b2d6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -48,11 +48,15 @@
 import com.android.os.AtomsProto.LmkKillOccurred;
 import com.android.os.AtomsProto.MediaCodecStateChanged;
 import com.android.os.AtomsProto.OverlayStateChanged;
+import com.android.os.AtomsProto.PackageNotificationPreferences;
+import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
+import com.android.os.AtomsProto.PackageNotificationChannelGroupPreferences;
 import com.android.os.AtomsProto.PictureInPictureStateChanged;
 import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
 import com.android.os.AtomsProto.ProcessMemorySnapshot;
 import com.android.os.AtomsProto.ProcessMemoryState;
 import com.android.os.AtomsProto.ScheduledJobStateChanged;
+import com.android.os.AtomsProto.SnapshotMergeReported;
 import com.android.os.AtomsProto.SyncStateChanged;
 import com.android.os.AtomsProto.TestAtomReported;
 import com.android.os.AtomsProto.VibratorStateChanged;
@@ -66,6 +70,7 @@
 
 import com.google.common.collect.Range;
 
+import java.lang.ProcessBuilder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -120,6 +125,32 @@
         assertThat(atom.getOomAdjScore()).isAtLeast(500);
     }
 
+    public void testSnapshotMergeReported() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        // Test is only valid for Virtual A/B devices
+        if (!"true".equals(getProperty("ro.virtual_ab.enabled"))) {
+            return;
+        }
+
+        final int atomTag = Atom.SNAPSHOT_MERGE_REPORTED_FIELD_NUMBER;
+        createAndUploadConfig(atomTag, false);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        Process process = new ProcessBuilder("snapshotctl", "--dry-run", "--report").start();
+
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        List<EventMetricData> data = getEventMetricDataList();
+
+        SnapshotMergeReported atom = data.get(0).getAtom().getSnapshotMergeReported();
+        assertThat(atom.getFinalState()).isEqualTo(SnapshotMergeReported.UpdateState.MERGE_COMPLETED);
+        assertThat(atom.getDurationMillis()).isEqualTo(1234);
+        assertThat(atom.getIntermediateReboots()).isEqualTo(56);
+    }
+
     public void testAppCrashOccurred() throws Exception {
         if (statsdDisabled()) {
             return;
@@ -1594,4 +1625,130 @@
         assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
     }
 
+    public void testNotificationPackagePreferenceExtraction() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        StatsdConfig.Builder config = getPulledConfig();
+        addGaugeAtomWithDimensions(config,
+                    Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER,
+                    null);
+        uploadConfig(config);
+        Thread.sleep(WAIT_TIME_SHORT);
+        runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
+        Thread.sleep(WAIT_TIME_SHORT);
+        setAppBreadcrumbPredicate();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        List<PackageNotificationPreferences> allPreferences = new ArrayList<>();
+        for (Atom atom : getGaugeMetricDataList()){
+            if(atom.hasPackageNotificationPreferences()) {
+                allPreferences.add(atom.getPackageNotificationPreferences());
+            }
+        }
+        assertThat(allPreferences.size()).isGreaterThan(0);
+
+        boolean foundTestPackagePreferences = false;
+        int uid = getUid();
+        for (PackageNotificationPreferences pref : allPreferences) {
+            assertThat(pref.getUid()).isGreaterThan(0);
+            assertTrue(pref.hasImportance());
+            assertTrue(pref.hasVisibility());
+            assertTrue(pref.hasUserLockedFields());
+            if(pref.getUid() == uid){
+                assertThat(pref.getImportance()).isEqualTo(-1000);  //UNSPECIFIED_IMPORTANCE
+                assertThat(pref.getVisibility()).isEqualTo(-1000);  //UNSPECIFIED_VISIBILITY
+                foundTestPackagePreferences = true;
+            }
+        }
+        assertTrue(foundTestPackagePreferences);
+    }
+
+    public void testNotificationChannelPreferencesExtraction() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        StatsdConfig.Builder config = getPulledConfig();
+        addGaugeAtomWithDimensions(config,
+                    Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER,
+                    null);
+        uploadConfig(config);
+        Thread.sleep(WAIT_TIME_SHORT);
+        runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
+        Thread.sleep(WAIT_TIME_SHORT);
+        setAppBreadcrumbPredicate();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        List<PackageNotificationChannelPreferences> allChannelPreferences = new ArrayList<>();
+        for(Atom atom : getGaugeMetricDataList()) {
+            if (atom.hasPackageNotificationChannelPreferences()) {
+               allChannelPreferences.add(atom.getPackageNotificationChannelPreferences());
+            }
+        }
+        assertThat(allChannelPreferences.size()).isGreaterThan(0);
+
+        boolean foundTestPackagePreferences = false;
+        int uid = getUid();
+        for (PackageNotificationChannelPreferences pref : allChannelPreferences) {
+            assertThat(pref.getUid()).isGreaterThan(0);
+            assertTrue(pref.hasChannelId());
+            assertTrue(pref.hasChannelName());
+            assertTrue(pref.hasDescription());
+            assertTrue(pref.hasImportance());
+            assertTrue(pref.hasUserLockedFields());
+            assertTrue(pref.hasIsDeleted());
+            if(uid == pref.getUid() && pref.getChannelId().equals("StatsdCtsChannel")) {
+                assertThat(pref.getChannelName()).isEqualTo("Statsd Cts");
+                assertThat(pref.getDescription()).isEqualTo("Statsd Cts Channel");
+                assertThat(pref.getImportance()).isEqualTo(3);  // IMPORTANCE_DEFAULT
+                foundTestPackagePreferences = true;
+            }
+        }
+        assertTrue(foundTestPackagePreferences);
+    }
+
+    public void testNotificationChannelGroupPreferencesExtraction() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        StatsdConfig.Builder config = getPulledConfig();
+        addGaugeAtomWithDimensions(config,
+                    Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER,
+                    null);
+        uploadConfig(config);
+        Thread.sleep(WAIT_TIME_SHORT);
+        runActivity("StatsdCtsForegroundActivity", "action", "action.create_channel_group");
+        Thread.sleep(WAIT_TIME_SHORT);
+        setAppBreadcrumbPredicate();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        List<PackageNotificationChannelGroupPreferences> allGroupPreferences = new ArrayList<>();
+        for(Atom atom : getGaugeMetricDataList()) {
+            if (atom.hasPackageNotificationChannelGroupPreferences()) {
+                allGroupPreferences.add(atom.getPackageNotificationChannelGroupPreferences());
+            }
+        }
+        assertThat(allGroupPreferences.size()).isGreaterThan(0);
+
+        boolean foundTestPackagePreferences = false;
+        int uid = getUid();
+        for(PackageNotificationChannelGroupPreferences pref : allGroupPreferences) {
+            assertThat(pref.getUid()).isGreaterThan(0);
+            assertTrue(pref.hasGroupId());
+            assertTrue(pref.hasGroupName());
+            assertTrue(pref.hasDescription());
+            assertTrue(pref.hasIsBlocked());
+            assertTrue(pref.hasUserLockedFields());
+            if(uid == pref.getUid() && pref.getGroupId().equals("StatsdCtsGroup")) {
+                assertThat(pref.getGroupName()).isEqualTo("Statsd Cts Group");
+                assertThat(pref.getDescription()).isEqualTo("StatsdCtsGroup Description");
+                assertThat(pref.getIsBlocked()).isFalse();
+                foundTestPackagePreferences = true;
+            }
+        }
+        assertTrue(foundTestPackagePreferences);
+    }
 }
diff --git a/hostsidetests/userspacereboot/AndroidTest.xml b/hostsidetests/userspacereboot/AndroidTest.xml
index 97f67af..5df919f 100644
--- a/hostsidetests/userspacereboot/AndroidTest.xml
+++ b/hostsidetests/userspacereboot/AndroidTest.xml
@@ -20,7 +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="not_secondary_user" />
-    <test class="com.android.tradefed.testtype.HostTest" >
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="class" value="com.android.cts.userspacereboot.host.UserspaceRebootHostTest" />
     </test>
 </configuration>
diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
index f953a1d..0100e6a 100644
--- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
+++ b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
@@ -155,7 +155,7 @@
     }
 
     private void rebootUserspaceAndWaitForBootComplete() throws Exception {
-        getDevice().setProperty("test.userspace_reboot.requested", "1");
+        assertThat(getDevice().setProperty("test.userspace_reboot.requested", "1")).isTrue();
         getDevice().rebootUserspace();
         assertWithMessage("Device did not boot withing 2 minutes").that(
                 getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue();
@@ -164,7 +164,11 @@
     private void assertUserspaceRebootSucceed() throws Exception {
         // If userspace reboot fails and fallback to hard reboot is triggered then
         // test.userspace_reboot.requested won't be set.
-        assertWithMessage("Userspace reboot failed and fallback to full reboot was triggered").that(
-                getDevice().getProperty("test.userspace_reboot.requested")).isEqualTo("1");
+        final String bootReason = getDevice().getProperty("sys.boot.reason.last");
+        final boolean result = getDevice().getBooleanProperty("test.userspace_reboot.requested",
+                false);
+        assertWithMessage(
+                "Userspace reboot failed and fallback to full reboot was triggered. Boot reason: "
+                        + "%s", bootReason).that(result).isTrue();
     }
 }
diff --git a/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java b/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java
index 80a8964..fae440b 100644
--- a/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java
+++ b/hostsidetests/webkit/app/src/com/android/cts/webkit/WebViewDeviceSideStartupTest.java
@@ -174,6 +174,8 @@
                 .build());
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectAll()
+                // TODO(b/149790106): Fix and remove.
+                .permitIncorrectContextUse()
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
diff --git a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
index 8dcc4d3..0b4882a 100644
--- a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
+++ b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
@@ -162,5 +162,7 @@
         } finally {
             in.close();
         }
+        //Re-enable Wi-Fi as part of CTS Pre-conditions
+        device.executeShellCommand("svc wifi enable; sleep 1");
     }
 }
diff --git a/tests/BlobStore/Android.bp b/tests/BlobStore/Android.bp
index c168eb6..0d51b2d 100644
--- a/tests/BlobStore/Android.bp
+++ b/tests/BlobStore/Android.bp
@@ -25,11 +25,35 @@
     ],
     srcs: [
         "src/**/*.java",
+        ":CtsBlobStoreTestsAidl",
     ],
     test_suites: [
         "cts",
         "vts",
         "general-tests",
     ],
-    platform_apis: true,
+    sdk_version: "test_current"
+}
+
+android_test_helper_app {
+    name: "CtsBlobStoreTestHelper",
+    defaults: ["cts_defaults"],
+    srcs: [
+        "helper-app/src/**/*.java",
+        ":CtsBlobStoreTestsAidl"
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    manifest: "helper-app/AndroidManifest.xml",
+    sdk_version: "test_current"
+}
+
+filegroup {
+    name: "CtsBlobStoreTestsAidl",
+    srcs: [
+        "aidl/**/*.aidl",
+    ]
 }
\ No newline at end of file
diff --git a/tests/BlobStore/AndroidTest.xml b/tests/BlobStore/AndroidTest.xml
index 98b9d86..42c6a67 100644
--- a/tests/BlobStore/AndroidTest.xml
+++ b/tests/BlobStore/AndroidTest.xml
@@ -22,6 +22,7 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsBlobStoreTestCases.apk" />
+        <option name="test-file-name" value="CtsBlobStoreTestHelper.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.cts.blob" />
diff --git a/tests/BlobStore/aidl/com/android/cts/blob/ICommandReceiver.aidl b/tests/BlobStore/aidl/com/android/cts/blob/ICommandReceiver.aidl
new file mode 100644
index 0000000..86d2f18
--- /dev/null
+++ b/tests/BlobStore/aidl/com/android/cts/blob/ICommandReceiver.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.blob;
+
+import android.app.blob.BlobHandle;
+import android.app.usage.StorageStats;
+
+interface ICommandReceiver {
+    void acquireLease(in BlobHandle blobHandle);
+    void releaseLease(in BlobHandle blobHandle);
+
+    StorageStats queryStatsForPackage();
+    StorageStats queryStatsForUid();
+}
\ No newline at end of file
diff --git a/tests/BlobStore/helper-app/AndroidManifest.xml b/tests/BlobStore/helper-app/AndroidManifest.xml
new file mode 100644
index 0000000..5ad854f
--- /dev/null
+++ b/tests/BlobStore/helper-app/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.cts.blob.helper" >
+
+    <application>
+        <service android:name=".BlobStoreTestService" android:exported="true"/>
+    </application>
+
+</manifest>
+
diff --git a/tests/BlobStore/helper-app/src/com/android/cts/blob/helper/BlobStoreTestService.java b/tests/BlobStore/helper-app/src/com/android/cts/blob/helper/BlobStoreTestService.java
new file mode 100644
index 0000000..4694dc5
--- /dev/null
+++ b/tests/BlobStore/helper-app/src/com/android/cts/blob/helper/BlobStoreTestService.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.blob.helper;
+
+import static android.os.storage.StorageManager.UUID_DEFAULT;
+
+import android.app.Service;
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.IBinder;
+import android.os.Process;
+
+import com.android.cts.blob.ICommandReceiver;
+
+import java.io.IOException;
+
+public class BlobStoreTestService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new CommandReceiver();
+    }
+
+    private class CommandReceiver extends ICommandReceiver.Stub {
+        public void acquireLease(BlobHandle blobHandle) {
+            final BlobStoreManager blobStoreManager = getSystemService(
+                    BlobStoreManager.class);
+            try {
+                blobStoreManager.acquireLease(blobHandle, "Test description");
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public void releaseLease(BlobHandle blobHandle) {
+            final BlobStoreManager blobStoreManager = getSystemService(
+                    BlobStoreManager.class);
+            try {
+                blobStoreManager.releaseLease(blobHandle);
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public StorageStats queryStatsForPackage() {
+            final StorageStatsManager storageStatsManager = getSystemService(
+                    StorageStatsManager.class);
+            try {
+                return storageStatsManager
+                        .queryStatsForPackage(UUID_DEFAULT, getPackageName(), getUser());
+            } catch (IOException | NameNotFoundException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public StorageStats queryStatsForUid() {
+            final StorageStatsManager storageStatsManager = getSystemService(
+                    StorageStatsManager.class);
+            try {
+                return storageStatsManager
+                        .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
index ab76368..853f7ce 100644
--- a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
+++ b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
@@ -15,16 +15,29 @@
  */
 package com.android.cts.blob;
 
+import static android.os.storage.StorageManager.UUID_DEFAULT;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.testng.Assert.assertThrows;
 
 import android.app.blob.BlobHandle;
 import android.app.blob.BlobStoreManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.util.Log;
 
 import com.android.cts.blob.R;
+import com.android.cts.blob.ICommandReceiver;
 import com.android.utils.blob.DummyBlobData;
 
 import org.junit.After;
@@ -34,7 +47,9 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -42,8 +57,15 @@
 
 @RunWith(AndroidJUnit4.class)
 public class BlobStoreManagerTest {
+    private static final String TAG = "BlobStoreTest";
+
     private static final long TIMEOUT_COMMIT_CALLBACK_SEC = 5;
 
+    private static final long TIMEOUT_BIND_SERVICE_SEC = 2;
+
+    private static final String HELPER_PKG = "com.android.cts.blob.helper";
+    private static final String HELPER_SERVICE = HELPER_PKG + ".BlobStoreTestService";
+
     private Context mContext;
     private BlobStoreManager mBlobStoreManager;
 
@@ -56,18 +78,17 @@
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
         mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
                 Context.BLOB_STORE_SERVICE);
-        mCreatedSessionIds.clear();
+        clearAllBlobsData();
     }
 
     @After
     public void tearDown() {
-        for (long sessionId : mCreatedSessionIds) {
-            try {
-                mBlobStoreManager.deleteSession(sessionId);
-            } catch (Exception e) {
-                // Ignore
-            }
-        }
+        clearAllBlobsData();
+    }
+
+    private void clearAllBlobsData() {
+        executeCmd("cmd blob_store clear-all-sessions");
+        executeCmd("cmd blob_store clear-all-blobs");
     }
 
     @Test
@@ -443,9 +464,199 @@
         assertThrows(NullPointerException.class, () -> mBlobStoreManager.releaseLease(null));
     }
 
+    @Test
+    public void testStorageAttributedToSelf() throws Exception {
+        final DummyBlobData blobData = new DummyBlobData(mContext);
+        blobData.prepare();
+        final long partialFileSize = 3373L;
+
+        final StorageStatsManager storageStatsManager = mContext.getSystemService(
+                StorageStatsManager.class);
+        StorageStats beforeStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        StorageStats beforeStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // Create a session and write some data.
+        final long sessionId = createSession(blobData.getBlobHandle());
+        assertThat(sessionId).isGreaterThan(0L);
+        try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+            blobData.writeToSession(session, 0, partialFileSize);
+        }
+
+        StorageStats afterStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        StorageStats afterStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // 'partialFileSize' bytes were written, verify the size increase.
+        assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+                .isEqualTo(partialFileSize);
+        assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+                .isEqualTo(partialFileSize);
+
+        // Complete writing data.
+        final long totalFileSize = blobData.getFileSize();
+        try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+            blobData.writeToSession(session, partialFileSize, totalFileSize - partialFileSize);
+        }
+
+        afterStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        afterStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // 'totalFileSize' bytes were written so far, verify the size increase.
+        assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+                .isEqualTo(totalFileSize);
+        assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+                .isEqualTo(totalFileSize);
+
+        // Commit the session.
+        try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+            blobData.writeToSession(session, partialFileSize, session.getSize() - partialFileSize);
+            final CompletableFuture<Integer> callback = new CompletableFuture<>();
+            session.commit(mContext.getMainExecutor(), callback::complete);
+            assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
+                    .isEqualTo(0);
+        }
+
+        mBlobStoreManager.acquireLease(blobData.getBlobHandle(), R.string.test_desc);
+
+        afterStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        afterStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // Session was committed but no one else is using it, verify the size increase stays
+        // the same as earlier.
+        assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+                .isEqualTo(totalFileSize);
+        assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+                .isEqualTo(totalFileSize);
+
+        mBlobStoreManager.releaseLease(blobData.getBlobHandle());
+
+        afterStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        afterStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // No leases on the blob, so it should not be attributed.
+        assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+                .isEqualTo(0L);
+        assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+                .isEqualTo(0L);
+    }
+
+    @Test
+    public void testStorageAttribution_acquireLease() throws Exception {
+        final DummyBlobData blobData = new DummyBlobData(mContext);
+        blobData.prepare();
+
+        final StorageStatsManager storageStatsManager = mContext.getSystemService(
+                StorageStatsManager.class);
+        StorageStats beforeStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        StorageStats beforeStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+        try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+            blobData.writeToSession(session);
+            session.allowPublicAccess();
+
+            final CompletableFuture<Integer> callback = new CompletableFuture<>();
+            session.commit(mContext.getMainExecutor(), callback::complete);
+            assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
+                    .isEqualTo(0);
+        }
+
+        StorageStats afterStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        StorageStats afterStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // No leases on the blob, so it should not be attributed.
+        assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+                .isEqualTo(0L);
+        assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+                .isEqualTo(0L);
+
+        final ICommandReceiver commandReceiver = bindToHelperService();
+
+        StorageStats beforeStatsForHelperPkg = commandReceiver.queryStatsForPackage();
+        StorageStats beforeStatsForHelperUid = commandReceiver.queryStatsForUid();
+
+        commandReceiver.acquireLease(blobData.getBlobHandle());
+
+        StorageStats afterStatsForHelperPkg = commandReceiver.queryStatsForPackage();
+        StorageStats afterStatsForHelperUid = commandReceiver.queryStatsForUid();
+
+        assertThat(afterStatsForHelperPkg.getDataBytes() - beforeStatsForHelperPkg.getDataBytes())
+                .isEqualTo(blobData.getFileSize());
+        assertThat(afterStatsForHelperUid.getDataBytes() - beforeStatsForHelperUid.getDataBytes())
+                .isEqualTo(blobData.getFileSize());
+
+        afterStatsForPkg = storageStatsManager
+                .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+        afterStatsForUid = storageStatsManager
+                .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+        // There shouldn't be no change in stats for this package
+        assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+                .isEqualTo(0L);
+        assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+                .isEqualTo(0L);
+
+        commandReceiver.releaseLease(blobData.getBlobHandle());
+
+        afterStatsForHelperPkg = commandReceiver.queryStatsForPackage();
+        afterStatsForHelperUid = commandReceiver.queryStatsForUid();
+
+        // Lease is released, so it should not be attributed anymore.
+        assertThat(afterStatsForHelperPkg.getDataBytes() - beforeStatsForHelperPkg.getDataBytes())
+                .isEqualTo(0L);
+        assertThat(afterStatsForHelperUid.getDataBytes() - beforeStatsForHelperUid.getDataBytes())
+                .isEqualTo(0L);
+    }
+
+    private ICommandReceiver bindToHelperService() throws Exception {
+        final TestServiceConnection serviceConnection = new TestServiceConnection();
+        final Intent intent = new Intent()
+                .setComponent(new ComponentName(HELPER_PKG, HELPER_SERVICE));
+        mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+        return ICommandReceiver.Stub.asInterface(serviceConnection.getService());
+    }
+
+    private class TestServiceConnection implements ServiceConnection {
+        private BlockingQueue<IBinder> mBlockingQueue = new LinkedBlockingQueue<>();
+
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            Log.i(TAG, "Service got connected: " + componentName);
+            mBlockingQueue.offer(service);
+        }
+
+        public void onServiceDisconnected(ComponentName componentName) {
+            Log.e(TAG, "Service got disconnected: " + componentName);
+        }
+
+        public IBinder getService() throws Exception {
+            final IBinder service = mBlockingQueue.poll(TIMEOUT_BIND_SERVICE_SEC,
+                    TimeUnit.SECONDS);
+            return service;
+        }
+    }
+
     private long createSession(BlobHandle blobHandle) throws Exception {
         final long sessionId = mBlobStoreManager.createSession(blobHandle);
         mCreatedSessionIds.add(sessionId);
         return sessionId;
     }
+
+    private String executeCmd(String cmd) {
+        final String result = runShellCommand(cmd).trim();
+        Log.d(TAG, "Output of <" + cmd + ">: <" + result + ">");
+        return result;
+    }
 }
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index c0fada5..9c177cd 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -75,8 +75,7 @@
         super.setUp();
 
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
-        mCm =
-                (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
 
         PackageManager packageManager = mContext.getPackageManager();
         mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI);
@@ -84,7 +83,9 @@
         mBuilder =
                 new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent);
 
-        mInitialWiFiState = mWifiManager.isWifiEnabled();
+        if (mHasWifi) {
+            mInitialWiFiState = mWifiManager.isWifiEnabled();
+        }
         mInitialRestrictBackground = SystemUtil
                 .runShellCommand(getInstrumentation(), RESTRICT_BACKGROUND_GET_CMD)
                 .contains("enabled");
@@ -101,7 +102,7 @@
         setDataSaverEnabled(mInitialRestrictBackground);
 
         // Ensure that we leave WiFi in its previous state.
-        if (mWifiManager.isWifiEnabled() != mInitialWiFiState) {
+        if (mHasWifi && mWifiManager.isWifiEnabled() != mInitialWiFiState) {
             setWifiState(mInitialWiFiState, mCm, mWifiManager);
         }
 
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index ada5019..d2f7c2d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -98,7 +98,7 @@
                 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
                 | AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
         assertEquals("foo.bar.Activity", speakingService.getSettingsActivityName());
-        assertEquals(R.drawable.size_48x48, speakingService.getAnimatedImageRes());
+        assertNotNull(speakingService.loadAnimatedImage(getInstrumentation().getContext()));
         assertEquals("Some description", speakingService.loadDescription(
                 getInstrumentation().getContext().getPackageManager()));
         assertEquals("Some html description", speakingService.loadHtmlDescription(
diff --git a/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml b/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
index 58824d4..c41c1fd 100644
--- a/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
+++ b/tests/accessibilityservice/res/layout/accessibility_text_traversal_test.xml
@@ -20,6 +20,7 @@
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
+    android:id="@+id/viewGroup"
     >
 
    <View
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
index c832368..659aaf7 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
@@ -313,7 +313,7 @@
             final Rect boundsInScreen = new Rect();
             final DisplayMetrics displayMetrics = new DisplayMetrics();
             buttonNode.getBoundsInScreen(boundsInScreen);
-            mInstrumentation.getContext().getDisplay().getMetrics(displayMetrics);
+            activity.getDisplay().getMetrics(displayMetrics);
             final Rect displayRect = new Rect(0, 0,
                     displayMetrics.widthPixels, displayMetrics.heightPixels);
             // The boundsInScreen of button is adjusted to outside of screen by framework,
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
index 4cc0116..ca59377 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -16,6 +16,7 @@
 
 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
 import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -47,7 +48,11 @@
 import android.text.style.ImageSpan;
 import android.text.style.ReplacementSpan;
 import android.text.style.URLSpan;
+import android.util.DisplayMetrics;
+import android.util.Size;
+import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
@@ -465,6 +470,65 @@
                 textView, EditorInfo.IME_ACTION_DONE, null);
     }
 
+    @Test
+    public void testExtraRendering_textViewShouldProvideExtraDataTextSizeWhenRequested() {
+        final Bundle arg = new Bundle();
+        final DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
+        final TextView textView = mActivity.findViewById(R.id.text);
+        final String stringToSet = mActivity.getString(R.string.foo_bar_baz);
+        final int expectedWidthInPx = textView.getLayoutParams().width;
+        final int expectedHeightInPx = textView.getLayoutParams().height;
+        final float expectedTextSize = textView.getTextSize();
+        final float newTextSize = 20f;
+        final float expectedNewTextSize = (int) (0.5f + TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_SP, newTextSize, displayMetrics));
+        makeTextViewVisibleAndSetText(textView, stringToSet);
+
+        final AccessibilityNodeInfo info = sUiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByText(stringToSet).get(0);
+        assertTrue("Text view should offer extra data to accessibility ",
+                info.getAvailableExtraData().contains(EXTRA_DATA_RENDERING_INFO_KEY));
+
+        AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo;
+        assertNull(info.getExtraRenderingInfo());
+        assertTrue("Refresh failed", info.refreshWithExtraData(
+                EXTRA_DATA_RENDERING_INFO_KEY , arg));
+        assertNotNull(info.getExtraRenderingInfo());
+        extraRenderingInfo = info.getExtraRenderingInfo();
+        assertNotNull(extraRenderingInfo.getLayoutParams());
+        assertEquals(expectedWidthInPx, extraRenderingInfo.getLayoutParams().getWidth());
+        assertEquals(expectedHeightInPx, extraRenderingInfo.getLayoutParams().getHeight());
+        assertEquals(expectedTextSize, extraRenderingInfo.getTextSizeInPx(), 0f);
+        assertEquals(TypedValue.COMPLEX_UNIT_DIP, extraRenderingInfo.getTextSizeUnit());
+
+        // After changing text size
+        sInstrumentation.runOnMainSync(() ->
+                textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, newTextSize));
+        assertTrue("Refresh failed", info.refreshWithExtraData(
+                EXTRA_DATA_RENDERING_INFO_KEY, arg));
+        extraRenderingInfo = info.getExtraRenderingInfo();
+        assertEquals(expectedNewTextSize, extraRenderingInfo.getTextSizeInPx(), 0f);
+        assertEquals(TypedValue.COMPLEX_UNIT_SP, extraRenderingInfo.getTextSizeUnit());
+    }
+
+    @Test
+    public void testExtraRendering_viewGroupShouldNotProvideLayoutParamsWhenNotRequested() {
+        final AccessibilityNodeInfo info = sUiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByViewId(
+                        "android.accessibilityservice.cts:id/viewGroup").get(0);
+
+        assertTrue("ViewGroup should offer extra data to accessibility",
+                info.getAvailableExtraData().contains(EXTRA_DATA_RENDERING_INFO_KEY));
+        assertNull(info.getExtraRenderingInfo());
+        assertTrue("Refresh failed", info.refreshWithExtraData(
+                EXTRA_DATA_RENDERING_INFO_KEY, new Bundle()));
+        assertNotNull(info.getExtraRenderingInfo());
+        assertNotNull(info.getExtraRenderingInfo().getLayoutParams());
+        final Size size = info.getExtraRenderingInfo().getLayoutParams();
+        assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, size.getWidth());
+        assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, size.getHeight());
+    }
+
     private void verifyImeActionLabel(AccessibilityNodeInfo node, String label) {
         final List<AccessibilityNodeInfo.AccessibilityAction> actionList = node.getActionList();
         final int indexOfActionImeEnter =
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index 9042d62..bc30c7d4 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -68,8 +68,10 @@
 import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.graphics.Region;
 import android.platform.test.annotations.AppModeFull;
 import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -353,8 +355,7 @@
         dispatch(doubleTap(mTapLocation));
         mHoverListener.assertNonePropagated();
         mTouchListener.assertNonePropagated();
-        mService.assertPropagated(
-                TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+        mService.assertPropagated(TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
         mService.clearEvents();
         mLongClickListener.assertNoneLongClicked();
     }
@@ -470,6 +471,84 @@
                 ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_MOVE, ACTION_POINTER_UP, ACTION_UP);
     }
 
+    /**
+     * Test the gesture detection passthrough by performing a fast swipe in the passthrough region.
+     * It should bypass the gesture detector entirely.
+     */
+    @Test
+    @AppModeFull
+    public void testGestureDetectionPassthrough_initiatesTouchExploration() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        setRightSideOfScreenGestureDetectionPassthrough();
+        // Swipe in the passthrough region. This should generate hover events.
+        dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+        mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        mService.assertPropagated(
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_END,
+                TYPE_TOUCH_INTERACTION_END);
+        mService.clearEvents();
+        // Swipe starting inside the passthrough region but ending outside of it. This should still
+        // behave as a passthrough interaction.
+        dispatch(swipe(mTapLocation, add(mTapLocation, -mSwipeDistance, 0)));
+        mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        mService.assertPropagated(
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_END,
+                TYPE_TOUCH_INTERACTION_END);
+        mService.clearEvents();
+        // Swipe outside the passthrough region. This should not generate hover events.
+        dispatch(swipe(add(mTapLocation, -1, 0), add(mTapLocation, -mSwipeDistance, 0)));
+        mHoverListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_GESTURE_DETECTION_START,
+                TYPE_GESTURE_DETECTION_END,
+                TYPE_TOUCH_INTERACTION_END);
+        mService.clearEvents();
+        // There should be no touch events in this test.
+        mTouchListener.assertNonePropagated();
+        clearPassthroughRegions();
+    }
+
+    /**
+     * Test the touch exploration passthrough by performing a fast swipe in the passthrough region.
+     * It should generate touch events.
+     */
+    @Test
+    @AppModeFull
+    public void testTouchExplorationPassthrough_sendsTouchEvents() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        setRightSideOfScreenTouchExplorationPassthrough();
+        // Swipe in the passthrough region. This should generate  touch events.
+        dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+        mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+        // We still want accessibility events to tell us when the gesture starts and ends.
+        mService.assertPropagated(
+                TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+        mService.clearEvents();
+        // Swipe starting inside the passthrough region but ending outside of it. This should still
+        // behave as a passthrough interaction.
+        dispatch(swipe(mTapLocation, add(mTapLocation, -mSwipeDistance, 0)));
+        mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+        mService.assertPropagated(
+                TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+        mService.clearEvents();
+        // Swipe outside the passthrough region. This should not generate touch events.
+        dispatch(swipe(add(mTapLocation, -1, 0), add(mTapLocation, -mSwipeDistance, 0)));
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_GESTURE_DETECTION_START,
+                TYPE_GESTURE_DETECTION_END,
+                TYPE_TOUCH_INTERACTION_END);
+        // There should be no hover events in this test.
+        mHoverListener.assertNonePropagated();
+        clearPassthroughRegions();
+    }
+
     public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
         GestureDescription.Builder builder =
                 new GestureDescription.Builder().addStroke(firstStroke);
@@ -493,4 +572,44 @@
                             .performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
                 });
     }
+
+    private void setRightSideOfScreenGestureDetectionPassthrough() {
+        Region region = getRightSideOfScreenRegion();
+        mService.runOnServiceSync(
+                () -> {
+                    mService.setGestureDetectionPassthroughRegion(Display.DEFAULT_DISPLAY, region);
+                });
+    }
+
+    private void setRightSideOfScreenTouchExplorationPassthrough() {
+        Region region = getRightSideOfScreenRegion();
+        mService.runOnServiceSync(
+                () -> {
+                    mService.setTouchExplorationPassthroughRegion(Display.DEFAULT_DISPLAY, region);
+                });
+    }
+
+    private void clearPassthroughRegions() {
+        mService.runOnServiceSync(
+                () -> {
+                    mService.setGestureDetectionPassthroughRegion(
+                            Display.DEFAULT_DISPLAY, new Region());
+                    mService.setTouchExplorationPassthroughRegion(
+                            Display.DEFAULT_DISPLAY, new Region());
+                });
+    }
+
+    private Region getRightSideOfScreenRegion() {
+        WindowManager windowManager =
+                (WindowManager)
+                        mInstrumentation.getContext().getSystemService(Context.WINDOW_SERVICE);
+        final DisplayMetrics metrics = new DisplayMetrics();
+        windowManager.getDefaultDisplay().getRealMetrics(metrics);
+        int top = 0;
+        int left = metrics.widthPixels / 2;
+        int right = metrics.widthPixels;
+        int bottom = metrics.heightPixels;
+        Region region = new Region(left, top, right, bottom);
+        return region;
+    }
 }
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index dc3c57e..c1a02ab 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -145,55 +145,58 @@
         }
     }
 
-    public void testSetSecurityLoggingEnabled_failIfNotDeviceOwner() {
+    public void testSetSecurityLoggingEnabled_failIfNotOrganizationOwnedProfileOwner() {
         if (!mDeviceAdmin) {
-            Log.w(TAG, "Skipping testSetSecurityLoggingEnabled_failIfNotDeviceOwner");
+            Log.w(TAG, "Skipping testSetSecurityLoggingEnabled_"
+                    + "failIfNotOrganizationOwnedProfileOwner");
             return;
         }
         try {
             mDevicePolicyManager.setSecurityLoggingEnabled(mComponent, true);
             fail("did not throw expected SecurityException");
         } catch (SecurityException e) {
-            assertDeviceOwnerMessage(e.getMessage());
+            assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
         }
     }
 
-    public void testIsSecurityLoggingEnabled_failIfNotDeviceOwner() {
+    public void testIsSecurityLoggingEnabled_failIfNotOrganizationOwnedProfileOwner() {
         if (!mDeviceAdmin) {
-            Log.w(TAG, "Skipping testIsSecurityLoggingEnabled_failIfNotDeviceOwner");
+            Log.w(TAG, "Skipping testIsSecurityLoggingEnabled_"
+                    + "failIfNotOrganizationOwnedProfileOwner");
             return;
         }
         try {
             mDevicePolicyManager.isSecurityLoggingEnabled(mComponent);
             fail("did not throw expected SecurityException");
         } catch (SecurityException e) {
-            assertDeviceOwnerMessage(e.getMessage());
+            assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
         }
     }
 
-    public void testRetrieveSecurityLogs_failIfNotDeviceOwner() {
+    public void testRetrieveSecurityLogs_failIfNotOrganizationOwnedProfileOwner() {
         if (!mDeviceAdmin) {
-            Log.w(TAG, "Skipping testRetrieveSecurityLogs_failIfNotDeviceOwner");
+            Log.w(TAG, "Skipping testRetrieveSecurityLogs_failIfNotOrganizationOwnedProfileOwner");
             return;
         }
         try {
             mDevicePolicyManager.retrieveSecurityLogs(mComponent);
             fail("did not throw expected SecurityException");
         } catch (SecurityException e) {
-            assertDeviceOwnerMessage(e.getMessage());
+            assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
         }
     }
 
-    public void testRetrievePreRebootSecurityLogs_failIfNotDeviceOwner() {
+    public void testRetrievePreRebootSecurityLogs_failIfNotOrganizationOwnedProfileOwner() {
         if (!mDeviceAdmin) {
-            Log.w(TAG, "Skipping testRetrievePreRebootSecurityLogs_failIfNotDeviceOwner");
+            Log.w(TAG, "Skipping testRetrievePreRebootSecurityLogs_"
+                    + "failIfNotOrganizationOwnedProfileOwner");
             return;
         }
         try {
             mDevicePolicyManager.retrievePreRebootSecurityLogs(mComponent);
             fail("did not throw expected SecurityException");
         } catch (SecurityException e) {
-            assertDeviceOwnerMessage(e.getMessage());
+            assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
         }
     }
 
@@ -287,20 +290,6 @@
         }
     }
 
-    public void testRequestSetLocationProviderEnabled_failIfNotDeviceOwner() {
-        if (!mDeviceAdmin) {
-            Log.w(TAG, "Skipping testRequestSetLocationProviderEnabled_failIfNotDeviceOwner");
-            return;
-        }
-        try {
-            mDevicePolicyManager.requestSetLocationProviderAllowed(mComponent, "test_provider",
-                    true);
-            fail("did not throw expected SecurityException");
-        } catch (SecurityException e) {
-            assertDeviceOwnerMessage(e.getMessage());
-        }
-    }
-
     public void testSetGlobalSetting_failIfNotDeviceOwner() {
         if (!mDeviceAdmin) {
             Log.w(TAG, "Skipping testSetGlobalSetting_failIfNotDeviceOwner");
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 6115dfb..2c9ff16 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -731,10 +731,6 @@
     }
 
     public void testConsolidatedNotificationPolicy() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
         try {
@@ -794,10 +790,6 @@
     }
 
     public void testConsolidatedNotificationPolicyMultiRules() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
         try {
@@ -863,10 +855,6 @@
     }
 
     public void testPostPCanToggleAlarmsMediaSystemTest() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -897,10 +885,6 @@
     }
 
     public void testPostRCanToggleConversationsTest() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -1267,10 +1251,6 @@
     }
 
     public void testSuspendPackage() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -1313,10 +1293,6 @@
     }
 
     public void testSuspendedPackageSendsNotification() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -1405,10 +1381,6 @@
     }
 
     public void testShowBadging_ranking() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         final int originalBadging = Settings.Secure.getInt(
                 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING);
 
@@ -1461,10 +1433,6 @@
     }
 
     public void testGetSuppressedVisualEffectsOff_ranking() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -1491,10 +1459,6 @@
     }
 
     public void testGetSuppressedVisualEffects_ranking() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
         NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
         try {
@@ -1547,10 +1511,6 @@
     }
 
     public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -1952,10 +1912,6 @@
     }
 
     public void testTotalSilenceOnlyMuteStreams() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
         try {
@@ -1997,10 +1953,6 @@
     }
 
     public void testAlarmsOnlyMuteStreams() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
         try {
@@ -2042,10 +1994,6 @@
     }
 
     public void testAddAutomaticZenRule_configActivity() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2058,10 +2006,6 @@
     }
 
     public void testUpdateAutomaticZenRule_configActivity() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2076,10 +2020,6 @@
     }
 
     public void testRemoveAutomaticZenRule_configActivity() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2095,10 +2035,6 @@
     }
 
     public void testSetAutomaticZenRuleState() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2118,10 +2054,6 @@
     }
 
     public void testSetAutomaticZenRuleState_turnOff() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2150,10 +2082,6 @@
     }
 
     public void testSetAutomaticZenRuleState_deletedRule() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2178,10 +2106,6 @@
     }
 
     public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -2215,9 +2139,6 @@
     }
 
     public void testSetNotificationPolicy_P_setOldFields() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -2240,9 +2161,6 @@
     }
 
     public void testSetNotificationPolicy_P_setNewFields() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -2265,9 +2183,6 @@
     }
 
     public void testSetNotificationPolicy_P_setOldNewFields() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -2401,10 +2316,6 @@
 
     public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
             throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2555,10 +2466,6 @@
     }
 
     public void testShouldHideSilentStatusIcons() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         try {
             mNotificationManager.shouldHideSilentStatusBarIcons();
             fail("Non-privileged apps should not get this information");
@@ -2573,10 +2480,6 @@
     }
 
     public void testMatchesCallFilter() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // allow all callers
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
@@ -2634,10 +2537,6 @@
     }
 
     public void testNotificationListener_setNotificationsShown() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2667,10 +2566,6 @@
     }
 
     public void testNotificationListener_getNotificationChannels() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2687,10 +2582,6 @@
     }
 
     public void testNotificationListener_getNotificationChannelGroups() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2706,10 +2597,6 @@
     }
 
     public void testNotificationListener_updateNotificationChannel() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2730,10 +2617,6 @@
     }
 
     public void testNotificationListener_getActiveNotifications() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2762,10 +2645,6 @@
 
 
     public void testNotificationListener_getCurrentRanking() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -2780,10 +2659,6 @@
     }
 
     public void testNotificationListener_cancelNotifications() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
diff --git a/tests/autofillservice/Android.bp b/tests/autofillservice/Android.bp
index d39b86c..7882a76 100644
--- a/tests/autofillservice/Android.bp
+++ b/tests/autofillservice/Android.bp
@@ -17,6 +17,7 @@
     defaults: ["cts_defaults"],
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.autofill_autofill",
         "androidx.test.ext.junit",
         "compatibility-device-util-axt",
         "ctsdeviceutillegacy-axt",
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index cf21314..cd52ef5 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -164,6 +164,18 @@
             </meta-data>
         </service>
         <service
+            android:name=".inline.InstrumentedAutoFillServiceInlineEnabled"
+            android:label="InstrumentedAutoFillServiceInlineEnabled"
+            android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
+            <intent-filter>
+                <action android:name="android.service.autofill.AutofillService" />
+            </intent-filter>
+            <meta-data
+                android:name="android.autofill"
+                android:resource="@xml/autofill_service_inline_enabled">
+            </meta-data>
+        </service>
+        <service
             android:name=".NoOpAutofillService"
             android:label="NoOpAutofillService"
             android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml b/tests/autofillservice/res/xml/autofill_service_inline_enabled.xml
similarity index 62%
copy from hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
copy to tests/autofillservice/res/xml/autofill_service_inline_enabled.xml
index a649209..6e4f2bb 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
+++ b/tests/autofillservice/res/xml/autofill_service_inline_enabled.xml
@@ -13,14 +13,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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.cts.blob.helper">
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-            android:name="androidx.test.runner.AndroidJUnitRunner"
-            android:targetPackage="com.android.cts.blob.helper"  />
-</manifest>
\ No newline at end of file
+-->
+<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:supportsInlineSuggestions="true">
+</autofill-service>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
index e7c0cf7..e496c24 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
@@ -48,6 +48,13 @@
     }
 
     /**
+     * Performs a click on username.
+     */
+    protected void requestClickOnUsername() throws TimeoutException {
+        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::performClick));
+    }
+
+    /**
      * Requests focus on username and expect no Window event happens.
      */
     protected void requestFocusOnUsernameNoWindowChange() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index a5ac282..54986d6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -31,11 +31,16 @@
 import android.content.pm.PackageManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.service.autofill.InlinePresentation;
 import android.util.Log;
+import android.util.Size;
 import android.view.autofill.AutofillManager;
+import android.view.inline.InlinePresentationSpec;
 import android.widget.RemoteViews;
 
 import androidx.annotation.NonNull;
+import androidx.autofill.InlinePresentationBuilder;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
@@ -44,6 +49,7 @@
 import com.android.compatibility.common.util.SafeCleanerRule;
 import com.android.compatibility.common.util.SettingsStateKeeperRule;
 import com.android.compatibility.common.util.TestNameUtils;
+import com.android.cts.mockime.ImeSettings;
 import com.android.cts.mockime.MockImeSessionRule;
 
 import org.junit.AfterClass;
@@ -193,7 +199,10 @@
         };
 
         @ClassRule
-        public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule();
+        public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule(
+                InstrumentationRegistry.getTargetContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder().setInlineSuggestionsEnabled(true));
 
         protected static final RequiredFeatureRule sRequiredFeatureRule =
                 new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
@@ -416,6 +425,12 @@
             return presentation;
         }
 
+        protected InlinePresentation createInlinePresentation(String message) {
+            return new InlinePresentation(new InlinePresentationBuilder(message).build(),
+                    new InlinePresentationSpec.Builder(new Size(100, 100), new Size(400, 100))
+                            .build(), /* pinned= */ false);
+        }
+
         @NonNull
         protected AutofillManager getAutofillManager() {
             return mContext.getSystemService(AutofillManager.class);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index 5523e7a3..cd099d7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -27,6 +27,7 @@
 import android.service.autofill.FillCallback;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
 import android.service.autofill.SaveInfo;
 import android.service.autofill.UserData;
 import android.util.Log;
@@ -75,6 +76,7 @@
     private final CharSequence mSaveDescription;
     private final Bundle mExtras;
     private final RemoteViews mPresentation;
+    private final InlinePresentation mInlinePresentation;
     private final RemoteViews mHeader;
     private final RemoteViews mFooter;
     private final IntentSender mAuthentication;
@@ -106,6 +108,7 @@
         mSaveType = builder.mSaveType;
         mExtras = builder.mExtras;
         mPresentation = builder.mPresentation;
+        mInlinePresentation = builder.mInlinePresentation;
         mHeader = builder.mHeader;
         mFooter = builder.mFooter;
         mAuthentication = builder.mAuthentication;
@@ -227,7 +230,7 @@
         }
         if (mAuthenticationIds != null) {
             builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
-                    mAuthentication, mPresentation);
+                    mAuthentication, mPresentation, mInlinePresentation);
         }
         if (mDisableDuration > 0) {
             builder.disableAutofill(mDisableDuration);
@@ -278,6 +281,7 @@
                 + ", failureMessage=" + mFailureMessage
                 + ", saveDescription=" + mSaveDescription
                 + ", hasPresentation=" + (mPresentation != null)
+                + ", hasInlinePresentation=" + (mInlinePresentation != null)
                 + ", hasHeader=" + (mHeader != null)
                 + ", hasFooter=" + (mFooter != null)
                 + ", hasAuthentication=" + (mAuthentication != null)
@@ -313,6 +317,7 @@
         public int mSaveType = -1;
         private Bundle mExtras;
         private RemoteViews mPresentation;
+        private InlinePresentation mInlinePresentation;
         private RemoteViews mFooter;
         private RemoteViews mHeader;
         private IntentSender mAuthentication;
@@ -400,6 +405,14 @@
         }
 
         /**
+         * Sets the view to present the response in the UI.
+         */
+        public Builder setInlinePresentation(InlinePresentation inlinePresentation) {
+            mInlinePresentation = inlinePresentation;
+            return this;
+        }
+
+        /**
          * Sets the authentication intent.
          */
         public Builder setAuthentication(IntentSender authentication, String... ids) {
@@ -558,16 +571,20 @@
     public static class CannedDataset {
         private final Map<String, AutofillValue> mFieldValues;
         private final Map<String, RemoteViews> mFieldPresentations;
+        private final Map<String, InlinePresentation> mFieldInlinePresentations;
         private final Map<String, Pair<Boolean, Pattern>> mFieldFilters;
         private final RemoteViews mPresentation;
+        private final InlinePresentation mInlinePresentation;
         private final IntentSender mAuthentication;
         private final String mId;
 
         private CannedDataset(Builder builder) {
             mFieldValues = builder.mFieldValues;
             mFieldPresentations = builder.mFieldPresentations;
+            mFieldInlinePresentations = builder.mFieldInlinePresentations;
             mFieldFilters = builder.mFieldFilters;
             mPresentation = builder.mPresentation;
+            mInlinePresentation = builder.mInlinePresentation;
             mAuthentication = builder.mAuthentication;
             mId = builder.mId;
         }
@@ -576,9 +593,13 @@
          * Creates a new dataset, replacing the field ids by the real ids from the assist structure.
          */
         Dataset asDataset(Function<String, ViewNode> nodeResolver) {
-            final Dataset.Builder builder = (mPresentation == null)
-                    ? new Dataset.Builder()
-                    : new Dataset.Builder(mPresentation);
+            final Dataset.Builder builder = mPresentation != null
+                    ? mInlinePresentation == null
+                    ? new Dataset.Builder(mPresentation)
+                    : new Dataset.Builder(mPresentation, mInlinePresentation)
+                    : mInlinePresentation == null
+                            ? new Dataset.Builder()
+                            : new Dataset.Builder(mInlinePresentation);
 
             if (mFieldValues != null) {
                 for (Map.Entry<String, AutofillValue> entry : mFieldValues.entrySet()) {
@@ -590,18 +611,34 @@
                     final AutofillId autofillId = node.getAutofillId();
                     final AutofillValue value = entry.getValue();
                     final RemoteViews presentation = mFieldPresentations.get(id);
+                    final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(id);
                     final Pair<Boolean, Pattern> filter = mFieldFilters.get(id);
                     if (presentation != null) {
                         if (filter == null) {
-                            builder.setValue(autofillId, value, presentation);
+                            if (inlinePresentation != null) {
+                                builder.setValue(autofillId, value, presentation,
+                                        inlinePresentation);
+                            } else {
+                                builder.setValue(autofillId, value, presentation);
+                            }
                         } else {
-                            builder.setValue(autofillId, value, filter.second, presentation);
+                            if (inlinePresentation != null) {
+                                builder.setValue(autofillId, value, filter.second, presentation,
+                                        inlinePresentation);
+                            } else {
+                                builder.setValue(autofillId, value, filter.second, presentation);
+                            }
                         }
                     } else {
-                        if (filter == null) {
-                            builder.setValue(autofillId, value);
+                        if (inlinePresentation != null) {
+                            builder.setInlinePresentation(autofillId, value,
+                                    filter != null ? filter.second : null, inlinePresentation);
                         } else {
-                            builder.setValue(autofillId, value, filter.second);
+                            if (filter == null) {
+                                builder.setValue(autofillId, value);
+                            } else {
+                                builder.setValue(autofillId, value, filter.second);
+                            }
                         }
                     }
                 }
@@ -613,7 +650,9 @@
         @Override
         public String toString() {
             return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
+                    + ", hasInlinePresentation=" + (mInlinePresentation != null)
                     + ", fieldPresentations=" + (mFieldPresentations)
+                    + ", fieldInlinePresentations=" + (mFieldInlinePresentations)
                     + ", hasAuthentication=" + (mAuthentication != null)
                     + ", fieldValues=" + mFieldValues
                     + ", fieldFilters=" + mFieldFilters + "]";
@@ -622,9 +661,12 @@
         public static class Builder {
             private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
             private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
+            private final Map<String, InlinePresentation> mFieldInlinePresentations =
+                    new HashMap<>();
             private final Map<String, Pair<Boolean, Pattern>> mFieldFilters = new HashMap<>();
 
             private RemoteViews mPresentation;
+            private InlinePresentation mInlinePresentation;
             private IntentSender mAuthentication;
             private String mId;
 
@@ -749,6 +791,35 @@
             }
 
             /**
+             * Sets the canned value of a field based on its {@code id}.
+             *
+             * <p>The meaning of the id is defined by the object using the canned dataset.
+             * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+             * {@link IdMode}.
+             */
+            public Builder setField(String id, String text, RemoteViews presentation,
+                    InlinePresentation inlinePresentation) {
+                setField(id, text);
+                mFieldPresentations.put(id, presentation);
+                mFieldInlinePresentations.put(id, inlinePresentation);
+                return this;
+            }
+
+            /**
+             * Sets the canned value of a field based on its {@code id}.
+             *
+             * <p>The meaning of the id is defined by the object using the canned dataset.
+             * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+             * {@link IdMode}.
+             */
+            public Builder setField(String id, String text, RemoteViews presentation,
+                    InlinePresentation inlinePresentation, Pattern filter) {
+                setField(id, text, presentation, inlinePresentation);
+                mFieldFilters.put(id, new Pair<>(true, filter));
+                return this;
+            }
+
+            /**
              * Sets the view to present the response in the UI.
              */
             public Builder setPresentation(RemoteViews presentation) {
@@ -757,6 +828,14 @@
             }
 
             /**
+             * Sets the view to present the response in the UI.
+             */
+            public Builder setInlinePresentation(InlinePresentation inlinePresentation) {
+                mInlinePresentation = inlinePresentation;
+                return this;
+            }
+
+            /**
              * Sets the authentication intent.
              */
             public Builder setAuthentication(IntentSender authentication) {
@@ -772,6 +851,9 @@
                 return this;
             }
 
+            /**
+             * Builds the canned dataset.
+             */
             public CannedDataset build() {
                 return new CannedDataset(this);
             }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java b/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java
new file mode 100644
index 0000000..e50b772
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.slice.Slice;
+import android.app.slice.SliceSpec;
+import android.net.Uri;
+import android.os.Parcel;
+import android.service.autofill.InlinePresentation;
+import android.util.Size;
+import android.view.inline.InlinePresentationSpec;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InlinePresentationTest {
+
+    @Test
+    public void testNullInlinePresentationSpecsThrowsException() {
+        assertThrows(NullPointerException.class,
+                () -> createInlinePresentation(/* createSlice */true, /* createSpec */  false));
+    }
+
+    @Test
+    public void testNullSliceThrowsException() {
+        assertThrows(NullPointerException.class,
+                () -> createInlinePresentation(/* createSlice */false, /* createSpec */  true));
+    }
+
+    @Test
+    public void testInlinePresentationValues() {
+        InlinePresentation presentation =
+                createInlinePresentation(/* createSlice */true, /* createSpec */  true);
+
+        assertThat(presentation.isPinned()).isFalse();
+        assertThat(presentation.getInlinePresentationSpec()).isNotNull();
+        assertThat(presentation.getSlice()).isNotNull();
+        assertThat(presentation.getSlice().getItems().size()).isEqualTo(0);
+    }
+
+    @Test
+    public void testtInlinePresentationParcelizeDeparcelize() {
+        InlinePresentation presentation =
+                createInlinePresentation(/* createSlice */true, /* createSpec */  true);
+
+        Parcel p = Parcel.obtain();
+        presentation.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        InlinePresentation targetPresentation = InlinePresentation.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertThat(targetPresentation.isPinned()).isEqualTo(presentation.isPinned());
+        assertThat(targetPresentation.getInlinePresentationSpec()).isEqualTo(
+                presentation.getInlinePresentationSpec());
+        assertThat(targetPresentation.getSlice().getUri()).isEqualTo(
+                presentation.getSlice().getUri());
+        assertThat(targetPresentation.getSlice().getSpec()).isEqualTo(
+                presentation.getSlice().getSpec());
+    }
+
+    private InlinePresentation createInlinePresentation(boolean createSlice, boolean createSpec) {
+        Slice slice = createSlice ? new Slice.Builder(Uri.parse("testuri"),
+                new SliceSpec("type", 1)).build() : null;
+        InlinePresentationSpec spec = createSpec ? new InlinePresentationSpec.Builder(
+                new Size(100, 100), new Size(400, 100)).build() : null;
+        return new InlinePresentation(slice, spec, /* pined */ false);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
index d64aa41..a4799c8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
@@ -26,7 +26,7 @@
     private static final long ONE_TIMEOUT_TO_RULE_THEN_ALL_MS = 20_000;
     private static final long ONE_NAPTIME_TO_RULE_THEN_ALL_MS = 2_000;
 
-    static final long MOCK_IME_TIMEOUT_MS = 5_000;
+    public static final long MOCK_IME_TIMEOUT_MS = 5_000;
 
     /**
      * Timeout until framework binds / unbinds from service.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 9287e71..136bcba 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -70,6 +70,7 @@
 
 import com.android.compatibility.common.util.RetryableException;
 import com.android.compatibility.common.util.Timeout;
+import com.android.cts.mockime.MockIme;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -94,6 +95,8 @@
     private static final String RESOURCE_ID_SAVE_BUTTON_NO = "autofill_save_no";
     private static final String RESOURCE_ID_SAVE_BUTTON_YES = "autofill_save_yes";
     private static final String RESOURCE_ID_OVERFLOW = "overflow";
+    //TODO: Change magic constant
+    private static final String RESOURCE_ID_SUGGESTION_STRIP = "message";
 
     private static final String RESOURCE_STRING_SAVE_TITLE = "autofill_save_title";
     private static final String RESOURCE_STRING_SAVE_TITLE_WITH_TYPE =
@@ -132,6 +135,8 @@
     private static final BySelector SAVE_UI_SELECTOR = By.res("android", RESOURCE_ID_SAVE_SNACKBAR);
     private static final BySelector DATASET_HEADER_SELECTOR =
             By.res("android", RESOURCE_ID_DATASET_HEADER);
+    private static final BySelector SUGGESTION_STRIP_SELECTOR =
+            By.res("android", RESOURCE_ID_SUGGESTION_STRIP);
 
     // TODO: figure out a more reliable solution that does not depend on SystemUI resources.
     private static final String SPLIT_WINDOW_DIVIDER_ID =
@@ -258,6 +263,14 @@
     }
 
     /**
+     * Asserts the suggestion strip was never shown.
+     */
+    public void assertNoSuggestionStripEver() throws Exception {
+        assertNeverShown("suggestion strip", SUGGESTION_STRIP_SELECTOR,
+                DATASET_PICKER_NOT_SHOWN_NAPTIME_MS);
+    }
+
+    /**
      * Asserts the dataset chooser is shown and contains exactly the given datasets.
      *
      * @return the dataset picker object.
@@ -332,6 +345,32 @@
     }
 
     /**
+     * Asserts the suggestion strip on the {@link MockIme} is shown and contains the given number
+     * of child suggestions.
+     *
+     * @param childrenCount the expected number of children.
+     *
+     * @return the suggestion strip object
+     */
+    public UiObject2 assertSuggestionStrip(int childrenCount) throws Exception {
+        final UiObject2 strip = findSuggestionStrip(UI_TIMEOUT);
+        assertThat(strip.getChildCount()).isEqualTo(childrenCount);
+        return strip;
+    }
+
+    /**
+     * Selects the suggestion in the {@link MockIme}'s suggestion strip at the given index.
+     *
+     * @param index the index of the suggestion to select.
+     */
+    public void selectSuggestion(int index) throws Exception {
+        final UiObject2 strip = findSuggestionStrip(UI_TIMEOUT);
+        assertThat(index).isAtLeast(0);
+        assertThat(index).isLessThan(strip.getChildCount());
+        strip.getChildren().get(index).click();
+    }
+
+    /**
      * Gets the text of this object children.
      */
     public List<String> getChildrenAsText(UiObject2 object) {
@@ -1037,6 +1076,10 @@
         return picker;
     }
 
+    private UiObject2 findSuggestionStrip(Timeout timeout) throws Exception {
+        return waitForObject(SUGGESTION_STRIP_SELECTOR, timeout);
+    }
+
     /**
      * Asserts a given object has the expected accessibility title.
      */
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
new file mode 100644
index 0000000..39139eb
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts.inline;
+
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.Helper.getContext;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.autofillservice.cts.Timeouts.MOCK_IME_TIMEOUT_MS;
+import static android.autofillservice.cts.inline.InstrumentedAutoFillServiceInlineEnabled.SERVICE_NAME;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.autofillservice.cts.AbstractLoginActivityTestCase;
+import android.autofillservice.cts.CannedFillResponse;
+import android.autofillservice.cts.Helper;
+import android.autofillservice.cts.InstrumentedAutoFillService;
+import android.os.Process;
+import android.service.autofill.FillContext;
+
+import com.android.compatibility.common.util.RetryableException;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+
+import java.util.concurrent.TimeoutException;
+
+public class InlineLoginActivityTest extends AbstractLoginActivityTestCase {
+
+    private static final String TAG = "InlineLoginActivityTest";
+
+    @Override
+    protected void enableService() {
+        Helper.enableAutofillService(getContext(), SERVICE_NAME);
+    }
+
+    @Test
+    public void testAutofill_noDataset() throws Exception {
+        // Set service.
+        enableService();
+
+        final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+        assumeTrue("MockIME not available", mockImeSession != null);
+
+        sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+        final ImeEventStream stream = mockImeSession.openEventStream();
+        mockImeSession.callRequestShowSelf(0);
+
+        // Wait until the MockIme gets bound to the TestActivity.
+        expectBindInput(stream, Process.myPid(), MOCK_IME_TIMEOUT_MS);
+
+        // Trigger auto-fill.
+        requestFocusOnUsername();
+        expectEvent(stream, editorMatcher("onStartInput", mActivity.getUsername().getId()),
+                MOCK_IME_TIMEOUT_MS);
+
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoSuggestionStripEver();
+        mUiBot.assertNoDatasetsEver();
+
+        waitUntilDisconnected();
+    }
+
+    @Test
+    public void testAutofill_oneDataset() throws Exception {
+        testBasicLoginAutofill(/* numDatasets= */ 1, /* selectedDatasetIndex= */ 0);
+    }
+
+    @Test
+    public void testAutofill_twoDatasets_selectFirstDataset() throws Exception {
+        testBasicLoginAutofill(/* numDatasets= */ 2, /* selectedDatasetIndex= */ 0);
+
+    }
+
+    @Test
+    public void testAutofill_twoDatasets_selectSecondDataset() throws Exception {
+        testBasicLoginAutofill(/* numDatasets= */ 2, /* selectedDatasetIndex= */ 1);
+    }
+
+    private void testBasicLoginAutofill(int numDatasets, int selectedDatasetIndex)
+            throws Exception {
+        // Set service.
+        enableService();
+
+        final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+        assumeTrue("MockIME not available", mockImeSession != null);
+
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder();
+        for (int i = 0; i < numDatasets; i++) {
+            builder.addDataset(new CannedFillResponse.CannedDataset.Builder()
+                    .setField(ID_USERNAME, "dude" + i)
+                    .setField(ID_PASSWORD, "sweet" + i)
+                    .setPresentation(createPresentation("The Dude" + i))
+                    .setInlinePresentation(createInlinePresentation("The Dude" + i))
+                    .build());
+        }
+
+        sReplier.addResponse(builder.build());
+        mActivity.expectAutoFill("dude" + selectedDatasetIndex, "sweet" + selectedDatasetIndex);
+
+        // Dynamically set password to make sure it's sanitized.
+        mActivity.onPassword((v) -> v.setText("I AM GROOT"));
+
+        final ImeEventStream stream = mockImeSession.openEventStream();
+        mockImeSession.callRequestShowSelf(0);
+
+        // Wait until the MockIme gets bound to the TestActivity.
+        expectBindInput(stream, Process.myPid(), MOCK_IME_TIMEOUT_MS);
+
+        // Trigger auto-fill.
+        requestFocusOnUsername();
+        expectEvent(stream, editorMatcher("onStartInput", mActivity.getUsername().getId()),
+                MOCK_IME_TIMEOUT_MS);
+
+        //TODO: extServices bug cause test to fail first time, retry if suggestion strip missing.
+        try {
+            expectEvent(stream, event -> "onSuggestionViewUpdated".equals(event.getEventName()),
+                    MOCK_IME_TIMEOUT_MS);
+        } catch (TimeoutException e) {
+            sReplier.getNextFillRequest();
+            throw new RetryableException("Retry inline test");
+        }
+
+        mUiBot.assertSuggestionStrip(numDatasets);
+        mUiBot.assertNoDatasetsEver();
+
+        mUiBot.selectSuggestion(selectedDatasetIndex);
+
+        // Check the results.
+        mActivity.assertAutoFilled();
+
+        // Make sure input was sanitized.
+        final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
+        assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull();
+        assertTextIsSanitized(request.structure, ID_PASSWORD);
+        final FillContext fillContext = request.contexts.get(request.contexts.size() - 1);
+        assertThat(fillContext.getFocusedId())
+                .isEqualTo(findAutofillIdByResourceId(fillContext, ID_USERNAME));
+
+        // Make sure initial focus was properly set.
+        assertWithMessage("Username node is not focused").that(
+                findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue();
+        assertWithMessage("Password node is focused").that(
+                findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InstrumentedAutoFillServiceInlineEnabled.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InstrumentedAutoFillServiceInlineEnabled.java
new file mode 100644
index 0000000..a931c53
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InstrumentedAutoFillServiceInlineEnabled.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts.inline;
+
+import android.autofillservice.cts.InstrumentedAutoFillService;
+import android.service.autofill.AutofillService;
+
+/**
+ * Implementation of {@link AutofillService} that has inline suggestions support enabled.
+ */
+public class InstrumentedAutoFillServiceInlineEnabled extends InstrumentedAutoFillService {
+    @SuppressWarnings("hiding")
+    static final String SERVICE_PACKAGE = "android.autofillservice.cts";
+    @SuppressWarnings("hiding")
+    static final String SERVICE_CLASS = "InstrumentedAutoFillServiceInlineEnabled";
+    @SuppressWarnings("hiding")
+    static final String SERVICE_NAME = SERVICE_PACKAGE + "/.inline." + SERVICE_CLASS;
+
+    public InstrumentedAutoFillServiceInlineEnabled() {
+        sInstance.set(this);
+        sServiceLabel = SERVICE_CLASS;
+    }
+}
diff --git a/tests/camera/AndroidManifest.xml b/tests/camera/AndroidManifest.xml
index f311b99..85221d3 100644
--- a/tests/camera/AndroidManifest.xml
+++ b/tests/camera/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
 
+    <uses-sdk android:targetSdkVersion="28" />
     <application android:largeHeap="true">
         <uses-library android:name="android.test.runner" />
 
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 9dcc34f..31218db 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -939,18 +939,28 @@
     }
 
     camera_status_t initWithErrorLog() {
+        return initWithErrorLog(nullptr /*env*/, nullptr /*jOverrideCameraId*/);
+    }
+
+    camera_status_t initWithErrorLog(JNIEnv* env, jstring jOverrideCameraId) {
         camera_status_t ret = ACameraManager_getCameraIdList(
                 mCameraManager, &mCameraIdList);
         if (ret != ACAMERA_OK) {
             LOG_ERROR(errorString, "Get camera id list failed: ret %d", ret);
             return ret;
         }
+
+        if (env != nullptr && jOverrideCameraId != nullptr) {
+            mOverrideCameraId = env->GetStringUTFChars(jOverrideCameraId, 0);
+        }
         ret = ACameraManager_registerAvailabilityCallback(mCameraManager, &mServiceCb);
         if (ret != ACAMERA_OK) {
             LOG_ERROR(errorString, "Register availability callback failed: ret %d", ret);
             return ret;
         }
         mMgrInited = true;
+        mJOverrideCameraId = jOverrideCameraId;
+        mJNIEnv = env;
         return ACAMERA_OK;
     }
 
@@ -971,6 +981,12 @@
             mCameraIdList = nullptr;
         }
         mMgrInited = false;
+        if (mOverrideCameraId != nullptr && mJNIEnv != nullptr) {
+            mJNIEnv->ReleaseStringUTFChars(mJOverrideCameraId, mOverrideCameraId);
+            mOverrideCameraId = nullptr;
+            mJOverrideCameraId = nullptr;
+            mJNIEnv = nullptr;
+        }
         return ACAMERA_OK;
     }
 
@@ -978,6 +994,9 @@
         if (!mMgrInited || !mCameraIdList) {
             return -1;
         }
+        if (mOverrideCameraId != nullptr) {
+            return 1;
+        }
         return mCameraIdList->numCameras;
     }
 
@@ -985,6 +1004,13 @@
         if (!mMgrInited || !mCameraIdList || idx < 0 || idx >= mCameraIdList->numCameras) {
             return nullptr;
         }
+        if (mOverrideCameraId != nullptr) {
+            if (idx >= 1) {
+                return nullptr;
+            } else {
+                return mOverrideCameraId;
+            }
+        }
         return mCameraIdList->cameraIds[idx];
     }
 
@@ -993,10 +1019,18 @@
         if (!mMgrInited || !mCameraIdList || idx < 0 || idx >= mCameraIdList->numCameras) {
             return nullptr;
         }
+        const char* cameraId = mCameraIdList->cameraIds[idx];
+        if (mOverrideCameraId != nullptr) {
+            if (idx >= 1) {
+                return nullptr;
+            } else {
+                cameraId = mOverrideCameraId;
+            }
+        }
 
         ACameraMetadata* chars;
         camera_status_t ret = ACameraManager_getCameraCharacteristics(
-                mCameraManager, mCameraIdList->cameraIds[idx], &chars);
+                mCameraManager, cameraId, &chars);
         if (ret != ACAMERA_OK) {
             LOG_ERROR(errorString, "Get camera characteristics failed: ret %d", ret);
             return nullptr;
@@ -1618,6 +1652,9 @@
     ACameraOutputTarget* mReqPreviewOutput = nullptr;
     ACameraOutputTarget* mReqImgReaderOutput = nullptr;
     const char* mCameraId;
+    JNIEnv* mJNIEnv = nullptr;
+    jstring mJOverrideCameraId;
+    const char* mOverrideCameraId = nullptr;
 
     bool mMgrInited = false; // cameraId, serviceListener
     bool mImgReaderInited = false;
@@ -2014,13 +2051,13 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceOpenAndCloseNative(
-        JNIEnv* env, jclass /*clazz*/) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     int numCameras = 0;
     bool pass = false;
     PreviewTestCase testCase;
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -2084,7 +2121,7 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceCreateCaptureRequestNative(
-        JNIEnv* env, jclass /*clazz*/) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     bool pass = false;
     ACameraManager* mgr = ACameraManager_create();
@@ -2095,9 +2132,18 @@
     camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
 
     int numCameras = cameraIdList->numCameras;
+    const char* overrideCameraId = nullptr;
+    if (jOverrideCameraId != nullptr) {
+        overrideCameraId = env->GetStringUTFChars(jOverrideCameraId, nullptr);
+    }
+
     for (int i = 0; i < numCameras; i++) {
         CameraDeviceListener deviceListener;
         const char* cameraId = cameraIdList->cameraIds[i];
+        if (overrideCameraId != nullptr && strcmp(overrideCameraId, cameraId)) {
+            // Skip other cameras if overriding camera id to be tested.
+            continue;
+        }
         ACameraDevice_StateCallbacks deviceCb {
             &deviceListener,
             CameraDeviceListener::onDisconnected,
@@ -2322,13 +2368,14 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceSessionOpenAndCloseNative(
-        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     int numCameras = 0;
     bool pass = false;
     PreviewTestCase testCase;
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -2460,7 +2507,8 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceSharedOutputUpdate(
-        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface, jobject jSharedSurface) {
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface, jobject jSharedSurface,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     int numCameras = 0;
     bool pass = false;
@@ -2477,7 +2525,7 @@
     uint32_t timeoutSec = 1;
     uint32_t runPreviewSec = 2;
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -2729,13 +2777,14 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceSimplePreviewNative(
-        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     int numCameras = 0;
     bool pass = false;
     PreviewTestCase testCase;
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -2837,7 +2886,8 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDevicePreviewWithSessionParametersNative(
-        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     int numCameras = 0;
     bool pass = false;
@@ -2845,7 +2895,7 @@
     ACameraMetadata* chars = nullptr;
     PreviewTestCase testCase;
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -2965,7 +3015,8 @@
 }
 
 bool nativeCameraDeviceLogicalPhysicalStreaming(
-        JNIEnv* env, jobject jPreviewSurface, bool usePhysicalSettings) {
+        JNIEnv* env, jobject jPreviewSurface, bool usePhysicalSettings,
+        jstring jOverrideCameraId) {
     const int NUM_TEST_IMAGES = 10;
     const int TEST_WIDTH  = 640;
     const int TEST_HEIGHT = 480;
@@ -2981,7 +3032,7 @@
     uint32_t timeoutSec = 1;
     uint32_t runPreviewSec = 2;
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -3244,22 +3295,26 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceLogicalPhysicalStreamingNative(
-        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+        jstring jOverrideCameraId) {
     return nativeCameraDeviceLogicalPhysicalStreaming(env,
-            jPreviewSurface, false /*usePhysicalSettings*/);
+            jPreviewSurface, false /*usePhysicalSettings*/,
+            jOverrideCameraId);
 }
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceLogicalPhysicalSettingsNative(
-        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface,
+        jstring jOverrideCameraId) {
     return nativeCameraDeviceLogicalPhysicalStreaming(env,
-            jPreviewSurface, true /*usePhysicalSettings*/);
+            jPreviewSurface, true /*usePhysicalSettings*/,
+            jOverrideCameraId);
 }
 
-
 bool nativeImageReaderTestBase(
-        JNIEnv* env, jstring jOutPath, jint format, AImageReader_ImageCallback cb) {
+        JNIEnv* env, jstring jOutPath, jint format, AImageReader_ImageCallback cb,
+        jstring jOverrideCameraId) {
     const int NUM_TEST_IMAGES = 10;
     const int TEST_WIDTH  = 640;
     const int TEST_HEIGHT = 480;
@@ -3275,7 +3330,7 @@
         ALOGI("%s: out path is %s", __FUNCTION__, outPath);
     }
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
@@ -3523,7 +3578,7 @@
 // surface will eventually run out of free buffers and start reporting capture errors.
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
-testCameraDeviceCaptureFailureNative(JNIEnv* env) {
+testCameraDeviceCaptureFailureNative(JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
     const size_t NUM_TEST_IMAGES = 10;
     const size_t NUM_FAILED_FRAMES = 3; // Wait for at least 3 consecutive failed capture requests
     const int64_t NUM_TOTAL_FRAMES = 60; // Avoid waiting for more than 60 frames
@@ -3536,7 +3591,7 @@
     uint32_t timeoutSec = 10; // It is important to keep this timeout bigger than the framework
                               // timeout
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto exit;
@@ -3697,46 +3752,51 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeImageReaderTest_\
 testJpegNative(
-        JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_JPEG,
-            ImageReaderListener::validateImageCb);
+            ImageReaderListener::validateImageCb, jOverrideCameraId);
 }
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeImageReaderTest_\
 testY8Native(
-        JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_Y8,
-            ImageReaderListener::validateImageCb);
+            ImageReaderListener::validateImageCb, jOverrideCameraId);
 }
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeImageReaderTest_\
 testHeicNative(
-        JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_HEIC,
-            ImageReaderListener::validateImageCb);
+            ImageReaderListener::validateImageCb, jOverrideCameraId);
 }
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeImageReaderTest_\
 testDepthJpegNative(
-        JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_DEPTH_JPEG,
-            ImageReaderListener::validateImageCb);
+            ImageReaderListener::validateImageCb, jOverrideCameraId);
 }
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeImageReaderTest_\
 testImageReaderCloseAcquiredImagesNative(
-        JNIEnv* env, jclass /*clazz*/) {
+        JNIEnv* env, jclass /*clazz*/,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     return nativeImageReaderTestBase(env, nullptr, AIMAGE_FORMAT_JPEG,
-            ImageReaderListener::acquireImageCb);
+            ImageReaderListener::acquireImageCb, jOverrideCameraId);
 }
 
 template <>
@@ -3858,7 +3918,8 @@
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeStillCaptureTest_\
 testStillCaptureNative(
-        JNIEnv* env, jclass /*clazz*/, jstring jOutPath, jobject jPreviewSurface) {
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath, jobject jPreviewSurface,
+        jstring jOverrideCameraId) {
     ALOGV("%s", __FUNCTION__);
     const int NUM_TEST_IMAGES = 10;
     const int TEST_WIDTH  = 640;
@@ -3872,7 +3933,7 @@
     const char* outPath = env->GetStringUTFChars(jOutPath, nullptr);
     ALOGI("%s: out path is %s", __FUNCTION__, outPath);
 
-    camera_status_t ret = testCase.initWithErrorLog();
+    camera_status_t ret = testCase.initWithErrorLog(env, jOverrideCameraId);
     if (ret != ACAMERA_OK) {
         // Don't log error here. testcase did it
         goto cleanup;
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 8f9f44b..19223b0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -16,16 +16,13 @@
 
 package android.hardware.camera2.cts;
 
-import static org.mockito.Mockito.*;
-import static org.mockito.AdditionalMatchers.not;
-import static org.mockito.AdditionalMatchers.and;
 import static junit.framework.Assert.*;
 
-import android.app.ActivityManager;
+import static org.mockito.Mockito.*;
+
 import android.app.Instrumentation;
 import android.app.NotificationManager;
 import android.app.UiAutomation;
-import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCharacteristics;
@@ -40,17 +37,17 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
-import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.Pair;
+
 import androidx.test.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.PropertyUtil;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 
-import org.junit.runners.Parameterized;
-import org.junit.runner.RunWith;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
@@ -61,9 +58,9 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.Set;
 
 /**
  * <p>Basic test for CameraManager class.</p>
@@ -575,6 +572,10 @@
      */
     @Test
     public void testCameraManagerListenerCallbacks() throws Exception {
+        if (mOverrideCameraId != null) {
+            // Testing is done for individual camera. Skip.
+            return;
+        }
         testCameraManagerListenerCallbacks(/*useExecutor*/ false);
         testCameraManagerListenerCallbacks(/*useExecutor*/ true);
     }
@@ -809,14 +810,6 @@
             Log.i(TAG, "No cameras present, skipping test");
             return;
         }
-
-        ActivityManager am = mContext.getSystemService(ActivityManager.class);
-
-        // Go devices do not support all interrupt filtering functionality
-        if (am.isLowRamDevice()) {
-            return;
-        }
-
         // Allow the test package to adjust notification policy
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 30dba3e..770f3e4 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -2428,6 +2428,10 @@
             // Skip test, http://b/141496896
             return;
         }
+        if (mOverrideCameraId != null) {
+            // A single camera is being tested. Skip test.
+            return;
+        }
         int legacyDeviceCount = Camera.getNumberOfCameras();
         assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
                 mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index fc09e51..dc34087 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -25,6 +25,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
+import android.hardware.cts.helpers.CameraUtils;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
@@ -193,7 +194,8 @@
     @Presubmit
     @Test
     public void testCamera1() throws Exception {
-        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
+        int[] cameraIds = CameraUtils.deriveCameraIdsUnderTest();
+        for (int i : cameraIds) {
             Camera camera = null;
             try {
                 Log.i(TAG, "Testing android.hardware.Camera API for camera device " + i);
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index 207c3e3..ff1d790 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -47,13 +47,13 @@
     @Test
     public void testCameraDeviceOpenAndClose() {
         assertTrue("testCameraDeviceOpenAndClose fail, see log for details",
-                testCameraDeviceOpenAndCloseNative());
+                testCameraDeviceOpenAndCloseNative(mOverrideCameraId));
     }
 
     @Test
     public void testCameraDeviceCreateCaptureRequest() {
         assertTrue("testCameraDeviceCreateCaptureRequest fail, see log for details",
-                testCameraDeviceCreateCaptureRequestNative());
+                testCameraDeviceCreateCaptureRequestNative(mOverrideCameraId));
     }
 
     @Test
@@ -61,7 +61,7 @@
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
         assertTrue("testCameraDeviceSessionOpenAndClose fail, see log for details",
-                testCameraDeviceSessionOpenAndCloseNative(mPreviewSurface));
+                testCameraDeviceSessionOpenAndCloseNative(mPreviewSurface, mOverrideCameraId));
     }
 
     @Test
@@ -69,7 +69,7 @@
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
         assertTrue("testCameraDeviceSimplePreview fail, see log for details",
-                testCameraDeviceSimplePreviewNative(mPreviewSurface));
+                testCameraDeviceSimplePreviewNative(mPreviewSurface, mOverrideCameraId));
     }
 
     @Test
@@ -77,7 +77,8 @@
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
         assertTrue("testCameraDevicePreviewWithSessionParameters fail, see log for details",
-                testCameraDevicePreviewWithSessionParametersNative(mPreviewSurface));
+                testCameraDevicePreviewWithSessionParametersNative(mPreviewSurface,
+                mOverrideCameraId));
     }
 
     @Test
@@ -89,7 +90,8 @@
         outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
         Surface outputSurface = new Surface(outputTexture);
         assertTrue("testCameraDeviceSharedOutputUpdate fail, see log for details",
-                testCameraDeviceSharedOutputUpdate(mPreviewSurface, outputSurface));
+                testCameraDeviceSharedOutputUpdate(mPreviewSurface, outputSurface,
+                mOverrideCameraId));
     }
 
     @Test
@@ -101,7 +103,7 @@
         outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
         Surface outputSurface = new Surface(outputTexture);
         assertTrue("testCameraDeviceLogicalPhysicalStreaming fail, see log for details",
-                testCameraDeviceLogicalPhysicalStreamingNative(mPreviewSurface));
+                testCameraDeviceLogicalPhysicalStreamingNative(mPreviewSurface, mOverrideCameraId));
     }
 
     @Test
@@ -113,24 +115,30 @@
         outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
         Surface outputSurface = new Surface(outputTexture);
         assertTrue("testCameraDeviceLogicalPhysicalSettings fail, see log for details",
-                testCameraDeviceLogicalPhysicalSettingsNative(mPreviewSurface));
+                testCameraDeviceLogicalPhysicalSettingsNative(mPreviewSurface, mOverrideCameraId));
     }
 
     @Test
     public void testCameraDeviceCaptureFailure() {
         assertTrue("testCameraDeviceCaptureFailure fail, see log for details",
-                testCameraDeviceCaptureFailureNative());
+                testCameraDeviceCaptureFailureNative(mOverrideCameraId));
     }
 
-    private static native boolean testCameraDeviceOpenAndCloseNative();
-    private static native boolean testCameraDeviceCreateCaptureRequestNative();
-    private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview);
-    private static native boolean testCameraDeviceSimplePreviewNative(Surface preview);
+    private static native boolean testCameraDeviceOpenAndCloseNative(String overrideCameraId);
+    private static native boolean testCameraDeviceCreateCaptureRequestNative(
+            String overrideCameraId);
+    private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview,
+            String overrideCameraId);
+    private static native boolean testCameraDeviceSimplePreviewNative(Surface preview,
+            String overrideCameraId);
     private static native boolean testCameraDevicePreviewWithSessionParametersNative(
-            Surface preview);
-    private static native boolean testCameraDeviceSharedOutputUpdate(Surface src, Surface dst);
-    private static native boolean testCameraDeviceLogicalPhysicalStreamingNative(Surface preview);
-    private static native boolean testCameraDeviceLogicalPhysicalSettingsNative(Surface preview);
-    private static native boolean testCameraDeviceCaptureFailureNative();
+            Surface preview, String overrideCameraId);
+    private static native boolean testCameraDeviceSharedOutputUpdate(Surface src, Surface dst,
+            String overrideCameraId);
+    private static native boolean testCameraDeviceLogicalPhysicalStreamingNative(Surface preview,
+            String overrideCameraId);
+    private static native boolean testCameraDeviceLogicalPhysicalSettingsNative(Surface preview,
+            String overrideCameraId);
+    private static native boolean testCameraDeviceCaptureFailureNative(String overrideCameraId);
 
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
index 493e670..ef9a57c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -43,36 +43,36 @@
     @Test
     public void testJpeg() {
         assertTrue("testJpeg fail, see log for details",
-                testJpegNative(mDebugFileNameBase));
+                testJpegNative(mDebugFileNameBase, mOverrideCameraId));
     }
 
     @Test
     public void testY8() {
         assertTrue("testY8 fail, see log for details",
-                testY8Native(mDebugFileNameBase));
+                testY8Native(mDebugFileNameBase, mOverrideCameraId));
     }
 
     @Test
     public void testHeic() {
         assertTrue("testHeic fail, see log for details",
-                testHeicNative(mDebugFileNameBase));
+                testHeicNative(mDebugFileNameBase, mOverrideCameraId));
     }
 
     @Test
     public void testDepthJpeg() {
         assertTrue("testDepthJpeg fail, see log for details",
-                testDepthJpegNative(mDebugFileNameBase));
+                testDepthJpegNative(mDebugFileNameBase, mOverrideCameraId));
     }
 
     @Test
     public void testImageReaderCloseAcquiredImages() {
         assertTrue("testImageReaderClose fail, see log for details",
-                testImageReaderCloseAcquiredImagesNative());
+                testImageReaderCloseAcquiredImagesNative(mOverrideCameraId));
     }
 
-    private static native boolean testJpegNative(String filePath);
-    private static native boolean testY8Native(String filePath);
-    private static native boolean testHeicNative(String filePath);
-    private static native boolean testDepthJpegNative(String filePath);
-    private static native boolean testImageReaderCloseAcquiredImagesNative();
+    private static native boolean testJpegNative(String filePath, String overrideCameraId);
+    private static native boolean testY8Native(String filePath, String overrideCameraId);
+    private static native boolean testHeicNative(String filePath, String overrideCameraId);
+    private static native boolean testDepthJpegNative(String filePath, String overrideCameraId);
+    private static native boolean testImageReaderCloseAcquiredImagesNative(String overrideCameraId);
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
index a778f7d..cfefd62 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
@@ -48,9 +48,9 @@
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
         assertTrue("testStillCapture fail, see log for details",
-                testStillCaptureNative(mDebugFileNameBase, mPreviewSurface));
+                testStillCaptureNative(mDebugFileNameBase, mPreviewSurface, mOverrideCameraId));
     }
 
     private static native boolean testStillCaptureNative(
-            String filePath, Surface previewSurface);
+            String filePath, Surface previewSurface, String overrideCameraId);
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
index 91f6b80..13ce89a 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
@@ -38,12 +38,15 @@
 import android.media.Image;
 import android.media.Image.Plane;
 import android.media.ImageReader;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.Log;
 import android.view.Surface;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 
@@ -87,6 +90,10 @@
     private WindowManager mWindowManager;
     private Context mContext;
 
+    private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
+    private static final Bundle mBundle = InstrumentationRegistry.getArguments();
+    private static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
+
     public Camera2AndroidTestRule(Context context) {
         mContext = context;
     }
@@ -175,6 +182,20 @@
         return mCollector;
     }
 
+    private String[] deriveCameraIdsUnderTest() throws Exception {
+        String[] idsUnderTest = mCameraManager.getCameraIdListNoLazy();
+        assertNotNull("Camera ids shouldn't be null", idsUnderTest);
+        if (mOverrideCameraId != null) {
+            if (Arrays.asList(idsUnderTest).contains(mOverrideCameraId)) {
+                idsUnderTest = new String[]{mOverrideCameraId};
+            } else {
+                idsUnderTest = new String[]{};
+            }
+        }
+
+        return idsUnderTest;
+    }
+
     /**
      * Set up the camera2 test case required environments, including CameraManager,
      * HandlerThread, Camera IDs, and CameraStateCallback etc.
@@ -193,8 +214,7 @@
          */
         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
 
-        mCameraIdsUnderTest = mCameraManager.getCameraIdListNoLazy();
-        assertNotNull("Camera ids shouldn't be null", mCameraIdsUnderTest);
+        mCameraIdsUnderTest = deriveCameraIdsUnderTest();
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -239,8 +259,7 @@
         Log.v(TAG, "Tear down...");
         if (mCameraManager != null) {
             try {
-                String[] cameraIdsPostTest = mCameraManager.getCameraIdListNoLazy();
-                assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
+                String[] cameraIdsPostTest = deriveCameraIdsUnderTest();
                 Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
                 Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
                 assertTrue(
@@ -710,4 +729,4 @@
             // Expected.
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/camera/src/android/hardware/cts/CameraGLTest.java b/tests/camera/src/android/hardware/cts/CameraGLTest.java
index c83f074..a9e82ef 100644
--- a/tests/camera/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraGLTest.java
@@ -78,6 +78,7 @@
     private Looper mLooper = null;
     private final ConditionVariable mSurfaceTextureDone = new ConditionVariable();
     private final ConditionVariable mPreviewDone = new ConditionVariable();
+    private int[] mCameraIds;
 
     Camera mCamera;
     boolean mIsExternalCamera;
@@ -105,6 +106,8 @@
         GLSurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
         // Store a link to the view so we can redraw it when needed
         mGLView = ctsActivity.getView();
+
+        mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
     }
 
     @After
@@ -324,8 +327,7 @@
         wl.acquire();
         try {
             /* Run the requested test per camera */
-            int nCameras = Camera.getNumberOfCameras();
-            for (int id = 0; id < nCameras; id++) {
+            for (int id : mCameraIds) {
                 Log.v(TAG, "Camera id=" + id);
                 test.run(id);
             }
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index 47ffca6..c8330f7 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -124,6 +124,7 @@
     private final ConditionVariable mPreviewDone = new ConditionVariable();
     private final ConditionVariable mFocusDone = new ConditionVariable();
     private final ConditionVariable mSnapshotDone = new ConditionVariable();
+    private int[] mCameraIds;
 
     Camera mCamera;
     boolean mIsExternalCamera;
@@ -134,6 +135,7 @@
 
     @Before
     public void setUp() throws Exception {
+        mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
     }
 
     @After
@@ -408,8 +410,7 @@
     @UiThreadTest
     @Test
     public void testTakePicture() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             mCamera.startPreview();
@@ -454,8 +455,7 @@
     @UiThreadTest
     @Test
     public void testPreviewCallback() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testPreviewCallbackByCamera(id);
         }
@@ -509,8 +509,7 @@
     @UiThreadTest
     @Test
     public void testStabilizationOneShotPreviewCallback() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testStabilizationOneShotPreviewCallbackByCamera(id);
         }
@@ -535,8 +534,7 @@
     @UiThreadTest
     @Test
     public void testSetOneShotPreviewCallback() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testSetOneShotPreviewCallbackByCamera(id);
         }
@@ -559,8 +557,7 @@
     @UiThreadTest
     @Test
     public void testSetPreviewDisplay() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testSetPreviewDisplayByCamera(id);
         }
@@ -603,8 +600,7 @@
     @UiThreadTest
     @Test
     public void testDisplayOrientation() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testDisplayOrientationByCamera(id);
         }
@@ -642,8 +638,7 @@
     @UiThreadTest
     @Test
     public void testParameters() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testParametersByCamera(id);
         }
@@ -866,8 +861,7 @@
     @UiThreadTest
     @Test
     public void testJpegThumbnailSize() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             testJpegThumbnailSizeByCamera(false, 0, 0);
@@ -940,8 +934,7 @@
     @UiThreadTest
     @Test
     public void testJpegExif() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             testJpegExifByCamera(false);
@@ -1233,8 +1226,7 @@
     @UiThreadTest
     @Test
     public void testLockUnlock() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testLockUnlockByCamera(id);
         }
@@ -1426,8 +1418,7 @@
     @UiThreadTest
     @Test
     public void testPreviewCallbackWithBuffer() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testPreviewCallbackWithBufferByCamera(id);
         }
@@ -1524,8 +1515,7 @@
     @UiThreadTest
     @Test
     public void testImmediateZoom() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testImmediateZoomByCamera(id);
         }
@@ -1596,8 +1586,7 @@
     @UiThreadTest
     @Test
     public void testSmoothZoom() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testSmoothZoomByCamera(id);
         }
@@ -1722,8 +1711,7 @@
     @UiThreadTest
     @Test
     public void testFocusDistances() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testFocusDistancesByCamera(id);
         }
@@ -1831,8 +1819,7 @@
     @UiThreadTest
     @Test
     public void testCancelAutofocus() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testCancelAutofocusByCamera(id);
         }
@@ -1913,6 +1900,11 @@
     @UiThreadTest
     @Test
     public void testMultipleCameras() throws Exception {
+        if (CameraUtils.mOverrideCameraId != null) {
+            // A single camera is being tested. Skip.
+            return;
+        }
+
         int nCameras = Camera.getNumberOfCameras();
         Log.v(TAG, "total " + nCameras + " cameras");
         assertTrue(nCameras >= 0);
@@ -1976,8 +1968,7 @@
     @UiThreadTest
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testPreviewPictureSizesCombination() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testPreviewPictureSizesCombinationByCamera(id);
         }
@@ -2093,8 +2084,7 @@
     @UiThreadTest
     @Test
     public void testPreviewFpsRange() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testPreviewFpsRangeByCamera(id);
         }
@@ -2322,8 +2312,7 @@
     @UiThreadTest
     @Test
     public void testSceneMode() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testSceneModeByCamera(id);
         }
@@ -2423,8 +2412,7 @@
     @UiThreadTest
     @Test
     public void testInvalidParameters() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testInvalidParametersByCamera(id);
         }
@@ -2478,8 +2466,7 @@
     @UiThreadTest
     @Test
     public void testGetParameterDuringFocus() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testGetParameterDuringFocusByCamera(id);
         }
@@ -2514,8 +2501,7 @@
     @UiThreadTest
     @Test
     public void testPreviewFormats() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testPreviewFormatsByCamera(id);
         }
@@ -2539,6 +2525,11 @@
     @UiThreadTest
     @Test
     public void testMultiCameraRelease() throws Exception {
+        if (CameraUtils.mOverrideCameraId != null) {
+            // A single camera is being tested. Skip.
+            return;
+        }
+
         // Verify that multiple cameras exist, and that they can be opened at the same time
         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
         int nCameras = Camera.getNumberOfCameras();
@@ -2628,8 +2619,7 @@
     @UiThreadTest
     @Test
     public void testFocusAreas() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
 
             initializeMessageLooper(id);
@@ -2648,8 +2638,7 @@
     @UiThreadTest
     @Test
     public void testMeteringAreas() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             Parameters parameters = mCamera.getParameters();
@@ -2785,8 +2774,7 @@
     @UiThreadTest
     @Test
     public void testJpegCallbackStartPreview() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testJpegCallbackStartPreviewByCamera(id);
         }
@@ -2815,8 +2803,7 @@
     @UiThreadTest
     @Test
     public void testRecordingHint() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testRecordingHintByCamera(id);
         }
@@ -2911,8 +2898,7 @@
     @UiThreadTest
     @Test
     public void testAutoExposureLock() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             Parameters parameters = mCamera.getParameters();
@@ -2928,8 +2914,7 @@
     @UiThreadTest
     @Test
     public void testAutoWhiteBalanceLock() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             Parameters parameters = mCamera.getParameters();
@@ -2945,8 +2930,7 @@
     @UiThreadTest
     @Test
     public void test3ALockInteraction() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             initializeMessageLooper(id);
             Parameters parameters = mCamera.getParameters();
@@ -3170,8 +3154,7 @@
     @UiThreadTest
     @Test
     public void testFaceDetection() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testFaceDetectionByCamera(id);
         }
@@ -3297,8 +3280,7 @@
     @UiThreadTest
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testVideoSnapshot() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testVideoSnapshotByCamera(id, /*recordingHint*/false);
             testVideoSnapshotByCamera(id, /*recordingHint*/true);
@@ -3378,8 +3360,7 @@
 
     @Test
     public void testPreviewCallbackWithPicture() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testPreviewCallbackWithPictureByCamera(id);
         }
@@ -3438,8 +3419,7 @@
 
     @Test
     public void testEnableShutterSound() throws Exception {
-        int nCameras = Camera.getNumberOfCameras();
-        for (int id = 0; id < nCameras; id++) {
+        for (int id : mCameraIds) {
             Log.v(TAG, "Camera id=" + id);
             testEnableShutterSoundByCamera(id);
         }
diff --git a/tests/camera/src/android/hardware/cts/Camera_SizeTest.java b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
index 77e75dd..785535c 100644
--- a/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
+++ b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
@@ -70,7 +70,8 @@
      * @see {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      */
     public void testMaxAspectRatios() throws Exception {
-        for (int id = 0; id < Camera.getNumberOfCameras(); ++id) {
+        int[] cameraIds = CameraUtils.deriveCameraIdsUnderTest();
+        for (int id : cameraIds) {
             if (CameraUtils.isLegacyHAL(getContext(), id)) {
 
                 Camera camera = Camera.open(id);
diff --git a/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java b/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java
index ac3f6dd..5cc67fd 100644
--- a/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java
+++ b/tests/camera/src/android/hardware/cts/LegacyCameraPerformanceTest.java
@@ -20,6 +20,7 @@
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.Camera.Parameters;
+import android.hardware.cts.helpers.CameraUtils;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -49,29 +50,31 @@
 
     private Instrumentation mInstrumentation;
     private CameraPerformanceTestHelper mHelper;
+    private int[] mCameraIds;
 
     @Before
     public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mHelper = new CameraPerformanceTestHelper();
+        mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
     }
 
     @After
     public void tearDown() throws Exception {
         if (mHelper.getCamera() != null) {
             mHelper.getCamera().release();
-
         }
     }
 
     @Test
     public void testLegacyApiPerformance() throws Exception {
         final int NUM_TEST_LOOPS = 10;
+        if (mCameraIds.length == 0) return;
 
-        int nCameras = Camera.getNumberOfCameras();
-        double[] avgCameraTakePictureTimes = new double[nCameras];
+        double[] avgCameraTakePictureTimes = new double[mCameraIds.length];
 
-        for (int id = 0; id < nCameras; id++) {
+        int count = 0;
+        for (int id : mCameraIds) {
             DeviceReportLog reportLog = new DeviceReportLog(REPORT_LOG_NAME,
                     "test_camera_takePicture");
             reportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
@@ -172,7 +175,7 @@
                         + ". Max(ms): " + Stat.getMax(cameraTakePictureTimes));
             }
 
-            avgCameraTakePictureTimes[id] = Stat.getAverage(cameraTakePictureTimes);
+            avgCameraTakePictureTimes[count++] = Stat.getAverage(cameraTakePictureTimes);
             reportLog.addValues("camera_open_time", cameraOpenTimes, ResultType.LOWER_BETTER,
                     ResultUnit.MS);
             reportLog.addValues("camera_start_preview_time", startPreviewTimes,
@@ -191,7 +194,7 @@
             reportLog.submit(mInstrumentation);
         }
 
-        if (nCameras != 0) {
+        if (mCameraIds.length != 0) {
             DeviceReportLog reportLog = new DeviceReportLog(REPORT_LOG_NAME,
                     "test_camera_takepicture_average");
             reportLog.setSummary("camera_takepicture_average_time_for_all_cameras",
@@ -200,4 +203,4 @@
             reportLog.submit(mInstrumentation);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
index c7deb5a..6d90f2e 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
@@ -49,15 +49,13 @@
         System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
         assertNotNull("Unable to get CameraManager", mCameraManager);
-        mCameraIdsUnderTest =
-                CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+        mCameraIdsUnderTest = deriveCameraIdsUnderTest();
         assertNotNull("Unable to get camera ids", mCameraIdsUnderTest);
     }
 
     @Override
     public void tearDown() throws Exception {
-        String[] cameraIdsPostTest =
-                CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+        String[] cameraIdsPostTest = deriveCameraIdsUnderTest();
         assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
         Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
         Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
@@ -67,4 +65,19 @@
                 mCameraIdsUnderTest.length == cameraIdsPostTest.length);
         super.tearDown();
     }
+
+    private String[] deriveCameraIdsUnderTest() throws Exception {
+        String[] idsUnderTest =
+                CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+        assertNotNull("Camera ids shouldn't be null", idsUnderTest);
+        if (mOverrideCameraId != null) {
+            if (Arrays.asList(idsUnderTest).contains(mOverrideCameraId)) {
+                idsUnderTest = new String[]{mOverrideCameraId};
+            } else {
+                idsUnderTest = new String[]{};
+            }
+        }
+
+        return idsUnderTest;
+    }
 }
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
index b2f4c04..047fba5 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
@@ -18,6 +18,7 @@
 
 import android.app.UiAutomation;
 import android.content.Context;
+import android.os.Bundle;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -42,6 +43,10 @@
     @Parameter(0)
     public boolean mAdoptShellPerm;
 
+    private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
+    private static final Bundle mBundle = InstrumentationRegistry.getArguments();
+    protected static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
+
     @Parameters
     public static Iterable<? extends Object> data() {
         List<Boolean> adoptShellPerm = new ArrayList<Boolean>();
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
index 82df26e..2370023 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
@@ -20,14 +20,22 @@
 import android.hardware.Camera;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
 
 import java.util.Comparator;
+import java.util.stream.IntStream;
 
 /**
  * Utility class containing helper functions for the Camera CTS tests.
  */
 public class CameraUtils {
 
+    private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
+    private static final Bundle mBundle = InstrumentationRegistry.getArguments();
+    public static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
+
     /**
      * Returns {@code true} if this device only supports {@code LEGACY} mode operation in the
      * Camera2 API for the given camera ID.
@@ -104,4 +112,19 @@
         }
     }
 
+    public static int[] deriveCameraIdsUnderTest() throws Exception {
+        int numberOfCameras = Camera.getNumberOfCameras();
+        int[] cameraIds;
+        if (mOverrideCameraId == null) {
+            cameraIds = IntStream.range(0, numberOfCameras).toArray();
+        } else {
+            int overrideCameraId = Integer.parseInt(mOverrideCameraId);
+            if (overrideCameraId >= 0 && overrideCameraId < numberOfCameras) {
+                cameraIds = new int[]{overrideCameraId};
+            } else {
+                cameraIds = new int[]{};
+            }
+        }
+        return cameraIds;
+    }
 }
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 0f8da29..c95ae9c 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -27,7 +27,7 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
     <application android:label="CtsWindowManagerDeviceTestCases"
             android:requestLegacyExternalStorage="true">
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
index b3ab326..26c5b3a 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
@@ -41,6 +41,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.WindowInsets;
 
 import java.lang.ref.WeakReference;
 
@@ -73,14 +74,13 @@
         view.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        view.setOnApplyWindowInsetsListener((v, insets) -> {
-            final boolean cutoutExists = (insets.getDisplayCutout() != null);
-            Log.i(TAG, "cutout=" + cutoutExists);
-            TestJournalProvider.putExtras(BroadcastReceiverActivity.this,
-                    bundle -> bundle.putBoolean(EXTRA_CUTOUT_EXISTS, cutoutExists));
-            return insets;
-        });
         setContentView(view);
+
+        WindowInsets insets = getWindowManager().getCurrentWindowMetrics().getWindowInsets();
+        final boolean cutoutExists = (insets.getDisplayCutout() != null);
+        Log.i(TAG, "cutout=" + cutoutExists);
+        TestJournalProvider.putExtras(BroadcastReceiverActivity.this,
+                bundle -> bundle.putBoolean(EXTRA_CUTOUT_EXISTS, cutoutExists));
     }
 
     @Override
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java
index 3043ef3..c6baec7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTaskAffinityTests.java
@@ -33,7 +33,7 @@
 
 /**
  * Build/Install/Run:
- *     atest CtsWindowManagerDeviceTestCases:ActivitySecurityTests
+ *     atest CtsWindowManagerDeviceTestCases:ActivityTaskAffinityTests
  *
  * Tests if activities with the same taskAffinity can be in the same task.
  */
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index 28e07de..cbda48a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -234,7 +234,7 @@
 
         // Launch to fullscreen stack and record size.
         separateTestJournal();
-        launchActivity(activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        launchActivity(activityName, WINDOWING_MODE_FULLSCREEN);
         final SizeInfo initialFullscreenSizes = getActivityDisplaySize(activityName);
         final Rect displayRect = getDisplayRect(activityName);
 
@@ -256,8 +256,7 @@
                 width /* taskWidth */, height /* taskHeight */);
 
         // Move activity back to fullscreen stack.
-        setActivityTaskWindowingMode(activityName,
-                WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        setActivityTaskWindowingMode(activityName, WINDOWING_MODE_FULLSCREEN);
         final SizeInfo finalFullscreenSizes = getActivityDisplaySize(activityName);
 
         // After activity configuration was changed twice it must report same size as original one.
@@ -741,7 +740,7 @@
         // Move to fullscreen stack.
         separateTestJournal();
         setActivityTaskWindowingMode(
-                activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+                activityName, WINDOWING_MODE_FULLSCREEN);
         final SizeInfo fullscreenSizes = getActivityDisplaySize(activityName);
         assertSizesAreSane(fullscreenSizes, initialDockedSizes);
 
@@ -958,43 +957,6 @@
         assertSizesAreSame(freeformActivitySizes, applicationSizes);
     }
 
-    @Test
-    public void testAppConfigurationUpdatesWhenSplitScreenActivityFinishes() throws Exception {
-        assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
-
-        // Launch activity in fullscreen initially
-        final ActivitySession fullscreenActivitySession = createManagedActivityClientSession()
-                .startActivity(getLaunchActivityBuilder()
-                        .setUseInstrumentation()
-                        .setTargetActivity(TEST_ACTIVITY)
-                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN));
-        SizeInfo fullscreenActivitySizes = getActivitySizeInfo(fullscreenActivitySession);
-        SizeInfo initialApplicationSizes = getAppSizeInfo(fullscreenActivitySession);
-        assertSizesAreSame(fullscreenActivitySizes, initialApplicationSizes);
-
-        // Launch activity in split-screen and assert sizes
-        separateTestJournal();
-        final ActivitySession secondActivitySession = createManagedActivityClientSession()
-                .startActivity(getLaunchActivityBuilder()
-                        .setUseInstrumentation()
-                        .setTargetActivity(RESIZEABLE_ACTIVITY)
-                        .setNewTask(true)
-                        .setMultipleTask(true)
-                        .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY));
-        waitForOrFail("Activity and application configuration must match",
-                () -> activityAndAppSizesMatch(secondActivitySession));
-        SizeInfo dockedActivitySizes = getActivitySizeInfo(secondActivitySession);
-        SizeInfo newApplicationSizes = getAppSizeInfo(secondActivitySession);
-        assertSizesAreSame(dockedActivitySizes, newApplicationSizes);
-        assertSizesAreSane(fullscreenActivitySizes, dockedActivitySizes);
-
-        // Finish top activity and assert the size change
-        secondActivitySession.finish();
-        waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, "finishActivityOnTop");
-        waitForOrFail("Application configuration must be restored to the original size",
-                () -> initialApplicationSizes.equals(getAppSizeInfo(fullscreenActivitySession)));
-    }
-
     private boolean activityAndAppSizesMatch(ActivitySession activitySession) {
         final SizeInfo activitySize = activitySession.getConfigInfo().sizeInfo;
         final SizeInfo appSize = activitySession.getAppConfigInfo().sizeInfo;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index f87fad9..edc6f9d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -16,9 +16,9 @@
 
 package android.server.wm;
 
-import static android.server.wm.WindowManagerState.STATE_RESUMED;
 import static android.server.wm.StateLogger.log;
 import static android.server.wm.StateLogger.logE;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
 import static android.server.wm.app.Components.FONT_SCALE_ACTIVITY;
 import static android.server.wm.app.Components.FONT_SCALE_NO_RELAUNCH_ACTIVITY;
 import static android.server.wm.app.Components.FontScaleActivity.EXTRA_FONT_ACTIVITY_DPI;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
index 983cb54..369904f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
@@ -140,7 +140,7 @@
         launchHomeActivity();
 
         final DisplayMetricsSession displayMetricsSession =
-                mObjectTracker.manage(new DisplayMetricsSession(DEFAULT_DISPLAY));
+                createManagedDisplayMetricsSession(DEFAULT_DISPLAY);
         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
 
         // Read initial sizes.
@@ -189,32 +189,4 @@
             return config;
         }
     }
-
-    private static class DisplayMetricsSession implements AutoCloseable {
-
-        private final ReportedDisplayMetrics mInitialDisplayMetrics;
-        private final int mDisplayId;
-
-        DisplayMetricsSession(int displayId) {
-            mDisplayId = displayId;
-            mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
-        }
-
-        ReportedDisplayMetrics getInitialDisplayMetrics() {
-            return mInitialDisplayMetrics;
-        }
-
-        ReportedDisplayMetrics getDisplayMetrics() {
-            return ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
-        }
-
-        void overrideDisplayMetrics(final Size size, final int density) {
-            mInitialDisplayMetrics.setDisplayMetrics(size, density);
-        }
-
-        @Override
-        public void close() {
-            mInitialDisplayMetrics.restoreDisplayMetrics();
-        }
-    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
index 43ddaee..de0372f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -275,7 +275,7 @@
         nonResizeableSession.takeCallbackHistory();
 
         // Try to move the non-resizeable activity to the top of stack on secondary display.
-        moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
+        moveActivityToStackOrOnTop(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
         // Wait for a while to check that it will move.
         assertTrue("Non-resizeable activity should be moved",
                 mWmState.waitForWithAmState(
@@ -653,10 +653,10 @@
         assertEquals("Activity launched on secondary display must be resumed",
                 getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
         mWmState.assertFocusedStack("Top stack must be on primary display", frontStackId);
-        assertEquals("Top stack must only contain 1 task",
-                1, secondFrontStack.getTasks().size());
-        assertEquals("Top task must only contain 1 activity",
-                1, secondFrontStack.getTasks().get(0).mActivities.size());
+        assertEquals("Second display must only contain 1 root task", 1,
+                mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
+        assertEquals("Top task must only contain 1 activity", 1,
+                secondFrontStack.getActivities().size());
     }
 
     /**
@@ -719,10 +719,10 @@
                 getActivityName(ALT_LAUNCHING_ACTIVITY),
                 secondFrontStack.mResumedActivity);
         mWmState.assertFocusedStack("Top stack must be on secondary display", secondFrontStackId);
-        assertEquals("Top stack must only contain 1 task",
-                1, secondFrontStack.getTasks().size());
+        assertEquals("Second display must only contain 1 task",
+                1, mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
         assertEquals("Top stack task must contain 2 activities",
-                2, secondFrontStack.getTasks().get(0).mActivities.size());
+                2, secondFrontStack.getActivities().size());
     }
 
     /**
@@ -848,6 +848,7 @@
         getPendingIntentActivity(TEST_ACTIVITY).send(mContext, resultCode, null /* intent */,
                 null /* onFinished */, null /* handler */, null /* requiredPermission */,
                 options.toBundle());
+        waitAndAssertActivityState(TOP_ACTIVITY, STATE_STOPPED, "Activity should be stopped");
         waitAndAssertTopResumedActivity(TEST_ACTIVITY, displayContent.mId,
                 "Activity launched on secondary display and on top");
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
index b45d621..e1df6d0 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
@@ -18,6 +18,7 @@
 
 import static android.server.wm.UiDeviceUtils.pressBackButton;
 import static android.server.wm.app.Components.DISMISS_KEYGUARD_ACTIVITY;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 
 import static org.junit.Assert.assertFalse;
@@ -26,6 +27,7 @@
 
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.WindowManagerState.DisplayContent;
+import android.util.Size;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -70,7 +72,6 @@
 
     /**
      * Tests keyguard dialog shows on secondary display.
-     * @throws Exception
      */
     @Test
     public void testShowKeyguardDialogOnSecondaryDisplay() {
@@ -93,8 +94,77 @@
     }
 
     /**
+     * Tests keyguard dialog should exist after secondary display changed.
+     */
+    @Test
+    public void testShowKeyguardDialogSecondaryDisplayChange() {
+        final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+        final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
+
+        final DisplayContent publicDisplay = virtualDisplaySession
+                .setPublicDisplay(true)
+                .createDisplay();
+
+        lockScreenSession.gotoKeyguard();
+        assertTrue("KeyguardDialog must show on external public display",
+                mWmState.waitForWithAmState(
+                        state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+                        "keyguard window to show"));
+
+        // By default, a Presentation object should be dismissed if the DisplayMetrics changed.
+        // But this rule should not apply to KeyguardPresentation.
+        virtualDisplaySession.resizeDisplay();
+        mWmState.computeState();
+        assertTrue("KeyguardDialog must show on external public display even display changed",
+                mWmState.waitForWithAmState(
+                        state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+                        "keyguard window to show"));
+    }
+
+    /**
+     * Tests keyguard dialog should exist after default display changed.
+     */
+    @Test
+    public void testShowKeyguardDialogDefaultDisplayChange() {
+        final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+        final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
+        final DisplayMetricsSession displayMetricsSession =
+                createManagedDisplayMetricsSession(DEFAULT_DISPLAY);
+
+        // Use simulate display instead of virtual display, because VirtualDisplayActivity will
+        // relaunch after configuration change.
+        final DisplayContent publicDisplay = virtualDisplaySession
+                .setSimulateDisplay(true)
+                .createDisplay();
+
+        lockScreenSession.gotoKeyguard();
+        assertTrue("KeyguardDialog must show on external public display",
+                mWmState.waitForWithAmState(
+                        state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+                        "keyguard window to show"));
+
+        // Unlock then lock again, to ensure the display metrics has updated.
+        lockScreenSession.wakeUpDevice().unlockDevice();
+        // Overriding the display metrics on the default display should not affect Keyguard to show
+        // on secondary display.
+        final ReportedDisplayMetrics originalDisplayMetrics =
+                displayMetricsSession.getInitialDisplayMetrics();
+        final Size overrideSize = new Size(
+                (int) (originalDisplayMetrics.physicalSize.getWidth() * 1.5),
+                (int) (originalDisplayMetrics.physicalSize.getHeight() * 1.5));
+        final Integer overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
+        displayMetricsSession.overrideDisplayMetrics(overrideSize, overrideDensity);
+
+        lockScreenSession.gotoKeyguard();
+        assertTrue("KeyguardDialog must show on external public display",
+                mWmState.waitForWithAmState(
+                        state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+                        "keyguard window to show"));
+
+    }
+
+    /**
      * Tests keyguard dialog cannot be shown on private display.
-     * @throws Exception
      */
     @Test
     public void testNoKeyguardDialogOnPrivateDisplay() {
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 5ba2c63..da874ab 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -389,7 +389,7 @@
                 pair(newDisplay.mId, TEST_ACTIVITY));
 
         // Move activity from secondary display to primary.
-        moveActivityToStack(TEST_ACTIVITY, defaultDisplayStackId);
+        moveActivityToStackOrOnTop(TEST_ACTIVITY, defaultDisplayStackId);
         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
                 "Moved activity must be on top");
     }
@@ -734,8 +734,7 @@
     private void assertTopTaskSameSurfaceSizeWithDisplay(int displayId) {
         final DisplayContent display = mWmState.getDisplay(displayId);
         final int stackId = mWmState.getFrontRootTaskId(displayId);
-        final ActivityTask task =
-                mWmState.getRootTask(stackId).mTasks.get(0);
+        final ActivityTask task = mWmState.getRootTask(stackId).getTopTask();
 
         assertEquals("Task must have same surface width with its display",
                 display.getSurfaceSize(), task.getSurfaceWidth());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
index 1e2e8c3..c48d80a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
@@ -555,7 +555,8 @@
         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
                 "Top activity must be the newly launched one");
         frontStack = mWmState.getRootTask(frontStackId);
-        assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
+        assertEquals("Secondary display must contain 1 task", 1,
+                mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
     }
 
     /**
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index 8ff9745..f8f714c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -56,6 +56,7 @@
 import android.server.wm.CommandSession.ActivitySession;
 import android.server.wm.CommandSession.ActivitySessionClient;
 import android.server.wm.settings.SettingsSession;
+import android.util.DisplayMetrics;
 import android.util.Pair;
 import android.util.Size;
 import android.view.WindowManager;
@@ -168,10 +169,12 @@
         @Nullable
         final Integer overrideDensity;
 
+        final int mDisplayId;
+
         /** Get physical and override display metrics from WM for specified display. */
         public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
             return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
-                            + executeShellCommand(WM_DENSITY + " -d " + displayId));
+                            + executeShellCommand(WM_DENSITY + " -d " + displayId), displayId);
         }
 
         void setDisplayMetrics(final Size size, final int density) {
@@ -193,11 +196,12 @@
         }
 
         private void setSize(final Size size) {
-            executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
+            executeShellCommand(
+                    WM_SIZE + " " + size.getWidth() + "x" + size.getHeight() + " -d " + mDisplayId);
         }
 
         private void setDensity(final int density) {
-            executeShellCommand(WM_DENSITY + " " + density);
+            executeShellCommand(WM_DENSITY + " " + density + " -d " + mDisplayId);
         }
 
         /** Get display size that WM operates with. */
@@ -210,7 +214,8 @@
             return overrideDensity != null ? overrideDensity : physicalDensity;
         }
 
-        private ReportedDisplayMetrics(final String lines) {
+        private ReportedDisplayMetrics(final String lines, int displayId) {
+            mDisplayId = displayId;
             Matcher matcher = PHYSICAL_SIZE.matcher(lines);
             assertTrue("Physical display size must be reported", matcher.find());
             log(matcher.group());
@@ -241,6 +246,48 @@
         }
     }
 
+    public static class DisplayMetricsSession implements AutoCloseable {
+        private final ReportedDisplayMetrics mInitialDisplayMetrics;
+        private final int mDisplayId;
+
+        DisplayMetricsSession(int displayId) {
+            mDisplayId = displayId;
+            mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
+        }
+
+        ReportedDisplayMetrics getInitialDisplayMetrics() {
+            return mInitialDisplayMetrics;
+        }
+
+        ReportedDisplayMetrics getDisplayMetrics() {
+            return ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
+        }
+
+        void changeDisplayMetrics(double sizeRatio, double densityRatio) {
+            final Size originalSize = mInitialDisplayMetrics.physicalSize;
+            final int density = mInitialDisplayMetrics.physicalDensity;
+
+            final Size overrideSize = new Size((int)(originalSize.getWidth() * sizeRatio),
+                    (int)(originalSize.getHeight() * sizeRatio));
+            final int overrideDensity = (int)(density * densityRatio);
+            overrideDisplayMetrics(overrideSize, overrideDensity);
+        }
+
+        void overrideDisplayMetrics(final Size size, final int density) {
+            mInitialDisplayMetrics.setDisplayMetrics(size, density);
+        }
+
+        @Override
+        public void close() {
+            mInitialDisplayMetrics.restoreDisplayMetrics();
+        }
+    }
+
+    /** @see ObjectTracker#manage(AutoCloseable) */
+    protected DisplayMetricsSession createManagedDisplayMetricsSession(int displayId) {
+        return mObjectTracker.manage(new DisplayMetricsSession(displayId));
+    }
+
     void waitForDisplayGone(Predicate<DisplayContent> displayPredicate) {
         waitForOrFail("displays to be removed", () -> {
             mWmState.computeState();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index debc7ef..998f553 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -574,7 +574,7 @@
         launchActivity(PIP_ACTIVITY2, EXTRA_ENTER_PIP, "true");
 
         final ActivityTask pinnedStack = getPinnedStack();
-        assertEquals(1, pinnedStack.getTasks().size());
+        assertEquals(0, pinnedStack.getTasks().size());
         assertTrue(mWmState.containsActivityInWindowingMode(
                 PIP_ACTIVITY2, WINDOWING_MODE_PINNED));
         assertTrue(mWmState.containsActivityInWindowingMode(
@@ -1017,8 +1017,7 @@
         launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
         waitForEnterPip(PIP_ACTIVITY);
         assertPinnedStackExists();
-        ActivityTask stack = mWmState.getStandardStackByWindowingMode(
-                WINDOWING_MODE_PINNED);
+        ActivityTask stack = mWmState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
         int stackId = stack.mRootTaskId;
         int taskId = stack.getTopTask().mTaskId;
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
index a03a72f..f6646b4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
@@ -490,16 +490,6 @@
     }
 
     @Test
-    public void testFinishDockActivityWhileMinimized() throws Exception {
-        launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
-
-        mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
-        waitForDockNotMinimized();
-        mWmState.waitAndAssertActivityRemoved(TEST_ACTIVITY);
-        assertDockNotMinimized();
-    }
-
-    @Test
     public void testDockedStackToMinimizeWhenUnlocked() {
         if (!mIsHomeRecentsComponent) {
             launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -662,30 +652,6 @@
                 recentsStackIndex, lessThan(homeStackIndex));
     }
 
-    @Test
-    public void testStackListOrderOnSplitScreenDismissed() throws Exception {
-        launchActivitiesInSplitScreen(
-                getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
-                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
-
-        setActivityTaskWindowingMode(DOCKED_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
-        mWmState.computeState(new WaitForValidActivityState.Builder(DOCKED_ACTIVITY)
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                .build());
-
-        final int homeStackIndex = mWmState.getStackIndexByActivityType(ACTIVITY_TYPE_HOME);
-        final int prevSplitScreenPrimaryIndex =
-                mWmState.getStackIndexByActivity(DOCKED_ACTIVITY);
-        final int prevSplitScreenSecondaryIndex =
-                mWmState.getStackIndexByActivity(TEST_ACTIVITY);
-
-        final int expectedHomeStackIndex =
-                (prevSplitScreenPrimaryIndex > prevSplitScreenSecondaryIndex
-                        ? prevSplitScreenPrimaryIndex : prevSplitScreenSecondaryIndex) - 1;
-        assertEquals("Home stack needs to be directly behind the top stack",
-                expectedHomeStackIndex, homeStackIndex);
-    }
-
     private void launchActivityInDockStackAndMinimize(ComponentName activityName) {
         launchActivityInDockStackAndMinimize(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
new file mode 100644
index 0000000..e9bebce
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+@Presubmit
+public class WindowContextTests extends MultiDisplayTestBase {
+    private Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private Context mContext = mInstrumentation.getTargetContext();
+    private DisplayManager mDisplayManager = mContext.getSystemService(DisplayManager.class);
+
+    @Test
+    public void testWindowContextConfigChanges() {
+        final WindowManagerState.DisplayContent display =  createManagedVirtualDisplaySession()
+                .setSimulateDisplay(true).createDisplay();
+        final Context windowContext = createWindowContext(display.mId);
+        mInstrumentation.runOnMainSync(() -> {
+            final View view = new View(windowContext);
+            WindowManager wm = windowContext.getSystemService(WindowManager.class);
+            wm.addView(view, new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY));
+        });
+        mWmState.computeState();
+
+        final DisplayMetricsSession displayMetricsSession =
+                createManagedDisplayMetricsSession(display.mId);
+
+        displayMetricsSession.changeDisplayMetrics(1.2 /* sizeRatio */, 1.1 /* densityRatio */);
+
+        mWmState.computeState();
+
+        assertDisplayMetricsEquals(displayMetricsSession.getDisplayMetrics(),
+                windowContext.getResources().getDisplayMetrics());
+    }
+
+    private Context createWindowContext(int displayId) {
+        final Display display = mDisplayManager.getDisplay(displayId);
+        return mContext.createDisplayContext(display).createWindowContext(TYPE_APPLICATION_OVERLAY,
+                null /* options */);
+    }
+
+    private void assertDisplayMetricsEquals(ReportedDisplayMetrics expectedMetrics,
+            DisplayMetrics actualMetrics) {
+        assertEquals(expectedMetrics.getSize().getWidth(), actualMetrics.widthPixels);
+        assertEquals(expectedMetrics.getSize().getHeight(), actualMetrics.heightPixels);
+        assertEquals(expectedMetrics.getDensity(), actualMetrics.densityDpi);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java
index 958279a..1b04e13 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTests.java
@@ -18,11 +18,16 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 
 import android.app.Activity;
+import android.graphics.Insets;
+import android.graphics.Point;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
+import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowMetrics;
@@ -34,14 +39,23 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+/**
+ * Tests that verify the behavior of {@link WindowMetrics} and {@link android.app.WindowContext} API
+ *
+ * Build/Install/Run:
+ *     atest CtsWindowManagerDeviceTestCases:WindowMetricsTests
+ */
 @Presubmit
 public class WindowMetricsTests extends WindowManagerTestBase {
 
-    ActivityTestRule<MetricsActivity> mMetricsActivity =
+    private ActivityTestRule<MetricsActivity> mMetricsActivity =
             new ActivityTestRule<>(MetricsActivity.class);
 
     @Test
-    public void test_metrics() {
+    public void testMetricsSanity() {
+        // TODO(b/149668895): handle device with cutout.
+        assumeFalse(hasDisplayCutout());
+
         final MetricsActivity activity = mMetricsActivity.launchActivity(null);
         activity.waitForLayout();
 
@@ -57,7 +71,58 @@
                 activity.mOnCreateCurrentMetrics.getWindowInsets().getStableInsets());
         assertEquals(activity.mOnLayoutInsets.getDisplayCutout(),
                 activity.mOnCreateCurrentMetrics.getWindowInsets().getDisplayCutout());
+    }
 
+    @Test
+    public void testMetricsMatchesDisplay() {
+        final MetricsActivity activity = mMetricsActivity.launchActivity(null);
+        activity.waitForLayout();
+
+        final Display display = activity.getDisplay();
+
+        // Check window size
+        final Point displaySize = new Point();
+        display.getSize(displaySize);
+        final WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
+        final Size size = getLegacySize(windowMetrics);
+        assertEquals("Reported display width must match window width",
+                displaySize.x, size.getWidth());
+        assertEquals("Reported display height must match window height",
+                displaySize.y, size.getHeight());
+
+        // Check max window size
+        final Point realDisplaySize = new Point();
+        display.getRealSize(realDisplaySize);
+        final WindowMetrics maxWindowMetrics = activity.getWindowManager()
+                .getMaximumWindowMetrics();
+        assertEquals("Reported real display width must match max window size",
+                realDisplaySize.x, maxWindowMetrics.getSize().getWidth());
+        assertEquals("Reported real display height must match max window size",
+                realDisplaySize.y, maxWindowMetrics.getSize().getHeight());
+    }
+
+    private static Size getLegacySize(WindowMetrics windowMetrics) {
+        WindowInsets windowInsets = windowMetrics.getWindowInsets();
+        final Insets insetsWithCutout = getProperInsetsWithCutout(windowInsets.getStableInsets(),
+                windowInsets.getDisplayCutout());
+        final int insetsInWidth = insetsWithCutout.left + insetsWithCutout.right;
+        final int insetsInHeight = insetsWithCutout.top + insetsWithCutout.bottom;
+
+        Size size = windowMetrics.getSize();
+        return new Size(size.getWidth() - insetsInWidth, size.getHeight() - insetsInHeight);
+    }
+
+    private static Insets getProperInsetsWithCutout(Insets stableInsets, DisplayCutout cutout) {
+        final Insets excludingStatusBar = Insets.of(stableInsets.left, 0,
+                stableInsets.right, stableInsets.bottom);
+        if (cutout == null) {
+            return excludingStatusBar;
+        } else {
+            final Insets unionInsetsWithCutout = Insets.max(excludingStatusBar,
+                    Insets.of(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+                            cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()));
+            return unionInsetsWithCutout;
+        }
     }
 
     public static class MetricsActivity extends Activity implements View.OnLayoutChangeListener {
@@ -78,7 +143,6 @@
             getWindow().getDecorView().addOnLayoutChangeListener(this);
         }
 
-
         @Override
         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
                 int oldTop, int oldRight, int oldBottom) {
@@ -88,7 +152,7 @@
             mLayoutLatch.countDown();
         }
 
-        public void waitForLayout() {
+        void waitForLayout() {
             try {
                 assertTrue("timed out waiting for activity to layout",
                         mLayoutLatch.await(4, TimeUnit.SECONDS));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
index 4c21f49..93ecabc 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
@@ -112,7 +112,7 @@
     }
 
     @Test
-    public void testOccludingMovedBetweenStacks() throws Exception {
+    public void testOccludingOnSplitSecondaryStack() throws Exception {
         // Launch first activity
         final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
 
@@ -140,21 +140,10 @@
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
                 .launch();
         waitAndAssertActivityStates(state(secondActivity, ON_STOP));
-
-        // Move occluding third activity to side, it will occlude first now
-        getLifecycleLog().clear();
-        moveActivityToStack(getComponentName(ThirdActivity.class), primarySplitStack);
-
-        waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
-                state(firstActivity, ON_STOP));
-        LifecycleVerifier.assertEmptySequence(ThirdActivity.class, getLifecycleLog(), "moveToSide");
-        LifecycleVerifier.assertRestartAndResumeSequence(SecondActivity.class, getLifecycleLog());
-        LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
-                Arrays.asList(ON_PAUSE, ON_STOP), "moveToSide");
     }
 
     @Test
-    public void testTranslucentMovedBetweenStacks() throws Exception {
+    public void testTranslucentOnSplitSecondaryStack() throws Exception {
         // Launch first activity
         final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
 
@@ -183,17 +172,6 @@
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
                 .launch();
         waitAndAssertActivityStates(state(secondActivity, ON_PAUSE));
-
-        // Move translucent activity to side, it will be on top of the first now
-        getLifecycleLog().clear();
-        moveActivityToStack(getComponentName(TranslucentActivity.class), primarySplitStack);
-
-        waitAndAssertActivityStates(state(firstActivity, ON_PAUSE),
-                state(secondActivity, ON_RESUME));
-        LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
-                Arrays.asList(ON_PAUSE), "moveToSide");
-        // Translucent activity can be either relaunched or preserved depending on whether the split
-        // sizes match. Not verifying its lifecycle here.
     }
 
     @Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
index 3d31935..941b941 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
@@ -157,7 +157,7 @@
 
         // Move translucent activity into the stack with the first activity
         getLifecycleLog().clear();
-        moveActivityToStack(getComponentName(TranslucentActivity.class), firstActivityStack);
+        moveActivityToStackOrOnTop(getComponentName(TranslucentActivity.class), firstActivityStack);
 
         // Wait for translucent activity to resume and first activity to pause
         waitAndAssertActivityStates(state(translucentActivity, ON_RESUME),
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 b6fa201..b7352fe 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
@@ -60,7 +60,6 @@
 import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
 import static android.server.wm.ActivityLauncher.KEY_WINDOWING_MODE;
 import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
-import static android.server.wm.WindowManagerState.STATE_RESUMED;
 import static android.server.wm.CommandSession.KEY_FORWARD;
 import static android.server.wm.ComponentNameUtils.getActivityName;
 import static android.server.wm.ComponentNameUtils.getLogTag;
@@ -74,6 +73,7 @@
 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
 import static android.server.wm.UiDeviceUtils.waitForDeviceIdle;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
 import static android.server.wm.app.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
@@ -141,6 +141,7 @@
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -533,11 +534,33 @@
         return ComponentName.unflattenFromString(mContext.getResources().getString(resId));
     }
 
+    /**
+     * Move around center of the specific display to ensures the display to be focused without
+     * triggering potential clicked event to impact the test environment.
+     * (e.g: Keyguard credential activated unexpectedly.)
+     *
+     * @param displayId the display ID to gain focused by inject swipe action
+     */
+    protected void moveAroundCenterSync(int displayId) {
+        final Rect bounds = mWmState.getDisplay(displayId).getDisplayRect();
+        final long downTime = SystemClock.uptimeMillis();
+        final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        final int downX = bounds.left + bounds.width() / 2;
+        final int downY = bounds.top + bounds.height() / 2;
+        injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, downX, downY, displayId, true);
+
+        final int moveX = downX + Float.floatToIntBits(touchSlop / 2.0f);
+        final int moveY = downY + Float.floatToIntBits(touchSlop / 2.0f);
+        injectMotion(downTime, downTime, MotionEvent.ACTION_MOVE, moveX, moveY, displayId, true);
+
+        final long upTime = SystemClock.uptimeMillis();
+        injectMotion(downTime, upTime, MotionEvent.ACTION_UP, moveX, moveY, displayId, true);
+    }
+
     protected void tapOnDisplaySync(int x, int y, int displayId) {
         tapOnDisplay(x, y, displayId, true /* sync*/);
     }
 
-
     private void tapOnDisplay(int x, int y, int displayId, boolean sync) {
         final long downTime = SystemClock.uptimeMillis();
         injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, displayId, sync);
@@ -658,10 +681,19 @@
         mWmState.computeState();
         final List<WindowManagerState.ActivityTask> stacks = mWmState.getRootTasks();
         for (WindowManagerState.ActivityTask stack : stacks) {
-            for (WindowManagerState.ActivityTask task : stack.mTasks) {
-                if (task.mTaskId == taskId) {
-                    return stack;
-                }
+            if (stack.getTask(taskId) != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    protected WindowManagerState.ActivityTask getRootTask(int taskId) {
+        mWmState.computeState();
+        final List<WindowManagerState.ActivityTask> rootTasks = mWmState.getRootTasks();
+        for (WindowManagerState.ActivityTask rootTask : rootTasks) {
+            if (rootTask.getTaskId() == taskId) {
+                return rootTask;
             }
         }
         return null;
@@ -831,12 +863,25 @@
         return result[0];
     }
 
-    protected void moveActivityToStack(ComponentName activityName, int stackId) {
+    /** Move activity to stack or on top of the given stack when the stack is a leak task. */
+    protected void moveActivityToStackOrOnTop(ComponentName activityName, int stackId) {
         mWmState.computeState(activityName);
-        final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
-        SystemUtil.runWithShellPermissionIdentity(
-                () -> mAtm.moveTaskToStack(taskId, stackId, true));
-
+        WindowManagerState.ActivityTask rootTask = getRootTask(stackId);
+        if (rootTask.getActivities().size() != 0) {
+            // If the root task is a 1-level task, start the activity on top of given task.
+            getLaunchActivityBuilder()
+                    .setDisplayId(rootTask.mDisplayId)
+                    .setWindowingMode(rootTask.getWindowingMode())
+                    .setActivityType(rootTask.getActivityType())
+                    .setTargetActivity(activityName)
+                    .allowMultipleInstances(false)
+                    .setUseInstrumentation()
+                    .execute();
+        } else {
+            final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
+            SystemUtil.runWithShellPermissionIdentity(
+                    () -> mAtm.moveTaskToStack(taskId, stackId, true));
+        }
         mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
                 .setStackId(stackId)
                 .build());
@@ -1188,7 +1233,7 @@
         public LockScreenSession enterAndConfirmLockCredential() {
             // Ensure focus will switch to default display. Meanwhile we cannot tap on center area,
             // which may tap on input credential area.
-            tapOnDisplaySync(10, 10, DEFAULT_DISPLAY);
+            moveAroundCenterSync(DEFAULT_DISPLAY);
 
             waitForDeviceIdle(3000);
             SystemUtil.runWithShellPermissionIdentity(() ->
@@ -1230,7 +1275,7 @@
 
         LockScreenSession unlockDevice() {
             // Make sure the unlock button event is send to the default display.
-            tapOnDisplaySync(10, 10, DEFAULT_DISPLAY);
+            moveAroundCenterSync(DEFAULT_DISPLAY);
 
             pressUnlockButton();
             return this;
@@ -2201,7 +2246,7 @@
                 // all tests afterward would also fail (since the leakage is always there) and fire
                 // unnecessary false alarms.
                 try {
-                    mWmState.assertEmptyStackOrTask();
+                    mWmState.assertNoneEmptyTasks();
                 } catch (Throwable t) {
                     sStackTaskLeakFound = true;
                     addError(t);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
index e3a1b62..4039a13 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
@@ -956,7 +956,7 @@
         /** Same as {@link #getConfigInfo()}, but for Application. */
         private ConfigInfo getAppConfigInfo() {
             final Application application = (Application) getApplicationContext();
-            return new ConfigInfo(application, application.getDisplay());
+            return new ConfigInfo(application, getDisplay());
         }
     }
 
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 66db4af..61c3a380 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
@@ -76,6 +76,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -412,11 +413,9 @@
     public String getTopActivityName(int displayId) {
         if (!getDisplay(displayId).mRootTasks.isEmpty()) {
             final ActivityTask topStack = getDisplay(displayId).mRootTasks.get(0);
-            if (!topStack.mTasks.isEmpty()) {
-                final ActivityTask topTask = topStack.mTasks.get(0);
-                if (!topTask.mActivities.isEmpty()) {
-                    return topTask.mActivities.get(0).name;
-                }
+            final ActivityTask topTask = topStack.getTopTask();
+            if (!topTask.mActivities.isEmpty()) {
+                return topTask.mActivities.get(0).name;
             }
         }
         return null;
@@ -519,7 +518,7 @@
                 continue;
             }
             if (stack.getWindowingMode() == windowingMode) {
-                count += stack.mTasks.size();
+                count += stack.mTasks.isEmpty() ? 1 : stack.mTasks.size();
             }
         }
         return count;
@@ -673,16 +672,16 @@
 
     ActivityTask getHomeTask() {
         final ActivityTask homeStack = getStackByActivityType(ACTIVITY_TYPE_HOME);
-        if (homeStack != null && !homeStack.mTasks.isEmpty()) {
-            return homeStack.mTasks.get(0);
+        if (homeStack != null) {
+            return homeStack.getTopTask();
         }
         return null;
     }
 
     private ActivityTask getRecentsTask() {
         final ActivityTask recentsStack = getStackByActivityType(ACTIVITY_TYPE_RECENTS);
-        if (recentsStack != null && !recentsStack.mTasks.isEmpty()) {
-            return recentsStack.mTasks.get(0);
+        if (recentsStack != null) {
+            return recentsStack.getTopTask();
         }
         return null;
     }
@@ -704,24 +703,15 @@
     }
 
     public ActivityTask getTaskByActivity(ComponentName activityName) {
-        return getTaskByActivityInternal(getActivityName(activityName), WINDOWING_MODE_UNDEFINED);
+        return getTaskByActivity(activityName, WINDOWING_MODE_UNDEFINED);
     }
 
     ActivityTask getTaskByActivity(ComponentName activityName, int windowingMode) {
-        return getTaskByActivityInternal(getActivityName(activityName), windowingMode);
-    }
-
-    private ActivityTask getTaskByActivityInternal(String fullName, int windowingMode) {
         for (ActivityTask stack : mRootTasks) {
             if (windowingMode == WINDOWING_MODE_UNDEFINED
                     || windowingMode == stack.getWindowingMode()) {
-                for (ActivityTask task : stack.mTasks) {
-                    for (Activity activity : task.mActivities) {
-                        if (activity.name.equals(fullName)) {
-                            return task;
-                        }
-                    }
-                }
+                Activity activity = stack.getActivity(activityName);
+                if (activity != null) return activity.task;
             }
         }
         return null;
@@ -739,21 +729,20 @@
         // If activityName is null, count all activities in the task.
         // Otherwise count activities that have specified name.
         for (ActivityTask stack : mRootTasks) {
-            for (ActivityTask task : stack.mTasks) {
-                if (task.mTaskId == taskId) {
-                    if (activityName == null) {
-                        return task.mActivities.size();
-                    }
-                    final String fullName = getActivityName(activityName);
-                    int count = 0;
-                    for (Activity activity : task.mActivities) {
-                        if (activity.name.equals(fullName)) {
-                            count++;
-                        }
-                    }
-                    return count;
+            final ActivityTask task = stack.getTask(taskId);
+            if (task == null) continue;
+
+            if (activityName == null) {
+                return task.mActivities.size();
+            }
+            final String fullName = getActivityName(activityName);
+            int count = 0;
+            for (Activity activity : task.mActivities) {
+                if (activity.name.equals(fullName)) {
+                    count++;
                 }
             }
+            return count;
         }
         return 0;
     }
@@ -957,6 +946,12 @@
             }
             for (int i = 0; i < proto.tasks.length; i++) {
                 ActivityTask task = new ActivityTask(proto.tasks[i]);
+                if (task.mCreatedByOrganizer) {
+                    // TODO(b/149338177): figure out how CTS tests deal with organizer. For now,
+                    //                    don't treat them as regular stacks
+                    // Skip tasks created by an organizer
+                    continue;
+                }
                 mRootTasks.add(task);
                 // Also update activity manager state
                 amState.mRootTasks.add(task);
@@ -1067,6 +1062,7 @@
         boolean mAnimatingBounds;
         private int mSurfaceWidth;
         private int mSurfaceHeight;
+        boolean mCreatedByOrganizer;
 
         ActivityTask(TaskProto proto) {
             super(proto.windowContainer);
@@ -1085,6 +1081,7 @@
             mAnimatingBounds = proto.animatingBounds;
             mSurfaceWidth = proto.surfaceWidth;
             mSurfaceHeight = proto.surfaceHeight;
+            mCreatedByOrganizer = proto.createdByOrganizer;
 
             if (proto.resumedActivity != null) {
                 mResumedActivity = proto.resumedActivity.title;
@@ -1094,7 +1091,7 @@
                 mTasks.add(new ActivityTask(proto.tasks[i]));
             }
             for (int i = 0;  i < proto.activities.length; i++) {
-                mActivities.add(new Activity(proto.activities[i]));
+                mActivities.add(new Activity(proto.activities[i], this));
             }
         }
 
@@ -1122,33 +1119,37 @@
             return mActivities;
         }
 
-        /**
-         * @return the top task in the stack.
-         */
+        /** @return the top task in the stack. */
         ActivityTask getTopTask() {
-            if (!mTasks.isEmpty()) {
-                // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
-                //       so the indices are inverted
-                return mTasks.get(0);
-            }
-            return null;
+            // NOTE: Unlike the WindowManager internals, we dump the state from top to bottom,
+            //       so the indices are inverted
+            return getTask((t) -> true);
+        }
+
+        public String getResumedActivity() {
+            return mResumedActivity;
         }
 
         public List<ActivityTask> getTasks() {
             return new ArrayList<>(mTasks);
         }
 
-        ActivityTask getTask(int taskId) {
+        ActivityTask getTask(Predicate<ActivityTask> predicate) {
             for (ActivityTask task : mTasks) {
-                if (taskId == task.mTaskId) {
-                    return task;
-                }
+                if (predicate.test(task)) return task;
             }
-            return null;
+            return predicate.test(this) ? this : null;
         }
 
-        public String getResumedActivity() {
-            return mResumedActivity;
+        ActivityTask getTask(int taskId) {
+            return getTask((t) -> t.mTaskId == taskId);
+        }
+
+        void forAllTasks(Consumer<ActivityTask> consumer) {
+            for (ActivityTask task : mTasks) {
+                consumer.accept(task);
+            }
+            consumer.accept(this);
         }
 
         Activity getActivity(Predicate<Activity> predicate) {
@@ -1180,9 +1181,11 @@
         boolean frontOfTask;
         int procId = -1;
         public boolean translucent;
+        ActivityTask task;
 
-        Activity(ActivityRecordProto proto) {
+        Activity(ActivityRecordProto proto, ActivityTask parent) {
             super(proto.windowToken.windowContainer);
+            task = parent;
             name = proto.name;
             state = proto.state;
             visible = proto.visible;
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
index b387965..fc31bd6 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
@@ -590,17 +590,12 @@
                 getKeyguardControllerState().aodShowing);
     }
 
-    public void assertEmptyStackOrTask() {
+    public void assertNoneEmptyTasks() {
         computeState();
-        final List<ActivityTask> stacks = getRootTasks();
-        for (ActivityTask stack : stacks) {
-            assertWithMessage("Empty stack was found, id = " + stack.mRootTaskId)
-                    .that(stack.getTopTask()).isNotNull();
-            final List<ActivityTask> tasks = stack.getTasks();
-            for (ActivityTask task : tasks) {
-                assertWithMessage("Empty task was found, id = " + task.mTaskId)
-                        .that(task.getActivities().size()).isGreaterThan(0);
-            }
+        final List<ActivityTask> tasks = getRootTasks();
+        for (ActivityTask task : tasks) {
+            task.forAllTasks((t) -> assertWithMessage("Empty task was found, id = " + t.mTaskId)
+                    .that(t.mTasks.size() + t.mActivities.size()).isGreaterThan(0));
         }
     }
 
diff --git a/tests/inputmethod/mockime/res/xml/method.xml b/tests/inputmethod/mockime/res/xml/method.xml
index 2266fba..5b3cf85 100644
--- a/tests/inputmethod/mockime/res/xml/method.xml
+++ b/tests/inputmethod/mockime/res/xml/method.xml
@@ -15,5 +15,6 @@
   limitations under the License.
 -->
 
-<input-method xmlns:android="http://schemas.android.com/apk/res/android">
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+              android:supportsInlineSuggestions="true">
 </input-method>
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
index a7bf781..a9d1414 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -47,6 +47,7 @@
     private static final String INPUT_VIEW_SYSTEM_UI_VISIBILITY = "InputViewSystemUiVisibility";
     private static final String HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED =
             "HardKeyboardConfigurationBehaviorAllowed";
+    private static final String INLINE_SUGGESTIONS_ENABLED = "InlineSuggestionsEnabled";
 
     @NonNull
     private final PersistableBundle mBundle;
@@ -105,6 +106,10 @@
         return mBundle.getBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, defaultValue);
     }
 
+    public boolean getInlineSuggestionsEnabled() {
+        return mBundle.getBoolean(INLINE_SUGGESTIONS_ENABLED);
+    }
+
     static Bundle serializeToBundle(@NonNull String eventCallbackActionName,
             @Nullable Builder builder) {
         final Bundle result = new Bundle();
@@ -214,5 +219,16 @@
             mBundle.putBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, allowed);
             return this;
         }
+
+        /**
+         * Controls whether inline suggestions are enabled for {@link MockIme}. If enabled, a
+         * suggestion strip will be rendered at the top of the keyboard.
+         *
+         * @param enabled {@code true} when {@link MockIme} is enabled to show inline suggestions.
+         */
+        public Builder setInlineSuggestionsEnabled(boolean enabled) {
+            mBundle.putBoolean(INLINE_SUGGESTIONS_ENABLED, enabled);
+            return this;
+        }
     }
 }
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 590ce86..6142b9e 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.mockime;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
 import android.content.BroadcastReceiver;
@@ -25,6 +27,7 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.inputmethodservice.InputMethodService;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -36,31 +39,43 @@
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Size;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.inline.InlinePresentationSpec;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InlineSuggestion;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.InputMethod;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
+import android.widget.ScrollView;
 import android.widget.TextView;
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.CallSuper;
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
@@ -407,23 +422,38 @@
 
             final int defaultBackgroundColor =
                     getResources().getColor(android.R.color.holo_orange_dark, null);
-            setBackgroundColor(mSettings.getBackgroundColor(defaultBackgroundColor));
 
             final int mainSpacerHeight = mSettings.getInputViewHeightWithoutSystemWindowInset(
                     LayoutParams.WRAP_CONTENT);
             {
-                final RelativeLayout layout = new RelativeLayout(getContext());
+                final LinearLayout layout = new LinearLayout(getContext());
+                layout.setOrientation(LinearLayout.VERTICAL);
+
+                if (mSettings.getInlineSuggestionsEnabled()) {
+                    final ScrollView scrollView = new ScrollView(getContext());
+                    final LayoutParams scrollViewParams = new LayoutParams(MATCH_PARENT, 100);
+                    scrollView.setLayoutParams(scrollViewParams);
+
+                    sSuggestionView = new LinearLayout(getContext());
+                    sSuggestionView.setBackgroundColor(0xFFEEEEEE);
+                    //TODO: Change magic id
+                    sSuggestionView.setId(0x0102000b);
+                    scrollView.addView(sSuggestionView,
+                            new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+                    layout.addView(scrollView);
+                }
+
                 final TextView textView = new TextView(getContext());
-                final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
-                        RelativeLayout.LayoutParams.MATCH_PARENT,
-                        RelativeLayout.LayoutParams.WRAP_CONTENT);
-                params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+                final LayoutParams params = new LayoutParams(MATCH_PARENT, WRAP_CONTENT);
                 textView.setLayoutParams(params);
                 textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
                 textView.setGravity(Gravity.CENTER);
                 textView.setText(getImeId());
+                textView.setBackgroundColor(mSettings.getBackgroundColor(defaultBackgroundColor));
                 layout.addView(textView);
-                addView(layout, LayoutParams.MATCH_PARENT, mainSpacerHeight);
+
+                addView(layout, MATCH_PARENT, mainSpacerHeight);
             }
 
             final int systemUiVisibility = mSettings.getInputViewSystemUiVisibility(0);
@@ -598,6 +628,114 @@
         return new ImeState(hasInputBinding, hasDummyInputConnectionConnection);
     }
 
+    private static LinearLayout sSuggestionView;
+    @GuardedBy("this")
+    private List<View> mSuggestionViews = new ArrayList<>();
+    @GuardedBy("this")
+    private List<Size> mSuggestionViewSizes = new ArrayList<>();
+    @GuardedBy("this")
+    private boolean mSuggestionViewVisible = false;
+
+    public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(Bundle uiExtras) {
+        Log.d(TAG, "onCreateInlineSuggestionsRequest() called");
+        final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
+        presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
+                new Size(400, 100)).build());
+        presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
+                new Size(400, 100)).build());
+
+        return new InlineSuggestionsRequest.Builder(presentationSpecs)
+                .setMaxSuggestionCount(6)
+                .build();
+    }
+
+    @Override
+    public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
+        Log.d(TAG, "onInlineSuggestionsResponse() called");
+        AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+            onInlineSuggestionsResponseInternal(response);
+        });
+        return true;
+    }
+
+    private synchronized void updateInlineSuggestionVisibility(boolean visible, boolean force) {
+        Log.d(TAG, "updateInlineSuggestionVisibility() called, visible=" + visible + ", force="
+                + force);
+        mMainHandler.post(() -> {
+            Log.d(TAG, "updateInlineSuggestionVisibility() running");
+            if (visible == mSuggestionViewVisible && !force) {
+                return;
+            } else if (visible) {
+                sSuggestionView.removeAllViews();
+                final int size = mSuggestionViews.size();
+                for (int i = 0; i < size; i++) {
+                    if(mSuggestionViews.get(i) == null) {
+                        continue;
+                    }
+                    ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+                            mSuggestionViewSizes.get(i).getWidth(),
+                            mSuggestionViewSizes.get(i).getHeight());
+                    sSuggestionView.addView(mSuggestionViews.get(i), layoutParams);
+                }
+                mSuggestionViewVisible = true;
+            } else {
+                sSuggestionView.removeAllViews();
+                mSuggestionViewVisible = false;
+            }
+        });
+    }
+
+    private void onSuggestionViewUpdated() {
+        getTracer().onSuggestionViewUpdated();
+    }
+
+    private synchronized void updateSuggestionViews(View[] suggestionViews, Size[] sizes) {
+        Log.d(TAG, "updateSuggestionViews() called on " + suggestionViews.length + " views");
+        mSuggestionViews = Arrays.asList(suggestionViews);
+        mSuggestionViewSizes = Arrays.asList(sizes);
+        final boolean visible = !mSuggestionViews.isEmpty();
+        updateInlineSuggestionVisibility(visible, true);
+        onSuggestionViewUpdated();
+    }
+
+    private void onInlineSuggestionsResponseInternal(InlineSuggestionsResponse response) {
+        if (response == null || response.getInlineSuggestions() == null) {
+            Log.w(TAG, "onInlineSuggestionsResponseInternal() null response/suggestions");
+            return;
+        }
+        final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
+        final int totalSuggestionsCount = inlineSuggestions.size();
+        Log.d(TAG, "onInlineSuggestionsResponseInternal() called. Suggestion="
+                + totalSuggestionsCount);
+
+        if (totalSuggestionsCount == 0) {
+            updateSuggestionViews(new View[]{} , new Size[]{});
+        }
+
+        final AtomicInteger suggestionsCount = new AtomicInteger(totalSuggestionsCount);
+        final View[] suggestionViews = new View[totalSuggestionsCount];
+        final Size[] sizes = new Size[totalSuggestionsCount];
+
+        for (int i = 0; i < totalSuggestionsCount; i++) {
+            final int index = i;
+            InlineSuggestion inlineSuggestion = inlineSuggestions.get(index);
+            Size size = inlineSuggestion.getInfo().getPresentationSpec().getMaxSize();
+            Log.d(TAG, "Calling inflate on suggestion " + i);
+            inlineSuggestion.inflate(this, size,
+                    AsyncTask.THREAD_POOL_EXECUTOR,
+                    suggestionView -> {
+                        Log.d(TAG, "new inline suggestion view ready");
+                        if(suggestionView != null) {
+                            suggestionViews[index] = suggestionView;
+                            sizes[index] = size;
+                        }
+                        if (suggestionsCount.decrementAndGet() == 0) {
+                            updateSuggestionViews(suggestionViews, sizes);
+                        }
+                    });
+        }
+    }
+
     /**
      * Event tracing helper class for {@link MockIme}.
      */
@@ -822,5 +960,9 @@
             imeLayoutInfo.writeToBundle(arguments);
             recordEventInternal("onInputViewLayoutChanged", runnable, arguments);
         }
+
+        public void onSuggestionViewUpdated() {
+            recordEventInternal("onSuggestionViewUpdated", () -> {}, new Bundle());
+        }
     }
 }
diff --git a/tests/location/location_fine/AndroidManifest.xml b/tests/location/location_fine/AndroidManifest.xml
index 01fe459..0804333 100644
--- a/tests/location/location_fine/AndroidManifest.xml
+++ b/tests/location/location_fine/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.location.cts.fine"
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java
index fa7c032..5aa2bd3 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoRegistrationTest.java
@@ -71,32 +71,30 @@
 
     @Test
     public void testGnssAntennaInfoCallbackRegistration() {
-        TestGnssAntennaInfoCallback callback = new TestGnssAntennaInfoCallback();
-
         // TODO(skz): check that version code is greater than R
 
-        mManager.registerAntennaInfoCallback(Executors.newSingleThreadExecutor(), callback);
+        if(!mManager.getGnssCapabilities().hasGnssAntennaInfo()) {
+            // GnssAntennaInfo is not supported
+            return;
+        }
+
+        TestGnssAntennaInfoListener listener = new TestGnssAntennaInfoListener();
+
+        mManager.registerAntennaInfoListener(Executors.newSingleThreadExecutor(), listener);
         try {
             mAntennaInfoReciept.await(ANTENNA_INFO_TIMEOUT_SEC, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
             Log.e(TAG, "Test was interrupted.");
         }
 
-        if(!callback.verifyStatus()) {
-            // GnssAntennaInfo is not supported (or is taking too long)
-            mManager.unregisterAntennaInfoCallback(callback);
-            return;
-        }
+        listener.verifyRegistration();
 
-        callback.verifyRegistration();
-
-        mManager.unregisterAntennaInfoCallback(callback);
+        mManager.unregisterAntennaInfoListener(listener);
     }
 
-    private class TestGnssAntennaInfoCallback extends GnssAntennaInfo.Callback {
+    private class TestGnssAntennaInfoListener implements GnssAntennaInfo.Listener {
         private boolean receivedAntennaInfo = false;
         private int numResults = 0;
-        private int status = -10000;
 
         @Override
         public void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos) {
@@ -109,15 +107,6 @@
             }
         }
 
-        @Override
-        public void onStatusChanged(@GnssAntennaInfoStatus int status) {
-            this.status = status;
-        }
-
-        public boolean verifyStatus() {
-            return (status == GnssAntennaInfo.Callback.STATUS_READY);
-        }
-
         public void verifyRegistration() {
             assertTrue(receivedAntennaInfo);
             assertThat(numResults).isGreaterThan(0);
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java
index 7805059..cbaabf7 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GnssAntennaInfoTest.java
@@ -18,11 +18,11 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.location.GnssAntennaInfo;
-import android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates;
-import android.location.GnssAntennaInfo.PhaseCenterVariationCorrections;
-import android.location.GnssAntennaInfo.SignalGainCorrections;
+import android.location.GnssAntennaInfo.PhaseCenterOffset;
+import android.location.GnssAntennaInfo.SphericalCorrections;
 import android.os.Parcel;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -73,33 +73,56 @@
     };
 
     @Test
-    public void testDescribeContents() {
-        GnssAntennaInfo gnssAntennaInfo = createTestGnssAntennaInfo();
+    public void testFullAntennaInfoDescribeContents() {
+        GnssAntennaInfo gnssAntennaInfo = createFullTestGnssAntennaInfo();
         assertEquals(0, gnssAntennaInfo.describeContents());
     }
 
     @Test
-    public void testWriteToParcel() {
-        GnssAntennaInfo gnssAntennaInfo = createTestGnssAntennaInfo();
+    public void testPartialAntennaInfoDescribeContents() {
+        GnssAntennaInfo gnssAntennaInfo = createPartialTestGnssAntennaInfo();
+        assertEquals(0, gnssAntennaInfo.describeContents());
+    }
+
+    @Test
+    public void testFullAntennaInfoWriteToParcel() {
+        GnssAntennaInfo gnssAntennaInfo = createFullTestGnssAntennaInfo();
         Parcel parcel = Parcel.obtain();
         gnssAntennaInfo.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
         GnssAntennaInfo newGnssAntennaInfo = GnssAntennaInfo.CREATOR.createFromParcel(parcel);
-        verifyGnssAntennaInfoValuesAndGetters(newGnssAntennaInfo);
+        verifyFullGnssAntennaInfoValuesAndGetters(newGnssAntennaInfo);
         parcel.recycle();
     }
 
     @Test
-    public void testCreateGnssAntennaInfoAndGetValues() {
-        GnssAntennaInfo gnssAntennaInfo = createTestGnssAntennaInfo();
-        verifyGnssAntennaInfoValuesAndGetters(gnssAntennaInfo);
+    public void testPartialAntennaInfoWriteToParcel() {
+        GnssAntennaInfo gnssAntennaInfo = createPartialTestGnssAntennaInfo();
+        Parcel parcel = Parcel.obtain();
+        gnssAntennaInfo.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        GnssAntennaInfo newGnssAntennaInfo = GnssAntennaInfo.CREATOR.createFromParcel(parcel);
+        verifyPartialGnssAntennaInfoValuesAndGetters(newGnssAntennaInfo);
+        parcel.recycle();
     }
 
-    private static GnssAntennaInfo createTestGnssAntennaInfo() {
+    @Test
+    public void testCreateFullGnssAntennaInfoAndGetValues() {
+        GnssAntennaInfo gnssAntennaInfo = createFullTestGnssAntennaInfo();
+        verifyFullGnssAntennaInfoValuesAndGetters(gnssAntennaInfo);
+    }
+
+    @Test
+    public void testCreatePartialGnssAntennaInfoAndGetValues() {
+        GnssAntennaInfo gnssAntennaInfo = createPartialTestGnssAntennaInfo();
+        verifyPartialGnssAntennaInfoValuesAndGetters(gnssAntennaInfo);
+    }
+
+    private static GnssAntennaInfo createFullTestGnssAntennaInfo() {
         double carrierFrequencyMHz = 13758.0;
 
-        GnssAntennaInfo.PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates = new
-                GnssAntennaInfo.PhaseCenterOffsetCoordinates(
+        GnssAntennaInfo.PhaseCenterOffset phaseCenterOffset = new
+                GnssAntennaInfo.PhaseCenterOffset(
                         4.3d,
                     1.4d,
                     2.10d,
@@ -110,75 +133,109 @@
         double[][] phaseCenterVariationCorrectionsMillimeters = PHASE_CENTER_VARIATION_CORRECTIONS;
         double[][] phaseCenterVariationCorrectionsUncertaintyMillimeters =
                 PHASE_CENTER_VARIATION_CORRECTION_UNCERTAINTIES;
-        GnssAntennaInfo.PhaseCenterVariationCorrections
+        SphericalCorrections
                 phaseCenterVariationCorrections =
-                new GnssAntennaInfo.PhaseCenterVariationCorrections(
+                new SphericalCorrections(
                         phaseCenterVariationCorrectionsMillimeters,
                         phaseCenterVariationCorrectionsUncertaintyMillimeters);
 
         double[][] signalGainCorrectionsDbi = SIGNAL_GAIN_CORRECTIONS;
         double[][] signalGainCorrectionsUncertaintyDbi = SIGNAL_GAIN_CORRECTION_UNCERTAINTIES;
-        GnssAntennaInfo.SignalGainCorrections signalGainCorrections = new
-                GnssAntennaInfo.SignalGainCorrections(
+        SphericalCorrections signalGainCorrections = new
+                SphericalCorrections(
                 signalGainCorrectionsDbi,
                 signalGainCorrectionsUncertaintyDbi);
 
-        return new GnssAntennaInfo(carrierFrequencyMHz, phaseCenterOffsetCoordinates,
-                phaseCenterVariationCorrections, signalGainCorrections);
+        return new GnssAntennaInfo.Builder()
+                .setCarrierFrequencyMHz(carrierFrequencyMHz)
+                .setPhaseCenterOffset(phaseCenterOffset)
+                .setPhaseCenterVariationCorrections(phaseCenterVariationCorrections)
+                .setSignalGainCorrections(signalGainCorrections)
+                .build();
     }
 
-    private static void verifyGnssAntennaInfoValuesAndGetters(GnssAntennaInfo gnssAntennaInfo) {
+    private static GnssAntennaInfo createPartialTestGnssAntennaInfo() {
+        double carrierFrequencyMHz = 13758.0;
+
+        GnssAntennaInfo.PhaseCenterOffset phaseCenterOffset = new
+                GnssAntennaInfo.PhaseCenterOffset(
+                4.3d,
+                1.4d,
+                2.10d,
+                2.1d,
+                3.12d,
+                0.5d);
+
+        return new GnssAntennaInfo.Builder()
+                .setCarrierFrequencyMHz(carrierFrequencyMHz)
+                .setPhaseCenterOffset(phaseCenterOffset)
+                .build();
+    }
+
+    private static void verifyPartialGnssAntennaInfoValuesAndGetters(GnssAntennaInfo gnssAntennaInfo) {
         assertEquals(13758.0d, gnssAntennaInfo.getCarrierFrequencyMHz(), PRECISION);
 
         // Phase Center Offset Tests --------------------------------------------------------
-        PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates =
-                gnssAntennaInfo.getPhaseCenterOffsetCoordinates();
-        assertEquals(4.3d, phaseCenterOffsetCoordinates.getXCoordMillimeters(),
+        PhaseCenterOffset phaseCenterOffset =
+                gnssAntennaInfo.getPhaseCenterOffset();
+        assertEquals(4.3d, phaseCenterOffset.getXOffsetMm(),
                 PRECISION);
-        assertEquals(1.4d, phaseCenterOffsetCoordinates.getXCoordUncertaintyMillimeters(),
+        assertEquals(1.4d, phaseCenterOffset.getXOffsetUncertaintyMm(),
                 PRECISION);
-        assertEquals(2.10d, phaseCenterOffsetCoordinates.getYCoordMillimeters(),
+        assertEquals(2.10d, phaseCenterOffset.getYOffsetMm(),
                 PRECISION);
-        assertEquals(2.1d, phaseCenterOffsetCoordinates.getYCoordUncertaintyMillimeters(),
+        assertEquals(2.1d, phaseCenterOffset.getYOffsetUncertaintyMm(),
                 PRECISION);
-        assertEquals(3.12d, phaseCenterOffsetCoordinates.getZCoordMillimeters(),
+        assertEquals(3.12d, phaseCenterOffset.getZOffsetMm(),
                 PRECISION);
-        assertEquals(0.5d, phaseCenterOffsetCoordinates.getZCoordUncertaintyMillimeters(),
+        assertEquals(0.5d, phaseCenterOffset.getZOffsetUncertaintyMm(),
                 PRECISION);
 
         // Phase Center Variation Corrections Tests -----------------------------------------
-        PhaseCenterVariationCorrections phaseCenterVariationCorrections =
+        assertNull(gnssAntennaInfo.getPhaseCenterVariationCorrections());
+
+        // Signal Gain Corrections Tests -----------------------------------------------------
+        assertNull(gnssAntennaInfo.getSignalGainCorrections());
+    }
+
+    private static void verifyFullGnssAntennaInfoValuesAndGetters(GnssAntennaInfo gnssAntennaInfo) {
+        assertEquals(13758.0d, gnssAntennaInfo.getCarrierFrequencyMHz(), PRECISION);
+
+        // Phase Center Offset Tests --------------------------------------------------------
+        PhaseCenterOffset phaseCenterOffset =
+                gnssAntennaInfo.getPhaseCenterOffset();
+        assertEquals(4.3d, phaseCenterOffset.getXOffsetMm(),
+                PRECISION);
+        assertEquals(1.4d, phaseCenterOffset.getXOffsetUncertaintyMm(),
+                PRECISION);
+        assertEquals(2.10d, phaseCenterOffset.getYOffsetMm(),
+                PRECISION);
+        assertEquals(2.1d, phaseCenterOffset.getYOffsetUncertaintyMm(),
+                PRECISION);
+        assertEquals(3.12d, phaseCenterOffset.getZOffsetMm(),
+                PRECISION);
+        assertEquals(0.5d, phaseCenterOffset.getZOffsetUncertaintyMm(),
+                PRECISION);
+
+        // Phase Center Variation Corrections Tests -----------------------------------------
+        SphericalCorrections phaseCenterVariationCorrections =
                 gnssAntennaInfo.getPhaseCenterVariationCorrections();
 
-        assertEquals(6, phaseCenterVariationCorrections.getNumRows());
-        assertEquals(6, phaseCenterVariationCorrections.getNumColumns());
         assertEquals(60.0d, phaseCenterVariationCorrections.getDeltaTheta(), PRECISION);
         assertEquals(36.0d, phaseCenterVariationCorrections.getDeltaPhi(), PRECISION);
         assertArrayEquals(PHASE_CENTER_VARIATION_CORRECTIONS, phaseCenterVariationCorrections
-                .getRawCorrectionsArray());
+                .getCorrectionsArray());
         assertArrayEquals(PHASE_CENTER_VARIATION_CORRECTION_UNCERTAINTIES,
-                phaseCenterVariationCorrections.getRawCorrectionUncertaintiesArray());
-        assertEquals(13.46d, phaseCenterVariationCorrections
-                .getPhaseCenterVariationCorrectionMillimetersAt(2, 2), PRECISION);
-        assertEquals(2.23d, phaseCenterVariationCorrections
-                        .getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(2,
-                                2), PRECISION);
+                phaseCenterVariationCorrections.getCorrectionUncertaintiesArray());
 
         // Signal Gain Corrections Tests -----------------------------------------------------
-        SignalGainCorrections signalGainCorrections = gnssAntennaInfo.getSignalGainCorrections();
+        SphericalCorrections signalGainCorrections = gnssAntennaInfo.getSignalGainCorrections();
 
-        assertEquals(6, signalGainCorrections.getNumRows());
-        assertEquals(6, signalGainCorrections.getNumColumns());
         assertEquals(60.0d, signalGainCorrections.getDeltaTheta(), PRECISION);
         assertEquals(36.0d, signalGainCorrections.getDeltaPhi(), PRECISION);
         assertArrayEquals(SIGNAL_GAIN_CORRECTIONS, signalGainCorrections
-                .getRawCorrectionsArray());
+                .getCorrectionsArray());
         assertArrayEquals(SIGNAL_GAIN_CORRECTION_UNCERTAINTIES,
-                signalGainCorrections.getRawCorrectionUncertaintiesArray());
-        assertEquals(7.92d, signalGainCorrections
-                .getSignalGainCorrectionDbiAt(2, 2), PRECISION);
-        assertEquals(2.10d, signalGainCorrections
-                .getSignalGainCorrectionUncertaintyDbiAt(2,
-                        2), PRECISION);
+                signalGainCorrections.getCorrectionUncertaintiesArray());
     }
 }
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 9afc79b..9e0ed0b 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -1146,11 +1146,10 @@
 
     @Test
     public void testRegisterGnssAntennaInfoCallback() {
-        GnssAntennaInfo.Callback callback = new GnssAntennaInfo.Callback() {
-        };
+        GnssAntennaInfo.Listener listener = (gnssAntennaInfos) -> {};
 
-        mManager.registerAntennaInfoCallback(Executors.newSingleThreadExecutor(), callback);
-        mManager.unregisterAntennaInfoCallback(callback);
+        mManager.registerAntennaInfoListener(Executors.newSingleThreadExecutor(), listener);
+        mManager.unregisterAntennaInfoListener(listener);
     }
 
     @Test
diff --git a/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java b/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
index 802aa2c..b7853e8 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.platform.test.annotations.AppModeFull;
@@ -33,6 +34,7 @@
 import android.test.AndroidTestCase;
 
 import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.PollingCheck;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -76,12 +78,35 @@
     }
 
     @CddTest(requirement = "7.4.2/C-2-1")
-    public void testWifiScanningSettings() throws PackageManager.NameNotFoundException {
+    public void testWifiScanningSettings() throws Exception {
         if (isTv()) {
             return;
         }
         launchScanningSettings();
-        toggleSettingAndVerify(WIFI_SCANNING_TITLE_RES, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE);
+
+        final Resources res = mPackageManager.getResourcesForApplication(SETTINGS_PACKAGE);
+        final int resId = res.getIdentifier(WIFI_SCANNING_TITLE_RES, "string", SETTINGS_PACKAGE);
+        final UiObject2 pref = mDevice.findObject(By.text(res.getString(resId)));
+
+        final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+
+        final boolean checked = wifiManager.isScanAlwaysAvailable();
+
+        // Click the preference to toggle the setting.
+        pref.click();
+        PollingCheck.check(
+                "Scan Always Available wasn't toggled from " + checked + " to " + !checked,
+                TIMEOUT,
+                () -> !checked == wifiManager.isScanAlwaysAvailable()
+        );
+
+        // Click the preference again to toggle the setting back.
+        pref.click();
+        PollingCheck.check(
+                "Scan Always Available wasn't toggled from " + !checked + " to " + checked,
+                TIMEOUT,
+                () -> checked == wifiManager.isScanAlwaysAvailable()
+        );
     }
 
     @CddTest(requirement = "7.4.3/C-4-1")
diff --git a/tests/media/Android.bp b/tests/media/Android.bp
index b813fb2..adcf3de 100644
--- a/tests/media/Android.bp
+++ b/tests/media/Android.bp
@@ -24,6 +24,7 @@
         "android.test.runner.stubs",
         "android.test.base.stubs",
     ],
+    jni_uses_platform_apis: true,
     jni_libs: [
         "libctsmediav2muxer_jni",
         "libctsmediav2extractor_jni",
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index 19fe20a..f3ef46d 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -794,7 +794,6 @@
      * Test Decoder by Queuing CSD separately
      */
     @LargeTest
-    @Ignore("TODO(b/149031058)")
     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
     public void testSimpleDecodeQueueCSD() throws IOException, InterruptedException {
         MediaFormat format = setUpSource(mTestFile);
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index a09cf5a..739b60a 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -45,22 +45,24 @@
     private static final String LOG_TAG = CodecAsyncHandler.class.getSimpleName();
     private final Lock mLock = new ReentrantLock();
     private final Condition mCondition = mLock.newCondition();
-    private CallBackQueue mCbInputQueue;
-    private CallBackQueue mCbOutputQueue;
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue;
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue;
     private MediaFormat mOutFormat;
     private boolean mSignalledOutFormatChanged;
     private volatile boolean mSignalledError;
 
     CodecAsyncHandler() {
-        mCbInputQueue = new CallBackQueue();
-        mCbOutputQueue = new CallBackQueue();
+        mCbInputQueue = new LinkedList<>();
+        mCbOutputQueue = new LinkedList<>();
         mSignalledError = false;
         mSignalledOutFormatChanged = false;
     }
 
     void clearQueues() {
+        mLock.lock();
         mCbInputQueue.clear();
         mCbOutputQueue.clear();
+        mLock.unlock();
     }
 
     void resetContext() {
@@ -73,14 +75,20 @@
     @Override
     public void onInputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex) {
         assertTrue(bufferIndex >= 0);
-        mCbInputQueue.push(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+        mLock.lock();
+        mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+        mCondition.signalAll();
+        mLock.unlock();
     }
 
     @Override
     public void onOutputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex,
             @NonNull MediaCodec.BufferInfo info) {
         assertTrue(bufferIndex >= 0);
-        mCbOutputQueue.push(new Pair<>(bufferIndex, info));
+        mLock.lock();
+        mCbOutputQueue.add(new Pair<>(bufferIndex, info));
+        mCondition.signalAll();
+        mLock.unlock();
     }
 
     @Override
@@ -109,51 +117,52 @@
 
     Pair<Integer, MediaCodec.BufferInfo> getInput() throws InterruptedException {
         Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
         while (!mSignalledError) {
             if (mCbInputQueue.isEmpty()) {
-                mLock.lock();
                 mCondition.await();
-                mLock.unlock();
             } else {
-                element = mCbInputQueue.pop();
+                element = mCbInputQueue.remove(0);
                 break;
             }
         }
+        mLock.unlock();
         return element;
     }
 
     Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException {
         Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
         while (!mSignalledError) {
             if (mCbOutputQueue.isEmpty()) {
-                mLock.lock();
                 mCondition.await();
-                mLock.unlock();
             } else {
-                element = mCbOutputQueue.pop();
+                element = mCbOutputQueue.remove(0);
                 break;
             }
         }
+        mLock.unlock();
         return element;
     }
 
     Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException {
         Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
         while (!mSignalledError) {
             if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) {
-                mLock.lock();
                 mCondition.await();
-                mLock.unlock();
-            }
-            if (!mCbOutputQueue.isEmpty()) {
-                element = mCbOutputQueue.pop();
-                break;
-            }
-            if (!mCbInputQueue.isEmpty()) {
-                element = mCbInputQueue.pop();
-                break;
+            } else {
+                if (!mCbOutputQueue.isEmpty()) {
+                    element = mCbOutputQueue.remove(0);
+                    break;
+                }
+                if (!mCbInputQueue.isEmpty()) {
+                    element = mCbInputQueue.remove(0);
+                    break;
+                }
             }
         }
+        mLock.unlock();
         return element;
     }
 
@@ -168,38 +177,6 @@
     MediaFormat getOutputFormat() {
         return mOutFormat;
     }
-
-    private class CallBackQueue {
-        private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mQueue = new LinkedList<>();
-
-        void push(Pair<Integer, MediaCodec.BufferInfo> element) {
-            mLock.lock();
-            mQueue.add(element);
-            mCondition.signalAll();
-            mLock.unlock();
-        }
-
-        Pair<Integer, MediaCodec.BufferInfo> pop() {
-            Pair<Integer, MediaCodec.BufferInfo> element = null;
-            mLock.lock();
-            if (mQueue.size() != 0) element = mQueue.remove(0);
-            mLock.unlock();
-            return element;
-        }
-
-        boolean isEmpty() {
-            mLock.lock();
-            boolean isEmpty = (mQueue.size() == 0);
-            mLock.unlock();
-            return isEmpty;
-        }
-
-        void clear() {
-            mLock.lock();
-            mQueue.clear();
-            mLock.unlock();
-        }
-    }
 }
 
 class OutputManager {
@@ -461,8 +438,10 @@
                     int bufferID = element.first;
                     MediaCodec.BufferInfo info = element.second;
                     if (info != null) {
+                        // <id, info> corresponds to output callback. Handle it accordingly
                         dequeueOutput(bufferID, info);
                     } else {
+                        // <id, null> corresponds to input callback. Handle it accordingly
                         enqueueInput(bufferID);
                         frameCount++;
                     }
diff --git a/tests/sensor/src/android/hardware/cts/SensorTest.java b/tests/sensor/src/android/hardware/cts/SensorTest.java
index 80ac77d..5096b3d 100644
--- a/tests/sensor/src/android/hardware/cts/SensorTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorTest.java
@@ -250,17 +250,11 @@
                 sensors.get(0).isWakeUpSensor());
     }
 
-    private void hasAtLeastOneWakeupSensorOrEmpty(List<Sensor> sensors, String sensorName) {
-        if (sensors == null || sensors.isEmpty()) return;
-        boolean foundWakeup = false;
-        for (Sensor sensor : sensors) {
-            if (sensor.isWakeUpSensor()) {
-                foundWakeup = true;
-                break;
-            }
-        }
+    private void hasDefaultWakeupSensorOrEmpty(int sensorType, String sensorName) {
+        Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
+        if (sensor == null) return;
 
-        assertTrue("No wake-up " + sensorName + " sensors implemented", foundWakeup);
+        assertTrue("Default " + sensorName + " sensor is not a wake-up sensor", sensor.isWakeUpSensor());
     }
 
     // Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
@@ -276,8 +270,8 @@
         hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_GLANCE_GESTURE));
         hasOnlyOneWakeUpSensorOrEmpty(mSensorManager.getSensorList(TYPE_PICK_UP_GESTURE));
 
-        hasAtLeastOneWakeupSensorOrEmpty(mSensorManager.getSensorList(Sensor.TYPE_PROXIMITY), "proximity");
-        hasAtLeastOneWakeupSensorOrEmpty(mSensorManager.getSensorList(Sensor.TYPE_HINGE_ANGLE), "hinge");
+        hasDefaultWakeupSensorOrEmpty(Sensor.TYPE_PROXIMITY, "proximity");
+        hasDefaultWakeupSensorOrEmpty(Sensor.TYPE_HINGE_ANGLE, "hinge");
     }
 
     public void testGetDefaultSensorWithWakeUpFlag() {
diff --git a/tests/signature/api-check/system-annotation/AndroidTest.xml b/tests/signature/api-check/system-annotation/AndroidTest.xml
index 691516c..816ff81 100644
--- a/tests/signature/api-check/system-annotation/AndroidTest.xml
+++ b/tests/signature/api-check/system-annotation/AndroidTest.xml
@@ -28,7 +28,7 @@
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
         <option name="class" value="android.signature.cts.api.AnnotationTest" />
         <option name="instrumentation-arg" key="expected-api-files" value="system-current.txt,system-removed.txt,car-system-current.txt,car-system-removed.txt" />
-        <option name="instrumentation-arg" key="annotation-for-exact-match" value="@android.annotation.SystemApi\(client=PRIVILEGED_APPS,\ process=ALL\)" />
+        <option name="instrumentation-arg" key="annotation-for-exact-match" value="@android.annotation.SystemApi\(client=PRIVILEGED_APPS\)" />
         <option name="runtime-hint" value="30s" />
     </test>
 
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index b228c49..0ca1fc9 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -122,24 +123,27 @@
     private static final long TIME_DIFF_THRESHOLD = 200;
     private static final String CHANNEL_ID = "my_channel";
 
-
+    private Context mContext;
     private UiDevice mUiDevice;
     private UsageStatsManager mUsageStatsManager;
+    private KeyguardManager mKeyguardManager;
     private String mTargetPackage;
     private String mCachedUsageSourceSetting;
 
     @Before
     public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        mUsageStatsManager = (UsageStatsManager) InstrumentationRegistry.getInstrumentation()
-                .getContext().getSystemService(Context.USAGE_STATS_SERVICE);
-        mTargetPackage = InstrumentationRegistry.getContext().getPackageName();
+        mUsageStatsManager = (UsageStatsManager) mContext.getSystemService(
+                Context.USAGE_STATS_SERVICE);
+        mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+        mTargetPackage = mContext.getPackageName();
+
         assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled());
         setAppOpsMode("allow");
         mCachedUsageSourceSetting = getUsageSourceSetting();
     }
 
-
     @After
     public void cleanUp() throws Exception {
         if (mCachedUsageSourceSetting != null &&
@@ -158,33 +162,27 @@
     }
 
     private void setAppOpsMode(String mode) throws Exception {
-        final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND,
-                InstrumentationRegistry.getContext().getPackageName(), mode);
-        mUiDevice.executeShellCommand(command);
+        executeShellCmd(MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mTargetPackage, mode));
     }
 
-
     private String getUsageSourceSetting() throws Exception {
-        return mUiDevice.executeShellCommand(USAGE_SOURCE_GET_SHELL_COMMAND);
+        return executeShellCmd(USAGE_SOURCE_GET_SHELL_COMMAND);
     }
 
     private void setUsageSourceSetting(String usageSource) throws Exception {
         if (usageSource.equals("null")) {
-            mUiDevice.executeShellCommand(USAGE_SOURCE_DELETE_SHELL_COMMAND);
+            executeShellCmd(USAGE_SOURCE_DELETE_SHELL_COMMAND);
         } else {
-            final String command = MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND,
-                                                        usageSource);
-            mUiDevice.executeShellCommand(command);
+            executeShellCmd(MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND, usageSource));
         }
         mUsageStatsManager.forceUsageSourceSettingRead();
     }
 
     private void launchSubActivity(Class<? extends Activity> clazz) {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
         final Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClassName(mTargetPackage, clazz.getName());
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(intent);
+        mContext.startActivity(intent);
         mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT);
     }
 
@@ -316,7 +314,7 @@
     @Test
     public void testStandbyBucketChangeLog() throws Exception {
         final long startTime = System.currentTimeMillis();
-        mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
+        setStandByBucket(mTargetPackage, "rare");
 
         final long endTime = System.currentTimeMillis();
         UsageEvents events = mUsageStatsManager.queryEvents(startTime - 1_000, endTime + 1_000);
@@ -342,7 +340,7 @@
             assumeTrue("Skip GetAppStandby test: app standby is disabled.",
                     AppStandbyUtils.isAppStandbyEnabled());
 
-            mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
+            setStandByBucket(mTargetPackage, "rare");
             Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets();
             assertTrue("No bucket data returned", bucketMap.size() > 0);
             final int bucket = bucketMap.getOrDefault(mTargetPackage, -1);
@@ -365,9 +363,9 @@
         setAppOpsMode("ignore"); // To ensure permission is not required
         // Time drifts of 2s are expected inside usage stats
         final long start = System.currentTimeMillis() - 2_000;
-        mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
+        setStandByBucket(mTargetPackage, "rare");
         Thread.sleep(100);
-        mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " working_set");
+        setStandByBucket(mTargetPackage, "working_set");
         Thread.sleep(100);
         final long end = System.currentTimeMillis() + 2_000;
         final UsageEvents events = mUsageStatsManager.queryEventsForSelf(start, end);
@@ -537,20 +535,20 @@
         assertTrue(stats.isEmpty());
     }
 
-    private void generateAndSendNotification(Context context) throws Exception {
+    private void generateAndSendNotification() throws Exception {
         final NotificationManager mNotificationManager =
-                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         final NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "Channel",
                 NotificationManager.IMPORTANCE_DEFAULT);
         // Configure the notification channel.
         mChannel.setDescription("Test channel");
         mNotificationManager.createNotificationChannel(mChannel);
         final Notification.Builder mBuilder =
-                new Notification.Builder(context, CHANNEL_ID)
+                new Notification.Builder(mContext, CHANNEL_ID)
                         .setSmallIcon(R.drawable.ic_notification)
                         .setContentTitle("My notification")
                         .setContentText("Hello World!");
-        final PendingIntent pi = PendingIntent.getActivity(context, 1,
+        final PendingIntent pi = PendingIntent.getActivity(mContext, 1,
                 new Intent(Settings.ACTION_SETTINGS), 0);
         mBuilder.setContentIntent(pi);
         mNotificationManager.notify(1, mBuilder.build());
@@ -561,15 +559,15 @@
     @Test
     public void testNotificationSeen() throws Exception {
         final long startTime = System.currentTimeMillis();
-        final Context context = InstrumentationRegistry.getContext();
 
         // Skip the test for wearable devices and televisions; neither has a notification shade.
         assumeFalse("Test cannot run on a watch- notification shade is not shown",
-                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
         assumeFalse("Test cannot run on a television- notifications are not shown",
-                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+                mContext.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_LEANBACK_ONLY));
 
-        generateAndSendNotification(context);
+        generateAndSendNotification();
 
         long endTime = System.currentTimeMillis();
         UsageEvents events = queryEventsAsShell(startTime, endTime);
@@ -606,15 +604,15 @@
     @Test
     public void testNotificationInterruptionEventsObfuscation() throws Exception {
         final long startTime = System.currentTimeMillis();
-        final Context context = InstrumentationRegistry.getContext();
 
         // Skip the test for wearable devices and televisions; neither has a notification shade.
         assumeFalse("Test cannot run on a watch- notification shade is not shown",
-                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
         assumeFalse("Test cannot run on a television- notifications are not shown",
-                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+                mContext.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_LEANBACK_ONLY));
 
-        generateAndSendNotification(context);
+        generateAndSendNotification();
         final long endTime = System.currentTimeMillis();
 
         final UsageEvents obfuscatedEvents = mUsageStatsManager.queryEvents(startTime, endTime);
@@ -667,7 +665,7 @@
                 mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
 
         // User force shouldn't have to deal with the timeout.
-        mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " restricted");
+        setStandByBucket(mTargetPackage, "restricted");
         assertEquals("User was unable to force an ACTIVE app down into RESTRICTED bucket",
                 UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
                 mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
@@ -678,7 +676,7 @@
     @AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
     @Test
     public void testUserLaunchRemovesFromRestricted() throws IOException {
-        mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " restricted");
+        setStandByBucket(mTargetPackage, "restricted");
         assertEquals("User was unable to force an app into RESTRICTED bucket",
                 UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
                 mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
@@ -691,7 +689,7 @@
 
     @Test
     public void testIsAppInactive_Charging() throws Exception {
-        mUiDevice.executeShellCommand("am set-standby-bucket " + TEST_APP_PKG + " rare");
+        setStandByBucket(TEST_APP_PKG, "rare");
 
         try {
             BatteryUtils.runDumpsysBatteryUnplug();
@@ -707,17 +705,17 @@
         }
     }
 
-    static final int[] INTERACTIVE_EVENTS = new int[] {
+    private static final int[] INTERACTIVE_EVENTS = new int[] {
             Event.SCREEN_INTERACTIVE,
             Event.SCREEN_NON_INTERACTIVE
     };
 
-    static final int[] KEYGUARD_EVENTS = new int[] {
+    private static final int[] KEYGUARD_EVENTS = new int[] {
             Event.KEYGUARD_SHOWN,
             Event.KEYGUARD_HIDDEN
     };
 
-    static final int[] ALL_EVENTS = new int[] {
+    private static final int[] ALL_EVENTS = new int[] {
             Event.SCREEN_INTERACTIVE,
             Event.SCREEN_NON_INTERACTIVE,
             Event.KEYGUARD_SHOWN,
@@ -803,19 +801,9 @@
         final AggrEventData nonInteractive = new AggrEventData("Non-interactive");
         final AggrEventData keyguardShown = new AggrEventData("Keyguard shown");
         final AggrEventData keyguardHidden = new AggrEventData("Keyguard hidden");
-        int interactiveCount;
-        long interactiveDuration;
-        long interactiveLastEventTime;
-        int nonInteractiveCount;
-        long nonInteractiveDuration;
-        long nonInteractiveLastEventTime;
-        int keyguardShownCount;
-        long keyguardShownDuration;
-        int keyguardHiddenCount;
-        long keyguardHiddenDuration;
     }
 
-    private SparseArray<AggrAllEventsData> getAggrEventData(long beforeTime) {
+    private SparseArray<AggrAllEventsData> getAggrEventData() {
         final long endTime = System.currentTimeMillis();
 
         final SparseLongArray intervalLengths = new SparseLongArray();
@@ -954,49 +942,35 @@
     @AppModeFull(reason = "No usage events access in instant apps")
     @Test
     public void testInteractiveEvents() throws Exception {
-        final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
-                .getContext().getSystemService(KeyguardManager.class);
-
         // We need to start out with the screen on.
-        if (!mUiDevice.isScreenOn()) {
-            pressWakeUp();
-            SystemClock.sleep(1000);
-        }
-
-        // Also want to start out with the keyguard dismissed.
-        if (kmgr.isKeyguardLocked()) {
-            final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
-            mUiDevice.executeShellCommand("wm dismiss-keyguard");
-            ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
-            assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
-            SystemClock.sleep(500);
-        }
+        mUiDevice.wakeUp();
+        dismissKeyguard(); // also want to start out with the keyguard dismissed.
 
         try {
             ArrayList<Event> events;
 
             // Determine time to start looking for events.
             final long startTime = getEvents(ALL_EVENTS, 0, null) + 1;
-            SparseArray<AggrAllEventsData> baseAggr = getAggrEventData(0);
+            SparseArray<AggrAllEventsData> baseAggr = getAggrEventData();
 
             // First test -- put device to sleep and make sure we see this event.
-            pressSleep();
+            mUiDevice.sleep();
 
             // Do we have one event, going in to non-interactive mode?
             events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1);
             assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType());
-            SparseArray<AggrAllEventsData> offAggr = getAggrEventData(startTime);
+            SparseArray<AggrAllEventsData> offAggr = getAggrEventData();
             verifyAggrInteractiveEventData(baseAggr, offAggr, true, false);
 
             // Next test -- turn screen on and make sure we have a second event.
             // XXX need to wait a bit so we don't accidentally trigger double-power
             // to launch camera.  (SHOULD FIX HOW WE WAKEUP / SLEEP TO NOT USE POWER KEY)
             SystemClock.sleep(500);
-            pressWakeUp();
+            mUiDevice.wakeUp();
             events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 2);
             assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType());
             assertEquals(Event.SCREEN_INTERACTIVE, events.get(1).getEventType());
-            SparseArray<AggrAllEventsData> onAggr = getAggrEventData(startTime);
+            SparseArray<AggrAllEventsData> onAggr = getAggrEventData();
             verifyAggrInteractiveEventData(offAggr, onAggr, false, true);
 
             // If the device is doing a lock screen, verify that we are also seeing the
@@ -1005,25 +979,25 @@
             // the screen back on (at which point it must be shown).
             // XXX CTS seems to be preventing the keyguard from showing, so this path is
             // never being tested.
-            if (kmgr.isKeyguardLocked()) {
+            if (mKeyguardManager.isKeyguardLocked()) {
                 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
                 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType());
-                SparseArray<AggrAllEventsData> shownAggr = getAggrEventData(startTime);
+                SparseArray<AggrAllEventsData> shownAggr = getAggrEventData();
                 verifyAggrKeyguardEventData(offAggr, shownAggr, true, false);
 
                 // Now dismiss the keyguard and verify the resulting events.
-                mUiDevice.executeShellCommand("wm dismiss-keyguard");
+                executeShellCmd("wm dismiss-keyguard");
                 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 2);
                 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType());
                 assertEquals(Event.KEYGUARD_HIDDEN, events.get(1).getEventType());
-                SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData(startTime);
+                SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData();
                 verifyAggrKeyguardEventData(shownAggr, hiddenAggr, false, true);
             }
 
         } finally {
             // Dismiss keyguard to get device back in its normal state.
-            pressWakeUp();
-            mUiDevice.executeShellCommand("wm dismiss-keyguard");
+            mUiDevice.wakeUp();
+            executeShellCmd("wm dismiss-keyguard");
         }
     }
 
@@ -1032,14 +1006,14 @@
         final String fakePackageName = "android.fake.package.name";
         final int defaultValue = -1;
 
-        mUiDevice.executeShellCommand("am set-standby-bucket " + fakePackageName + " rare");
+        setStandByBucket(fakePackageName, "rare");
         // Verify the above does not add a new entry to the App Standby bucket map
         Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets();
         int bucket = bucketMap.getOrDefault(fakePackageName, defaultValue);
         assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName
                 + " after set-standby-bucket", bucket > 0);
 
-        mUiDevice.executeShellCommand("am get-standby-bucket " + fakePackageName);
+        executeShellCmd("am get-standby-bucket " + fakePackageName);
         // Verify the above does not add a new entry to the App Standby bucket map
         bucketMap = mUsageStatsManager.getAppStandbyBuckets();
         bucket = bucketMap.getOrDefault(fakePackageName, defaultValue);
@@ -1109,12 +1083,11 @@
         // This test start a foreground service then stop it. The event list should have one
         // FOREGROUND_SERVICE_START and one FOREGROUND_SERVICE_STOP event.
         final long startTime = System.currentTimeMillis();
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        context.startService(new Intent(context, TestService.class));
+        mContext.startService(new Intent(mContext, TestService.class));
         mUiDevice.wait(Until.hasObject(By.clazz(TestService.class)), TIMEOUT);
         final long sleepTime = 500;
         SystemClock.sleep(sleepTime);
-        context.stopService(new Intent(context, TestService.class));
+        mContext.stopService(new Intent(mContext, TestService.class));
         mUiDevice.wait(Until.gone(By.clazz(TestService.class)), TIMEOUT);
         final long endTime = System.currentTimeMillis();
         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
@@ -1161,17 +1134,8 @@
     @AppModeFull(reason = "No usage events access in instant apps")
     @Test
     public void testTaskRootEventField() throws Exception {
-        final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
-                .getContext().getSystemService(KeyguardManager.class);
         mUiDevice.wakeUp();
-        // Also want to start out with the keyguard dismissed.
-        if (kmgr.isKeyguardLocked()) {
-            final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
-            mUiDevice.executeShellCommand("wm dismiss-keyguard");
-            ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
-            assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
-            SystemClock.sleep(500);
-        }
+        dismissKeyguard(); // also want to start out with the keyguard dismissed.
 
         final long startTime = System.currentTimeMillis();
         launchSubActivity(TaskRootActivity.class);
@@ -1195,29 +1159,18 @@
     @AppModeFull(reason = "No usage events access in instant apps")
     @Test
     public void testUsageSourceAttribution() throws Exception {
-        final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
-                .getContext().getSystemService(KeyguardManager.class);
         mUiDevice.wakeUp();
-        // Also want to start out with the keyguard dismissed.
-        if (kmgr.isKeyguardLocked()) {
-            final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
-            mUiDevice.executeShellCommand("wm dismiss-keyguard");
-            ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
-            assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
-            SystemClock.sleep(500);
-        }
-
+        dismissKeyguard(); // also want to start out with the keyguard dismissed.
         mUiDevice.pressHome();
 
-        setUsageSourceSetting(Integer.toString(mUsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY));
+        setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY));
         launchSubActivity(TaskRootActivity.class);
         // Usage should be attributed to the test app package
         assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true);
 
         mUiDevice.pressHome();
 
-        setUsageSourceSetting(Integer.toString(
-                mUsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY));
+        setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY));
         launchSubActivity(TaskRootActivity.class);
         // Usage should be attributed to this package
         assertAppOrTokenUsed(mTargetPackage, true);
@@ -1248,13 +1201,12 @@
         int stops = 0;
 
         // Only look at events belongs to mTargetPackage.
-        ArrayList<UsageEvents.Event> eventList = new ArrayList<>();
         while (events.hasNextEvent()) {
             final UsageEvents.Event event = new UsageEvents.Event();
             assertTrue(events.getNextEvent(event));
             // There should be no events with this packages name
-            assertFalse("Instant app package name found in usage event list",
-                    mTargetPackage.equals(event.getPackageName()));
+            assertNotEquals("Instant app package name found in usage event list",
+                    mTargetPackage, event.getPackageName());
 
             // Look for the obfuscated instant app string instead
             if(UsageEvents.INSTANT_APP_PACKAGE_NAME.equals(event.getPackageName())) {
@@ -1279,28 +1231,17 @@
     @AppModeFull(reason = "No usage events access in instant apps")
     @Test
     public void testSuddenDestroy() throws Exception {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
-                .getContext().getSystemService(KeyguardManager.class);
         mUiDevice.wakeUp();
-        // Also want to start out with the keyguard dismissed.
-        if (kmgr.isKeyguardLocked()) {
-            final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
-            mUiDevice.executeShellCommand("wm dismiss-keyguard");
-            ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
-            assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
-            SystemClock.sleep(500);
-        }
-
+        dismissKeyguard(); // also want to start out with the keyguard dismissed.
         mUiDevice.pressHome();
 
         final long startTime = System.currentTimeMillis();
-        final ActivityManager mAm = context.getSystemService(ActivityManager.class);
+        final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
 
         Intent intent = new Intent();
         intent.setClassName(TEST_APP_PKG, TEST_APP_CLASS);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(intent);
+        mContext.startActivity(intent);
         mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT);
         SystemClock.sleep(500);
 
@@ -1352,13 +1293,12 @@
     }
 
     private void startAndDestroyActivityWithLocus() {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final ActivityManager mAm = context.getSystemService(ActivityManager.class);
+        final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
 
         Intent intent = new Intent();
         intent.setClassName(TEST_APP_PKG, TEST_APP_CLASS_LOCUS);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(intent);
+        mContext.startActivity(intent);
         mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS_LOCUS)), TIMEOUT);
         SystemClock.sleep(500);
 
@@ -1387,22 +1327,13 @@
         }
     }
 
-    private void pressWakeUp() {
-        mUiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
-    }
-
-    private void pressSleep() {
-        mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
-    }
-
     /**
      * Assert on an app or token's usage state.
      * @param entity name of the app or token
      * @param expected expected usage state, true for in use, false for not in use
      */
     private void assertAppOrTokenUsed(String entity, boolean expected) throws IOException {
-        final String activeUsages =
-                mUiDevice.executeShellCommand("dumpsys usagestats apptimelimit actives");
+        final String activeUsages = executeShellCmd("dumpsys usagestats apptimelimit actives");
         final String[] actives = activeUsages.split("\n");
         boolean found = false;
 
@@ -1421,6 +1352,24 @@
         }
     }
 
+    private void dismissKeyguard() throws Exception {
+        if (mKeyguardManager.isKeyguardLocked()) {
+            final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
+            executeShellCmd("wm dismiss-keyguard");
+            final ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
+            assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
+            SystemClock.sleep(500);
+        }
+    }
+
+    private void setStandByBucket(String packageName, String bucket) throws IOException {
+        executeShellCmd("am set-standby-bucket " + packageName + " " + bucket);
+    }
+
+    private String executeShellCmd(String command) throws IOException {
+        return mUiDevice.executeShellCommand(command);
+    }
+
     private UsageEvents queryEventsAsShell(long start, long end) {
         return SystemUtil.runWithShellPermissionIdentity(() ->
                 mUsageStatsManager.queryEvents(start, end));
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 929eae0..2ce1d39 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -131,6 +131,7 @@
     private static final String ALPHA_TAG_B = "tagB";
     private static final String NUMBER_A = "1234567890";
     private static final String NUMBER_B = "0987654321";
+    private static final String TESTING_PLMN = "12345";
 
     private static final int DSDS_PHONE_COUNT = 2;
 
@@ -485,6 +486,7 @@
             mTelephonyManager.getVoiceMailAlphaTag();
             mTelephonyManager.getForbiddenPlmns();
             mTelephonyManager.getServiceState();
+            mTelephonyManager.getManualNetworkSelectionPlmn();
             mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
         } catch (SecurityException e) {
             failMessage();
@@ -553,6 +555,19 @@
     static final int CARRIER_PRIVILEGE_LISTENERS =
             READ_PHONE_STATE_LISTENERS | READ_PRECISE_PHONE_STATE_LISTENERS;
 
+    public void testGetManualNetworkSelectionPlmnPersisted() throws Exception {
+        if (!hasCellular) return;
+
+        try {
+            mTelephonyManager.setNetworkSelectionModeManual(
+                     TESTING_PLMN/* operatorNumeric */, true /* persistSelection */);
+            String plmn = mTelephonyManager.getManualNetworkSelectionPlmn();
+            assertEquals(TESTING_PLMN, plmn);
+        } finally {
+            mTelephonyManager.setNetworkSelectionModeAutomatic();
+        }
+    }
+
     public void testPhoneStateListener() throws Exception {
         if (!hasCellular) return;
         PhoneStateListener psl = new PhoneStateListener((Runnable r) -> { });
@@ -563,6 +578,29 @@
         }
     }
 
+    public void testIsManualNetworkSelectionAllowed() throws Exception {
+        if (!hasCellular) return;
+
+        try {
+            assertTrue(mTelephonyManager.isManualNetworkSelectionAllowed());
+        } catch (SecurityException e) {
+            failMessage();
+        }
+    }
+
+    public void testGetNetworkSelectionMode() throws Exception {
+        if (!hasCellular) return;
+
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    (tm) -> tm.setNetworkSelectionModeAutomatic());
+            int networkMode = mTelephonyManager.getNetworkSelectionMode();
+            assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode);
+        } catch (SecurityException e) {
+            failMessage();
+        }
+    }
+
     public void testSubscriptionInfoChangeListener() throws Exception {
         if (!hasCellular) return;
         final AtomicReference<SecurityException> error = new AtomicReference<>();
diff --git a/tests/tests/content/OWNERS b/tests/tests/content/OWNERS
new file mode 100644
index 0000000..12c955a
--- /dev/null
+++ b/tests/tests/content/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+toddke@google.com
+patb@google.com
+schfan@google.com
+alexbuy@google.com
diff --git a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
index f1b5184..9e45d4a 100644
--- a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
+++ b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
@@ -18,6 +18,7 @@
           package="com.android.tests.securefrpinstall" >
 
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application>
         <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
diff --git a/tests/tests/content/res/xml/intentfilter.xml b/tests/tests/content/res/xml/intentfilter.xml
index 7ed3a53..9041513 100644
--- a/tests/tests/content/res/xml/intentfilter.xml
+++ b/tests/tests/content/res/xml/intentfilter.xml
@@ -20,6 +20,8 @@
     <action name="testAction"/>
     <cat name="testCategory" />
     <type name="vnd.android.cursor.dir/person"/>
+    <staticType name="text/plain"/>
+    <group name="testMimeGroup"/>
     <scheme name="testScheme"/>
     <auth host="testHost" port="80"/>
     <path name="testPath" literal="test"/>
diff --git a/tests/tests/content/src/android/content/cts/IntentFilterTest.java b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
index 80f47f9..6248044 100644
--- a/tests/tests/content/src/android/content/cts/IntentFilterTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
@@ -20,18 +20,6 @@
 import static android.os.PatternMatcher.PATTERN_PREFIX;
 import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -52,14 +40,28 @@
 
 import com.android.internal.util.FastXmlSerializer;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
 
 public class IntentFilterTest extends AndroidTestCase {
 
     private IntentFilter mIntentFilter;
     private static final String ACTION = "testAction";
     private static final String CATEGORY = "testCategory";
-    private static final String DATA_TYPE = "vnd.android.cursor.dir/person";
+    private static final String DATA_DYNAMIC_TYPE = "type/dynamic";
+    private static final String DATA_STATIC_TYPE = "vnd.android.cursor.dir/person";
     private static final String DATA_SCHEME = "testDataSchemes.";
+    private static final String MIME_GROUP = "mime_group";
     private static final String SSP = "testSsp";
     private static final String HOST = "testHost";
     private static final int PORT = 80;
@@ -80,11 +82,11 @@
         filter = new IntentFilter(ACTION);
         verifyContent(filter, ACTION, null);
 
-        final IntentFilter actionTypeFilter = new IntentFilter(ACTION, DATA_TYPE);
-        verifyContent(actionTypeFilter, ACTION, DATA_TYPE);
+        final IntentFilter actionTypeFilter = new IntentFilter(ACTION, DATA_STATIC_TYPE);
+        verifyContent(actionTypeFilter, ACTION, DATA_STATIC_TYPE);
 
         filter = new IntentFilter(actionTypeFilter);
-        verifyContent(filter, ACTION, DATA_TYPE);
+        verifyContent(filter, ACTION, DATA_STATIC_TYPE);
 
         final String dataType = "testdataType";
         try {
@@ -110,8 +112,10 @@
         if (dataType != null) {
             assertEquals(1, filter.countDataTypes());
             assertEquals(dataType, filter.getDataType(0));
+            assertEquals(1, filter.countStaticDataTypes());
         } else {
             assertEquals(0, filter.countDataTypes());
+            assertEquals(0, filter.countStaticDataTypes());
         }
     }
 
@@ -215,6 +219,125 @@
                         null), });
     }
 
+    public void testDynamicMimeTypes() {
+        IntentFilter filter = new Match()
+                .addDynamicMimeTypes(new String[] { "which1/what1" });
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+        });
+
+        filter = new Match()
+                .addDynamicMimeTypes(new String[] { "which1/what1", "which2/what2" });
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+        });
+
+        filter = new Match()
+                .addDynamicMimeTypes(new String[] { "which1/*" });
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what2",
+                        null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+        });
+
+        filter = new Match()
+                .addDynamicMimeTypes(new String[] { "*/*" });
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what2",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which3/what3",
+                        null), });
+    }
+
+    public void testClearDynamicMimeTypesWithStaticType() {
+        IntentFilter filter = new Match()
+                .addMimeTypes(new String[] {"which1/what1"})
+                .addDynamicMimeTypes(new String[] { "which2/what2" });
+
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+        });
+
+        filter.clearDynamicDataTypes();
+
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+        });
+    }
+
+    public void testClearDynamicMimeTypesWithAction() {
+        IntentFilter filter = new Match()
+                .addActions(new String[] {"action1"})
+                .addDynamicMimeTypes(new String[] { "which1/what1" });
+
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+                        null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+        });
+
+        filter.clearDynamicDataTypes();
+
+        checkMatches(filter, new MatchCondition[] {
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null, null, null, null),
+                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, "action1", null, null, null),
+                new MatchCondition(IntentFilter.NO_MATCH_TYPE, "action1", null, "which2/what2",
+                        null),
+        });
+    }
+
     public void testAccessPriority() {
         final int expected = 1;
         mIntentFilter.setPriority(expected);
@@ -255,9 +378,9 @@
     }
 
     public void testCreate() {
-        IntentFilter filter = IntentFilter.create(ACTION, DATA_TYPE);
+        IntentFilter filter = IntentFilter.create(ACTION, DATA_STATIC_TYPE);
         assertNotNull(filter);
-        verifyContent(filter, ACTION, DATA_TYPE);
+        verifyContent(filter, ACTION, DATA_STATIC_TYPE);
     }
 
 
@@ -492,18 +615,83 @@
 
     public void testDataTypes() throws MalformedMimeTypeException {
         for (int i = 0; i < 10; i++) {
-            mIntentFilter.addDataType(DATA_TYPE + i);
+            mIntentFilter.addDataType(DATA_STATIC_TYPE + i);
         }
         assertEquals(10, mIntentFilter.countDataTypes());
+        assertEquals(10, mIntentFilter.countStaticDataTypes());
         final Iterator<String> iter = mIntentFilter.typesIterator();
+        String actual;
+        int i = 0;
+        while (iter.hasNext()) {
+            actual = iter.next();
+            assertEquals(DATA_STATIC_TYPE + i, actual);
+            assertEquals(DATA_STATIC_TYPE + i, mIntentFilter.getDataType(i));
+            assertTrue(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i));
+            assertFalse(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i + 10));
+            i++;
+        }
+    }
+
+    public void testDynamicDataTypes() throws MalformedMimeTypeException {
+        for (int i = 0; i < 10; i++) {
+            mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE + i);
+        }
+        assertEquals(10, mIntentFilter.countDataTypes());
+        assertEquals(0, mIntentFilter.countStaticDataTypes());
+
+        final Iterator<String> iter = mIntentFilter.typesIterator();
+        String actual;
+        int i = 0;
+        while (iter.hasNext()) {
+            actual = iter.next();
+            assertEquals(DATA_DYNAMIC_TYPE + i, actual);
+            assertEquals(DATA_DYNAMIC_TYPE + i, mIntentFilter.getDataType(i));
+            assertTrue(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i));
+            assertFalse(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i + 10));
+            i++;
+        }
+    }
+
+    public void testClearDynamicDataTypes() throws MalformedMimeTypeException {
+        for (int i = 0; i < 10; i++) {
+            mIntentFilter.addDataType(DATA_STATIC_TYPE + i);
+            mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE + i);
+        }
+        assertEquals(20, mIntentFilter.countDataTypes());
+        assertEquals(10, mIntentFilter.countStaticDataTypes());
+
+        mIntentFilter.clearDynamicDataTypes();
+
+        assertEquals(10, mIntentFilter.countDataTypes());
+        assertEquals(10, mIntentFilter.countStaticDataTypes());
+
+        final Iterator<String> iter = mIntentFilter.typesIterator();
+        String actual;
+        int i = 0;
+        while (iter.hasNext()) {
+            actual = iter.next();
+            assertEquals(DATA_STATIC_TYPE + i, actual);
+            assertEquals(DATA_STATIC_TYPE + i, mIntentFilter.getDataType(i));
+            assertTrue(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i));
+            assertFalse(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i));
+            i++;
+        }
+    }
+
+    public void testMimeGroups() {
+        for (int i = 0; i < 10; i++) {
+            mIntentFilter.addMimeGroup(MIME_GROUP + i);
+        }
+        assertEquals(10, mIntentFilter.countMimeGroups());
+        final Iterator<String> iter = mIntentFilter.mimeGroupsIterator();
         String actual = null;
         int i = 0;
         while (iter.hasNext()) {
             actual = iter.next();
-            assertEquals(DATA_TYPE + i, actual);
-            assertEquals(DATA_TYPE + i, mIntentFilter.getDataType(i));
-            assertTrue(mIntentFilter.hasDataType(DATA_TYPE + i));
-            assertFalse(mIntentFilter.hasDataType(DATA_TYPE + i + 10));
+            assertEquals(MIME_GROUP + i, actual);
+            assertEquals(MIME_GROUP + i, mIntentFilter.getMimeGroup(i));
+            assertTrue(mIntentFilter.hasMimeGroup(MIME_GROUP + i));
+            assertFalse(mIntentFilter.hasMimeGroup(MIME_GROUP + i + 10));
             i++;
         }
     }
@@ -514,22 +702,23 @@
         assertEquals(expected, mIntentFilter.matchData(null, DATA_SCHEME, null));
 
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME,
-                URI));
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE,
+                DATA_SCHEME, URI));
 
         mIntentFilter.addDataScheme(DATA_SCHEME);
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE,
                 "mDataSchemestest", URI));
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE, "", URI));
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE, "",
+                URI));
 
         expected = IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL;
         assertEquals(expected, mIntentFilter.matchData(null, DATA_SCHEME, URI));
-        assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME,
-                URI));
+        assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.matchData(DATA_STATIC_TYPE,
+                DATA_SCHEME, URI));
 
-        mIntentFilter.addDataType(DATA_TYPE);
+        mIntentFilter.addDataType(DATA_STATIC_TYPE);
         assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
-                mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME, URI));
+                mIntentFilter.matchData(DATA_STATIC_TYPE, DATA_SCHEME, URI));
 
         mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
@@ -584,7 +773,9 @@
         mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
         mIntentFilter.addDataPath(DATA_PATH, 1);
         mIntentFilter.addDataScheme(DATA_SCHEME);
-        mIntentFilter.addDataType(DATA_TYPE);
+        mIntentFilter.addDataType(DATA_STATIC_TYPE);
+        mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE);
+        mIntentFilter.addMimeGroup(MIME_GROUP);
         mIntentFilter.writeToXml(xml);
         xml.flush();
         final XmlPullParser parser = Xml.newPullParser();
@@ -594,7 +785,9 @@
         intentFilter.readFromXml(parser);
         assertEquals(ACTION, intentFilter.getAction(0));
         assertEquals(CATEGORY, intentFilter.getCategory(0));
-        assertEquals(DATA_TYPE, intentFilter.getDataType(0));
+        assertTrue(intentFilter.hasExactStaticDataType(DATA_STATIC_TYPE));
+        assertTrue(intentFilter.hasExactDynamicDataType(DATA_DYNAMIC_TYPE));
+        assertEquals(MIME_GROUP, intentFilter.getMimeGroup(0));
         assertEquals(DATA_SCHEME, intentFilter.getDataScheme(0));
         assertEquals(DATA_PATH, intentFilter.getDataPath(0).getPath());
         assertEquals(HOST, intentFilter.getDataAuthority(0).getHost());
@@ -655,7 +848,11 @@
 
         assertEquals("testAction", mIntentFilter.getAction(0));
         assertEquals("testCategory", mIntentFilter.getCategory(0));
-        assertEquals("vnd.android.cursor.dir/person", mIntentFilter.getDataType(0));
+
+        assertTrue(mIntentFilter.hasExactDynamicDataType("vnd.android.cursor.dir/person"));
+        assertTrue(mIntentFilter.hasExactStaticDataType("text/plain"));
+
+        assertEquals("testMimeGroup", mIntentFilter.getMimeGroup(0));
         assertEquals("testScheme", mIntentFilter.getDataScheme(0));
         assertEquals("testHost", mIntentFilter.getDataAuthority(0).getHost());
         assertEquals(80, mIntentFilter.getDataAuthority(0).getPort());
@@ -781,10 +978,10 @@
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
         mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
-        intent.setType(DATA_TYPE);
+        intent.setType(DATA_STATIC_TYPE);
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
 
-        mIntentFilter.addDataType(DATA_TYPE);
+        mIntentFilter.addDataType(DATA_STATIC_TYPE);
 
         assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
                 mIntentFilter.match(resolver, intent, true, null));
@@ -797,7 +994,7 @@
         assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
                 mIntentFilter.match(resolver, intent, true, null));
 
-        intent.setDataAndType(uri, DATA_TYPE);
+        intent.setDataAndType(uri, DATA_STATIC_TYPE);
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
 
     }
@@ -815,18 +1012,18 @@
 
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
 
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, null, null));
 
         mIntentFilter.addDataScheme(DATA_SCHEME);
-        assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, null, null));
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE, "", URI,
-                null, null));
-        mIntentFilter.addDataType(DATA_TYPE);
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE, "",
+                URI, null, null));
+        mIntentFilter.addDataType(DATA_STATIC_TYPE);
 
         assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
-                mIntentFilter.match(ACTION, DATA_TYPE, DATA_SCHEME, URI, null, null));
+                mIntentFilter.match(ACTION, DATA_STATIC_TYPE, DATA_SCHEME, URI, null, null));
 
         assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, null, DATA_SCHEME,
                 URI, null, null));
@@ -835,29 +1032,29 @@
                 URI, cat, null));
 
         cat.add(CATEGORY);
-        assertEquals(IntentFilter.NO_MATCH_CATEGORY, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_CATEGORY, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, cat, null));
         cat = new HashSet<String>();
         mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
         assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, null, DATA_SCHEME,
                 URI, null, null));
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, null, null));
 
         final Uri uri = Uri.parse(DATA_SCHEME + "://" + HOST + ":" + PORT);
         mIntentFilter.addDataPath(DATA_PATH, PatternMatcher.PATTERN_LITERAL);
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, uri, null, null));
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, null, null));
 
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, cat, null));
         cat.add(CATEGORY);
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, cat, null));
         mIntentFilter.addCategory(CATEGORY);
-        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+        assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
                 DATA_SCHEME, URI, cat, null));
     }
 
@@ -867,7 +1064,9 @@
         mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
         mIntentFilter.addDataPath(DATA_PATH, 1);
         mIntentFilter.addDataScheme(DATA_SCHEME);
-        mIntentFilter.addDataType(DATA_TYPE);
+        mIntentFilter.addDataType(DATA_STATIC_TYPE);
+        mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE);
+        mIntentFilter.addMimeGroup(MIME_GROUP);
         Parcel parcel = Parcel.obtain();
         mIntentFilter.writeToParcel(parcel, 1);
         parcel.setDataPosition(0);
@@ -880,6 +1079,11 @@
                 target.getDataAuthority(0).getPort());
         assertEquals(mIntentFilter.getDataPath(0).getPath(), target.getDataPath(0).getPath());
         assertEquals(mIntentFilter.getDataScheme(0), target.getDataScheme(0));
+        assertEquals(mIntentFilter.getDataType(0), target.getDataType(0));
+        assertEquals(mIntentFilter.getDataType(1), target.getDataType(1));
+        assertEquals(mIntentFilter.countStaticDataTypes(), target.countStaticDataTypes());
+        assertEquals(mIntentFilter.countDataTypes(), target.countDataTypes());
+        assertEquals(mIntentFilter.getMimeGroup(0), target.getMimeGroup(0));
     }
 
     public void testAddDataType() throws MalformedMimeTypeException {
@@ -890,17 +1094,18 @@
             // expected
         }
 
-        mIntentFilter.addDataType(DATA_TYPE);
-        assertEquals(DATA_TYPE, mIntentFilter.getDataType(0));
+        mIntentFilter.addDataType(DATA_STATIC_TYPE);
+        assertEquals(DATA_STATIC_TYPE, mIntentFilter.getDataType(0));
     }
 
     private static class Match extends IntentFilter {
+        Match() {
+        }
+
         Match(String[] actions, String[] categories, String[] mimeTypes, String[] schemes,
                 String[] authorities, String[] ports) {
             if (actions != null) {
-                for (int i = 0; i < actions.length; i++) {
-                    addAction(actions[i]);
-                }
+                addActions(actions);
             }
             if (categories != null) {
                 for (int i = 0; i < categories.length; i++) {
@@ -948,6 +1153,35 @@
                 }
             }
         }
+
+        Match addDynamicMimeTypes(String[] dynamicMimeTypes) {
+            for (int i = 0; i < dynamicMimeTypes.length; i++) {
+                try {
+                    addDynamicDataType(dynamicMimeTypes[i]);
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    throw new RuntimeException("Bad mime type", e);
+                }
+            }
+            return this;
+        }
+
+        Match addMimeTypes(String[] mimeTypes) {
+            for (int i = 0; i < mimeTypes.length; i++) {
+                try {
+                    addDataType(mimeTypes[i]);
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    throw new RuntimeException("Bad mime type", e);
+                }
+            }
+            return this;
+        }
+
+        Match addActions(String[] actions) {
+            for (int i = 0; i < actions.length; i++) {
+                addAction(actions[i]);
+            }
+            return this;
+        }
     }
 
     private static class MatchCondition {
@@ -1179,7 +1413,7 @@
     public void testDump() throws MalformedMimeTypeException {
         TestPrinter printer = new TestPrinter();
         String prefix = "TestIntentFilter";
-        IntentFilter filter = new IntentFilter(ACTION, DATA_TYPE);
+        IntentFilter filter = new IntentFilter(ACTION, DATA_STATIC_TYPE);
         filter.dump(printer, prefix);
         assertTrue(printer.isPrintlnCalled);
     }
diff --git a/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java b/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
index d2e1998..3eed1b2 100644
--- a/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
+++ b/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
@@ -108,6 +108,6 @@
         assertEquals(info.state, copyInfo.state);
         assertEquals(info.userId, copyInfo.userId);
         assertEquals(info.priority, copyInfo.priority);
-        assertEquals(info.isStatic, copyInfo.isStatic);
+        assertEquals(info.isMutable, copyInfo.isMutable);
     }
 }
\ No newline at end of file
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index a78069c..36ba232 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -74,8 +74,7 @@
 
     @Parameters
     public static Iterable<Object> initParameters() {
-        return Arrays.asList(DATA_LOADER_TYPE_NONE, DATA_LOADER_TYPE_STREAMING,
-                DATA_LOADER_TYPE_INCREMENTAL);
+        return Arrays.asList(DATA_LOADER_TYPE_NONE);
     }
 
     private boolean mStreaming = false;
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index d6c1cc4..48dddbd 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -16,6 +16,8 @@
 
 package android.display.cts;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.*;
 
 import android.app.Activity;
@@ -88,6 +90,7 @@
     private UiModeManager mUiModeManager;
     private Context mContext;
     private ColorSpace[] mSupportedWideGamuts;
+    private Display mDefaultDisplay;
 
     // To test display mode switches.
     private TestPresentation mPresentation;
@@ -106,7 +109,8 @@
         mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
         mUiModeManager = (UiModeManager)mContext.getSystemService(Context.UI_MODE_SERVICE);
-        mSupportedWideGamuts = mContext.getDisplay().getSupportedWideColorGamut();
+        mDefaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        mSupportedWideGamuts = mDefaultDisplay.getSupportedWideColorGamut();
     }
 
     @After
@@ -171,7 +175,7 @@
         boolean hasDefaultDisplay = false;
         boolean hasSecondaryDisplay = false;
         for (Display display : displays) {
-            if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            if (display.getDisplayId() == DEFAULT_DISPLAY) {
                 hasDefaultDisplay = true;
             }
             if (isSecondaryDisplay(display)) {
@@ -188,7 +192,7 @@
     @Presubmit
     @Test
     public void testDefaultDisplay() {
-        assertEquals(Display.DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId());
+        assertEquals(DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId());
     }
 
     /**
@@ -196,7 +200,7 @@
      */
     @Test
     public void testDefaultDisplayHdrCapability() {
-        Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
         HdrCapabilities cap = display.getHdrCapabilities();
         int[] hdrTypes = cap.getSupportedHdrTypes();
         for (int type : hdrTypes) {
@@ -219,7 +223,7 @@
     public void testSecondaryDisplay() {
         Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
         assertNotNull(display);
-        assertTrue(Display.DEFAULT_DISPLAY != display.getDisplayId());
+        assertTrue(DEFAULT_DISPLAY != display.getDisplayId());
     }
 
     /**
@@ -384,10 +388,9 @@
      */
     @Test
     public void testGetPreferredWideGamutColorSpace() {
-        final Display defaultDisplay = mWindowManager.getDefaultDisplay();
-        final ColorSpace colorSpace = defaultDisplay.getPreferredWideGamutColorSpace();
+        final ColorSpace colorSpace = mDefaultDisplay.getPreferredWideGamutColorSpace();
 
-        if (defaultDisplay.isWideColorGamut()) {
+        if (mDefaultDisplay.isWideColorGamut()) {
             assertFalse(colorSpace.isSrgb());
             assertTrue(colorSpace.isWideGamut());
         } else {
@@ -430,7 +433,7 @@
         final ColorSpace displayP3 = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
         final ColorSpace dciP3 = ColorSpace.get(ColorSpace.Named.DCI_P3);
         final List<ColorSpace> list = Arrays.asList(mSupportedWideGamuts);
-        final boolean supportsWideGamut = mContext.getDisplay().isWideColorGamut()
+        final boolean supportsWideGamut = mDefaultDisplay.isWideColorGamut()
                 && mSupportedWideGamuts.length > 0;
         final boolean supportsP3 = list.contains(displayP3) || list.contains(dciP3);
         assertEquals(supportsWideGamut, supportsP3);
diff --git a/tests/tests/dynamic_linker/Android.bp b/tests/tests/dynamic_linker/Android.bp
index 76b6603..ee88e21 100644
--- a/tests/tests/dynamic_linker/Android.bp
+++ b/tests/tests/dynamic_linker/Android.bp
@@ -14,6 +14,7 @@
 
 cc_test_library {
     name: "libdynamiclinker_native_lib_a",
+    sdk_version: "current",
     srcs: ["native_lib_a.cpp"],
     cflags: [
         "-Wall",
@@ -27,6 +28,7 @@
 
 cc_test_library {
     name: "libdynamiclinker_native_lib_b",
+    sdk_version: "current",
     srcs: ["native_lib_b.cpp"],
     cflags: [
         "-Wall",
diff --git a/tests/tests/graphics/AndroidManifest.xml b/tests/tests/graphics/AndroidManifest.xml
index 98c392a..2e53417 100644
--- a/tests/tests/graphics/AndroidManifest.xml
+++ b/tests/tests/graphics/AndroidManifest.xml
@@ -53,6 +53,10 @@
         <activity android:name="android.graphics.drawable.cts.DrawableStubActivity"
                   android:theme="@style/WhiteBackgroundNoWindowAnimation"
           android:screenOrientation="locked"/>
+        <activity android:name="android.graphics.drawable.cts.AnimatedImageActivity"
+                  android:theme="@style/WhiteBackgroundNoWindowAnimation"
+            android:screenOrientation="locked">
+        </activity>
         <provider
             android:name=".EmptyProvider"
             android:exported="true"
diff --git a/tests/tests/graphics/jni/Android.bp b/tests/tests/graphics/jni/Android.bp
index 87105a2..7327949 100644
--- a/tests/tests/graphics/jni/Android.bp
+++ b/tests/tests/graphics/jni/Android.bp
@@ -38,6 +38,7 @@
         "VulkanPreTransformTestHelpers.cpp",
         "VulkanTestHelpers.cpp",
     ],
+    include_dirs: ["system/core/include"],
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
index 1361e3d..2b4bef6 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
@@ -17,60 +17,232 @@
 
 #define LOG_TAG "FrameRateCtsActivity"
 
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_jni.h>
+#include <android/log.h>
 #include <android/native_window.h>
 #include <android/native_window_jni.h>
+#include <android/rect.h>
 #include <android/surface_control.h>
 #include <jni.h>
+#include <utils/Errors.h>
 
 #include <array>
+#include <string>
+
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+using namespace android;
 
 namespace {
 
-jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate) {
+class Buffer {
+public:
+    Buffer(int width, int height, int rgbaColor) {
+        AHardwareBuffer_Desc desc;
+        memset(&desc, 0, sizeof(desc));
+        desc.width = width;
+        desc.height = height;
+        desc.layers = 1;
+        desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+        desc.usage =
+                AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+        int rc = AHardwareBuffer_allocate(&desc, &mBuffer);
+        if (rc < 0 || mBuffer == nullptr) {
+            ALOGE("AHardwareBuffer_allocate failed: %s (%d)", strerror(-rc), -rc);
+            return;
+        }
+        int8_t* buf = nullptr;
+        int32_t bytesPerPixel = 0;
+        int32_t bytesPerStride = 0;
+        std::string lockFunctionName = "AHardwareBuffer_lockAndGetInfo";
+        rc = AHardwareBuffer_lockAndGetInfo(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
+                                            /*fence=*/-1,
+                                            /*rect=*/nullptr, reinterpret_cast<void**>(&buf),
+                                            &bytesPerPixel, &bytesPerStride);
+        if (rc == INVALID_OPERATION) {
+            // Older versions of gralloc don't implement AHardwareBuffer_lockAndGetInfo(). Fall back
+            // to AHardwareBuffer_lock().
+            lockFunctionName = "AHardwareBuffer_lock";
+            bytesPerPixel = 4;
+            bytesPerStride = width * bytesPerPixel;
+            rc = AHardwareBuffer_lock(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
+                                      /*fence=*/-1,
+                                      /*rect=*/nullptr, reinterpret_cast<void**>(&buf));
+        }
+        if (rc < 0 || buf == nullptr) {
+            ALOGE("%s failed: %s (%d)", lockFunctionName.c_str(), strerror(-rc), -rc);
+            AHardwareBuffer_release(mBuffer);
+            mBuffer = nullptr;
+            return;
+        }
+
+        // There's a bug where Qualcomm returns pixels per stride instead of bytes per stride. See
+        // b/149601846.
+        if (bytesPerStride < width * bytesPerPixel) {
+            bytesPerStride *= bytesPerPixel;
+        }
+
+        int8_t* rgbaBytes = reinterpret_cast<int8_t*>(&rgbaColor);
+        for (int row = 0; row < height; row++) {
+            int8_t* ptr = buf + row * bytesPerStride;
+            for (int col = 0; col < width; col++, ptr += bytesPerPixel) {
+                ptr[0] = rgbaBytes[0];
+                ptr[1] = rgbaBytes[1];
+                ptr[2] = rgbaBytes[2];
+                ptr[3] = rgbaBytes[3];
+            }
+        }
+
+        rc = AHardwareBuffer_unlock(mBuffer, /*fence=*/nullptr);
+        if (rc < 0) {
+            ALOGE("AHardwareBuffer_unlock failed: %s (%d)", strerror(-rc), -rc);
+            AHardwareBuffer_release(mBuffer);
+            mBuffer = nullptr;
+            return;
+        }
+    }
+
+    ~Buffer() {
+        if (mBuffer) {
+            AHardwareBuffer_release(mBuffer);
+        }
+    }
+
+    bool isValid() const { return mBuffer != nullptr; }
+    AHardwareBuffer* getBuffer() const { return mBuffer; }
+
+private:
+    AHardwareBuffer* mBuffer = nullptr;
+};
+
+class Surface {
+public:
+    Surface(ANativeWindow* parentWindow, const std::string& name, int left, int top, int right,
+            int bottom) {
+        mSurface = ASurfaceControl_createFromWindow(parentWindow, name.c_str());
+        if (mSurface == nullptr) {
+            return;
+        }
+
+        mWidth = right - left;
+        mHeight = bottom - top;
+        ARect source{0, 0, mWidth, mHeight};
+        ARect dest{left, top, right, bottom};
+        ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+        ASurfaceTransaction_setGeometry(transaction, mSurface, source, dest,
+                                        ANATIVEWINDOW_TRANSFORM_IDENTITY);
+        ASurfaceTransaction_apply(transaction);
+        ASurfaceTransaction_delete(transaction);
+    }
+
+    ~Surface() {
+        ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+        ASurfaceTransaction_reparent(transaction, mSurface, nullptr);
+        ASurfaceTransaction_apply(transaction);
+        ASurfaceTransaction_delete(transaction);
+        ASurfaceControl_release(mSurface);
+    }
+
+    bool isValid() const { return mSurface != nullptr; }
+    ASurfaceControl* getSurfaceControl() const { return mSurface; }
+    int getWidth() const { return mWidth; }
+    int getHeight() const { return mHeight; }
+
+private:
+    ASurfaceControl* mSurface = nullptr;
+    int mWidth = 0;
+    int mHeight = 0;
+};
+
+jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate,
+                              jint compatibility) {
     ANativeWindow* window = nullptr;
     if (jSurface) {
         window = ANativeWindow_fromSurface(env, jSurface);
     }
-    return ANativeWindow_setFrameRate(window, frameRate);
+    return ANativeWindow_setFrameRate(window, frameRate, compatibility);
 }
 
-jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface) {
-    ANativeWindow* window = nullptr;
-    if (jParentSurface) {
-        window = ANativeWindow_fromSurface(env, jParentSurface);
-    }
-    if (!window) {
+jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface, jstring jName, jint left,
+                           jint top, jint right, jint bottom) {
+    if (!jParentSurface || !jName) {
         return 0;
     }
-    return reinterpret_cast<jlong>(
-            ASurfaceControl_createFromWindow(window, "SetFrameRateTestSurface"));
+    ANativeWindow* parentWindow = ANativeWindow_fromSurface(env, jParentSurface);
+    if (!parentWindow) {
+        return 0;
+    }
+
+    const char* name = env->GetStringUTFChars(jName, nullptr);
+    std::string strName = name;
+    env->ReleaseStringUTFChars(jName, name);
+
+    Surface* surface = new Surface(parentWindow, strName, left, top, right, bottom);
+    if (!surface->isValid()) {
+        delete surface;
+        return 0;
+    }
+
+    return reinterpret_cast<jlong>(surface);
 }
 
 void surfaceControlDestroy(JNIEnv*, jclass, jlong surfaceControlLong) {
     if (surfaceControlLong == 0) {
         return;
     }
-    ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlLong);
-    ASurfaceTransaction* transaction = ASurfaceTransaction_create();
-    ASurfaceTransaction_reparent(transaction, surfaceControl, nullptr);
-    ASurfaceTransaction_apply(transaction);
-    ASurfaceTransaction_delete(transaction);
-    ASurfaceControl_release(surfaceControl);
+    delete reinterpret_cast<Surface*>(surfaceControlLong);
 }
 
-void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate) {
-    ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlLong);
+void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate,
+                                int compatibility) {
+    ASurfaceControl* surfaceControl =
+            reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
     ASurfaceTransaction* transaction = ASurfaceTransaction_create();
-    ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate);
+    ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate, compatibility);
     ASurfaceTransaction_apply(transaction);
     ASurfaceTransaction_delete(transaction);
 }
 
-const std::array<JNINativeMethod, 4> JNI_METHODS = {{
-        {"nativeWindowSetFrameRate", "(Landroid/view/Surface;F)I", (void*)nativeWindowSetFrameRate},
-        {"nativeSurfaceControlCreate", "(Landroid/view/Surface;)J", (void*)surfaceControlCreate},
+void surfaceControlSetVisibility(JNIEnv*, jclass, jlong surfaceControlLong, jboolean visible) {
+    ASurfaceControl* surfaceControl =
+            reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
+    ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+    ASurfaceTransaction_setVisibility(transaction, surfaceControl,
+                                      visible == JNI_TRUE ? ASURFACE_TRANSACTION_VISIBILITY_SHOW
+                                                          : ASURFACE_TRANSACTION_VISIBILITY_HIDE);
+    ASurfaceTransaction_apply(transaction);
+    ASurfaceTransaction_delete(transaction);
+}
+
+jboolean surfaceControlPostBuffer(JNIEnv*, jclass, jlong surfaceControlLong, jint argbColor) {
+    Surface* surface = reinterpret_cast<Surface*>(surfaceControlLong);
+    ASurfaceControl* surfaceControl = surface->getSurfaceControl();
+    // Android's Color.* values are represented as ARGB. Convert to RGBA.
+    int rgbaColor = ((argbColor >> 16) & 0xff) | ((argbColor >> 8) & 0xff) |
+            ((argbColor >> 0) & 0xff) | ((argbColor >> 24) & 0xff);
+
+    Buffer buffer(surface->getWidth(), surface->getHeight(), rgbaColor);
+    if (!buffer.isValid()) {
+        return JNI_FALSE;
+    }
+
+    ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+    ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer.getBuffer());
+    ASurfaceTransaction_apply(transaction);
+    ASurfaceTransaction_delete(transaction);
+    return JNI_TRUE;
+}
+
+const std::array<JNINativeMethod, 6> JNI_METHODS = {{
+        {"nativeWindowSetFrameRate", "(Landroid/view/Surface;FI)I",
+         (void*)nativeWindowSetFrameRate},
+        {"nativeSurfaceControlCreate", "(Landroid/view/Surface;Ljava/lang/String;IIII)J",
+         (void*)surfaceControlCreate},
         {"nativeSurfaceControlDestroy", "(J)V", (void*)surfaceControlDestroy},
-        {"nativeSurfaceControlSetFrameRate", "(JF)V", (void*)surfaceControlSetFrameRate},
+        {"nativeSurfaceControlSetFrameRate", "(JFI)V", (void*)surfaceControlSetFrameRate},
+        {"nativeSurfaceControlSetVisibility", "(JZ)V", (void*)surfaceControlSetVisibility},
+        {"nativeSurfaceControlPostBuffer", "(JI)Z", (void*)surfaceControlPostBuffer},
 }};
 
 } // namespace
diff --git a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
index 37a91a4..2cef070 100644
--- a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
+++ b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
@@ -16,11 +16,14 @@
 
 package android.graphics.cts;
 
+import static android.system.OsConstants.EINVAL;
+
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.Handler;
@@ -48,6 +51,7 @@
     private static final long WAIT_FOR_SURFACE_TIMEOUT_SECONDS = 3;
     private static final long FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS = 1;
     private static final long STABLE_FRAME_RATE_WAIT_SECONDS = 1;
+    private static final long POST_BUFFER_INTERVAL_MILLIS = 500;
 
     private DisplayManager mDisplayManager;
     private SurfaceView mSurfaceView;
@@ -104,6 +108,179 @@
         public void onDisplayRemoved(int displayId) {}
     };
 
+    public enum Api {
+        SURFACE("Surface"),
+        ANATIVE_WINDOW("ANativeWindow"),
+        SURFACE_CONTROL("SurfaceControl"),
+        NATIVE_SURFACE_CONTROL("ASurfaceControl");
+
+        private final String mName;
+        Api(String name) {
+            mName = name;
+        }
+
+        public String toString() {
+            return mName;
+        }
+    }
+
+    private static class TestSurface {
+        private Api mApi;
+        private String mName;
+        private SurfaceControl mSurfaceControl;
+        private Surface mSurface;
+        private long mNativeSurfaceControl;
+        private int mColor;
+        private boolean mLastBufferPostTimeValid;
+        private long mLastBufferPostTime;
+
+        TestSurface(Api api, SurfaceControl parentSurfaceControl, Surface parentSurface,
+                String name, Rect destFrame, boolean visible, int color) {
+            mApi = api;
+            mName = name;
+            mColor = color;
+
+            if (mApi == Api.SURFACE || mApi == Api.ANATIVE_WINDOW || mApi == Api.SURFACE_CONTROL) {
+                assertTrue("No parent surface", parentSurfaceControl != null);
+                mSurfaceControl = new SurfaceControl.Builder()
+                                          .setParent(parentSurfaceControl)
+                                          .setName(mName)
+                                          .setBufferSize(destFrame.right - destFrame.left,
+                                                  destFrame.bottom - destFrame.top)
+                                          .build();
+                SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+                try {
+                    transaction.setGeometry(mSurfaceControl, null, destFrame, Surface.ROTATION_0)
+                            .apply();
+                } finally {
+                    transaction.close();
+                }
+                mSurface = new Surface(mSurfaceControl);
+            } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+                assertTrue("No parent surface", parentSurface != null);
+                mNativeSurfaceControl = nativeSurfaceControlCreate(parentSurface, mName,
+                        destFrame.left, destFrame.top, destFrame.right, destFrame.bottom);
+                assertTrue("Failed to create a native SurfaceControl", mNativeSurfaceControl != 0);
+            }
+
+            setVisibility(visible);
+            postBuffer();
+        }
+
+        public int setFrameRate(float frameRate, int compatibility) {
+            Log.i(TAG,
+                    String.format("Setting frame rate for %s: fps=%.0f compatibility=%s", mName,
+                            frameRate, frameRateCompatibilityToString(compatibility)));
+
+            int rc = 0;
+            if (mApi == Api.SURFACE) {
+                mSurface.setFrameRate(frameRate, compatibility);
+            } else if (mApi == Api.ANATIVE_WINDOW) {
+                rc = nativeWindowSetFrameRate(mSurface, frameRate, compatibility);
+            } else if (mApi == Api.SURFACE_CONTROL) {
+                SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+                try {
+                    transaction.setFrameRate(mSurfaceControl, frameRate, compatibility).apply();
+                } finally {
+                    transaction.close();
+                }
+            } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+                nativeSurfaceControlSetFrameRate(mNativeSurfaceControl, frameRate, compatibility);
+            }
+            return rc;
+        }
+
+        public void setInvalidFrameRate(float frameRate, int compatibility) {
+            if (mApi == Api.SURFACE) {
+                boolean caughtIllegalArgException = false;
+                try {
+                    setFrameRate(frameRate, compatibility);
+                } catch (IllegalArgumentException exc) {
+                    caughtIllegalArgException = true;
+                }
+                assertTrue("Expected an IllegalArgumentException from invalid call to"
+                                + " Surface.setFrameRate()",
+                        caughtIllegalArgException);
+            } else {
+                int rc = setFrameRate(frameRate, compatibility);
+                if (mApi == Api.ANATIVE_WINDOW) {
+                    assertTrue("Expected -EINVAL return value from invalid call to"
+                                    + " ANativeWindow_setFrameRate()",
+                            rc == -EINVAL);
+                }
+            }
+        }
+
+        public void setVisibility(boolean visible) {
+            Log.i(TAG,
+                    String.format("Setting visibility for %s: %s", mName,
+                            visible ? "visible" : "hidden"));
+            if (mApi == Api.SURFACE || mApi == Api.ANATIVE_WINDOW || mApi == Api.SURFACE_CONTROL) {
+                SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+                try {
+                    transaction.setVisibility(mSurfaceControl, visible).apply();
+                } finally {
+                    transaction.close();
+                }
+            } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+                nativeSurfaceControlSetVisibility(mNativeSurfaceControl, visible);
+            }
+        }
+
+        public void postBuffer() {
+            mLastBufferPostTimeValid = true;
+            mLastBufferPostTime = System.nanoTime();
+            if (mApi == Api.SURFACE || mApi == Api.ANATIVE_WINDOW || mApi == Api.SURFACE_CONTROL) {
+                Canvas canvas = mSurface.lockHardwareCanvas();
+                canvas.drawColor(mColor);
+                mSurface.unlockCanvasAndPost(canvas);
+            } else if (mApi == Api.NATIVE_SURFACE_CONTROL) {
+                assertTrue("Posting a buffer failed",
+                        nativeSurfaceControlPostBuffer(mNativeSurfaceControl, mColor));
+            }
+        }
+
+        public long getLastBufferPostTime() {
+            assertTrue("No buffer posted yet", mLastBufferPostTimeValid);
+            return mLastBufferPostTime;
+        }
+
+        public void release() {
+            if (mSurface != null) {
+                mSurface.release();
+                mSurface = null;
+            }
+            if (mSurfaceControl != null) {
+                mSurfaceControl.release();
+                mSurfaceControl = null;
+            }
+            if (mNativeSurfaceControl != 0) {
+                nativeSurfaceControlDestroy(mNativeSurfaceControl);
+                mNativeSurfaceControl = 0;
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                release();
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+
+    private static String frameRateCompatibilityToString(int compatibility) {
+        switch (compatibility) {
+            case Surface.FRAME_RATE_COMPATIBILITY_DEFAULT:
+                return "default";
+            case Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
+                return "fixed_source";
+            default:
+                return "invalid(" + compatibility + ")";
+        }
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -150,6 +327,29 @@
         return uniqueFrameRates;
     }
 
+    private boolean isFrameRateMultiple(float higherFrameRate, float lowerFrameRate) {
+        float multiple = higherFrameRate / lowerFrameRate;
+        int roundedMultiple = Math.round(multiple);
+        return roundedMultiple > 0
+                && Math.abs(roundedMultiple * lowerFrameRate - higherFrameRate) <= 0.1f;
+    }
+
+    // Returns two device-supported frame rates that aren't multiples of each other, or null if no
+    // such incompatible frame rates are available. This is useful for testing behavior where we
+    // have layers with conflicting frame rates.
+    private float[] getIncompatibleFrameRates() {
+        ArrayList<Float> frameRates = getFrameRatesToTest();
+        for (int i = 0; i < frameRates.size(); i++) {
+            for (int j = i + 1; j < frameRates.size(); j++) {
+                if (!isFrameRateMultiple(Math.max(frameRates.get(i), frameRates.get(j)),
+                            Math.min(frameRates.get(i), frameRates.get(j)))) {
+                    return new float[] {frameRates.get(i), frameRates.get(j)};
+                }
+            }
+        }
+        return null;
+    }
+
     private void waitForSurface() throws InterruptedException {
         if (mSurface == null) {
             Log.i(TAG, "Waiting for surface");
@@ -164,60 +364,52 @@
         }
     }
 
-    private boolean isDeviceFrameRateCompatibleWithAppRequest(
-            float deviceFrameRate, float appRequestedFrameRate) {
-        float multiple = deviceFrameRate / appRequestedFrameRate;
-        int roundedMultiple = Math.round(multiple);
-        return roundedMultiple > 0
-                && Math.abs(roundedMultiple * appRequestedFrameRate - deviceFrameRate) <= 0.1f;
-    }
-
-    private void postBuffer() {
-        Canvas canvas = mSurfaceView.getHolder().lockHardwareCanvas();
-        canvas.drawColor(Color.BLUE);
-        mSurfaceView.getHolder().unlockCanvasAndPost(canvas);
-    }
-
-    private void setFrameRateSurface(float frameRate) {
-        Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
-        mSurface.setFrameRate(frameRate);
-        postBuffer();
-    }
-
-    private void setFrameRateANativeWindow(float frameRate) {
-        Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
-        nativeWindowSetFrameRate(mSurface, frameRate);
-        postBuffer();
-    }
-
-    private void setFrameRateSurfaceControl(float frameRate) {
-        Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
-        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
-        transaction.setFrameRate(mSurfaceView.getSurfaceControl(), frameRate).apply();
-        transaction.close();
-        postBuffer();
-    }
-
-    private void setFrameRateNativeSurfaceControl(long surfaceControl, float frameRate) {
-        Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
-        nativeSurfaceControlSetFrameRate(surfaceControl, frameRate);
-    }
-
-    private void verifyCompatibleAndStableFrameRate(float appRequestedFrameRate)
+    // Returns true if we reached waitUntilNanos, false if some other event occurred.
+    private boolean waitForEvents(long waitUntilNanos, ArrayList<TestSurface> surfaces)
             throws InterruptedException {
+        mFrameRateChangedEvents.clear();
+        long nowNanos = System.nanoTime();
+        while (nowNanos < waitUntilNanos) {
+            long surfacePostTime = Long.MAX_VALUE;
+            for (TestSurface surface : surfaces) {
+                surfacePostTime = Math.min(surfacePostTime,
+                        surface.getLastBufferPostTime()
+                                + (POST_BUFFER_INTERVAL_MILLIS * 1_000_000L));
+            }
+            long timeoutNs = Math.min(waitUntilNanos, surfacePostTime) - nowNanos;
+            long timeoutMs = timeoutNs / 1_000_000L;
+            int remainderNs = (int) (timeoutNs % 1_000_000L);
+            // Don't call wait(0, 0) - it blocks indefinitely.
+            if (timeoutMs > 0 || remainderNs > 0) {
+                mLock.wait(timeoutMs, remainderNs);
+            }
+            nowNanos = System.nanoTime();
+            assertTrue("Lost the surface", mSurface != null);
+            if (!mFrameRateChangedEvents.isEmpty()) {
+                return false;
+            }
+            if (nowNanos >= surfacePostTime) {
+                for (TestSurface surface : surfaces) {
+                    surface.postBuffer();
+                }
+            }
+        }
+        return true;
+    }
+
+    private void verifyCompatibleAndStableFrameRate(float appRequestedFrameRate,
+            ArrayList<TestSurface> surfaces) throws InterruptedException {
         Log.i(TAG, "Verifying compatible and stable frame rate");
         long nowNanos = System.nanoTime();
         long gracePeriodEndTimeNanos =
                 nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L;
         while (true) {
             // Wait until we switch to a compatible frame rate.
-            while (!isDeviceFrameRateCompatibleWithAppRequest(
-                           mDeviceFrameRate, appRequestedFrameRate)
-                    && gracePeriodEndTimeNanos > nowNanos) {
-                mLock.wait((gracePeriodEndTimeNanos - nowNanos) / 1_000_000);
-                nowNanos = System.nanoTime();
-                assertTrue("Lost the surface", mSurface != null);
+            while (!isFrameRateMultiple(mDeviceFrameRate, appRequestedFrameRate)
+                    && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
+                // Empty
             }
+            nowNanos = System.nanoTime();
 
             // TODO(b/148033900): Remove the if and error log below, and replace it with this
             // assertTrue() call, once we have a way to ignore display manager policy.
@@ -241,95 +433,142 @@
             // that frame rate.
             long endTimeNanos = nowNanos + STABLE_FRAME_RATE_WAIT_SECONDS * 1_000_000_000L;
             while (endTimeNanos > nowNanos) {
-                mFrameRateChangedEvents.clear();
-                mLock.wait((endTimeNanos - nowNanos) / 1_000_000);
-                nowNanos = System.nanoTime();
-                assertTrue("Lost the surface", mSurface != null);
-                if (!mFrameRateChangedEvents.isEmpty()) {
-                    break;
-                }
-                if (nowNanos >= endTimeNanos) {
+                if (waitForEvents(endTimeNanos, surfaces)) {
                     Log.i(TAG, String.format("Stable frame rate %.0f verified", mDeviceFrameRate));
                     return;
                 }
+                nowNanos = System.nanoTime();
+                if (!mFrameRateChangedEvents.isEmpty()) {
+                    break;
+                }
             }
         }
     }
 
-    public void testSurfaceSetFrameRate() throws InterruptedException {
+    private void testExactFrameRateMatch(Api api) throws InterruptedException {
+        Log.i(TAG, String.format("Testing %s exact frame rate match", api));
         ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
-        Log.i(TAG, "Testing Surface.setFrameRate()");
-        synchronized (mLock) {
-            waitForSurface();
-            for (float frameRate : frameRatesToTest) {
-                setFrameRateSurface(frameRate);
-                verifyCompatibleAndStableFrameRate(frameRate);
-            }
-            setFrameRateSurface(-100.f);
-            Thread.sleep(1000);
-            setFrameRateSurface(0.f);
-        }
-        Log.i(TAG, "Done testing Surface.setFrameRate()");
-    }
-
-    public void testANativeWindowSetFrameRate() throws InterruptedException {
-        ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
-        Log.i(TAG, "Testing ANativeWindow_setFrameRate()");
-        synchronized (mLock) {
-            waitForSurface();
-            for (float frameRate : frameRatesToTest) {
-                setFrameRateANativeWindow(frameRate);
-                verifyCompatibleAndStableFrameRate(frameRate);
-            }
-            setFrameRateANativeWindow(-100.f);
-            Thread.sleep(1000);
-            setFrameRateANativeWindow(0.f);
-        }
-        Log.i(TAG, "Done testing ANativeWindow_setFrameRate()");
-    }
-
-    public void testSurfaceControlSetFrameRate() throws InterruptedException {
-        ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
-        Log.i(TAG, "Testing SurfaceControl.Transaction.setFrameRate()");
-        synchronized (mLock) {
-            waitForSurface();
-            for (float frameRate : frameRatesToTest) {
-                setFrameRateSurfaceControl(frameRate);
-                verifyCompatibleAndStableFrameRate(frameRate);
-            }
-            setFrameRateSurfaceControl(-100.f);
-            Thread.sleep(1000);
-            setFrameRateSurfaceControl(0.f);
-        }
-        Log.i(TAG, "Done testing SurfaceControl.Transaction.setFrameRate()");
-    }
-
-    public void testNativeSurfaceControlSetFrameRate() throws InterruptedException {
-        ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
-        Log.i(TAG, "Testing ASurfaceTransaction_setFrameRate()");
-        long nativeSurfaceControl = 0;
+        TestSurface surface = null;
         try {
             synchronized (mLock) {
                 waitForSurface();
-                nativeSurfaceControl = nativeSurfaceControlCreate(mSurface);
-                assertTrue("Failed to create a native SurfaceControl", nativeSurfaceControl != 0);
+                surface = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+                        "testSurface", mSurfaceView.getHolder().getSurfaceFrame(),
+                        /*visible=*/true, Color.RED);
+                ArrayList<TestSurface> surfaces = new ArrayList<>();
+                surfaces.add(surface);
                 for (float frameRate : frameRatesToTest) {
-                    setFrameRateNativeSurfaceControl(nativeSurfaceControl, frameRate);
-                    verifyCompatibleAndStableFrameRate(frameRate);
+                    surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+                    verifyCompatibleAndStableFrameRate(frameRate, surfaces);
                 }
-                setFrameRateNativeSurfaceControl(nativeSurfaceControl, -100.f);
-                Thread.sleep(1000);
-                setFrameRateNativeSurfaceControl(nativeSurfaceControl, 0.f);
+                surface.setFrameRate(0.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
             }
         } finally {
-            nativeSurfaceControlDestroy(nativeSurfaceControl);
+            if (surface != null) {
+                surface.release();
+            }
         }
-        Log.i(TAG, "Done testing ASurfaceTransaction_setFrameRate()");
     }
 
-    private static native int nativeWindowSetFrameRate(Surface surface, float frameRate);
-    private static native long nativeSurfaceControlCreate(Surface parentSurface);
+    public void testExactFrameRateMatch() throws InterruptedException {
+        for (Api api : Api.values()) {
+            testExactFrameRateMatch(api);
+        }
+    }
+
+    private void testFixedSource(Api api) throws InterruptedException {
+        Log.i(TAG, String.format("Testing %s fixed_source behavior", api));
+        float[] incompatibleFrameRates = getIncompatibleFrameRates();
+        if (incompatibleFrameRates == null) {
+            Log.i(TAG, "No incompatible frame rates to use for testing fixed_source behavior");
+            return;
+        }
+
+        float frameRateA = incompatibleFrameRates[0];
+        float frameRateB = incompatibleFrameRates[1];
+        Log.i(TAG,
+                String.format("Testing with incompatible frame rates: surfaceA=%.0f surfaceB=%.0f",
+                        frameRateA, frameRateB));
+        synchronized (mLock) {
+            waitForSurface();
+            TestSurface surfaceA = null;
+            TestSurface surfaceB = null;
+            try {
+                int width = mSurfaceView.getHolder().getSurfaceFrame().width();
+                int height = mSurfaceView.getHolder().getSurfaceFrame().height() / 2;
+                Rect destFrameA =
+                        new Rect(/*left=*/0, /*top=*/0, /*right=*/width, /*bottom=*/height);
+                surfaceA = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+                        "surfaceA", destFrameA, /*visible=*/true, Color.RED);
+                Rect destFrameB = new Rect(
+                        /*left=*/0, /*top=*/height, /*right=*/width, /*bottom=*/height * 2);
+                surfaceB = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+                        "surfaceB", destFrameB, /*visible=*/false, Color.GREEN);
+
+                surfaceA.setFrameRate(frameRateA, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+                surfaceB.setFrameRate(frameRateB, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+
+                ArrayList<TestSurface> surfaces = new ArrayList<>();
+                surfaces.add(surfaceA);
+                surfaces.add(surfaceB);
+
+                verifyCompatibleAndStableFrameRate(frameRateA, surfaces);
+                surfaceB.setVisibility(true);
+                verifyCompatibleAndStableFrameRate(frameRateB, surfaces);
+            } finally {
+                if (surfaceA != null) {
+                    surfaceA.release();
+                }
+                if (surfaceB != null) {
+                    surfaceB.release();
+                }
+            }
+        }
+    }
+
+    public void testFixedSource() throws InterruptedException {
+        for (Api api : Api.values()) {
+            testFixedSource(api);
+        }
+    }
+
+    private void testInvalidParams(Api api) throws InterruptedException {
+        Log.i(TAG, String.format("Testing %s invalid params behavior", api));
+        TestSurface surface = null;
+        try {
+            synchronized (mLock) {
+                waitForSurface();
+                surface = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+                        "testSurface", mSurfaceView.getHolder().getSurfaceFrame(),
+                        /*visible=*/true, Color.RED);
+                surface.setInvalidFrameRate(-100.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+                surface.setInvalidFrameRate(
+                        Float.POSITIVE_INFINITY, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+                surface.setInvalidFrameRate(Float.NaN, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+                surface.setInvalidFrameRate(0.f, -10);
+                surface.setInvalidFrameRate(0.f, 50);
+            }
+        } finally {
+            if (surface != null) {
+                surface.release();
+            }
+        }
+    }
+
+    public void testInvalidParams() throws InterruptedException {
+        for (Api api : Api.values()) {
+            testInvalidParams(api);
+        }
+    }
+
+    private static native int nativeWindowSetFrameRate(
+            Surface surface, float frameRate, int compatibility);
+    private static native long nativeSurfaceControlCreate(
+            Surface parentSurface, String name, int left, int top, int right, int bottom);
     private static native void nativeSurfaceControlDestroy(long surfaceControl);
     private static native void nativeSurfaceControlSetFrameRate(
-            long surfaceControl, float frameRate);
+            long surfaceControl, float frameRate, int compatibility);
+    private static native void nativeSurfaceControlSetVisibility(
+            long surfaceControl, boolean visible);
+    private static native boolean nativeSurfaceControlPostBuffer(long surfaceControl, int color);
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java b/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
index 65dcfcd..ec7ef2d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
@@ -32,11 +32,20 @@
             new ActivityTestRule<>(FrameRateCtsActivity.class);
 
     @Test
-    public void testSetFrameRate() throws InterruptedException {
+    public void testExactFrameRateMatch() throws InterruptedException {
         FrameRateCtsActivity activity = mActivityRule.getActivity();
-        activity.testSurfaceSetFrameRate();
-        activity.testANativeWindowSetFrameRate();
-        activity.testSurfaceControlSetFrameRate();
-        activity.testNativeSurfaceControlSetFrameRate();
+        activity.testExactFrameRateMatch();
+    }
+
+    @Test
+    public void testFixedSource() throws InterruptedException {
+        FrameRateCtsActivity activity = mActivityRule.getActivity();
+        activity.testFixedSource();
+    }
+
+    @Test
+    public void testInvalidParams() throws InterruptedException {
+        FrameRateCtsActivity activity = mActivityRule.getActivity();
+        activity.testInvalidParams();
     }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/Utils.java b/tests/tests/graphics/src/android/graphics/cts/Utils.java
index 85b5b40..c558e89 100644
--- a/tests/tests/graphics/src/android/graphics/cts/Utils.java
+++ b/tests/tests/graphics/src/android/graphics/cts/Utils.java
@@ -40,7 +40,7 @@
         return getResources().openRawResource(resId);
     }
 
-    static Uri getAsResourceUri(int resId) {
+    public static Uri getAsResourceUri(int resId) {
         Resources res = getResources();
         return new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageActivity.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageActivity.java
new file mode 100644
index 0000000..c1524f8
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable.cts;
+
+import android.app.Activity;
+import android.graphics.cts.R;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * Simple Activity with an ImageView for inserting an AnimatedImageDrawable in
+ * AnimatedImageDrawableTest.
+ */
+public final class AnimatedImageActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.animated_image_layout);
+
+        // AnimatedImageDrawable only animates if the screen is on, so ensure that the screen stays
+        // on for the duration of the test.
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index ee3347d..453f1ad 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -36,6 +36,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.cts.R;
+import android.graphics.cts.Utils;
 import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -47,8 +48,9 @@
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.BitmapUtils;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WidgetTestUtils;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,8 +68,7 @@
 
 @RunWith(JUnitParamsRunner.class)
 public class AnimatedImageDrawableTest {
-    private Resources mRes;
-    private ContentResolver mContentResolver;
+    private ImageView mImageView;
 
     private static final int RES_ID = R.drawable.animated;
     private static final int WIDTH = 278;
@@ -75,27 +76,24 @@
     private static final int NUM_FRAMES = 4;
     private static final int FRAME_DURATION = 250; // in milliseconds
     private static final int DURATION = NUM_FRAMES * FRAME_DURATION;
-    private static final int LAYOUT = R.layout.animated_image_layout;
-    private static final int IMAGE_ID = R.id.animated_image;
+
     @Rule
-    public ActivityTestRule<DrawableStubActivity> mActivityRule =
-            new ActivityTestRule<DrawableStubActivity>(DrawableStubActivity.class);
+    public ActivityTestRule<AnimatedImageActivity> mActivityRule =
+            new ActivityTestRule<AnimatedImageActivity>(AnimatedImageActivity.class);
     private Activity mActivity;
 
-    private Uri getAsResourceUri(int resId) {
-        return new Uri.Builder()
-            .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
-            .authority(mRes.getResourcePackageName(resId))
-            .appendPath(mRes.getResourceTypeName(resId))
-            .appendPath(mRes.getResourceEntryName(resId))
-            .build();
+    private Resources getResources() {
+        return InstrumentationRegistry.getTargetContext().getResources();
     }
 
-    @Before
-    public void setup() {
-        mRes = InstrumentationRegistry.getTargetContext().getResources();
-        mContentResolver = InstrumentationRegistry.getTargetContext().getContentResolver();
+    private ContentResolver getContentResolver() {
+        return InstrumentationRegistry.getTargetContext().getContentResolver();
+    }
+
+    private void setupActivity() {
         mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        mImageView = mActivity.findViewById(R.id.animated_image);
     }
 
     @Test
@@ -105,8 +103,9 @@
 
     @Test
     public void testMutate() {
-        AnimatedImageDrawable aid1 = (AnimatedImageDrawable) mRes.getDrawable(R.drawable.animated);
-        AnimatedImageDrawable aid2 = (AnimatedImageDrawable) mRes.getDrawable(R.drawable.animated);
+        Resources res = getResources();
+        AnimatedImageDrawable aid1 = (AnimatedImageDrawable) res.getDrawable(R.drawable.animated);
+        AnimatedImageDrawable aid2 = (AnimatedImageDrawable) res.getDrawable(R.drawable.animated);
 
         final int originalAlpha = aid1.getAlpha();
         assertEquals(255, originalAlpha);
@@ -117,15 +116,15 @@
             aid1.setAlpha(100);
             assertEquals(originalAlpha, aid2.getAlpha());
         } finally {
-            mRes.getDrawable(R.drawable.animated).setAlpha(originalAlpha);
+            res.getDrawable(R.drawable.animated).setAlpha(originalAlpha);
         }
     }
 
     private AnimatedImageDrawable createFromImageDecoder(int resId) {
         Uri uri = null;
         try {
-            uri = getAsResourceUri(resId);
-            ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, uri);
+            uri = Utils.getAsResourceUri(resId);
+            ImageDecoder.Source source = ImageDecoder.createSource(getContentResolver(), uri);
             Drawable drawable = ImageDecoder.decodeDrawable(source);
             assertTrue(drawable instanceof AnimatedImageDrawable);
             return (AnimatedImageDrawable) drawable;
@@ -176,6 +175,7 @@
 
     @Test
     public void testRegisterCallback() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated);
 
         mActivityRule.runOnUiThread(() -> {
@@ -191,6 +191,7 @@
 
     @Test
     public void testClearCallbacks() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated);
 
         Callback[] callbacks = new Callback[] {
@@ -218,26 +219,14 @@
         }
     }
 
-    /**
-     *  Helper for attaching drawable to the view system.
-     *
-     *  Necessary for the drawable to animate.
-     *
-     *  Must be called from UI thread.
-     */
-    private void setContentView(AnimatedImageDrawable drawable) {
-        mActivity.setContentView(LAYOUT);
-        ImageView imageView = (ImageView) mActivity.findViewById(IMAGE_ID);
-        imageView.setImageDrawable(drawable);
-    }
-
     @Test
     public void testUnregisterCallback() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated);
 
         Callback cb = new Callback(drawable);
-        mActivityRule.runOnUiThread(() -> {
-            setContentView(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
 
             drawable.registerAnimationCallback(cb);
             assertTrue(drawable.unregisterAnimationCallback(cb));
@@ -255,14 +244,15 @@
     @Test
     @FlakyTest (bugId = 120280954)
     public void testLifeCycle() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
 
         // Only run the animation one time.
         drawable.setRepeatCount(0);
 
         Callback cb = new Callback(drawable);
-        mActivityRule.runOnUiThread(() -> {
-            setContentView(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
 
             drawable.registerAnimationCallback(cb);
         });
@@ -271,13 +261,15 @@
         cb.assertStarted(false);
         cb.assertEnded(false);
 
-        mActivityRule.runOnUiThread(() -> {
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
             drawable.start();
             assertTrue(drawable.isRunning());
         });
         cb.waitForStart();
         cb.assertStarted(true);
 
+        // FIXME: Now that it seems the reason for the flakiness has been solved (b/129400990),
+        // reduce this extra duration workaround.
         // Extra time, to wait for the message to post.
         cb.waitForEnd(DURATION * 20);
         cb.assertEnded(true);
@@ -286,6 +278,7 @@
 
     @Test
     public void testLifeCycleSoftware() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
 
         Bitmap bm = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
@@ -332,16 +325,19 @@
     @Test
     @FlakyTest (bugId = 72737527)
     public void testAddCallbackAfterStart() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
         Callback cb = new Callback(drawable);
-        mActivityRule.runOnUiThread(() -> {
-            setContentView(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
 
             drawable.setRepeatCount(0);
             drawable.start();
             drawable.registerAnimationCallback(cb);
         });
 
+        // FIXME: Now that it seems the reason for the flakiness has been solved (b/129400990),
+        // reduce this extra duration workaround.
         // Add extra duration to wait for the message posted by the end of the
         // animation. This should help fix flakiness.
         cb.waitForEnd(DURATION * 10);
@@ -350,10 +346,11 @@
 
     @Test
     public void testStop() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
         Callback cb = new Callback(drawable);
-        mActivityRule.runOnUiThread(() -> {
-            setContentView(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
 
             drawable.registerAnimationCallback(cb);
 
@@ -377,42 +374,45 @@
 
     @Test
     @FlakyTest (bugId = 72737527)
-    public void testRepeatCounts() throws Throwable {
-        for (int repeatCount : new int[] { 3, 5, 7, 16 }) {
-            AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
-            assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
+    @Parameters({ "3", "5", "7", "16" })
+    public void testRepeatCounts(int repeatCount) throws Throwable {
+        setupActivity();
+        AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
+        assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
 
-            Callback cb = new Callback(drawable);
-            mActivityRule.runOnUiThread(() -> {
-                setContentView(drawable);
+        Callback cb = new Callback(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
 
-                drawable.registerAnimationCallback(cb);
-                drawable.setRepeatCount(repeatCount);
-                assertEquals(repeatCount, drawable.getRepeatCount());
-                drawable.start();
-            });
+            drawable.registerAnimationCallback(cb);
+            drawable.setRepeatCount(repeatCount);
+            assertEquals(repeatCount, drawable.getRepeatCount());
+            drawable.start();
+        });
 
-            cb.waitForStart();
-            cb.assertStarted(true);
+        cb.waitForStart();
+        cb.assertStarted(true);
 
-            // The animation runs repeatCount + 1 total times.
-            cb.waitForEnd(DURATION * repeatCount);
-            cb.assertEnded(false);
+        // The animation runs repeatCount + 1 total times.
+        cb.waitForEnd(DURATION * repeatCount);
+        cb.assertEnded(false);
 
-            cb.waitForEnd(DURATION * 20);
-            cb.assertEnded(true);
+        // FIXME: Now that it seems the reason for the flakiness has been solved (b/129400990),
+        // reduce this extra duration workaround.
+        cb.waitForEnd(DURATION * 20);
+        cb.assertEnded(true);
 
-            drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
-            assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
-        }
+        drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
+        assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount());
     }
 
     @Test
     public void testRepeatCountInfinite() throws Throwable {
+        setupActivity();
         AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
         Callback cb = new Callback(drawable);
-        mActivityRule.runOnUiThread(() -> {
-            setContentView(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
 
             drawable.registerAnimationCallback(cb);
             drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
@@ -522,8 +522,8 @@
         AnimatedImageDrawable testDrawable = null;
         Uri uri = null;
         try {
-            uri = getAsResourceUri(RES_ID);
-            ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, uri);
+            uri = Utils.getAsResourceUri(RES_ID);
+            ImageDecoder.Source source = ImageDecoder.createSource(getContentResolver(), uri);
             Drawable dr = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
                 decoder.setPostProcessor((canvas) -> {
                     canvas.drawRect(rectCreator.apply(canvas.getWidth(),
@@ -550,32 +550,36 @@
 
     @Test
     public void testCreateFromXml() throws XmlPullParserException, IOException {
-        XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_tag);
-        Drawable drawable = Drawable.createFromXml(mRes, parser);
+        Resources res = getResources();
+        XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_tag);
+        Drawable drawable = Drawable.createFromXml(res, parser);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
     }
 
     @Test
     public void testCreateFromXmlClass() throws XmlPullParserException, IOException {
-        XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable);
-        Drawable drawable = Drawable.createFromXml(mRes, parser);
+        Resources res = getResources();
+        XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable);
+        Drawable drawable = Drawable.createFromXml(res, parser);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
     }
 
     @Test
     public void testCreateFromXmlClassAttribute() throws XmlPullParserException, IOException {
-        XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_class);
-        Drawable drawable = Drawable.createFromXml(mRes, parser);
+        Resources res = getResources();
+        XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_class);
+        Drawable drawable = Drawable.createFromXml(res, parser);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
     }
 
     @Test(expected=XmlPullParserException.class)
     public void testMissingSrcInflate() throws XmlPullParserException, IOException  {
-        XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_nosrc);
-        Drawable drawable = Drawable.createFromXml(mRes, parser);
+        Resources res = getResources();
+        XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_nosrc);
+        Drawable drawable = Drawable.createFromXml(res, parser);
     }
 
     @Test
@@ -600,8 +604,9 @@
     }
 
     private AnimatedImageDrawable parseXml(int resId) throws XmlPullParserException, IOException {
-        XmlPullParser parser = mRes.getXml(resId);
-        Drawable drawable = Drawable.createFromXml(mRes, parser);
+        Resources res = getResources();
+        XmlPullParser parser = res.getXml(resId);
+        Drawable drawable = Drawable.createFromXml(res, parser);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
         return (AnimatedImageDrawable) drawable;
@@ -666,8 +671,9 @@
 
     @Test
     public void testRepeatCountFromXml() throws XmlPullParserException, IOException {
-        XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count);
-        Drawable drawable = Drawable.createFromXml(mRes, parser);
+        Resources res = getResources();
+        XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_loop_count);
+        Drawable drawable = Drawable.createFromXml(res, parser);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
 
@@ -678,15 +684,16 @@
     @Test
     public void testInfiniteRepeatCountFromXml() throws XmlPullParserException, IOException {
         // This image has an encoded repeat count of 1. Verify that.
-        Drawable drawable = mRes.getDrawable(R.drawable.animated_one_loop);
+        Resources res = getResources();
+        Drawable drawable = res.getDrawable(R.drawable.animated_one_loop);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
         AnimatedImageDrawable aid = (AnimatedImageDrawable) drawable;
         assertEquals(1, aid.getRepeatCount());
 
         // This layout uses the same image and overrides the repeat count to infinity.
-        XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count_infinite);
-        drawable = Drawable.createFromXml(mRes, parser);
+        XmlPullParser parser = res.getXml(R.drawable.animatedimagedrawable_loop_count_infinite);
+        drawable = Drawable.createFromXml(res, parser);
         assertNotNull(drawable);
         assertTrue(drawable instanceof AnimatedImageDrawable);
 
@@ -697,8 +704,9 @@
     // Verify that decoding on the AnimatedImageThread works.
     private void decodeInBackground(AnimatedImageDrawable drawable) throws Throwable {
         final Callback cb = new Callback(drawable);
-        mActivityRule.runOnUiThread(() -> {
-            setContentView(drawable);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mImageView, () -> {
+            mImageView.setImageDrawable(drawable);
+
             drawable.registerAnimationCallback(cb);
             drawable.start();
         });
@@ -713,9 +721,11 @@
 
     @Test
     public void testInputStream() throws Throwable {
-        try (InputStream in = mRes.openRawResource(R.drawable.animated)) {
+        setupActivity();
+        Resources res = getResources();
+        try (InputStream in = res.openRawResource(R.drawable.animated)) {
             ImageDecoder.Source src =
-                    ImageDecoder.createSource(mRes, in, Bitmap.DENSITY_NONE);
+                    ImageDecoder.createSource(res, in, Bitmap.DENSITY_NONE);
             AnimatedImageDrawable drawable =
                     (AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
             decodeInBackground(drawable);
@@ -725,7 +735,7 @@
 
     private byte[] getAsByteArray() {
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        try (InputStream in = mRes.openRawResource(RES_ID)) {
+        try (InputStream in = getResources().openRawResource(RES_ID)) {
             byte[] buf = new byte[4096];
             int bytesRead;
             while ((bytesRead = in.read(buf)) != -1) {
@@ -758,6 +768,7 @@
 
     @Test
     public void testByteBuffer() throws Throwable {
+        setupActivity();
         // Natively, this tests ByteArrayStream.
         byte[] array = getAsByteArray();
         ByteBuffer byteBuffer = ByteBuffer.wrap(array);
@@ -767,6 +778,7 @@
 
     @Test
     public void testReadOnlyByteBuffer() throws Throwable {
+        setupActivity();
         // Natively, this tests ByteBufferStream.
         byte[] array = getAsByteArray();
         ByteBuffer byteBuffer = ByteBuffer.wrap(array).asReadOnlyBuffer();
@@ -776,6 +788,7 @@
 
     @Test
     public void testDirectByteBuffer() throws Throwable {
+        setupActivity();
         ByteBuffer byteBuffer = getAsDirectByteBuffer();
         final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
         decodeInBackground(drawable);
diff --git a/tests/tests/hardware/res/raw/asus_gamepad_register.json b/tests/tests/hardware/res/raw/asus_gamepad_register.json
index dd422a4..64cf5e4 100644
--- a/tests/tests/hardware/res/raw/asus_gamepad_register.json
+++ b/tests/tests/hardware/res/raw/asus_gamepad_register.json
@@ -4,6 +4,7 @@
   "name": "Asus Gamepad (Test)",
   "vid": 0x0b05,
   "pid": 0x4500,
+  "bus": "bluetooth",
   "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
     0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
     0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
diff --git a/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json b/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json
index a426740..263b14d 100644
--- a/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json
+++ b/tests/tests/hardware/res/raw/microsoft_designer_keyboard_register.json
@@ -4,6 +4,7 @@
   "name": "Designer Keyboard (Test)",
   "vid": 0x045e,
   "pid": 0x0806,
+  "bus": "bluetooth",
   "descriptor": [
     0x06, 0xbc, 0xff, 0x09, 0x88, 0xa1, 0x01, 0x85, 0x22, 0x06, 0x00, 0xff,
     0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x13, 0x0a, 0x0a, 0xfa,
diff --git a/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json b/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json
index a9fd5ab..8ee16b4 100644
--- a/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json
+++ b/tests/tests/hardware/res/raw/microsoft_sculpttouch_register.json
@@ -4,6 +4,7 @@
   "name": "Microsoft Sculpt Touch Mouse (Test)",
   "vid": 0x045e,
   "pid": 0x077c,
+  "bus": "bluetooth",
   "descriptor": [
     0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x05, 0x01, 0x09, 0x02, 0xa1, 0x02,
     0x85, 0x1a, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x05,
diff --git a/tests/tests/hardware/res/raw/microsoft_xboxones_register.json b/tests/tests/hardware/res/raw/microsoft_xboxones_register.json
index c1757e8..d789297 100755
--- a/tests/tests/hardware/res/raw/microsoft_xboxones_register.json
+++ b/tests/tests/hardware/res/raw/microsoft_xboxones_register.json
@@ -1,28 +1,31 @@
 {

-    "id": 1,

-    "command": "register",

-    "name": "Xbox One S (Bluetooth Test)",

-    "vid": 0x045e,

-    "pid": 0x02fd,

-    "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30,

-      0x09, 0x31, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0,

-      0x09, 0x01, 0xa1, 0x00, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95,

-      0x02, 0x75, 0x10, 0x81, 0x02, 0xc0, 0x05, 0x02, 0x09, 0xc5, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95,

-      0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05,

-      0x02, 0x09, 0xc4, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00,

-      0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25, 0x08,

-      0x35, 0x00, 0x46, 0x3b, 0x01, 0x66, 0x14, 0x00, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x75, 0x04,

-      0x95, 0x01, 0x15, 0x00, 0x25, 0x00, 0x35, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x03, 0x05, 0x09,

-      0x19, 0x01, 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0f, 0x81, 0x02, 0x15, 0x00,

-      0x25, 0x00, 0x75, 0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x15, 0x00, 0x25,

-      0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01, 0x81,

-      0x03, 0x05, 0x0c, 0x09, 0x01, 0x85, 0x02, 0xa1, 0x01, 0x05, 0x0c, 0x0a, 0x23, 0x02, 0x15, 0x00,

-      0x25, 0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01,

-      0x81, 0x03, 0xc0, 0x05, 0x0f, 0x09, 0x21, 0x85, 0x03, 0xa1, 0x02, 0x09, 0x97, 0x15, 0x00, 0x25,

-      0x01, 0x75, 0x04, 0x95, 0x01, 0x91, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x04, 0x95, 0x01, 0x91,

-      0x03, 0x09, 0x70, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x04, 0x91, 0x02, 0x09, 0x50, 0x66,

-      0x01, 0x10, 0x55, 0x0e, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x09,

-      0xa7, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x65, 0x00, 0x55, 0x00,

-      0x09, 0x7c, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0xc0, 0x05, 0x06,

-      0x09, 0x20, 0x85, 0x04, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xc0]

-  }

+  "id": 1,

+  "command": "register",

+  "name": "Xbox One S (Bluetooth Test)",

+  "vid": 0x045e,

+  "pid": 0x02fd,

+  "bus": "bluetooth",

+  "descriptor": [

+    0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30,

+    0x09, 0x31, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0,

+    0x09, 0x01, 0xa1, 0x00, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95,

+    0x02, 0x75, 0x10, 0x81, 0x02, 0xc0, 0x05, 0x02, 0x09, 0xc5, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95,

+    0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05,

+    0x02, 0x09, 0xc4, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00,

+    0x25, 0x00, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25, 0x08,

+    0x35, 0x00, 0x46, 0x3b, 0x01, 0x66, 0x14, 0x00, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x75, 0x04,

+    0x95, 0x01, 0x15, 0x00, 0x25, 0x00, 0x35, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x03, 0x05, 0x09,

+    0x19, 0x01, 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0f, 0x81, 0x02, 0x15, 0x00,

+    0x25, 0x00, 0x75, 0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x15, 0x00, 0x25,

+    0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01, 0x81,

+    0x03, 0x05, 0x0c, 0x09, 0x01, 0x85, 0x02, 0xa1, 0x01, 0x05, 0x0c, 0x0a, 0x23, 0x02, 0x15, 0x00,

+    0x25, 0x01, 0x95, 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x07, 0x95, 0x01,

+    0x81, 0x03, 0xc0, 0x05, 0x0f, 0x09, 0x21, 0x85, 0x03, 0xa1, 0x02, 0x09, 0x97, 0x15, 0x00, 0x25,

+    0x01, 0x75, 0x04, 0x95, 0x01, 0x91, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, 0x04, 0x95, 0x01, 0x91,

+    0x03, 0x09, 0x70, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x04, 0x91, 0x02, 0x09, 0x50, 0x66,

+    0x01, 0x10, 0x55, 0x0e, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x09,

+    0xa7, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0x65, 0x00, 0x55, 0x00,

+    0x09, 0x7c, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0xc0, 0x05, 0x06,

+    0x09, 0x20, 0x85, 0x04, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xc0

+  ]

+}

diff --git a/tests/tests/hardware/res/raw/razer_serval_register.json b/tests/tests/hardware/res/raw/razer_serval_register.json
index 3d8bb96..ab27177 100644
--- a/tests/tests/hardware/res/raw/razer_serval_register.json
+++ b/tests/tests/hardware/res/raw/razer_serval_register.json
@@ -4,6 +4,7 @@
   "name": "Razer Serval (Test)",
   "vid": 0x1532,
   "pid": 0x0900,
+  "bus": "bluetooth",
   "descriptor": [
     0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0xa1, 0x02, 0x85, 0x01, 0x75, 0x08, 0x95, 0x04,
     0x15, 0x00, 0x26, 0xff, 0x00, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81,
diff --git a/tests/tests/hardware/res/raw/sony_dualshock3_usb_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock3_usb_keyeventtests.json
new file mode 100644
index 0000000..fd152c2
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock3_usb_keyeventtests.json
@@ -0,0 +1,325 @@
+[
+  {
+    "name": "Press BUTTON_A",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_A"},
+      {"action": "UP", "keycode": "BUTTON_A"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_B",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_B"},
+      {"action": "UP", "keycode": "BUTTON_B"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_X",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_X"},
+      {"action": "UP", "keycode": "BUTTON_X"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_Y",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_Y"},
+      {"action": "UP", "keycode": "BUTTON_Y"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_L1",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_L1"},
+      {"action": "UP", "keycode": "BUTTON_L1"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_R1",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_R1"},
+      {"action": "UP", "keycode": "BUTTON_R1"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_L2",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_L2"},
+      {"action": "UP", "keycode": "BUTTON_L2"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_R2",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_R2"},
+      {"action": "UP", "keycode": "BUTTON_R2"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_L3",
+    "reports": [
+      [0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_THUMBL"},
+      {"action": "UP", "keycode": "BUTTON_THUMBL"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_R3",
+    "reports": [
+      [0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_THUMBR"},
+      {"action": "UP", "keycode": "BUTTON_THUMBR"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_SELECT",
+    "reports": [
+      [0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_SELECT"},
+      {"action": "UP", "keycode": "BUTTON_SELECT"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_START",
+    "reports": [
+      [0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_START"},
+      {"action": "UP", "keycode": "BUTTON_START"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_PS",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_MODE"},
+      {"action": "UP", "keycode": "BUTTON_MODE"}
+    ]
+  },
+
+  {
+    "name": "Press left DPAD key",
+    "reports": [
+      [0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "KEYCODE_DPAD_LEFT"},
+      {"action": "UP", "keycode": "KEYCODE_DPAD_LEFT"}
+    ]
+  },
+
+  {
+    "name": "Press right DPAD key",
+    "reports": [
+      [0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "KEYCODE_DPAD_RIGHT"},
+      {"action": "UP", "keycode": "KEYCODE_DPAD_RIGHT"}
+    ]
+  },
+
+  {
+    "name": "Press up DPAD key",
+    "reports": [
+      [0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "KEYCODE_DPAD_UP"},
+      {"action": "UP", "keycode": "KEYCODE_DPAD_UP"}
+    ]
+  },
+
+  {
+    "name": "Press down DPAD key",
+    "reports": [
+      [0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "KEYCODE_DPAD_DOWN"},
+      {"action": "UP", "keycode": "KEYCODE_DPAD_DOWN"}
+    ]
+  }
+]
+
diff --git a/tests/tests/hardware/res/raw/sony_dualshock3_usb_motioneventtests.json b/tests/tests/hardware/res/raw/sony_dualshock3_usb_motioneventtests.json
new file mode 100644
index 0000000..bae0f94
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock3_usb_motioneventtests.json
@@ -0,0 +1,257 @@
+[
+  {
+    "name": "Sanity check - should not produce any events",
+    "reports": [
+      [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+      0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+      0x2e, 0x80, 0xf2]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+    ]
+  },
+
+  {
+    "name": "Left stick - press left",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_X": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_X": -1}},
+      {"action": "MOVE", "axes": {"AXIS_X": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press right",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_X": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_X": 1}},
+      {"action": "MOVE", "axes": {"AXIS_X": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press up",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Y": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_Y": -1}},
+      {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press down",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Y": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_Y": 1}},
+      {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press left",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Z": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_Z": -1}},
+      {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press right",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Z": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_Z": 1}},
+      {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press up",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_RZ": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": -1}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press down",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_RZ": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": 1}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+    ]
+  },
+
+  {
+    "name": "Left trigger - quick press",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0.5, "AXIS_BRAKE": 0.5}},
+      {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 1.0, "AXIS_BRAKE": 1.0}},
+      {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0, "AXIS_BRAKE": 0}}
+    ]
+  },
+
+  {
+    "name": "Right trigger - quick press",
+    "reports": [
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb],
+      [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0xee, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0xd6, 0x77, 0x00, 0x40, 0x01, 0xfa, 0x02, 0x62,
+      0x02, 0x33, 0x01, 0xeb]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0.5, "AXIS_GAS": 0.5}},
+      {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 1.0, "AXIS_GAS": 1.0}},
+      {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0, "AXIS_GAS": 0}}
+    ]
+  }
+]
+
diff --git a/tests/tests/hardware/res/raw/sony_dualshock3_usb_register.json b/tests/tests/hardware/res/raw/sony_dualshock3_usb_register.json
new file mode 100644
index 0000000..3d22120
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock3_usb_register.json
@@ -0,0 +1,30 @@
+{
+  "id": 1,
+  "command": "register",
+  "name": "Sony DS3 (model CECHZC2U)(USB Test)",
+  "vid": 0x054c,
+  "pid": 0x0268,
+  "bus": "usb",
+  "descriptor": [0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, 0x85, 0x01, 0x75, 0x08, 0x95, 0x01,
+    0x15, 0x00, 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, 0x13, 0x15, 0x00, 0x25, 0x01, 0x35,
+    0x00, 0x45, 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06,
+    0x00, 0xff, 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75,
+    0x08, 0x95, 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35,
+    0x81, 0x02, 0xc0, 0x05, 0x01, 0x75, 0x08, 0x95, 0x27, 0x09, 0x01, 0x81, 0x02, 0x75, 0x08, 0x95,
+    0x30, 0x09, 0x01, 0x91, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02,
+    0x85, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75,
+    0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0xef, 0x75, 0x08, 0x95, 0x30,
+    0x09, 0x01, 0xb1, 0x02, 0xc0, 0xc0],
+  "feature_reports": [
+    {
+      "id": 0xf2,
+      "data": [0xf2, 0xff, 0xff, 0x00, 0x08, 0xa9, 0x5a, 0x15, 0x4a, 0xfe, 0x00, 0x03, 0x50, 0x81,
+        0xd8, 0x01, 0x8a]
+    },
+    {
+      "id": 0xf5,
+      "data": [0x01, 0x00, 0xc8, 0xbc, 0xc8, 0xcf, 0x6f, 0xb8]
+    }
+  ]
+}
+
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_keyeventtests.json
similarity index 100%
rename from tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json
rename to tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_keyeventtests.json
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_motioneventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_motioneventtests.json
similarity index 100%
rename from tests/tests/hardware/res/raw/sony_dualshock4_motioneventtests.json
rename to tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_motioneventtests.json
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_register.json b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_register.json
new file mode 100644
index 0000000..4c49453
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_bluetooth_register.json
@@ -0,0 +1,46 @@
+{
+  "id": 1,
+  "command": "register",
+  "name": "Sony DS4 (model CUH-ZCT1U)(Bluetooth Test)",
+  "vid": 0x054c,
+  "pid": 0x05c4,
+  "bus": "bluetooth",
+  "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
+    0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
+    0x00, 0x25, 0x07, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15,
+    0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0e, 0x81, 0x02, 0x75, 0x06, 0x95, 0x01, 0x81, 0x01, 0x05,
+    0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02,
+    0x06, 0x04, 0xff, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x25, 0x95,
+    0x30, 0xb1, 0x02, 0x85, 0x05, 0x09, 0x26, 0x95, 0x28, 0xb1, 0x02, 0x85, 0x06, 0x09, 0x27, 0x95,
+    0x34, 0xb1, 0x02, 0x85, 0x07, 0x09, 0x28, 0x95, 0x30, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x29, 0x95,
+    0x2f, 0xb1, 0x02, 0x06, 0x03, 0xff, 0x85, 0x03, 0x09, 0x21, 0x95, 0x26, 0xb1, 0x02, 0x85, 0x04,
+    0x09, 0x22, 0x95, 0x2e, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1,
+    0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49, 0x95, 0x0f, 0xb1, 0x02, 0x06, 0x00,
+    0xff, 0x85, 0x11, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x4d, 0x81, 0x02,
+    0x09, 0x21, 0x91, 0x02, 0x85, 0x12, 0x09, 0x22, 0x95, 0x8d, 0x81, 0x02, 0x09, 0x23, 0x91, 0x02,
+    0x85, 0x13, 0x09, 0x24, 0x95, 0xcd, 0x81, 0x02, 0x09, 0x25, 0x91, 0x02, 0x85, 0x14, 0x09, 0x26,
+    0x96, 0x0d, 0x01, 0x81, 0x02, 0x09, 0x27, 0x91, 0x02, 0x85, 0x15, 0x09, 0x28, 0x96, 0x4d, 0x01,
+    0x81, 0x02, 0x09, 0x29, 0x91, 0x02, 0x85, 0x16, 0x09, 0x2a, 0x96, 0x8d, 0x01, 0x81, 0x02, 0x09,
+    0x2b, 0x91, 0x02, 0x85, 0x17, 0x09, 0x2c, 0x96, 0xcd, 0x01, 0x81, 0x02, 0x09, 0x2d, 0x91, 0x02,
+    0x85, 0x18, 0x09, 0x2e, 0x96, 0x0d, 0x02, 0x81, 0x02, 0x09, 0x2f, 0x91, 0x02, 0x85, 0x19, 0x09,
+    0x30, 0x96, 0x22, 0x02, 0x81, 0x02, 0x09, 0x31, 0x91, 0x02, 0x06, 0x80, 0xff, 0x85, 0x82, 0x09,
+    0x22, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x24, 0xb1,
+    0x02, 0x85, 0x90, 0x09, 0x30, 0xb1, 0x02, 0x85, 0x91, 0x09, 0x31, 0xb1, 0x02, 0x85, 0x92, 0x09,
+    0x32, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0xb1, 0x02, 0x85,
+    0xa4, 0x09, 0x44, 0xb1, 0x02, 0xc0],
+  "feature_reports": [
+    {
+      "id": 0x05,
+      "data": [0x05, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0xbe, 0x22, 0x8d, 0x22, 0x4f,
+        0xdd, 0x4d, 0xdd, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
+        0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00, 0xca, 0xfc, 0x64, 0x4d]
+    },
+    {
+      "id": 0xa3,
+      "data": [0xa3, 0x41, 0x70, 0x72, 0x20, 0x20, 0x38, 0x20, 0x32, 0x30, 0x31, 0x34, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x30, 0x39, 0x3a, 0x34, 0x36, 0x3a, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x43, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05,
+        0x00, 0x00, 0x80, 0x03, 0x00]
+    }
+  ]
+}
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_usb_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_usb_keyeventtests.json
new file mode 100644
index 0000000..17ab0c9
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_usb_keyeventtests.json
@@ -0,0 +1,275 @@
+[
+  {
+    "name": "Press BUTTON_A",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x28, 0x00, 0x8c, 0x00, 0x00, 0xfb, 0x8c, 0x10, 0x0e, 0x00,
+      0x06, 0x00, 0xe6, 0xff, 0x09, 0x00, 0x87, 0x1d, 0x61, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_A"},
+      {"action": "UP", "keycode": "BUTTON_A"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_B",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x48, 0x00, 0x5c, 0x00, 0x00, 0xf0, 0xd8, 0x12, 0x0f, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0x68, 0x00, 0xf8, 0x1d, 0xc4, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_B"},
+      {"action": "UP", "keycode": "BUTTON_B"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_X",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x18, 0x00, 0x68, 0x00, 0x00, 0xfe, 0x2f, 0x12, 0x0f, 0x00,
+      0x04, 0x00, 0xe5, 0xff, 0x36, 0x00, 0xd3, 0x1d, 0xdb, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_X"},
+      {"action": "UP", "keycode": "BUTTON_X"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_Y",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x88, 0x00, 0x58, 0x00, 0x00, 0x1f, 0x91, 0x12, 0x0e, 0x00,
+      0x06, 0x00, 0xe4, 0xff, 0x41, 0x00, 0x30, 0x1e, 0xed, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0x37, 0x81, 0x3e, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_Y"},
+      {"action": "UP", "keycode": "BUTTON_Y"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_L1",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x01, 0xf0, 0x00, 0x00, 0xe9, 0x57, 0x12, 0x14, 0x00,
+      0xf0, 0xff, 0xf3, 0xff, 0xbc, 0xfb, 0x58, 0x1d, 0xc4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0x37, 0x81, 0x3e, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_L1"},
+      {"action": "UP", "keycode": "BUTTON_L1"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_R1",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x02, 0xfc, 0x00, 0x00, 0x2f, 0x36, 0x12, 0x0e, 0x00,
+      0x02, 0x00, 0xe3, 0xff, 0x50, 0x00, 0xfe, 0x1d, 0xec, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0x37, 0x81, 0x3e, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_R1"},
+      {"action": "UP", "keycode": "BUTTON_R1"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_L2",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x04, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_L2"},
+      {"action": "UP", "keycode": "BUTTON_L2"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_R2",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x08, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_R2"},
+      {"action": "UP", "keycode": "BUTTON_R2"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_L3",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x40, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_THUMBL"},
+      {"action": "UP", "keycode": "BUTTON_THUMBL"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_R3",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x80, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_THUMBR"},
+      {"action": "UP", "keycode": "BUTTON_THUMBR"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_SHARE",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x10, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_SELECT"},
+      {"action": "UP", "keycode": "BUTTON_SELECT"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_OPTIONS",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x20, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_START"},
+      {"action": "UP", "keycode": "BUTTON_START"}
+    ]
+  },
+
+  {
+    "name": "Press BUTTON_PS",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x11, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "KEYBOARD | GAMEPAD",
+    "events": [
+      {"action": "DOWN", "keycode": "BUTTON_MODE"},
+      {"action": "UP", "keycode": "BUTTON_MODE"}
+    ]
+  }
+
+]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_usb_motioneventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_usb_motioneventtests.json
new file mode 100644
index 0000000..f0be94e
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_usb_motioneventtests.json
@@ -0,0 +1,370 @@
+[
+  {
+    "name": "Sanity check - should not produce any events",
+    "reports": [
+      [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+      0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+      0x2e, 0x80, 0xf2]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+    ]
+  },
+
+  {
+    "name": "Press left DPAD key",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x06, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_HAT_X": -1}},
+      {"action": "MOVE", "axes": {"AXIS_HAT_X": 0}}
+    ]
+  },
+
+  {
+    "name": "Press right DPAD key",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x02, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_HAT_X": 1}},
+      {"action": "MOVE", "axes": {"AXIS_HAT_X": 0}}
+    ]
+  },
+
+  {
+    "name": "Press up DPAD key",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_HAT_Y": -1}},
+      {"action": "MOVE", "axes": {"AXIS_HAT_Y": 0}}
+    ]
+  },
+
+  {
+    "name": "Press down DPAD key",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x04, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_HAT_Y": 1}},
+      {"action": "MOVE", "axes": {"AXIS_HAT_Y": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press left",
+    "reports": [
+      [0x01, 0x40, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x00, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_X": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_X": -1}},
+      {"action": "MOVE", "axes": {"AXIS_X": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press right",
+    "reports": [
+      [0x01, 0xc0, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0xff, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_X": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_X": 1}},
+      {"action": "MOVE", "axes": {"AXIS_X": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press up",
+    "reports": [
+      [0x01, 0x80, 0x40, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x00, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Y": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_Y": -1}},
+      {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+    ]
+  },
+
+  {
+    "name": "Left stick - press down",
+    "reports": [
+      [0x01, 0x80, 0xc0, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0xff, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Y": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_Y": 1}},
+      {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press left",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x40, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x00, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Z": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_Z": -1}},
+      {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press right",
+    "reports": [
+      [0x01, 0x80, 0x80, 0xc0, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0xff, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_Z": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_Z": 1}},
+      {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press up",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x40, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_RZ": -0.5}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": -1}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+    ]
+  },
+
+  {
+    "name": "Right stick - press down",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0xc0, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0xff, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_RZ": 0.51}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": 1}},
+      {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+    ]
+  },
+
+  {
+    "name": "Left trigger - quick press",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x80, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0xff, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0.5, "AXIS_BRAKE": 0.5}},
+      {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 1.0, "AXIS_BRAKE": 1.0}},
+      {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0, "AXIS_BRAKE": 0}}
+    ]
+  },
+
+  {
+    "name": "Right trigger - quick press",
+    "reports": [
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x80, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0xff, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00],
+      [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x10, 0x00, 0x00, 0x34, 0xdf, 0x10, 0x0e, 0x00,
+      0x05, 0x00, 0xe6, 0xff, 0xe9, 0xff, 0x3e, 0x1e, 0x9c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x1b, 0x00, 0x00, 0x01, 0xf4, 0x80, 0x55, 0x70, 0x25, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+      0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+      0x00, 0x00, 0x80, 0x00]
+    ],
+    "source": "JOYSTICK",
+    "events": [
+      {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0.5, "AXIS_GAS": 0.5}},
+      {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 1.0, "AXIS_GAS": 1.0}},
+      {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0, "AXIS_GAS": 0}}
+    ]
+  }
+]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_usb_register.json b/tests/tests/hardware/res/raw/sony_dualshock4_usb_register.json
new file mode 100644
index 0000000..5654c50
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_usb_register.json
@@ -0,0 +1,58 @@
+{
+  "id": 1,
+  "command": "register",
+  "name": "Sony DS4 (model CUH-ZCT1U)(USB Test)",
+  "vid": 0x054c,
+  "pid": 0x05c4,
+  "bus": "usb",
+  "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
+    0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
+    0x00, 0x25, 0x07, 0x35, 0x00, 0x46, 0x3b, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42,
+    0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0e,
+    0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00, 0x25, 0x7f, 0x81,
+    0x02, 0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02,
+    0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09, 0x22, 0x95,
+    0x1f, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24, 0xb1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95,
+    0x24, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x25, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x10, 0x09, 0x26, 0x95,
+    0x04, 0xb1, 0x02, 0x85, 0x11, 0x09, 0x27, 0x95, 0x02, 0xb1, 0x02, 0x85, 0x12, 0x06, 0x02, 0xff,
+    0x09, 0x21, 0x95, 0x0f, 0xb1, 0x02, 0x85, 0x13, 0x09, 0x22, 0x95, 0x16, 0xb1, 0x02, 0x85, 0x14,
+    0x06, 0x05, 0xff, 0x09, 0x20, 0x95, 0x10, 0xb1, 0x02, 0x85, 0x15, 0x09, 0x21, 0x95, 0x2c, 0xb1,
+    0x02, 0x06, 0x80, 0xff, 0x85, 0x80, 0x09, 0x20, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x81, 0x09, 0x21,
+    0x95, 0x06, 0xb1, 0x02, 0x85, 0x82, 0x09, 0x22, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x24, 0x95, 0x04, 0xb1, 0x02, 0x85, 0x85, 0x09, 0x25,
+    0x95, 0x06, 0xb1, 0x02, 0x85, 0x86, 0x09, 0x26, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x87, 0x09, 0x27,
+    0x95, 0x23, 0xb1, 0x02, 0x85, 0x88, 0x09, 0x28, 0x95, 0x22, 0xb1, 0x02, 0x85, 0x89, 0x09, 0x29,
+    0x95, 0x02, 0xb1, 0x02, 0x85, 0x90, 0x09, 0x30, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x91, 0x09, 0x31,
+    0x95, 0x03, 0xb1, 0x02, 0x85, 0x92, 0x09, 0x32, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33,
+    0x95, 0x0c, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0x95, 0x06, 0xb1, 0x02, 0x85, 0xa1, 0x09, 0x41,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xa2, 0x09, 0x42, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x43,
+    0x95, 0x30, 0xb1, 0x02, 0x85, 0xa4, 0x09, 0x44, 0x95, 0x0d, 0xb1, 0x02, 0x85, 0xa5, 0x09, 0x45,
+    0x95, 0x15, 0xb1, 0x02, 0x85, 0xa6, 0x09, 0x46, 0x95, 0x15, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47,
+    0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49,
+    0x95, 0x0f, 0xb1, 0x02, 0x85, 0xa7, 0x09, 0x4a, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa8, 0x09, 0x4b,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xa9, 0x09, 0x4c, 0x95, 0x08, 0xb1, 0x02, 0x85, 0xaa, 0x09, 0x4e,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xab, 0x09, 0x4f, 0x95, 0x39, 0xb1, 0x02, 0x85, 0xac, 0x09, 0x50,
+    0x95, 0x39, 0xb1, 0x02, 0x85, 0xad, 0x09, 0x51, 0x95, 0x0b, 0xb1, 0x02, 0x85, 0xae, 0x09, 0x52,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xaf, 0x09, 0x53, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb0, 0x09, 0x54,
+    0x95, 0x3f, 0xb1, 0x02, 0x85, 0xb1, 0x09, 0x55, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb2, 0x09, 0x56,
+    0x95, 0x02, 0xb1, 0x02, 0xc0],
+  "feature_reports": [
+    {
+      "id": 0x02,
+      "data": [0x02, 0xf8, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xdf, 0x21, 0xf1, 0xdd, 0x95, 0x22, 0x67,
+        0xdd, 0xf2, 0x23, 0x1c, 0xdc, 0x1c, 0x02, 0x1c, 0x02, 0xaa, 0x1f, 0x56, 0xe0, 0xf7, 0x20,
+        0x08, 0xdf, 0x0a, 0x20, 0xf7, 0xdf, 0x06, 0x00]
+    },
+    {
+      "id": 0xa3,
+      "data": [0xa3, 0x41, 0x70, 0x72, 0x20, 0x20, 0x38, 0x20, 0x32, 0x30, 0x31, 0x34, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x30, 0x39, 0x3a, 0x34, 0x36, 0x3a, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x43, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05,
+        0x00, 0x00, 0x80, 0x03, 0x00]
+    },
+    {
+      "id": 0x81,
+      "data": [0x81, 0x62, 0x97, 0xc9, 0x00, 0x00, 0x00]
+    }
+  ]
+}
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_register.json b/tests/tests/hardware/res/raw/sony_dualshock4pro_bluetooth_register.json
similarity index 96%
rename from tests/tests/hardware/res/raw/sony_dualshock4_register.json
rename to tests/tests/hardware/res/raw/sony_dualshock4pro_bluetooth_register.json
index cd33b9e..d4fa66d 100644
--- a/tests/tests/hardware/res/raw/sony_dualshock4_register.json
+++ b/tests/tests/hardware/res/raw/sony_dualshock4pro_bluetooth_register.json
@@ -1,9 +1,10 @@
 {
   "id": 1,
   "command": "register",
-  "name": "Sony DS4 Joystick (Test)",
+  "name": "Sony DS4 Pro (model CUH-ZCT2U)(Bluetooth Test)",
   "vid": 0x054c,
   "pid": 0x09cc,
+  "bus": "bluetooth",
   "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
     0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
     0x00, 0x25, 0x07, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15,
@@ -33,7 +34,7 @@
     0x46, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb4, 0x09, 0x46, 0xb1, 0x02, 0xc0],
   "feature_reports": [
     {
-      "id": 5,
+      "id": 0x05,
       "data": [0x05, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0xbe, 0x22, 0x8d, 0x22, 0x4f,
         0xdd, 0x4d, 0xdd, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
         0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00, 0xca, 0xfc, 0x64, 0x4d]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4pro_usb_register.json b/tests/tests/hardware/res/raw/sony_dualshock4pro_usb_register.json
new file mode 100644
index 0000000..4514fd4
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualshock4pro_usb_register.json
@@ -0,0 +1,59 @@
+{
+  "id": 1,
+  "command": "register",
+  "name": "Sony DS4 Pro (model CUH-ZCT2U)(USB Test)",
+  "vid": 0x054c,
+  "pid": 0x09cc,
+  "bus": "usb",
+  "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
+    0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15,
+    0x00, 0x25, 0x07, 0x35, 0x00, 0x46, 0x3b, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42,
+    0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0e,
+    0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00, 0x25, 0x7f, 0x81,
+    0x02, 0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02,
+    0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09, 0x22, 0x95,
+    0x1f, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24, 0xb1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95,
+    0x24, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x25, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x10, 0x09, 0x26, 0x95,
+    0x04, 0xb1, 0x02, 0x85, 0x11, 0x09, 0x27, 0x95, 0x02, 0xb1, 0x02, 0x85, 0x12, 0x06, 0x02, 0xff,
+    0x09, 0x21, 0x95, 0x0f, 0xb1, 0x02, 0x85, 0x13, 0x09, 0x22, 0x95, 0x16, 0xb1, 0x02, 0x85, 0x14,
+    0x06, 0x05, 0xff, 0x09, 0x20, 0x95, 0x10, 0xb1, 0x02, 0x85, 0x15, 0x09, 0x21, 0x95, 0x2c, 0xb1,
+    0x02, 0x06, 0x80, 0xff, 0x85, 0x80, 0x09, 0x20, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x81, 0x09, 0x21,
+    0x95, 0x06, 0xb1, 0x02, 0x85, 0x82, 0x09, 0x22, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x24, 0x95, 0x04, 0xb1, 0x02, 0x85, 0x85, 0x09, 0x25,
+    0x95, 0x06, 0xb1, 0x02, 0x85, 0x86, 0x09, 0x26, 0x95, 0x06, 0xb1, 0x02, 0x85, 0x87, 0x09, 0x27,
+    0x95, 0x23, 0xb1, 0x02, 0x85, 0x88, 0x09, 0x28, 0x95, 0x22, 0xb1, 0x02, 0x85, 0x89, 0x09, 0x29,
+    0x95, 0x02, 0xb1, 0x02, 0x85, 0x90, 0x09, 0x30, 0x95, 0x05, 0xb1, 0x02, 0x85, 0x91, 0x09, 0x31,
+    0x95, 0x03, 0xb1, 0x02, 0x85, 0x92, 0x09, 0x32, 0x95, 0x03, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33,
+    0x95, 0x0c, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0x95, 0x06, 0xb1, 0x02, 0x85, 0xa1, 0x09, 0x41,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xa2, 0x09, 0x42, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x43,
+    0x95, 0x30, 0xb1, 0x02, 0x85, 0xa4, 0x09, 0x44, 0x95, 0x0d, 0xb1, 0x02, 0x85, 0xa5, 0x09, 0x45,
+    0x95, 0x15, 0xb1, 0x02, 0x85, 0xa6, 0x09, 0x46, 0x95, 0x15, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47,
+    0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49,
+    0x95, 0x0f, 0xb1, 0x02, 0x85, 0xa7, 0x09, 0x4a, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xa8, 0x09, 0x4b,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xa9, 0x09, 0x4c, 0x95, 0x08, 0xb1, 0x02, 0x85, 0xaa, 0x09, 0x4e,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xab, 0x09, 0x4f, 0x95, 0x39, 0xb1, 0x02, 0x85, 0xac, 0x09, 0x50,
+    0x95, 0x39, 0xb1, 0x02, 0x85, 0xad, 0x09, 0x51, 0x95, 0x0b, 0xb1, 0x02, 0x85, 0xae, 0x09, 0x52,
+    0x95, 0x01, 0xb1, 0x02, 0x85, 0xaf, 0x09, 0x53, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb0, 0x09, 0x54,
+    0x95, 0x3f, 0xb1, 0x02, 0x85, 0xb1, 0x09, 0x55, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb2, 0x09, 0x56,
+    0x95, 0x02, 0xb1, 0x02, 0x85, 0xe0, 0x09, 0x57, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x55,
+    0x95, 0x3f, 0xb1, 0x02, 0x85, 0xb4, 0x09, 0x55, 0x95, 0x3f, 0xb1, 0x02, 0xc0],
+  "feature_reports": [
+    {
+      "id": 0x02,
+      "data": [0x02, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0x4f, 0xdd, 0x4d, 0xdd, 0xbe,
+        0x22, 0x8d, 0x22, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
+        0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00]
+    },
+    {
+      "id": 0xa3,
+      "data": [0xa3, 0x41, 0x70, 0x72, 0x20, 0x20, 0x38, 0x20, 0x32, 0x30, 0x31, 0x34, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x30, 0x39, 0x3a, 0x34, 0x36, 0x3a, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x43, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05,
+        0x00, 0x00, 0x80, 0x03, 0x00]
+    },
+    {
+      "id": 0x81,
+      "data": [0x81, 0x62, 0x97, 0xc9, 0x00, 0x00, 0x00]
+    }
+  ]
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock3UsbTest.java
similarity index 69%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock3UsbTest.java
index 409c84f..e65fa7a 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock3UsbTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 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.
@@ -26,19 +26,20 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock3UsbTest extends InputTestCase {
 
-    public SonyDualshock4Test() {
-        super(R.raw.sony_dualshock4_register);
+    // Simulates the behavior of PlayStation DualShock3 gamepad (model CECHZC2U)
+    public SonyDualshock3UsbTest() {
+        super(R.raw.sony_dualshock3_usb_register);
     }
 
     @Test
     public void testAllKeys() {
-        testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+        testInputEvents(R.raw.sony_dualshock3_usb_keyeventtests);
     }
 
     @Test
     public void testAllMotions() {
-        testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+        testInputEvents(R.raw.sony_dualshock3_usb_motioneventtests);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
similarity index 68%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
index 409c84f..ca36a68 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 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.
@@ -26,19 +26,20 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4BluetoothTest extends InputTestCase {
 
-    public SonyDualshock4Test() {
-        super(R.raw.sony_dualshock4_register);
+    // Simulates the behavior of PlayStation DualShock4 gamepad (model CUH-ZCT1U)
+    public SonyDualshock4BluetoothTest() {
+        super(R.raw.sony_dualshock4_bluetooth_register);
     }
 
     @Test
     public void testAllKeys() {
-        testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+        testInputEvents(R.raw.sony_dualshock4_bluetooth_keyeventtests);
     }
 
     @Test
     public void testAllMotions() {
-        testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+        testInputEvents(R.raw.sony_dualshock4_bluetooth_motioneventtests);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
similarity index 70%
rename from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
rename to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
index 409c84f..c5f761f 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
@@ -26,19 +26,20 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4ProBluetoothTest extends InputTestCase {
 
-    public SonyDualshock4Test() {
-        super(R.raw.sony_dualshock4_register);
+    // Simulates the behavior of PlayStation DualShock4 Pro gamepad (model CUH-ZCT2U)
+    public SonyDualshock4ProBluetoothTest() {
+        super(R.raw.sony_dualshock4pro_bluetooth_register);
     }
 
     @Test
     public void testAllKeys() {
-        testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+        testInputEvents(R.raw.sony_dualshock4_bluetooth_keyeventtests);
     }
 
     @Test
     public void testAllMotions() {
-        testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+        testInputEvents(R.raw.sony_dualshock4_bluetooth_motioneventtests);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProUsbTest.java
similarity index 68%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProUsbTest.java
index 409c84f..8e967ff 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProUsbTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 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.
@@ -26,19 +26,20 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4ProUsbTest extends InputTestCase {
 
-    public SonyDualshock4Test() {
-        super(R.raw.sony_dualshock4_register);
+    // Simulates the behavior of PlayStation DualShock4 Pro gamepad (model CUH-ZCT2U)
+    public SonyDualshock4ProUsbTest() {
+        super(R.raw.sony_dualshock4pro_usb_register);
     }
 
     @Test
     public void testAllKeys() {
-        testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+        testInputEvents(R.raw.sony_dualshock4_usb_keyeventtests);
     }
 
     @Test
     public void testAllMotions() {
-        testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+        testInputEvents(R.raw.sony_dualshock4_usb_motioneventtests);
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
similarity index 69%
copy from tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
copy to tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
index 409c84f..062dced 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4Test.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 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.
@@ -26,19 +26,20 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SonyDualshock4Test extends InputTestCase {
+public class SonyDualshock4UsbTest extends InputTestCase {
 
-    public SonyDualshock4Test() {
-        super(R.raw.sony_dualshock4_register);
+    // Simulates the behavior of PlayStation DualShock4 gamepad (model CUH-ZCT1U)
+    public SonyDualshock4UsbTest() {
+        super(R.raw.sony_dualshock4_usb_register);
     }
 
     @Test
     public void testAllKeys() {
-        testInputEvents(R.raw.sony_dualshock4_keyeventtests);
+        testInputEvents(R.raw.sony_dualshock4_usb_keyeventtests);
     }
 
     @Test
     public void testAllMotions() {
-        testInputEvents(R.raw.sony_dualshock4_motioneventtests);
+        testInputEvents(R.raw.sony_dualshock4_usb_motioneventtests);
     }
 }
diff --git a/tests/tests/identity/AndroidTest.xml b/tests/tests/identity/AndroidTest.xml
index 25e1dfe..6c08b5e 100644
--- a/tests/tests/identity/AndroidTest.xml
+++ b/tests/tests/identity/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsIdentityTestCases.apk" />
diff --git a/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
new file mode 100644
index 0000000..34b0cf2
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import android.security.identity.IdentityCredential;
+import android.security.identity.IdentityCredentialStore;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.Ignore;
+
+import com.google.common.primitives.Bytes;
+
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import java.util.Optional;
+
+public class AttestationTest {
+    private static final String TAG = "AttestationTest";
+
+    // The subset of Keymaster tags we care about. See
+    //
+    //   https://source.android.com/security/keystore/tags
+    //
+    // for a list of known tags.
+    //
+    public static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
+    public static final int KM_TAG_IDENTITY_CREDENTIAL_KEY = 721;
+
+    @Ignore("Not ready until SW implementation produces correct attestations")
+    @Test
+    public void attestationTest() throws Exception {
+        Context appContext = InstrumentationRegistry.getTargetContext();
+        IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+        // Use a random challenge of length between 16 and 32.
+        SecureRandom random = new SecureRandom();
+        int challengeLength = 16 + random.nextInt(16);
+        byte[] challenge = new byte[challengeLength];
+        random.nextBytes(challenge);
+
+        String credentialName = "test";
+
+        store.deleteCredentialByName(credentialName);
+        Collection<X509Certificate> certChain = ProvisioningTest.createCredentialWithChallenge(
+            store, credentialName, challenge);
+        store.deleteCredentialByName("test");
+
+        // Check that each certificate in the chain isn't expired and also that it's signed by the
+        // next one.
+        assertTrue(verifyCertificateChain(certChain));
+
+        // Parse the attestation record... Identity Credential uses standard KeyMaster-style
+        // attestation with only a few differences:
+        //
+        // - Tag::IDENTITY_CREDENTIAL_KEY (boolean) must be set to identify the key as an Identity
+        //   Credential key.
+        //
+        // - the KeyMaster version and security-level should be set to that of Identity Credential
+        //
+        // In this test we only test for these two things and a) the attestationApplicationId
+        // matches the calling application; and b) the given challenge matches what was sent.
+        //
+        // We could test for all the other expected attestation records but currently we assume
+        // this is already tested by Keymaster/Android Keystore tests since the TA is expected to
+        // re-use the same infrastructure.
+        //
+        X509Certificate cert = certChain.iterator().next();
+        ParsedAttestationRecord record = new ParsedAttestationRecord(cert);
+
+        // Need at least attestation version 3 and attestations to be done in either the TEE
+        // or in a StrongBox.
+        assertTrue(record.getAttestationVersion() >= 3);
+
+        // Also per the IC HAL, the keymasterSecurityLevel field is used as the securityLevel of
+        // the IC TA and this must be either TEE or in a StrongBox... except we specifically
+        // allow our software implementation to report Software here.
+        //
+        // Since we cannot get the implementation name or author at this layer, we can't test for
+        // it. This can be tested for in the VTS test, however.
+
+        // As per the IC HAL, the keymasterVersion field should be the version of the Identity
+        // Credential HAL - 1.0 - and this is encoded as major*10 + minor. This field is used by
+        // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
+        // and integers greater or equal than 41 (for KM starting with 4.1).
+        //
+        // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
+        // version isn't errornously returned.
+        assertTrue(record.getKeymasterVersion() >= 10);
+        assertTrue(record.getKeymasterVersion() < 40);
+
+        // Check that the challenge we passed in, is in fact in the attestation record.
+        assertArrayEquals(challenge, record.getAttestationChallenge());
+
+        // Tag::ATTESTATION_APPLICATION_ID is used to identify the set of possible applications of
+        // which one has initiated a key attestation. This is complicated ASN.1 which we don't want
+        // to parse and we don't need to [1].. we can however easily check that our applicationId
+        // is appearing as a substring somewhere in this blob.
+        //
+        // [1] : and the point of this test isn't to verify that attestations are done correctly,
+        // that's tested elsewhere in e.g. KM and Android Keystore.
+        //
+        Optional<byte[]> attestationApplicationId =
+                record.getSoftwareAuthorizationByteString(KM_TAG_ATTESTATION_APPLICATION_ID);
+        assertTrue(attestationApplicationId.isPresent());
+        String appId = appContext.getPackageName();
+        assertTrue(Bytes.indexOf(attestationApplicationId.get(), appId.getBytes()) != -1);
+
+        // Tag::IDENTITY_CREDENTIAL_KEY is used in attestations produced by the Identity Credential
+        // HAL when that HAL attests to Credential Keys.
+        boolean isIdentityCredentialKey =
+                record.getTeeAuthorizationBoolean(KM_TAG_IDENTITY_CREDENTIAL_KEY);
+        assertTrue(isIdentityCredentialKey);
+    }
+
+    // This only verifies each cert hasn't expired and signed by the next one.
+    private static boolean verifyCertificateChain(Collection<X509Certificate> certChain) {
+        X509Certificate[] certs = new X509Certificate[certChain.size()];
+        certs = certChain.toArray(certs);
+        X509Certificate parent = certs[certs.length - 1];
+        for (int i = certs.length - 1; i >= 0; i--) {
+            X509Certificate cert = certs[i];
+            try {
+                cert.checkValidity();
+                cert.verify(parent.getPublicKey());
+            } catch (Exception e) {
+                e.printStackTrace();
+                return false;
+            }
+            parent = cert;
+        }
+        return true;
+    }
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java b/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java
new file mode 100644
index 0000000..e4297d7
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.Optional;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+
+// This code is loosely based on https://github.com/google/android-key-attestation
+
+class ParsedAttestationRecord {
+    private static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
+
+    enum SecurityLevel {
+        SOFTWARE,
+        TRUSTED_ENVIRONMENT,
+        STRONG_BOX
+    }
+
+    private static final int ATTESTATION_VERSION_INDEX = 0;
+    private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+    private static final int KEYMASTER_VERSION_INDEX = 2;
+    private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+    private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+    private static final int UNIQUE_ID_INDEX = 5;
+    private static final int SW_ENFORCED_INDEX = 6;
+    private static final int TEE_ENFORCED_INDEX = 7;
+
+    // Some security values. The complete list is in this AOSP file:
+    // hardware/libhardware/include/hardware/keymaster_defs.h
+    private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
+    private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
+    private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
+
+    private int attestationVersion;
+    private SecurityLevel attestationSecurityLevel;
+    private int keymasterVersion;
+    private SecurityLevel keymasterSecurityLevel;
+    private byte[] attestationChallenge;
+    private byte[] uniqueId;
+
+    private Map<Integer, ASN1Primitive> softwareEnforcedAuthorizations;
+    private Map<Integer, ASN1Primitive> teeEnforcedAuthorizations;
+
+    public int getAttestationVersion() {
+        return attestationVersion;
+    }
+
+    public SecurityLevel getAttestationSecurityLevel() {
+        return attestationSecurityLevel;
+    }
+
+    public int getKeymasterVersion() {
+        return keymasterVersion;
+    }
+
+    public SecurityLevel getKeymasterSecurityLevel() {
+        return attestationSecurityLevel;
+    }
+
+    public byte[] getAttestationChallenge() {
+        return attestationChallenge;
+    }
+
+    public byte[] getUniqueId() {
+        return uniqueId;
+    }
+
+    public Set<Integer> getSoftwareEnforcedAuthorizationTags() {
+        return softwareEnforcedAuthorizations.keySet();
+    }
+
+    public Set<Integer> getTeeEnforcedAuthorizationTags() {
+        return teeEnforcedAuthorizations.keySet();
+    }
+
+    private static ASN1Primitive findAuthorizationListEntry(
+        Map<Integer, ASN1Primitive> authorizationMap, int tag) {
+        return authorizationMap.getOrDefault(tag, null);
+    }
+
+    public Optional<Integer> getSoftwareAuthorizationInteger(int tag) {
+        ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+        return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
+    }
+
+    public Optional<Integer> getTeeAuthorizationInteger(int tag) {
+        ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+        return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
+    }
+    public boolean getSoftwareAuthorizationBoolean(int tag) {
+        ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+        return entry != null;
+    }
+
+    public boolean getTeeAuthorizationBoolean(int tag) {
+        ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+        return entry != null;
+    }
+
+    public Optional<byte[]> getSoftwareAuthorizationByteString(int tag) {
+        ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+        return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
+    }
+
+    public Optional<byte[]> getTeeAuthorizationByteString(int tag) {
+        ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+        return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
+    }
+
+    private static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) {
+        if (asn1Value instanceof ASN1Boolean) {
+            return ((ASN1Boolean) asn1Value).isTrue();
+        } else {
+            throw new RuntimeException(
+                "Boolean value expected; found " + asn1Value.getClass().getName() + " instead.");
+        }
+    }
+
+    private static int getIntegerFromAsn1(ASN1Encodable asn1Value) {
+        if (asn1Value instanceof ASN1Integer) {
+            return ((ASN1Integer) asn1Value).getValue().intValue();
+        } else if (asn1Value instanceof ASN1Enumerated) {
+            return ((ASN1Enumerated) asn1Value).getValue().intValue();
+        } else {
+            throw new IllegalArgumentException(
+                "Integer value expected; found " + asn1Value.getClass().getName() + " instead.");
+        }
+    }
+
+    private static Map<Integer, ASN1Primitive> getAuthorizationMap(
+        ASN1Encodable[] authorizationList) {
+        Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>();
+        for (ASN1Encodable entry : authorizationList) {
+            ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+            authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject());
+        }
+        return authorizationMap;
+    }
+
+    public ParsedAttestationRecord(X509Certificate cert) throws IOException {
+        byte[] attestationExtensionBytes = cert.getExtensionValue(KEY_DESCRIPTION_OID);
+        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+            throw new IllegalArgumentException("Couldn't find keystore attestation extension.");
+        }
+
+        ASN1Sequence seq;
+        try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) {
+            // The extension contains one object, a sequence, in the
+            // Distinguished Encoding Rules (DER)-encoded form. Get the DER
+            // bytes.
+            byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets();
+            // Decode the bytes as an ASN1 sequence object.
+            try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) {
+                seq = (ASN1Sequence) seqInputStream.readObject();
+            }
+        }
+
+        this.attestationVersion = getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
+        this.attestationSecurityLevel =
+                securityLevelToEnum(getIntegerFromAsn1(
+                    seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX)));
+        this.keymasterVersion = getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
+        this.keymasterSecurityLevel = securityLevelToEnum(
+            getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX)));
+        this.attestationChallenge =
+                ((ASN1OctetString) seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets();
+        this.uniqueId = ((ASN1OctetString) seq.getObjectAt(UNIQUE_ID_INDEX)).getOctets();
+
+        this.softwareEnforcedAuthorizations = getAuthorizationMap(
+            ((ASN1Sequence) seq.getObjectAt(SW_ENFORCED_INDEX)).toArray());
+
+        this.teeEnforcedAuthorizations = getAuthorizationMap(
+            ((ASN1Sequence) seq.getObjectAt(TEE_ENFORCED_INDEX)).toArray());
+    }
+
+    private static SecurityLevel securityLevelToEnum(int securityLevel) {
+        switch (securityLevel) {
+            case KM_SECURITY_LEVEL_SOFTWARE:
+                return SecurityLevel.SOFTWARE;
+            case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
+                return SecurityLevel.TRUSTED_ENVIRONMENT;
+            case KM_SECURITY_LEVEL_STRONG_BOX:
+                return SecurityLevel.STRONG_BOX;
+            default:
+                throw new IllegalArgumentException("Invalid security level.");
+        }
+    }
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
index 8269234..48cc405 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
@@ -107,11 +107,17 @@
 
     static Collection<X509Certificate> createCredential(IdentityCredentialStore store,
             String credentialName) throws IdentityCredentialException {
+        return createCredentialWithChallenge(store, credentialName, "SomeChallenge".getBytes());
+    }
+
+    static Collection<X509Certificate> createCredentialWithChallenge(IdentityCredentialStore store,
+            String credentialName,
+            byte[] challenge) throws IdentityCredentialException {
         WritableIdentityCredential wc = null;
         wc = store.createCredential(credentialName, "org.iso.18013-5.2019.mdl");
 
         Collection<X509Certificate> certificateChain =
-                wc.getCredentialKeyCertificateChain("SomeChallenge".getBytes());
+                wc.getCredentialKeyCertificateChain(challenge);
         // TODO: inspect cert-chain
 
         // Profile 0 (no authentication)
diff --git a/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java b/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java
index 5d9d7aa..527dee5 100644
--- a/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/DesCipherPerformanceTest.java
@@ -18,8 +18,6 @@
 
 import android.security.keystore.KeyProperties;
 
-import org.junit.Test;
-
 import java.security.AlgorithmParameters;
 
 import javax.crypto.Cipher;
@@ -31,18 +29,30 @@
     final int[] TEST_MESSAGE_SIZES = {1 << 6, 1 << 10, 1 << 17};
 
     public void testDESede_CBC_NoPadding() throws Exception {
+        if (!TestUtils.supports3DES()) {
+            return;
+        }
         testDesCipher("DESede/CBC/NoPadding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
     }
 
     public void testDESede_CBC_PKCS7Padding() throws Exception {
+        if (!TestUtils.supports3DES()) {
+            return;
+        }
         testDesCipher("DESede/CBC/PKCS7Padding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
     }
 
     public void testDESede_ECB_NoPadding() throws Exception {
+        if (!TestUtils.supports3DES()) {
+            return;
+        }
         testDesCipher("DESede/ECB/NoPadding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
     }
 
     public void testDESede_ECB_PKCS7Padding() throws Exception {
+        if (!TestUtils.supports3DES()) {
+            return;
+        }
         testDesCipher("DESede/ECB/PKCS7Padding", SUPPORTED_DES_KEY_SIZES, TEST_MESSAGE_SIZES);
     }
 
diff --git a/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java b/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java
index 195d573..0d9d627 100644
--- a/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/DesKeyGenPerformanceTest.java
@@ -18,13 +18,14 @@
 
 import android.security.keystore.KeyProperties;
 
-import org.junit.Test;
-
 public class DesKeyGenPerformanceTest extends PerformanceTestBase {
 
     final int[] SUPPORTED_DES_KEY_SIZES = {168};
 
     public void testDesKeyGen() throws Exception {
+        if (!TestUtils.supports3DES()) {
+            return;
+        }
         for (int keySize : SUPPORTED_DES_KEY_SIZES) {
             measure(
                     new KeystoreSecretKeyGenMeasurable(
diff --git a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
index ba94982..e967438 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
@@ -406,10 +406,9 @@
     private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception {
         KeyPairGenerator kpg =
                 KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
-
         kpg.initialize(
                 new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY)
-                        .setDigests(KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_SHA256)
+                        .setDigests(KeyProperties.DIGEST_SHA256)
                         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
                         .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
                         .setIsStrongBoxBacked(isStrongBoxBacked)
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 55fecfe..46592ca 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -234,14 +234,16 @@
         KeyGenParameterSpec.Builder builder =
             new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
                     .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
-                    .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
                     .setAttestationChallenge(new byte[128])
                     .setKeyValidityStart(now)
                     .setKeyValidityForOriginationEnd(originationEnd)
                     .setKeyValidityForConsumptionEnd(consumptionEnd);
 
         if (TestUtils.hasStrongBox(getContext())) {
+            builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
             builder.setIsStrongBoxBacked(true);
+        } else {
+            builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
         }
 
         generateKeyPair(KEY_ALGORITHM_EC, builder.build());
@@ -251,7 +253,7 @@
 
         try {
             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            verifyCertificateChain(certificates);
+            verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
             checkDeviceLocked(new Attestation(attestationCert));
@@ -392,7 +394,10 @@
                     .setKeyValidityForConsumptionEnd(consumptionEnd);
 
         if (TestUtils.hasStrongBox(getContext())) {
+            builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
             builder.setIsStrongBoxBacked(true);
+        } else {
+            builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
         }
 
         generateKeyPair(KEY_ALGORITHM_RSA, builder.build());
@@ -402,7 +407,7 @@
 
         try {
             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            verifyCertificateChain(certificates);
+            verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
             checkDeviceLocked(new Attestation(attestationCert));
@@ -504,7 +509,7 @@
 
         try {
             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            verifyCertificateChain(certificates);
+            verifyCertificateChain(certificates, false /* expectStrongBox */);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
             Attestation attestation = new Attestation(attestationCert);
@@ -558,7 +563,7 @@
 
         try {
             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
-            verifyCertificateChain(certificates);
+            verifyCertificateChain(certificates, false /* expectStrongBox */);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
             Attestation attestation = new Attestation(attestationCert);
@@ -1076,7 +1081,7 @@
         keyPairGenerator.generateKeyPair();
     }
 
-    private void verifyCertificateChain(Certificate[] certChain)
+    private void verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox)
             throws GeneralSecurityException {
         assertNotNull(certChain);
         for (int i = 1; i < certChain.length; ++i) {
@@ -1107,14 +1112,15 @@
                     assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key"));
                 } else {
                     // Only strongbox implementations should have strongbox in the subject line
-                    assertFalse(x509CurrCert.getSubjectDN()
-                                            .getName()
-                                            .toLowerCase()
-                                            .contains("strongbox"));
+                    assertEquals(expectStrongBox, x509CurrCert.getSubjectDN()
+                                                              .getName()
+                                                              .toLowerCase()
+                                                              .contains("strongbox"));
                 }
             } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
                     | NoSuchProviderException | SignatureException e) {
-                throw new GeneralSecurityException("Failed to verify certificate "
+                throw new GeneralSecurityException("Using StrongBox: " + expectStrongBox + "\n"
+                        + "Failed to verify certificate "
                         + certChain[i - 1] + " with public key " + certChain[i].getPublicKey(), e);
             }
         }
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index 6353fed..40970f5 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -66,6 +66,7 @@
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.X509ExtendedKeyManager;
@@ -1812,11 +1813,23 @@
         }
 
         @Override
+        public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
+            SSLEngine engine) {
+            return "fake";
+        }
+
+        @Override
         public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
             return "fake";
         }
 
         @Override
+        public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+            SSLEngine engine) {
+            return "fake";
+        }
+
+        @Override
         public X509Certificate[] getCertificateChain(String alias) {
             return chain;
         }
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 23886be..dcfcc85 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -827,36 +827,37 @@
     }
 
     static int getMaxSupportedPlaintextInputSizeBytes(String transformation, int keySizeBits) {
+        String encryptionPadding = getCipherEncryptionPadding(transformation);
+        int modulusSizeBytes = (keySizeBits + 7) / 8;
+        if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) {
+            return modulusSizeBytes - 1;
+        } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
+                encryptionPadding)) {
+            return modulusSizeBytes - 11;
+        } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(
+                encryptionPadding)) {
+            String digest = getCipherDigest(transformation);
+            int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8;
+            return modulusSizeBytes - 2 * digestOutputSizeBytes - 2;
+        } else {
+            throw new IllegalArgumentException(
+                    "Unsupported encryption padding scheme: " + encryptionPadding);
+        }
+
+    }
+
+    static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) {
         String keyAlgorithm = getCipherKeyAlgorithm(transformation);
         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)
                 || KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
             return Integer.MAX_VALUE;
         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
-            String encryptionPadding = getCipherEncryptionPadding(transformation);
-            int modulusSizeBytes = (keySizeBits + 7) / 8;
-            if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) {
-                return modulusSizeBytes - 1;
-            } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
-                    encryptionPadding)) {
-                return modulusSizeBytes - 11;
-            } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(
-                    encryptionPadding)) {
-                String digest = getCipherDigest(transformation);
-                int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8;
-                return modulusSizeBytes - 2 * digestOutputSizeBytes - 2;
-            } else {
-                throw new IllegalArgumentException(
-                        "Unsupported encryption padding scheme: " + encryptionPadding);
-            }
+            return getMaxSupportedPlaintextInputSizeBytes(transformation, getKeySizeBits(key));
         } else {
             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
         }
     }
 
-    static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) {
-        return getMaxSupportedPlaintextInputSizeBytes(transformation, getKeySizeBits(key));
-    }
-
     static int getDigestOutputSizeBits(String digest) {
         if (KeyProperties.DIGEST_NONE.equals(digest)) {
             return -1;
diff --git a/tests/tests/libthermalndk/Android.bp b/tests/tests/libthermalndk/Android.bp
new file mode 100644
index 0000000..2b7d126
--- /dev/null
+++ b/tests/tests/libthermalndk/Android.bp
@@ -0,0 +1,38 @@
+// 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.
+
+android_test {
+    name: "CtsThermalTestCases",
+    defaults: ["cts_defaults"],
+    compile_multilib: "both",
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+    jni_libs: [
+        "libctsthermal_jni",
+    ],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml b/tests/tests/libthermalndk/AndroidManifest.xml
similarity index 68%
copy from hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
copy to tests/tests/libthermalndk/AndroidManifest.xml
index a649209..eb6eb22 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
+++ b/tests/tests/libthermalndk/AndroidManifest.xml
@@ -14,13 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  -->
+
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.cts.blob.helper">
+        package="android.thermal.cts">
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-
-    <instrumentation
-            android:name="androidx.test.runner.AndroidJUnitRunner"
-            android:targetPackage="com.android.cts.blob.helper"  />
-</manifest>
\ No newline at end of file
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.thermal.cts"
+            android:label="CTS Thermal tests of android.thermal" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/libthermalndk/AndroidTest.xml b/tests/tests/libthermalndk/AndroidTest.xml
new file mode 100644
index 0000000..e4ab0ac
--- /dev/null
+++ b/tests/tests/libthermalndk/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS Thermal test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsThermalTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.thermal.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/libthermalndk/OWNERS b/tests/tests/libthermalndk/OWNERS
new file mode 100644
index 0000000..1c617ec
--- /dev/null
+++ b/tests/tests/libthermalndk/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 46788
+michaelwr@google.com
+lzye@google.com
diff --git a/tests/tests/libthermalndk/jni/Android.bp b/tests/tests/libthermalndk/jni/Android.bp
new file mode 100644
index 0000000..9f95e7f
--- /dev/null
+++ b/tests/tests/libthermalndk/jni/Android.bp
@@ -0,0 +1,31 @@
+// 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.
+
+cc_test_library {
+    name: "libctsthermal_jni",
+    srcs: [
+        "NativeThermalTest.cpp",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libbase",
+    ],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    gtest: false,
+}
diff --git a/tests/tests/libthermalndk/jni/NativeThermalTest.cpp b/tests/tests/libthermalndk/jni/NativeThermalTest.cpp
new file mode 100644
index 0000000..913a8ae
--- /dev/null
+++ b/tests/tests/libthermalndk/jni/NativeThermalTest.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeThermalTest"
+
+#include <condition_variable>
+#include <jni.h>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <inttypes.h>
+#include <time.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android/thermal.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <utils/Errors.h>
+
+using namespace android;
+using namespace std::chrono_literals;
+using android::base::StringPrintf;
+
+struct AThermalTestContext {
+    AThermalManager *mThermalMgr;
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    std::vector<AThermalStatus> mListenerStatus GUARDED_BY(mMutex);
+};
+
+static jclass    gNativeThermalTest_class;
+static jmethodID gNativeThermalTest_thermalOverrideMethodID;
+
+int onStatusChange(void *data, AThermalStatus status) {
+    AThermalTestContext *ctx = static_cast<AThermalTestContext *>(data);
+    if (ctx == nullptr) {
+        return BAD_VALUE;
+    } else {
+        std::lock_guard<std::mutex> guard(ctx->mMutex);
+        ctx->mListenerStatus.push_back(status);
+        ctx->mCv.notify_all();
+    }
+    return OK;
+}
+
+static inline void setThermalStatusOverride(JNIEnv* env, jobject obj, int32_t level) {
+    env->CallVoidMethod(obj, gNativeThermalTest_thermalOverrideMethodID, level);
+}
+
+static inline jstring returnJString(JNIEnv *env, std::optional<std::string> result) {
+    if (result.has_value()) {
+        return env->NewStringUTF(result.value().c_str());
+    } else {
+        return env->NewStringUTF("");
+    }
+}
+
+static std::optional<std::string> testGetCurrentThermalStatus(
+                                        JNIEnv *env, jobject obj, int32_t level) {
+    AThermalTestContext ctx;
+
+    ctx.mThermalMgr = AThermal_acquireManager();
+    if (ctx.mThermalMgr == nullptr) {
+        return "AThermal_acquireManager failed";
+    }
+
+    setThermalStatusOverride(env, obj, level);
+    AThermalStatus thermalStatus = AThermal_getCurrentThermalStatus(ctx.mThermalMgr);
+    if (thermalStatus == ATHERMAL_STATUS_ERROR) {
+        return "getCurrentThermalStatus returns ATHERMAL_STATUS_ERROR";
+    }
+    // Verify the current thermal status is same as override
+    if (thermalStatus != static_cast<AThermalStatus>(level)) {
+        return StringPrintf("getCurrentThermalStatus %" PRId32 " != override %" PRId32 ".",
+                            thermalStatus, level);
+    }
+
+    AThermal_releaseManager(ctx.mThermalMgr);
+    return std::nullopt;
+}
+
+static jstring nativeTestGetCurrentThermalStatus(JNIEnv *env, jobject obj, jint level) {
+    return returnJString(env, testGetCurrentThermalStatus(env, obj, static_cast<int32_t>(level)));
+}
+
+static std::optional<std::string> testRegisterThermalStatusListener(JNIEnv *env, jobject obj) {
+    AThermalTestContext ctx;
+    std::unique_lock<std::mutex> lock(ctx.mMutex);
+
+    ctx.mThermalMgr = AThermal_acquireManager();
+    if (ctx.mThermalMgr == nullptr) {
+        return "AThermal_acquireManager failed";
+    }
+
+    // Register a listener with valid callback
+    int ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != 0) {
+        return StringPrintf("AThermal_registerThermalStatusListener failed: %s",
+                    strerror(ret));
+    }
+
+    // Expect the callback after registration
+    if (ctx.mCv.wait_for(lock, 1s) == std::cv_status::timeout) {
+        return "Listener callback should be called after registration";
+    }
+
+    // Verify the current thermal status is same as listener callback
+    auto thermalStatus = AThermal_getCurrentThermalStatus(ctx.mThermalMgr);
+    auto listenerStatus = ctx.mListenerStatus.back();
+    if (thermalStatus != listenerStatus) {
+        return StringPrintf("thermalStatus %" PRId32 " != Listener status %" PRId32 ".",
+                            thermalStatus, listenerStatus);
+    }
+
+    // Change override level and verify the listener callback
+    for (int32_t level = ATHERMAL_STATUS_LIGHT; level <= ATHERMAL_STATUS_SHUTDOWN; level++) {
+        setThermalStatusOverride(env, obj, level);
+        if (ctx.mCv.wait_for(lock, 1s) == std::cv_status::timeout) {
+            return StringPrintf("Listener callback timeout at level %" PRId32, level);
+        }
+        auto overrideStatus = static_cast<AThermalStatus>(level);
+        auto listenerStatus = ctx.mListenerStatus.back();
+        if (listenerStatus != overrideStatus) {
+            return StringPrintf("Listener thermalStatus%" PRId32 " != override %" PRId32 ".",
+                            listenerStatus, overrideStatus);
+        }
+    }
+
+    // Unregister listener
+    ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != 0) {
+        return StringPrintf("AThermal_unregisterThermalStatusListener failed: %s",
+                    strerror(ret));
+    }
+
+    AThermal_releaseManager(ctx.mThermalMgr);
+    return std::nullopt;
+}
+
+static jstring nativeTestRegisterThermalStatusListener(JNIEnv *env, jobject obj) {
+    return returnJString(env, testRegisterThermalStatusListener(env, obj));
+}
+
+static std::optional<std::string> testThermalStatusRegisterNullListener() {
+    AThermalTestContext ctx;
+
+    ctx.mThermalMgr = AThermal_acquireManager();
+    if (ctx.mThermalMgr == nullptr) {
+        return StringPrintf("AThermal_acquireManager failed");
+    }
+
+    // Register a listener with null callback
+    int ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, nullptr, &ctx);
+    if (ret != EINVAL) {
+        return "AThermal_registerThermalStatusListener should fail with null callback";
+    }
+
+    // Register a listener with null data
+    ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != 0) {
+        return StringPrintf("AThermal_registerThermalStatusListener failed: %s",
+                    strerror(ret));
+    }
+
+    // Unregister listener with null callback and null data
+    ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, nullptr, nullptr);
+    if (ret != EINVAL) {
+        return "AThermal_unregisterThermalStatusListener should fail with null listener";
+    }
+
+    AThermal_releaseManager(ctx.mThermalMgr);
+    return std::nullopt;
+}
+
+static jstring nativeTestThermalStatusRegisterNullListener(JNIEnv *env, jobject) {
+    return returnJString(env, testThermalStatusRegisterNullListener());
+}
+
+static std::optional<std::string> testThermalStatusListenerDoubleRegistration
+                                                        (JNIEnv *env, jobject obj) {
+    AThermalTestContext ctx;
+    std::unique_lock<std::mutex> lock(ctx.mMutex);
+
+    ctx.mThermalMgr = AThermal_acquireManager();
+    if (ctx.mThermalMgr == nullptr) {
+        return "AThermal_acquireManager failed";
+    }
+
+    // Register a listener with valid callback
+    int ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != 0) {
+        return StringPrintf("AThermal_registerThermalStatusListener failed: %s",
+                    strerror(ret));
+    }
+
+    // Register the listener again with same callback and data
+    ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != EINVAL) {
+        return "Register should fail as listener already registered";
+    }
+
+    // Register a listener with same callback but null data
+    ret = AThermal_registerThermalStatusListener(ctx.mThermalMgr, onStatusChange, nullptr);
+    if (ret != 0) {
+        return StringPrintf("Register listener with null data failed: %s", strerror(ret));
+    }
+
+    // Expect listener callback
+    if (ctx.mCv.wait_for(lock, 1s) == std::cv_status::timeout) {
+        return "Thermal listener callback timeout";
+    }
+
+    // Unregister listener
+    ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != 0) {
+        return StringPrintf("AThermal_unregisterThermalStatusListener failed: %s",
+                    strerror(ret));
+    }
+
+    for (int32_t level = ATHERMAL_STATUS_LIGHT; level <= ATHERMAL_STATUS_SHUTDOWN; level++) {
+        setThermalStatusOverride(env, obj, level);
+        // Expect no listener callback
+        if (ctx.mCv.wait_for(lock, 1s) != std::cv_status::timeout) {
+            return "Thermal listener got callback after unregister.";
+        }
+    }
+
+    // Unregister listener already unregistered
+    ret = AThermal_unregisterThermalStatusListener(ctx.mThermalMgr, onStatusChange, &ctx);
+    if (ret != EINVAL) {
+        return "Unregister should fail with listener already unregistered";
+    }
+
+    AThermal_releaseManager(ctx.mThermalMgr);
+    return std::nullopt;
+}
+
+static jstring nativeTestThermalStatusListenerDoubleRegistration(JNIEnv *env, jobject obj) {
+    return returnJString(env, testThermalStatusListenerDoubleRegistration(env, obj));
+}
+
+extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
+    JNIEnv* env;
+    const JNINativeMethod methodTable[] = {
+        {"nativeTestGetCurrentThermalStatus", "(I)Ljava/lang/String;",
+         (void*)nativeTestGetCurrentThermalStatus},
+        {"nativeTestRegisterThermalStatusListener", "()Ljava/lang/String;",
+         (void*)nativeTestRegisterThermalStatusListener},
+        {"nativeTestThermalStatusRegisterNullListener", "()Ljava/lang/String;",
+         (void*)nativeTestThermalStatusRegisterNullListener},
+        {"nativeTestThermalStatusListenerDoubleRegistration", "()Ljava/lang/String;",
+         (void*)nativeTestThermalStatusListenerDoubleRegistration},
+    };
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return JNI_ERR;
+    }
+    gNativeThermalTest_class = env->FindClass("android/thermal/cts/NativeThermalTest");
+    gNativeThermalTest_thermalOverrideMethodID =
+                env->GetMethodID(gNativeThermalTest_class, "setOverrideStatus", "(I)V");
+    if (gNativeThermalTest_thermalOverrideMethodID == nullptr) {
+        return JNI_ERR;
+    }
+    if (env->RegisterNatives(gNativeThermalTest_class, methodTable,
+            sizeof(methodTable) / sizeof(JNINativeMethod)) != JNI_OK) {
+        return JNI_ERR;
+    }
+    return JNI_VERSION_1_6;
+}
diff --git a/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java b/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
new file mode 100644
index 0000000..ab6d518
--- /dev/null
+++ b/tests/tests/libthermalndk/src/android/thermal/cts/NativeThermalTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.thermal.cts;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.support.test.uiautomator.UiDevice;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import com.google.common.base.Strings;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Tests native thermal API for get current thermal status, register and unregister
+ * thermal status listeners.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NativeThermalTest {
+    private UiDevice mUiDevice;
+    private Executor mExec = Executors.newSingleThreadExecutor();
+
+    private native String nativeTestGetCurrentThermalStatus(int level);
+    private native String nativeTestRegisterThermalStatusListener();
+    private native String nativeTestThermalStatusRegisterNullListener();
+    private native String nativeTestThermalStatusListenerDoubleRegistration();
+
+    @Before
+    public void setUp() throws Exception {
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mUiDevice.executeShellCommand("cmd thermalservice reset");
+    }
+
+    /**
+     * Helper function to set override status
+     */
+    public void setOverrideStatus (int level)  throws Exception {
+        mUiDevice.executeShellCommand("cmd thermalservice override-status " + level);
+    }
+
+    /**
+     * Confirm that we can get thermal status.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testGetCurrentThermalStatus() throws Exception {
+        for (int level = PowerManager.THERMAL_STATUS_NONE;
+                level < PowerManager.THERMAL_STATUS_SHUTDOWN; level++) {
+            final String failureMessage = nativeTestGetCurrentThermalStatus(level);
+            if (!Strings.isNullOrEmpty(failureMessage)) {
+                fail(failureMessage);
+            }
+        }
+    }
+
+    /**
+     * Confirm that we can register thermal status listener and get callback.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testRegisterThermalStatusListener() throws Exception {
+        final String failureMessage = nativeTestRegisterThermalStatusListener();
+        if (!Strings.isNullOrEmpty(failureMessage)) {
+            fail(failureMessage);
+        }
+    }
+
+    /**
+     * Confirm that register null thermal status listener fails with error.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testThermalStatusRegisterNullListener() throws Exception {
+        final String failureMessage = nativeTestThermalStatusRegisterNullListener();
+        if (!Strings.isNullOrEmpty(failureMessage)) {
+            fail(failureMessage);
+        }
+    }
+
+    /**
+     * Confirm that double register and unregister same listener fails with error.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testThermalStatusListenerDoubleRegistration() throws Exception {
+        final String failureMessage = nativeTestThermalStatusListenerDoubleRegistration();
+        if (!Strings.isNullOrEmpty(failureMessage)) {
+            fail(failureMessage);
+        }
+    }
+
+    static {
+        System.loadLibrary("ctsthermal_jni");
+    }
+}
diff --git a/tests/tests/match_flags/Android.bp b/tests/tests/match_flags/Android.bp
new file mode 100644
index 0000000..7debb8a
--- /dev/null
+++ b/tests/tests/match_flags/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_test {
+    name: "CtsMatchFlagTestCases",
+    defaults: ["cts_defaults"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    static_libs: [
+        "ctstestrunner-axt",
+        "compatibility-device-util-axt",
+	    "androidx.test.ext.junit",
+    ],
+
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+}
diff --git a/tests/tests/match_flags/AndroidManifest.xml b/tests/tests/match_flags/AndroidManifest.xml
new file mode 100644
index 0000000..1b6cb42
--- /dev/null
+++ b/tests/tests/match_flags/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.matchflags.cts">
+
+    <application>
+      <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.matchflags.cts"
+                     android:label="CTS tests for match flags">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/match_flags/AndroidTest.xml b/tests/tests/match_flags/AndroidTest.xml
new file mode 100644
index 0000000..a5fadab
--- /dev/null
+++ b/tests/tests/match_flags/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<configuration description="Config for match flag CTS test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <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" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsMatchFlagTestCases.apk" />
+        <option name="test-file-name" value="CtsMatchFlagsUniqueAndSharedUri.apk" />
+        <option name="test-file-name" value="CtsMatchFlagsSharedUri.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.matchflags.cts" />
+        <option name="runtime-hint" value="12m30s" />
+    </test>
+</configuration>
diff --git a/tests/tests/match_flags/OWNERS b/tests/tests/match_flags/OWNERS
new file mode 100644
index 0000000..8a44fb2
--- /dev/null
+++ b/tests/tests/match_flags/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+patb@google.com
+toddke@google.com
+chiuwinson@google.com
+rtmitchell@google.com
diff --git a/tests/tests/match_flags/app/a/Android.bp b/tests/tests/match_flags/app/a/Android.bp
new file mode 100644
index 0000000..9a5db56
--- /dev/null
+++ b/tests/tests/match_flags/app/a/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 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: "CtsMatchFlagsUniqueAndSharedUri",
+    manifest: "AndroidManifest.xml",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/a/AndroidManifest.xml b/tests/tests/match_flags/app/a/AndroidManifest.xml
new file mode 100644
index 0000000..8ad54de
--- /dev/null
+++ b/tests/tests/match_flags/app/a/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.matchflags.app.uniqueandshared">
+    <application>
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name="android.matchflags.cts.app.TestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW"/>
+                <category android:name="android.intent.category.BROWSABLE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:scheme="https"
+                      android:host="unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.matchflags.app.UNIQUE_ACTION"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.matchflags.app.SHARED_ACTION"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java b/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java
new file mode 100644
index 0000000..e9046eb
--- /dev/null
+++ b/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.matchflags.cts.app;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/b/Android.bp b/tests/tests/match_flags/app/b/Android.bp
new file mode 100644
index 0000000..8c49047
--- /dev/null
+++ b/tests/tests/match_flags/app/b/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 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: "CtsMatchFlagsSharedUri",
+    manifest: "AndroidManifest.xml",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/b/AndroidManifest.xml b/tests/tests/match_flags/app/b/AndroidManifest.xml
new file mode 100644
index 0000000..6d2c9d0
--- /dev/null
+++ b/tests/tests/match_flags/app/b/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.matchflags.app.shared">
+    <application>
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name="android.matchflags.cts.app.TestActivity">
+            <intent-filter>
+                <action android:name="android.matchflags.app.SHARED_ACTION"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java b/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java
new file mode 100644
index 0000000..e9046eb
--- /dev/null
+++ b/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.matchflags.cts.app;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
new file mode 100644
index 0000000..da7e212
--- /dev/null
+++ b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.matchflags.cts;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.hamcrest.core.IsNull;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class MatchFlagTests {
+
+    private static final String ONLY_BROWSER_URI =
+            "https://nohandler-02xgpcssu1v7xvpek0skc905glnyu7ihjtza3eufox0mauqyri.com";
+    private static final String UNIQUE_URI =
+            "https://unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com";
+    @Rule
+    public TestName name = new TestName();
+
+    @BeforeClass
+    public static void setup() {
+
+    }
+
+    @Test
+    public void startNoBrowserIntentWithNoMatchingApps() throws Exception {
+        Intent onlyBrowserIntent = new Intent(Intent.ACTION_VIEW)
+                .addCategory(Intent.CATEGORY_BROWSABLE)
+                .setData(Uri.parse(ONLY_BROWSER_URI));
+
+        startActivity(onlyBrowserIntent);
+
+        Intent noBrowserWithBrowserOnlyIntent = new Intent(onlyBrowserIntent)
+                .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
+
+        try {
+            startActivity(noBrowserWithBrowserOnlyIntent);
+            fail("Should not have started a browser-only intent with NON_BROWSER flag set");
+        } catch (ActivityNotFoundException e) {
+            // hooray!
+        }
+    }
+
+    @Test
+    public void startRequireDefaultWithNoDefault() throws Exception {
+        Intent sharedIntent = new Intent("android.matchflags.app.SHARED_ACTION");
+
+        startActivity(sharedIntent);
+
+        Intent sharedIntentRequireDefault = new Intent(sharedIntent)
+                .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+
+        try {
+            startActivity(sharedIntentRequireDefault);
+            fail("Should have started intent with no default when default required");
+        } catch (ActivityNotFoundException e) {
+            // hooray!
+        }
+    }
+
+    @Test
+    public void startRequireDefaultWithSingleMatch() throws Exception {
+        Intent uniqueIntent = new Intent("android.matchflags.app.UNIQUE_ACTION")
+                .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+        startActivity(uniqueIntent);
+    }
+
+    @Test
+    public void startNoBrowserRequireDefault() throws Exception {
+        Intent uniqueUriIntent = new Intent(Intent.ACTION_VIEW)
+                .addCategory(Intent.CATEGORY_BROWSABLE)
+                .setData(Uri.parse(UNIQUE_URI));
+
+        startActivity(uniqueUriIntent);
+
+        Intent uniqueUriIntentNoBrowserRequireDefault = new Intent(uniqueUriIntent)
+                .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER)
+                .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+
+        startActivity(uniqueUriIntentNoBrowserRequireDefault);
+    }
+
+    private static void startActivity(Intent onlyBrowserIntent) {
+        InstrumentationRegistry.getInstrumentation().getContext().startActivity(
+                onlyBrowserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+}
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index a7727b1..1bec10d 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -143,7 +143,7 @@
             android:foregroundServiceType="mediaProjection"
             android:enabled="true">
         </service>
-        <service android:name=".SampleMediaRoute2ProviderService"
+        <service android:name=".StubMediaRoute2ProviderService"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.media.MediaRoute2ProviderService" />
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 90f517a..7e475d1 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -37,7 +37,6 @@
 import static android.media.AudioManager.VIBRATE_TYPE_RINGER;
 import static android.provider.Settings.System.SOUND_EFFECTS_ENABLED;
 
-import android.app.ActivityManager;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
@@ -114,8 +113,6 @@
         mIsSingleVolume = mContext.getResources().getBoolean(
                 Resources.getSystem().getIdentifier("config_single_volume", "bool", "android"));
         mSkipRingerTests = mUseFixedVolume || mIsTelevision || mIsSingleVolume;
-        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        mSupportNotificationPolicyAccess = !am.isLowRamDevice();
 
         // Store the original volumes that that they can be recovered in tearDown().
         final int[] streamTypes = {
@@ -133,19 +130,18 @@
             mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
         }
 
-        if (mSupportNotificationPolicyAccess) {
-            try {
-                Utils.toggleNotificationPolicyAccess(
-                        mContext.getPackageName(), getInstrumentation(), true);
-                mOriginalNotificationPolicy = mNm.getNotificationPolicy();
-                mOriginalZen = mNm.getCurrentInterruptionFilter();
-            } finally {
-                setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
-                Utils.toggleNotificationPolicyAccess(
-                        mContext.getPackageName(), getInstrumentation(), false);
-            }
+        try {
+            Utils.toggleNotificationPolicyAccess(
+                    mContext.getPackageName(), getInstrumentation(), true);
+            mOriginalNotificationPolicy = mNm.getNotificationPolicy();
+            mOriginalZen = mNm.getCurrentInterruptionFilter();
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+            Utils.toggleNotificationPolicyAccess(
+                    mContext.getPackageName(), getInstrumentation(), false);
         }
 
+
         // Check original mirchrophone mute/unmute status
         mDoNotCheckUnmute = false;
         if (mAudioManager.isMicrophoneMute()) {
@@ -159,9 +155,6 @@
 
     @Override
     protected void tearDown() throws Exception {
-        if (!mSupportNotificationPolicyAccess) {
-            return;
-        }
         try {
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), true);
@@ -303,9 +296,6 @@
     }
 
     public void testCheckingZenModeBlockDoesNotRequireNotificationPolicyAccess() throws Exception {
-        if (!mSupportNotificationPolicyAccess) {
-            return;
-        }
         try {
             // set zen mode to priority only, so playSoundEffect will check notification policy
             Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
@@ -396,7 +386,7 @@
     }
 
     public void testVibrateNotification() throws Exception {
-        if (mUseFixedVolume || !mHasVibrator || !mSupportNotificationPolicyAccess) {
+        if (mUseFixedVolume || !mHasVibrator) {
             return;
         }
         Utils.toggleNotificationPolicyAccess(
@@ -459,7 +449,7 @@
     }
 
     public void testVibrateRinger() throws Exception {
-        if (mUseFixedVolume || !mHasVibrator || !mSupportNotificationPolicyAccess) {
+        if (mUseFixedVolume || !mHasVibrator) {
             return;
         }
         Utils.toggleNotificationPolicyAccess(
@@ -523,9 +513,6 @@
     }
 
     public void testAccessRingMode() throws Exception {
-        if (!mSupportNotificationPolicyAccess) {
-            return;
-        }
         Utils.toggleNotificationPolicyAccess(
                 mContext.getPackageName(), getInstrumentation(), true);
         mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
@@ -550,7 +537,7 @@
     }
 
     public void testSetRingerModePolicyAccess() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         // Apps without policy access cannot change silent -> normal or silent -> vibrate.
@@ -613,9 +600,6 @@
     }
 
     public void testVolume() throws Exception {
-        if (!mSupportNotificationPolicyAccess) {
-            return;
-        }
         Utils.toggleNotificationPolicyAccess(
                 mContext.getPackageName(), getInstrumentation(), true);
         int volume, volumeDelta;
@@ -838,7 +822,7 @@
     }
 
     public void testMuteDndAffectedStreams() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         int[] streams = { AudioManager.STREAM_RING };
@@ -912,7 +896,7 @@
     }
 
     public void testMuteDndUnaffectedStreams() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         int[] streams = {
@@ -1006,7 +990,7 @@
     }
 
     public void testAdjustVolumeInTotalSilenceMode() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -1027,7 +1011,7 @@
     }
 
     public void testAdjustVolumeInAlarmsOnlyMode() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -1052,7 +1036,7 @@
     }
 
     public void testSetStreamVolumeInTotalSilenceMode() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -1076,7 +1060,7 @@
     }
 
     public void testSetStreamVolumeInAlarmsOnlyMode() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -1098,7 +1082,7 @@
     }
 
     public void testSetStreamVolumeInPriorityOnlyMode() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         Utils.toggleNotificationPolicyAccess(
@@ -1136,7 +1120,7 @@
     }
 
     public void testAdjustVolumeInPriorityOnly() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
@@ -1181,7 +1165,7 @@
     }
 
     public void testPriorityOnlyMuteAll() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
@@ -1216,7 +1200,7 @@
     }
 
     public void testPriorityOnlyMediaAllowed() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
         Utils.toggleNotificationPolicyAccess(
@@ -1251,7 +1235,7 @@
     }
 
     public void testPriorityOnlySystemAllowed() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
@@ -1285,7 +1269,7 @@
     }
 
     public void testPriorityOnlySystemDisallowedWithRingerMuted() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
@@ -1322,7 +1306,7 @@
     }
 
     public void testPriorityOnlyAlarmsAllowed() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
@@ -1358,7 +1342,7 @@
     }
 
     public void testPriorityOnlyRingerAllowed() throws Exception {
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
@@ -1394,7 +1378,7 @@
 
     public void testPriorityOnlyChannelsCanBypassDnd() throws Exception {
         final String NOTIFICATION_CHANNEL_ID = "test_id";
-        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+        if (mSkipRingerTests) {
             return;
         }
 
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
index ee23442..7b00c9a 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
@@ -42,7 +42,6 @@
 import android.media.projection.MediaProjection;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.rule.ActivityTestRule;
@@ -78,7 +77,6 @@
     private AudioManager mAudioManager;
     private boolean mPlaybackBeforeCapture;
     private int mUid; //< UID of this test
-    private int mUserId; // userId of this test
     private AudioPlaybackCaptureActivity mActivity;
     private MediaProjection mMediaProjection;
     @Rule
@@ -90,8 +88,6 @@
         public @AttributeUsage int[] excludeUsages;
         public int[] matchingUids;
         public int[] excludeUids;
-        public int[] matchingUserIds;
-        public int[] excludeUserIds;
         private AudioPlaybackCaptureConfiguration build(MediaProjection projection)
                 throws Exception {
             AudioPlaybackCaptureConfiguration.Builder apccBuilder =
@@ -117,16 +113,6 @@
                     apccBuilder.excludeUid(uid);
                 }
             }
-            if (matchingUserIds != null) {
-                for (int userId : matchingUserIds) {
-                    apccBuilder.addMatchingUserId(userId);
-                }
-            }
-            if (excludeUserIds != null) {
-                for (int userId : excludeUserIds) {
-                    apccBuilder.excludeUserId(userId);
-                }
-            }
             AudioPlaybackCaptureConfiguration config = apccBuilder.build();
             assertCorreclyBuilt(config);
             return config;
@@ -137,8 +123,6 @@
             assertEqualNullIsEmpty("excludeUsages", excludeUsages, config.getExcludeUsages());
             assertEqualNullIsEmpty("matchingUids", matchingUids, config.getMatchingUids());
             assertEqualNullIsEmpty("excludeUids", excludeUids, config.getExcludeUids());
-            assertEqualNullIsEmpty("matchingUserIds", matchingUserIds, config.getMatchingUserIds());
-            assertEqualNullIsEmpty("excludeUserIds", excludeUserIds, config.getExcludeUserIds());
         }
 
         private void assertEqualNullIsEmpty(String msg, int[] expected, int[] found) {
@@ -158,8 +142,6 @@
         mActivity = mActivityRule.getActivity();
         mAudioManager = mActivity.getSystemService(AudioManager.class);
         mUid = mActivity.getApplicationInfo().uid;
-        mUserId = UserHandle.getUserHandleForUid(mActivity.getApplicationInfo().uid)
-                .getIdentifier();
         mMediaProjection = mActivity.waitForMediaProjection();
     }
 
@@ -339,13 +321,6 @@
         testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testCombineUserId() throws Exception {
-        mAPCTestConfig.matchingUserIds = new int[]{ mUserId };
-        mAPCTestConfig.excludeUserIds = new int[]{ mUserId + 1 };
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
-    }
-
     @Test
     public void testCaptureMatchingAllowedUsage() throws Exception {
         for (int usage : ALLOWED_USAGES) {
@@ -396,17 +371,6 @@
     }
 
     @Test
-    public void testCaptureMatchingUserId() throws Exception {
-        mAPCTestConfig.matchingUserIds = new int[]{ mUserId };
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
-        testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE);
-
-        mAPCTestConfig.matchingUserIds = new int[]{ mUserId + 1 };
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
-    }
-
-    @Test
     public void testCaptureExcludeUid() throws Exception {
         mAPCTestConfig.excludeUids = new int[]{ 0 };
         testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
@@ -417,17 +381,6 @@
         testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
     }
 
-    @Test
-    public void testCaptureExcludeUserId() throws Exception {
-        mAPCTestConfig.excludeUserIds = new int[]{ mUserId + 1 };
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
-        testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE);
-
-        mAPCTestConfig.excludeUserIds = new int[]{ mUserId };
-        testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
-    }
-
     @Test(expected = UnsupportedOperationException.class)
     public void testStoppedMediaProjection() throws Exception {
         mMediaProjection.stop();
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 5f1d322..2384704 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -19,6 +19,7 @@
 import android.media.MediaCodec;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.os.Handler;
 import android.util.Log;
 import android.view.Surface;
 import java.nio.ByteBuffer;
@@ -50,6 +51,7 @@
     private MediaFormat mFormat;
     private MediaFormat mOutputFormat;
     private NonBlockingAudioTrack mAudioTrack;
+    private OnFrameRenderedListener mOnFrameRenderedListener;
 
     /**
      * Manages audio and video playback using MediaCodec and AudioTrack.
@@ -84,6 +86,11 @@
         String mime = mFormat.getString(MediaFormat.KEY_MIME);
         Log.d(TAG, "CodecState::CodecState " + mime);
         mIsAudio = mime.startsWith("audio/");
+
+        if (mTunneled && !mIsAudio) {
+            mOnFrameRenderedListener = new OnFrameRenderedListener();
+            codec.setOnFrameRenderedListener(mOnFrameRenderedListener, new Handler());
+        }
     }
 
     public void release() {
@@ -100,6 +107,11 @@
         mAvailableOutputBufferIndices = null;
         mAvailableOutputBufferInfos = null;
 
+        if (mOnFrameRenderedListener != null) {
+            mCodec.setOnFrameRenderedListener(null, null);
+            mOnFrameRenderedListener = null;
+        }
+
         mCodec.release();
         mCodec = null;
 
@@ -218,11 +230,6 @@
                 Log.d(TAG, "sampleSize: " + sampleSize + " trackIndex:" + trackIndex +
                         " sampleTime:" + sampleTime + " sampleFlags:" + sampleFlags);
                 mSawInputEOS = true;
-                // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
-                // we should use stream duration
-                if (mTunneled && !mIsAudio) {
-                    mSawOutputEOS = true;
-                }
                 return false;
             }
 
@@ -231,9 +238,6 @@
                     mSampleBaseTimeUs = sampleTime;
                 }
                 sampleTime -= mSampleBaseTimeUs;
-                // FIX-ME: in tunneled mode we currently use input buffer time
-                // as video presentation time. This is not accurate and should be fixed
-                mPresentationTimeUs = sampleTime;
             }
 
             if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
@@ -255,11 +259,6 @@
             Log.d(TAG, "saw input EOS on track " + mTrackIndex);
 
             mSawInputEOS = true;
-            // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
-            // we should use stream duration
-            if (mTunneled && !mIsAudio) {
-                mSawOutputEOS = true;
-            }
 
             mCodec.queueInputBuffer(
                     index, 0 /* offset */, 0 /* sampleSize */,
@@ -375,6 +374,23 @@
         }
     }
 
+    /** Callback called by the renderer in tunneling mode. */
+    private class OnFrameRenderedListener implements MediaCodec.OnFrameRenderedListener {
+        private static final long TUNNELING_EOS_PRESENTATION_TIME_US = Long.MAX_VALUE;
+
+        @Override
+        public void onFrameRendered(MediaCodec codec, long presentationTimeUs, long nanoTime) {
+            if (this != mOnFrameRenderedListener) {
+                return; // stale event
+            }
+            if (presentationTimeUs == TUNNELING_EOS_PRESENTATION_TIME_US) {
+                 mSawOutputEOS = true;
+            } else {
+                 mPresentationTimeUs = presentationTimeUs;
+            }
+        }
+    }
+
     public long getAudioTimeUs() {
         if (mAudioTrack == null) {
             return 0;
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index f5e5027..27a8879 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -252,7 +252,7 @@
         MediaDescrambler descrambler = null;
 
         try {
-            mediaCas = new MediaCas(sClearKeySystemId, "TIS_Session_1",
+            mediaCas = new MediaCas(getContext(), sClearKeySystemId, "TIS_Session_1",
                 android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
             descrambler = new MediaDescrambler(sClearKeySystemId);
 
@@ -266,9 +266,7 @@
                 fail("Can't open session for program");
             }
 
-            if (!PropertyUtil.isVendorApiLevelNewerThan(API_LEVEL_BEFORE_CAS_SESSION + 1)) {
-                Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
-            }
+            Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
 
             session.setPrivateData(pvtData);
 
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
index 27f9ddc..7123d9c 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
@@ -173,12 +173,16 @@
             mExtractor.advance();
             mSignaledEos = mExtractor.getSampleTrackIndex() == -1
                     || timestampUs >= mLastBufferTimestampUs;
-            codec.getQueueRequest(index).setLinearBlock(
-                    input.block,
-                    input.offset,
-                    written,
-                    timestampUs,
-                    mSignaledEos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0).queue();
+            codec.getQueueRequest(index)
+                    .setLinearBlock(
+                            input.block,
+                            input.offset,
+                            written,
+                            null /* cryptoInfo */)
+                    .setPresentationTimeUs(timestampUs)
+                    .setFlags(
+                            mSignaledEos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0)
+                    .queue();
             input.offset += written;
         }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 90125d0..193345a 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -2532,4 +2532,104 @@
             }
         }
     }
+
+    /**
+     * Test if flushing early in the playback does not prevent client from getting the
+     * latest configuration. Empirically, this happens most often when the
+     * codec is flushed after the first buffer is queued, so this test walks
+     * through the scenario.
+     */
+    public void testFlushAfterFirstBuffer() throws Exception {
+        for (int i = 0; i < 100; ++i) {
+            doFlushAfterFirstBuffer();
+        }
+    }
+
+    private void doFlushAfterFirstBuffer() throws Exception {
+        MediaExtractor extractor = null;
+        MediaCodec codec = null;
+
+        try {
+            MediaFormat newFormat = null;
+            extractor = getMediaExtractorForMimeType(
+                    R.raw.noise_2ch_48khz_aot29_dr_sbr_sig2_mp4, "audio/");
+            int trackIndex = extractor.getSampleTrackIndex();
+            MediaFormat format = extractor.getTrackFormat(trackIndex);
+            codec = createCodecByType(
+                    format.getString(MediaFormat.KEY_MIME), false /* isEncoder */);
+            codec.configure(format, null, null, 0);
+            codec.start();
+            int firstInputIndex = codec.dequeueInputBuffer(0);
+            while (firstInputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                firstInputIndex = codec.dequeueInputBuffer(5000);
+            }
+            assertTrue(firstInputIndex >= 0);
+            extractor.readSampleData(codec.getInputBuffer(firstInputIndex), 0);
+            codec.queueInputBuffer(
+                    firstInputIndex, 0, Math.toIntExact(extractor.getSampleSize()),
+                    extractor.getSampleTime(), extractor.getSampleFlags());
+            // Don't advance, so the first buffer will be read again after flush
+            codec.flush();
+            ByteBuffer csd = format.getByteBuffer("csd-0");
+            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+            // We don't need to decode many frames
+            int numFrames = 10;
+            boolean eos = false;
+            while (!eos) {
+                if (numFrames > 0) {
+                    int inputIndex = codec.dequeueInputBuffer(0);
+                    if (inputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                        inputIndex = codec.dequeueInputBuffer(5000);
+                    }
+                    if (inputIndex >= 0) {
+                        ByteBuffer inputBuffer = codec.getInputBuffer(inputIndex);
+                        if (csd != null) {
+                            inputBuffer.clear();
+                            inputBuffer.put(csd);
+                            codec.queueInputBuffer(
+                                    inputIndex, 0, inputBuffer.position(), 0,
+                                    MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+                            csd = null;
+                        } else {
+                            int size = extractor.readSampleData(inputBuffer, 0);
+                            if (size <= 0) {
+                                break;
+                            }
+                            int flags = extractor.getSampleFlags();
+                            --numFrames;
+                            if (numFrames <= 0) {
+                                flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                            }
+                            codec.queueInputBuffer(
+                                    inputIndex, 0, size, extractor.getSampleTime(), flags);
+                            extractor.advance();
+                        }
+                    }
+                }
+
+                int outputIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
+                if (outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                    outputIndex = codec.dequeueOutputBuffer(bufferInfo, 5000);
+                }
+                if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    newFormat = codec.getOutputFormat();
+                } else if (outputIndex >= 0) {
+                    if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        eos = true;
+                    }
+                    codec.releaseOutputBuffer(outputIndex, false);
+                }
+            }
+            assertNotNull(newFormat);
+            assertEquals(48000, newFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
+        } finally {
+            if (extractor != null) {
+                extractor.release();
+            }
+            if (codec != null) {
+                codec.stop();
+                codec.release();
+            }
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 05a3970..e5bf714 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -45,11 +45,14 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.MediaUtils;
+import java.io.Closeable;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -62,6 +65,7 @@
 public class MediaMetadataRetrieverTest extends AndroidTestCase {
     private static final String TAG = "MediaMetadataRetrieverTest";
     private static final boolean SAVE_BITMAP_OUTPUT = false;
+    private static final String TEST_MEDIA_FILE = "retriever_test.3gp";
 
     protected Resources mResources;
     protected MediaMetadataRetriever mRetriever;
@@ -93,6 +97,10 @@
     protected void tearDown() throws Exception {
         super.tearDown();
         mRetriever.release();
+        File file = new File(Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE);
+        if (file.exists()) {
+            file.delete();
+        }
     }
 
     protected void setDataSourceFd(int resid) {
@@ -368,27 +376,23 @@
     }
 
     public void testSetDataSourcePath() {
-        File outputFile = new File(Environment.getExternalStorageDirectory(), "retriever_test.3gp");
+        copyMeidaFile();
+        File file = new File(Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE);
         try {
-            recordMedia(outputFile);
-            mRetriever.setDataSource(outputFile.getAbsolutePath());
+            mRetriever.setDataSource(file.getAbsolutePath());
         } catch (Exception ex) {
             fail("Failed setting data source with path, caught exception:" + ex);
-        } finally {
-            outputFile.delete();
         }
     }
 
     public void testSetDataSourceUri() {
-        File outputFile = new File(Environment.getExternalStorageDirectory(), "retriever_test.3gp");
+        copyMeidaFile();
+        File file = new File(Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE);
         try {
-            recordMedia(outputFile);
-            Uri uri = Uri.parse(outputFile.getAbsolutePath());
+            Uri uri = Uri.parse(file.getAbsolutePath());
             mRetriever.setDataSource(getContext(), uri);
         } catch (Exception ex) {
             fail("Failed setting data source with Uri, caught exception:" + ex);
-        } finally {
-            outputFile.delete();
         }
     }
 
@@ -1005,20 +1009,42 @@
         }
     }
 
-    private void recordMedia(File outputFile) throws Exception {
-        MediaRecorder mr = new MediaRecorder();
+    private void copyMeidaFile() {
+        InputStream inputStream = null;
+        FileOutputStream outputStream = null;
+        String outputPath = new File(
+            Environment.getExternalStorageDirectory(), TEST_MEDIA_FILE).getAbsolutePath();
         try {
-            mr.setAudioSource(MediaRecorder.AudioSource.MIC);
-            mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
-            mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
-            mr.setOutputFile(outputFile.getAbsolutePath());
+            inputStream = getContext().getResources().openRawResource(R.raw.testvideo);
+            outputStream = new FileOutputStream(outputPath);
+            copy(inputStream, outputStream);
+        } catch (Exception e) {
 
-            mr.prepare();
-            mr.start();
-            Thread.sleep(SLEEP_TIME);
-            mr.stop();
-        } finally {
-            mr.release();
+        }finally {
+            closeQuietly(inputStream);
+            closeQuietly(outputStream);
+        }
+    }
+
+    private int copy(InputStream in, OutputStream out) throws IOException {
+        int total = 0;
+        byte[] buffer = new byte[8192];
+        int c;
+        while ((c = in.read(buffer)) != -1) {
+            total += c;
+            out.write(buffer, 0, c);
+        }
+        return total;
+    }
+
+    private void closeQuietly(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) {
+            }
         }
     }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index b9d4873..3e547bb 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -15,7 +15,6 @@
  */
 package android.media.cts;
 
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
@@ -827,8 +826,7 @@
         int oldRingerMode = Integer.MIN_VALUE;
         int oldVolume = Integer.MIN_VALUE;
         try {
-            if (am.getRingerMode() != AudioManager.RINGER_MODE_NORMAL
-                    && !ActivityManager.isLowRamDeviceStatic()) {
+            if (am.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), true /* on */);
             }
@@ -932,10 +930,8 @@
             if (oldVolume != Integer.MIN_VALUE) {
                 am.setStreamVolume(AudioManager.STREAM_MUSIC, oldVolume, 0);
             }
-            if (!ActivityManager.isLowRamDeviceStatic()) {
-                Utils.toggleNotificationPolicyAccess(
-                        mContext.getPackageName(), getInstrumentation(), false  /* on == false */);
-            }
+            Utils.toggleNotificationPolicyAccess(
+                    mContext.getPackageName(), getInstrumentation(), false  /* on == false */);
         }
     }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java b/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java
index ec9e8b4..773aede 100644
--- a/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRoute2ProviderServiceTest.java
@@ -17,12 +17,12 @@
 package android.media.cts;
 
 import static android.media.cts.MediaRouter2Test.releaseControllers;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID1;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID2;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID1;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -40,7 +40,7 @@
 import android.media.MediaRouter2.TransferCallback;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
-import android.media.cts.SampleMediaRoute2ProviderService.Proxy;
+import android.media.cts.StubMediaRoute2ProviderService.Proxy;
 import android.os.Bundle;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.LargeTest;
@@ -48,6 +48,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.PollingCheck;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -64,14 +66,14 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "The system should be able to bind to SampleMediaRoute2ProviderService")
+@AppModeFull(reason = "The system should be able to bind to StubMediaRoute2ProviderService")
 @LargeTest
 public class MediaRoute2ProviderServiceTest {
     private static final String TAG = "MR2ProviderServiceTest";
     Context mContext;
     private MediaRouter2 mRouter2;
     private Executor mExecutor;
-    private SampleMediaRoute2ProviderService mServiceInstance;
+    private StubMediaRoute2ProviderService mService;
 
     private static final int TIMEOUT_MS = 5000;
 
@@ -87,61 +89,68 @@
         mContext = InstrumentationRegistry.getTargetContext();
         mRouter2 = MediaRouter2.getInstance(mContext);
         mExecutor = Executors.newSingleThreadExecutor();
-        mServiceInstance = SampleMediaRoute2ProviderService.getInstance();
+
+        new PollingCheck(TIMEOUT_MS) {
+            @Override
+            protected boolean check() {
+                StubMediaRoute2ProviderService service =
+                        StubMediaRoute2ProviderService.getInstance();
+                if (service != null) {
+                    mService = service;
+                    return true;
+                }
+                return false;
+            }
+        }.run();
     }
 
     @After
     public void tearDown() throws Exception {
-        if (mServiceInstance != null) {
-            mServiceInstance.clear();
-            mServiceInstance = null;
+        if (mService != null) {
+            mService.clear();
+            mService = null;
         }
     }
 
     @Test
     public void testGetSessionInfoAndGetAllSessionInfo() {
-        SampleMediaRoute2ProviderService service = mServiceInstance;
-        assertNotNull(service);
-        assertEquals(0, service.getAllSessionInfo().size());
+        assertEquals(0, mService.getAllSessionInfo().size());
 
         // Add a session
         RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
                 SESSION_ID_1, "" /* clientPackageName */)
                 .addSelectedRoute(ROUTE_ID1)
                 .build();
-        service.notifySessionCreated(sessionInfo1, MediaRoute2ProviderService.REQUEST_ID_UNKNOWN);
-        assertEquals(1, service.getAllSessionInfo().size());
-        assertEquals(sessionInfo1, service.getAllSessionInfo().get(0));
-        assertEquals(sessionInfo1, service.getSessionInfo(SESSION_ID_1));
+        mService.notifySessionCreated(sessionInfo1, MediaRoute2ProviderService.REQUEST_ID_NONE);
+        assertEquals(1, mService.getAllSessionInfo().size());
+        assertEquals(sessionInfo1, mService.getAllSessionInfo().get(0));
+        assertEquals(sessionInfo1, mService.getSessionInfo(SESSION_ID_1));
 
         // Add another session
         RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(
                 SESSION_ID_2, "" /* clientPackageName */)
                 .addSelectedRoute(ROUTE_ID2)
                 .build();
-        service.notifySessionCreated(
-                sessionInfo2, MediaRoute2ProviderService.REQUEST_ID_UNKNOWN);
-        assertEquals(2, service.getAllSessionInfo().size());
-        assertEquals(sessionInfo2, service.getSessionInfo(SESSION_ID_2));
+        mService.notifySessionCreated(
+                sessionInfo2, MediaRoute2ProviderService.REQUEST_ID_NONE);
+        assertEquals(2, mService.getAllSessionInfo().size());
+        assertEquals(sessionInfo2, mService.getSessionInfo(SESSION_ID_2));
 
         // Remove the first session
-        service.notifySessionReleased(SESSION_ID_1);
-        assertNull(service.getSessionInfo(SESSION_ID_1));
-        assertEquals(1, service.getAllSessionInfo().size());
-        assertEquals(sessionInfo2, service.getAllSessionInfo().get(0));
-        assertEquals(sessionInfo2, service.getSessionInfo(SESSION_ID_2));
+        mService.notifySessionReleased(SESSION_ID_1);
+        assertNull(mService.getSessionInfo(SESSION_ID_1));
+        assertEquals(1, mService.getAllSessionInfo().size());
+        assertEquals(sessionInfo2, mService.getAllSessionInfo().get(0));
+        assertEquals(sessionInfo2, mService.getSessionInfo(SESSION_ID_2));
 
         // Remove the remaining session
-        service.notifySessionReleased(SESSION_ID_2);
-        assertEquals(0, service.getAllSessionInfo().size());
-        assertNull(service.getSessionInfo(SESSION_ID_2));
+        mService.notifySessionReleased(SESSION_ID_2);
+        assertEquals(0, mService.getAllSessionInfo().size());
+        assertNull(mService.getSessionInfo(SESSION_ID_2));
     }
 
     @Test
     public void testNotifyRoutesInvokesMediaRouter2RouteCallback() throws Exception {
-        SampleMediaRoute2ProviderService service = mServiceInstance;
-        assertNotNull(service);
-
         final String routeId0 = "routeId0";
         final String routeName0 = "routeName0";
         final String routeId1 = "routeId1";
@@ -236,7 +245,7 @@
         mRouter2.registerRouteCallback(mExecutor, routeCallback,
                 new RouteDiscoveryPreference.Builder(features, true).build());
         try {
-            service.notifyRoutes(routes);
+            mService.notifyRoutes(routes);
             assertTrue(onRoutesAddedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // Change the connection state of route2 in order to invoke onRoutesChanged()
@@ -244,12 +253,12 @@
                     .setConnectionState(newConnectionState)
                     .build();
             routes.set(1, newRoute2);
-            service.notifyRoutes(routes);
+            mService.notifyRoutes(routes);
             assertTrue(onRoutesChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // Now remove all the routes
             routes.clear();
-            service.notifyRoutes(routes);
+            mService.notifyRoutes(routes);
             assertTrue(onRoutesRemovedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
             mRouter2.unregisterRouteCallback(routeCallback);
@@ -258,10 +267,8 @@
 
     @Test
     public void testSessionRelatedCallbacks() throws Exception {
-        SampleMediaRoute2ProviderService service = mServiceInstance;
-        assertNotNull(service);
-        service.initializeRoutes();
-        service.publishRoutes();
+        mService.initializeRoutes();
+        mService.publishRoutes();
 
         List<String> featuresSample = Collections.singletonList(FEATURE_SAMPLE);
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(featuresSample);
@@ -281,7 +288,7 @@
         // Now test all session-related callbacks.
         setProxy(new Proxy() {
             @Override
-            public void onCreateSession(String packageName, String routeId, long requestId,
+            public void onCreateSession(long requestId, String packageName, String routeId,
                     Bundle sessionHints) {
                 assertEquals(mContext.getPackageName(), packageName);
                 assertEquals(ROUTE_ID1, routeId);
@@ -295,65 +302,64 @@
                         .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
                         .build();
-                service.notifySessionCreated(info, requestId);
+                mService.notifySessionCreated(info, requestId);
                 onCreateSessionLatch.countDown();
             }
 
             @Override
-            public void onSelectRoute(String sessionId, String routeId) {
+            public void onSelectRoute(long requestId, String sessionId, String routeId) {
                 assertEquals(SESSION_ID_1, sessionId);
                 assertEquals(ROUTE_ID4_TO_SELECT_AND_DESELECT, routeId);
 
-                RoutingSessionInfo oldInfo = service.getSessionInfo(SESSION_ID_1);
+                RoutingSessionInfo oldInfo = mService.getSessionInfo(SESSION_ID_1);
                 RoutingSessionInfo newInfo = new RoutingSessionInfo.Builder(oldInfo)
                         .addSelectedRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .removeSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .addDeselectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .build();
-                service.notifySessionUpdated(newInfo);
+                mService.notifySessionUpdated(newInfo);
                 onSelectRouteLatch.countDown();
             }
 
             @Override
-            public void onDeselectRoute(String sessionId, String routeId) {
+            public void onDeselectRoute(long requestId, String sessionId, String routeId) {
                 assertEquals(SESSION_ID_1, sessionId);
                 assertEquals(ROUTE_ID4_TO_SELECT_AND_DESELECT, routeId);
 
-                RoutingSessionInfo oldInfo = service.getSessionInfo(SESSION_ID_1);
+                RoutingSessionInfo oldInfo = mService.getSessionInfo(SESSION_ID_1);
                 RoutingSessionInfo newInfo = new RoutingSessionInfo.Builder(oldInfo)
                         .removeSelectedRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .removeDeselectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .build();
-                service.notifySessionUpdated(newInfo);
+                mService.notifySessionUpdated(newInfo);
                 onDeselectRouteLatch.countDown();
             }
 
             @Override
-            public void onTransferToRoute(String sessionId, String routeId) {
+            public void onTransferToRoute(long requestId, String sessionId, String routeId) {
                 assertEquals(SESSION_ID_1, sessionId);
                 assertEquals(ROUTE_ID5_TO_TRANSFER_TO, routeId);
 
-                RoutingSessionInfo oldInfo = service.getSessionInfo(SESSION_ID_1);
+                RoutingSessionInfo oldInfo = mService.getSessionInfo(SESSION_ID_1);
                 RoutingSessionInfo newInfo = new RoutingSessionInfo.Builder(oldInfo)
                         .clearDeselectableRoutes()
                         .clearSelectedRoutes()
                         .clearDeselectableRoutes()
                         .addSelectedRoute(ROUTE_ID5_TO_TRANSFER_TO)
                         .build();
-                service.notifySessionUpdated(newInfo);
+                mService.notifySessionUpdated(newInfo);
                 onTransferToRouteLatch.countDown();
             }
 
             @Override
-            public void onReleaseSession(String sessionId) {
+            public void onReleaseSession(long requestId, String sessionId) {
                 assertEquals(SESSION_ID_1, sessionId);
-                service.notifySessionReleased(sessionId);
+                mService.notifySessionReleased(sessionId);
                 onReleaseSessionLatch.countDown();
             }
         });
 
-
         CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
         CountDownLatch onControllerUpdatedForSelectLatch = new CountDownLatch(1);
         CountDownLatch onControllerUpdatedForDeselectLatch = new CountDownLatch(1);
@@ -364,10 +370,7 @@
             @Override
             public void onTransferred(RoutingController oldController,
                     RoutingController newController) {
-                // TODO: Make RoutingController#getOriginalId() as @TestApi and use it.
-                if (newController != null
-                        && ROUTE_ID1.equals(newController
-                        .getSelectedRoutes().get(0).getOriginalId())) {
+                if (newController != null && SESSION_ID_1.equals(newController.getOriginalId())) {
                     controllers.add(newController);
                     onControllerCreatedLatch.countDown();
                 }
@@ -399,7 +402,7 @@
             }
         };
 
-        // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+        // TODO: Remove this once the MediaRouter2 becomes always connected to the mService.
         RouteCallback dummyCallback = new RouteCallback();
         try {
             mRouter2.registerRouteCallback(mExecutor, dummyCallback,
@@ -441,10 +444,8 @@
 
     @Test
     public void testNotifySessionReleased() throws Exception {
-        SampleMediaRoute2ProviderService service = mServiceInstance;
-        assertNotNull(service);
-        service.initializeRoutes();
-        service.publishRoutes();
+        mService.initializeRoutes();
+        mService.publishRoutes();
 
         List<String> featuresSample = Collections.singletonList(FEATURE_SAMPLE);
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(featuresSample);
@@ -454,7 +455,7 @@
         CountDownLatch onCreateSessionLatch = new CountDownLatch(1);
         setProxy(new Proxy() {
             @Override
-            public void onCreateSession(String packageName, String routeId, long requestId,
+            public void onCreateSession(long requestId, String packageName, String routeId,
                     Bundle sessionHints) {
                 assertEquals(mContext.getPackageName(), packageName);
                 assertEquals(ROUTE_ID1, routeId);
@@ -466,7 +467,7 @@
                         .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                         .addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
                         .build();
-                service.notifySessionCreated(info, requestId);
+                mService.notifySessionCreated(info, requestId);
                 onCreateSessionLatch.countDown();
             }
         });
@@ -480,16 +481,14 @@
             @Override
             public void onTransferred(RoutingController oldController,
                     RoutingController newController) {
-                // TODO: Make RoutingController#getOriginalId() as @TestApi and use it.
                 if (newController != null) {
-                    if (ROUTE_ID1.equals(newController
-                            .getSelectedRoutes().get(0).getOriginalId())) {
+                    if (SESSION_ID_1.equals(newController.getOriginalId())) {
                         controllers.add(newController);
                         onControllerCreatedLatch.countDown();
                     }
                 } else {
-                    if (ROUTE_ID1.equals(oldController
-                            .getSelectedRoutes().get(0).getOriginalId())) {
+                    // newController == null means that the oldController is released
+                    if (SESSION_ID_1.equals(oldController.getOriginalId())) {
                         assertTrue(oldController.isReleased());
                         onControllerReleasedLatch.countDown();
                     }
@@ -497,7 +496,7 @@
             }
         };
 
-        // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+        // TODO: Remove this once the MediaRouter2 becomes always connected to the mService.
         RouteCallback dummyCallback = new RouteCallback();
         try {
             mRouter2.registerRouteCallback(mExecutor, dummyCallback,
@@ -509,7 +508,7 @@
             assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             assertFalse(controllers.isEmpty());
 
-            service.notifySessionReleased(SESSION_ID_1);
+            mService.notifySessionReleased(SESSION_ID_1);
             assertTrue(onControllerReleasedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
             mRouter2.unregisterRouteCallback(dummyCallback);
@@ -558,10 +557,10 @@
         }
     }
 
-    void setProxy(SampleMediaRoute2ProviderService.Proxy proxy) {
-        SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
-        if (instance != null) {
-            instance.setProxy(proxy);
+    void setProxy(StubMediaRoute2ProviderService.Proxy proxy) {
+        StubMediaRoute2ProviderService service = mService;
+        if (service != null) {
+            service.setProxy(proxy);
         }
     }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
index c73a447..0c87a2a 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
@@ -18,14 +18,14 @@
 
 import static android.content.Context.AUDIO_SERVICE;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURES_SPECIAL;
-import static android.media.cts.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID1;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID2;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID3_SESSION_CREATION_FAILED;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
-import static android.media.cts.SampleMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURES_SPECIAL;
+import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID1;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID3_SESSION_CREATION_FAILED;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static android.media.cts.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -54,6 +54,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.PollingCheck;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -70,7 +72,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "The system should be able to bind to SampleMediaRoute2ProviderService")
+@AppModeFull(reason = "The system should be able to bind to StubMediaRoute2ProviderService")
 @LargeTest
 public class MediaRouter2Test {
     private static final String TAG = "MR2Test";
@@ -78,7 +80,7 @@
     private MediaRouter2 mRouter2;
     private Executor mExecutor;
     private AudioManager mAudioManager;
-    private SampleMediaRoute2ProviderService mServiceInstance;
+    private StubMediaRoute2ProviderService mService;
 
     private static final int TIMEOUT_MS = 5000;
     private static final int WAIT_MS = 2000;
@@ -95,18 +97,27 @@
         mExecutor = Executors.newSingleThreadExecutor();
         mAudioManager = (AudioManager) mContext.getSystemService(AUDIO_SERVICE);
 
-        mServiceInstance = SampleMediaRoute2ProviderService.getInstance();
-        if (mServiceInstance != null) {
-            mServiceInstance.initializeRoutes();
-            mServiceInstance.publishRoutes();
-        }
+        new PollingCheck(TIMEOUT_MS) {
+            @Override
+            protected boolean check() {
+                StubMediaRoute2ProviderService service =
+                        StubMediaRoute2ProviderService.getInstance();
+                if (service != null) {
+                    mService = service;
+                    return true;
+                }
+                return false;
+            }
+        }.run();
+        mService.initializeRoutes();
+        mService.publishRoutes();
     }
 
     @After
     public void tearDown() throws Exception {
-        if (mServiceInstance != null) {
-            mServiceInstance.clear();
-            mServiceInstance = null;
+        if (mService != null) {
+            mService.clear();
+            mService = null;
         }
     }
 
@@ -351,7 +362,7 @@
                     assertTrue(createRouteMap(newController.getSelectedRoutes()).containsKey(
                             ROUTE_ID1));
 
-                    // The SampleMediaRoute2ProviderService is supposed to set control hints
+                    // The StubMediaRoute2ProviderService is supposed to set control hints
                     // with the given controllerHints.
                     Bundle controlHints = newController.getControlHints();
                     assertNotNull(controlHints);
@@ -376,7 +387,7 @@
         try {
             mRouter2.registerTransferCallback(mExecutor, transferCallback);
 
-            // The SampleMediaRoute2ProviderService supposed to set control hints
+            // The StubMediaRoute2ProviderService supposed to set control hints
             // with the given creationSessionHints.
             mRouter2.setOnGetControllerHintsListener(listener);
             mRouter2.transferTo(route);
diff --git a/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java b/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
index 3ff68c3..f305a74 100644
--- a/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
@@ -70,8 +70,7 @@
         mDefaultUri = RingtoneManager.getActualDefaultRingtoneUri(mContext,
                 RingtoneManager.TYPE_RINGTONE);
 
-        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT
-                && !ActivityManager.isLowRamDeviceStatic()) {
+        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
             try {
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), true);
@@ -87,19 +86,17 @@
 
     @Override
     protected void tearDown() throws Exception {
-        if (!ActivityManager.isLowRamDeviceStatic()) {
-            try {
-                Utils.toggleNotificationPolicyAccess(
-                        mContext.getPackageName(), getInstrumentation(), true);
-                // restore original ringer settings
-                if (mAudioManager != null) {
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_RING, mOriginalVolume,
-                            AudioManager.FLAG_ALLOW_RINGER_MODES);
-                }
-            } finally {
-                Utils.toggleNotificationPolicyAccess(
-                        mContext.getPackageName(), getInstrumentation(), false);
+        try {
+            Utils.toggleNotificationPolicyAccess(
+                    mContext.getPackageName(), getInstrumentation(), true);
+            // restore original ringer settings
+            if (mAudioManager != null) {
+                mAudioManager.setStreamVolume(AudioManager.STREAM_RING, mOriginalVolume,
+                        AudioManager.FLAG_ALLOW_RINGER_MODES);
             }
+        } finally {
+            Utils.toggleNotificationPolicyAccess(
+                    mContext.getPackageName(), getInstrumentation(), false);
         }
         RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE,
                 mDefaultUri);
diff --git a/tests/tests/media/src/android/media/cts/RingtoneTest.java b/tests/tests/media/src/android/media/cts/RingtoneTest.java
index c7ddbd8..ba289f3 100644
--- a/tests/tests/media/src/android/media/cts/RingtoneTest.java
+++ b/tests/tests/media/src/android/media/cts/RingtoneTest.java
@@ -16,8 +16,6 @@
 
 package android.media.cts;
 
-import android.app.ActivityManager;
-import android.app.UiAutomation;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
@@ -63,7 +61,7 @@
         } else if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
             mAudioManager.setStreamVolume(AudioManager.STREAM_RING, maxVolume / 2,
                     AudioManager.FLAG_ALLOW_RINGER_MODES);
-        } else if (!ActivityManager.isLowRamDeviceStatic()) {
+        } else {
             try {
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), true);
@@ -101,7 +99,7 @@
             if (mRingtone.isPlaying()) mRingtone.stop();
             mRingtone.setStreamType(mOriginalStreamType);
         }
-        if (mAudioManager != null && !ActivityManager.isLowRamDeviceStatic()) {
+        if (mAudioManager != null) {
             try {
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), true);
diff --git a/tests/tests/media/src/android/media/cts/SampleMediaRoute2ProviderService.java b/tests/tests/media/src/android/media/cts/StubMediaRoute2ProviderService.java
similarity index 88%
rename from tests/tests/media/src/android/media/cts/SampleMediaRoute2ProviderService.java
rename to tests/tests/media/src/android/media/cts/StubMediaRoute2ProviderService.java
index 4b51731..326adb0 100644
--- a/tests/tests/media/src/android/media/cts/SampleMediaRoute2ProviderService.java
+++ b/tests/tests/media/src/android/media/cts/StubMediaRoute2ProviderService.java
@@ -31,7 +31,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.text.TextUtils;
-import android.util.Log;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -41,7 +40,7 @@
 
 import javax.annotation.concurrent.GuardedBy;
 
-public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
+public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
     private static final String TAG = "SampleMR2ProviderSvc";
     private static final Object sLock = new Object();
 
@@ -89,7 +88,7 @@
     private int mNextSessionId = 1000;
 
     @GuardedBy("sLock")
-    private static SampleMediaRoute2ProviderService sInstance;
+    private static StubMediaRoute2ProviderService sInstance;
     private Proxy mProxy;
 
     public void initializeRoutes() {
@@ -140,7 +139,7 @@
         mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
     }
 
-    public static SampleMediaRoute2ProviderService getInstance() {
+    public static StubMediaRoute2ProviderService getInstance() {
         synchronized (sLock) {
             return sInstance;
         }
@@ -183,7 +182,7 @@
     }
 
     @Override
-    public void onSetRouteVolume(String routeId, int volume) {
+    public void onSetRouteVolume(long requestId, String routeId, int volume) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null) {
             return;
@@ -196,7 +195,7 @@
     }
 
     @Override
-    public void onSetSessionVolume(String sessionId, int volume) {
+    public void onSetSessionVolume(long requestId, String sessionId, int volume) {
         RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
         if (sessionInfo == null) {
             return;
@@ -209,11 +208,11 @@
     }
 
     @Override
-    public void onCreateSession(String packageName, String routeId, long requestId,
+    public void onCreateSession(long requestId, String packageName, String routeId,
             @Nullable Bundle sessionHints) {
         Proxy proxy = mProxy;
         if (doesProxyOverridesMethod(proxy, "onCreateSession")) {
-            proxy.onCreateSession(packageName, routeId, requestId, sessionHints);
+            proxy.onCreateSession(requestId, packageName, routeId, sessionHints);
             return;
         }
 
@@ -223,7 +222,7 @@
             notifySessionCreationFailed(requestId);
             return;
         }
-        maybeDeselectRoute(routeId);
+        maybeDeselectRoute(routeId, requestId);
 
         final String sessionId = String.valueOf(mNextSessionId);
         mNextSessionId++;
@@ -248,10 +247,10 @@
     }
 
     @Override
-    public void onReleaseSession(String sessionId) {
+    public void onReleaseSession(long requestId, String sessionId) {
         Proxy proxy = mProxy;
         if (doesProxyOverridesMethod(proxy, "onReleaseSession")) {
-            proxy.onReleaseSession(sessionId);
+            proxy.onReleaseSession(requestId, sessionId);
             return;
         }
 
@@ -286,10 +285,10 @@
     }
 
     @Override
-    public void onSelectRoute(String sessionId, String routeId) {
+    public void onSelectRoute(long requestId, String sessionId, String routeId) {
         Proxy proxy = mProxy;
         if (doesProxyOverridesMethod(proxy, "onSelectRoute")) {
-            proxy.onSelectRoute(sessionId, routeId);
+            proxy.onSelectRoute(requestId, sessionId, routeId);
             return;
         }
 
@@ -298,7 +297,7 @@
         if (route == null || sessionInfo == null) {
             return;
         }
-        maybeDeselectRoute(routeId);
+        maybeDeselectRoute(routeId, requestId);
 
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
                 .setClientPackageName(sessionInfo.getClientPackageName())
@@ -315,10 +314,10 @@
     }
 
     @Override
-    public void onDeselectRoute(String sessionId, String routeId) {
+    public void onDeselectRoute(long requestId, String sessionId, String routeId) {
         Proxy proxy = mProxy;
         if (doesProxyOverridesMethod(proxy, "onDeselectRoute")) {
-            proxy.onDeselectRoute(sessionId, routeId);
+            proxy.onDeselectRoute(requestId, sessionId, routeId);
             return;
         }
 
@@ -350,10 +349,10 @@
     }
 
     @Override
-    public void onTransferToRoute(String sessionId, String routeId) {
+    public void onTransferToRoute(long requestId, String sessionId, String routeId) {
         Proxy proxy = mProxy;
         if (doesProxyOverridesMethod(proxy, "onTransferToRoute")) {
-            proxy.onTransferToRoute(sessionId, routeId);
+            proxy.onTransferToRoute(requestId, sessionId, routeId);
             return;
         }
 
@@ -389,13 +388,13 @@
         publishRoutes();
     }
 
-    void maybeDeselectRoute(String routeId) {
+    void maybeDeselectRoute(String routeId, long requestId) {
         if (!mRouteIdToSessionId.containsKey(routeId)) {
             return;
         }
 
         String sessionId = mRouteIdToSessionId.get(routeId);
-        onDeselectRoute(sessionId, routeId);
+        onDeselectRoute(requestId, sessionId, routeId);
     }
 
     void publishRoutes() {
@@ -403,12 +402,15 @@
     }
 
     public static class Proxy {
-        public void onCreateSession(@NonNull String packageName, @NonNull String routeId,
-                long requestId, @Nullable Bundle sessionHints) {}
-        public void onReleaseSession(@NonNull String sessionId) {}
-        public void onSelectRoute(@NonNull String sessionId, @NonNull String routeId) {}
-        public void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId) {}
-        public void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId) {}
+        public void onCreateSession(long requestId, @NonNull String packageName,
+                @NonNull String routeId, @Nullable Bundle sessionHints) {}
+        public void onReleaseSession(long requestId, @NonNull String sessionId) {}
+        public void onSelectRoute(long requestId, @NonNull String sessionId,
+                @NonNull String routeId) {}
+        public void onDeselectRoute(long requestId, @NonNull String sessionId,
+                @NonNull String routeId) {}
+        public void onTransferToRoute(long requestId, @NonNull String sessionId,
+                @NonNull String routeId) {}
         public void onDiscoveryPreferenceChanged(RouteDiscoveryPreference preference) {}
         // TODO: Handle onSetRouteVolume() && onSetSessionVolume()
     }
diff --git a/tests/tests/mediaparser/AndroidTest.xml b/tests/tests/mediaparser/AndroidTest.xml
index 905e7e4..1f28389 100644
--- a/tests/tests/mediaparser/AndroidTest.xml
+++ b/tests/tests/mediaparser/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="media" />
     <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsMediaParserTestCases.apk" />
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
index 818ce18..bdc7dfb 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
@@ -364,7 +364,7 @@
 
         mediaParser.advance(mockInput);
         if (expectedExtractorName != null) {
-            assertThat(expectedExtractorName).isEqualTo(mediaParser.getExtractorName());
+            assertThat(expectedExtractorName).isEqualTo(mediaParser.getParserName());
             // We are only checking that the extractor is the right one.
             return;
         }
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
index 813b40c..4a89d69 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
@@ -55,17 +55,48 @@
     return latencyMillis;
 }
 
-using CbTestParams = std::tuple<aaudio_sharing_mode_t, int32_t, aaudio_performance_mode_t>;
+using CbTestParams = std::tuple<aaudio_sharing_mode_t, int32_t,
+                                aaudio_performance_mode_t, int32_t, aaudio_format_t>;
 enum {
     PARAM_SHARING_MODE = 0,
     PARAM_FRAMES_PER_CB,
-    PARAM_PERF_MODE
+    PARAM_PERF_MODE,
+    PARAM_ALLOW_MMAP,
+    PARAM_AUDIO_FORMAT
 };
 
+enum {
+    MMAP_NOT_ALLOWED,
+    MMAP_ALLOWED,
+};
+
+static const char* allowMMapToString(int allow) {
+    switch (allow) {
+        case MMAP_NOT_ALLOWED: return "NOTMMAP";
+        case MMAP_ALLOWED:
+        default:
+            return "MMAPOK";
+    }
+}
+
+static const char* audioFormatToString(aaudio_format_t format) {
+    switch (format) {
+        case AAUDIO_FORMAT_UNSPECIFIED: return "UNSP";
+        case AAUDIO_FORMAT_PCM_I16: return "I16";
+        case AAUDIO_FORMAT_PCM_FLOAT: return "FLT";
+        default:
+            return "BAD";
+    }
+}
+
 static std::string getTestName(const ::testing::TestParamInfo<CbTestParams>& info) {
-    return std::string() + sharingModeToString(std::get<PARAM_SHARING_MODE>(info.param)) +
-            "__" + std::to_string(std::get<PARAM_FRAMES_PER_CB>(info.param)) +
-            "__" + performanceModeToString(std::get<PARAM_PERF_MODE>(info.param));
+    return std::string()
+            + sharingModeToString(std::get<PARAM_SHARING_MODE>(info.param))
+            + "__" + std::to_string(std::get<PARAM_FRAMES_PER_CB>(info.param))
+            + "__" + performanceModeToString(std::get<PARAM_PERF_MODE>(info.param))
+            + "__" + allowMMapToString(std::get<PARAM_ALLOW_MMAP>(info.param))
+            + "__" + audioFormatToString(std::get<PARAM_AUDIO_FORMAT>(info.param))
+            ;
 }
 
 template<typename T>
@@ -143,11 +174,15 @@
 }
 
 void AAudioInputStreamCallbackTest::SetUp() {
+    aaudio_policy_t originalPolicy = AAUDIO_POLICY_AUTO;
+
     mSetupSuccesful = false;
     if (!deviceSupportsFeature(FEATURE_RECORDING)) return;
     mHelper.reset(new InputStreamBuilderHelper(
                     std::get<PARAM_SHARING_MODE>(GetParam()),
-                    std::get<PARAM_PERF_MODE>(GetParam())));
+                    std::get<PARAM_PERF_MODE>(GetParam()),
+                    std::get<PARAM_AUDIO_FORMAT>(GetParam()))
+                    );
     mHelper->initBuilder();
 
     int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
@@ -158,7 +193,23 @@
         AAudioStreamBuilder_setFramesPerDataCallback(builder(), framesPerDataCallback);
     }
 
+    // Turn off MMap if requested.
+    int allowMMap = std::get<PARAM_ALLOW_MMAP>(GetParam()) == MMAP_ALLOWED;
+    if (AAudioExtensions::getInstance().isMMapSupported()) {
+        originalPolicy = AAudioExtensions::getInstance().getMMapPolicy();
+        AAudioExtensions::getInstance().setMMapEnabled(allowMMap);
+    }
+
     mHelper->createAndVerifyStream(&mSetupSuccesful);
+
+    // Restore policy for next test.
+    if (AAudioExtensions::getInstance().isMMapSupported()) {
+        AAudioExtensions::getInstance().setMMapPolicy(originalPolicy);
+    }
+    if (!allowMMap) {
+        ASSERT_FALSE(AAudioExtensions::getInstance().isMMapUsed(mHelper->stream()));
+    }
+
 }
 
 // Test Reading from an AAudioStream using a Callback
@@ -166,12 +217,12 @@
     if (!mSetupSuccesful) return;
 
     const int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
-    const int32_t actualFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
+    const int32_t streamFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
     if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
-        ASSERT_EQ(framesPerDataCallback, actualFramesPerDataCallback);
+        ASSERT_EQ(framesPerDataCallback, streamFramesPerDataCallback);
     }
 
-    mCbData->reset(actualFramesPerDataCallback);
+    mCbData->reset(streamFramesPerDataCallback);
 
     mHelper->startStream();
     // See b/62090113. For legacy path, the device is only known after
@@ -189,8 +240,8 @@
     sleep(1);
     EXPECT_EQ(oldCallbackCount, mCbData->callbackCount); // expect not advancing
 
-    if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
-        ASSERT_EQ(framesPerDataCallback, mCbData->actualFramesPerCallback);
+    if (streamFramesPerDataCallback != AAUDIO_UNSPECIFIED) {
+        ASSERT_EQ(streamFramesPerDataCallback, mCbData->actualFramesPerCallback);
     }
 
     ASSERT_EQ(AAUDIO_OK, mCbData->callbackError);
@@ -201,16 +252,42 @@
                 std::make_tuple(
                         AAUDIO_SHARING_MODE_SHARED,
                         AAUDIO_UNSPECIFIED,
-                        AAUDIO_PERFORMANCE_MODE_NONE),
-                // cb buffer size: arbitrary prime number < 192
-                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 109, AAUDIO_PERFORMANCE_MODE_NONE),
+                        AAUDIO_PERFORMANCE_MODE_NONE,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                // cb buffer size: arbitrary prime number < 96
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+                        AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(AAUDIO_SHARING_MODE_EXCLUSIVE, 67,
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+                        AAUDIO_FORMAT_PCM_I16),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67,
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+                        AAUDIO_FORMAT_PCM_FLOAT),
                 // cb buffer size: arbitrary prime number > 192
-                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223, AAUDIO_PERFORMANCE_MODE_NONE),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223,
+                        AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
                 // Recording in POWER_SAVING mode isn't supported, b/62291775.
                 std::make_tuple(
                         AAUDIO_SHARING_MODE_SHARED,
                         AAUDIO_UNSPECIFIED,
-                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY)),
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(
+                        AAUDIO_SHARING_MODE_EXCLUSIVE,
+                        AAUDIO_UNSPECIFIED,
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED)),
         &getTestName);
 
 
@@ -224,7 +301,10 @@
 
 // Callback function that fills the audio output buffer.
 aaudio_data_callback_result_t AAudioOutputStreamCallbackTest::MyDataCallbackProc(
-        AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) {
+        AAudioStream *stream,
+        void *userData,
+        void *audioData,
+        int32_t numFrames) {
     int32_t channelCount = AAudioStream_getChannelCount(stream);
     int32_t numSamples = channelCount * numFrames;
     if (AAudioStream_getFormat(stream) == AAUDIO_FORMAT_PCM_I16) {
@@ -247,7 +327,9 @@
     if (!deviceSupportsFeature(FEATURE_PLAYBACK)) return;
     mHelper.reset(new OutputStreamBuilderHelper(
                     std::get<PARAM_SHARING_MODE>(GetParam()),
-                    std::get<PARAM_PERF_MODE>(GetParam())));
+                    std::get<PARAM_PERF_MODE>(GetParam()),
+                    std::get<PARAM_AUDIO_FORMAT>(GetParam()))
+                    );
     mHelper->initBuilder();
 
     int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
@@ -259,6 +341,7 @@
     }
 
     mHelper->createAndVerifyStream(&mSetupSuccesful);
+
 }
 
 // Test Writing to an AAudioStream using a Callback
@@ -266,15 +349,15 @@
     if (!mSetupSuccesful) return;
 
     const int32_t framesPerDataCallback = std::get<PARAM_FRAMES_PER_CB>(GetParam());
-    const int32_t actualFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
+    const int32_t streamFramesPerDataCallback = AAudioStream_getFramesPerDataCallback(stream());
     if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
-        ASSERT_EQ(framesPerDataCallback, actualFramesPerDataCallback);
+        ASSERT_EQ(framesPerDataCallback, streamFramesPerDataCallback);
     }
 
     // Start/stop more than once to see if it fails after the first time.
     // Write some data and measure the rate to see if the timing is OK.
     for (int loopIndex = 0; loopIndex < 2; loopIndex++) {
-        mCbData->reset(actualFramesPerDataCallback);
+        mCbData->reset(streamFramesPerDataCallback);
 
         mHelper->startStream();
         // See b/62090113. For legacy path, the device is only known after
@@ -297,8 +380,8 @@
         sleep(1);
         EXPECT_EQ(oldCallbackCount, mCbData->callbackCount); // expect not advancing
 
-        if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
-            ASSERT_EQ(framesPerDataCallback, mCbData->actualFramesPerCallback);
+        if (streamFramesPerDataCallback != AAUDIO_UNSPECIFIED) {
+            ASSERT_EQ(streamFramesPerDataCallback, mCbData->actualFramesPerCallback);
         }
 
         EXPECT_GE(mCbData->minLatency, 1);   // Absurdly low
@@ -318,17 +401,39 @@
                 std::make_tuple(
                         AAUDIO_SHARING_MODE_SHARED,
                         AAUDIO_UNSPECIFIED,
-                        AAUDIO_PERFORMANCE_MODE_NONE),
-                // cb buffer size: arbitrary prime number < 192
-                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 109, AAUDIO_PERFORMANCE_MODE_NONE),
+                        AAUDIO_PERFORMANCE_MODE_NONE,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                // cb buffer size: arbitrary prime number < 96
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(AAUDIO_SHARING_MODE_EXCLUSIVE, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+                        AAUDIO_FORMAT_PCM_I16),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 67, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, MMAP_NOT_ALLOWED,
+                        AAUDIO_FORMAT_PCM_FLOAT),
                 // cb buffer size: arbitrary prime number > 192
-                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223, AAUDIO_PERFORMANCE_MODE_NONE),
+                std::make_tuple(AAUDIO_SHARING_MODE_SHARED, 223, AAUDIO_PERFORMANCE_MODE_NONE, MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
                 std::make_tuple(
                         AAUDIO_SHARING_MODE_SHARED,
                         AAUDIO_UNSPECIFIED,
-                        AAUDIO_PERFORMANCE_MODE_POWER_SAVING),
+                        AAUDIO_PERFORMANCE_MODE_POWER_SAVING,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
                 std::make_tuple(
                         AAUDIO_SHARING_MODE_SHARED,
                         AAUDIO_UNSPECIFIED,
-                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY)),
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED),
+                std::make_tuple(
+                        AAUDIO_SHARING_MODE_EXCLUSIVE,
+                        AAUDIO_UNSPECIFIED,
+                        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+                        MMAP_ALLOWED,
+                        AAUDIO_FORMAT_UNSPECIFIED)),
         &getTestName);
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
index fd67644..f08e1df 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
@@ -28,43 +28,9 @@
 #include "test_aaudio.h"
 #include "utils.h"
 
-/* These definitions are from aaudio/AAudioTesting.h */
-enum {
-    AAUDIO_POLICY_NEVER = 1,
-    AAUDIO_POLICY_AUTO,
-    AAUDIO_POLICY_ALWAYS
-};
-typedef int32_t aaudio_policy_t;
-
-static aaudio_result_t (*s_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
-static aaudio_policy_t (*s_getMMapPolicy)() = nullptr;
-
-/**
- * @return integer value or -1 on error
- */
-static int getSystemPropertyInt(const char *propName, int defaultValue) {
-    char valueText[PROP_VALUE_MAX] = {'\0'};
-    if (__system_property_get(propName, valueText) <= 0) {
-        return defaultValue;
-    }
-    char *endptr = nullptr;
-    int value = strtol(valueText, &endptr, 10);
-    if (endptr == nullptr || *endptr != '\0') {
-        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
-                "getSystemPropertyInt() - non-integer value = %s", valueText);
-        return -1;
-    } else {
-        return value;
-    }
-}
-
-static int getSystemMMapPolicy() {
-    return getSystemPropertyInt("aaudio.mmap_policy", AAUDIO_UNSPECIFIED);
-}
-
 // Test allowed values of policy.
 TEST(test_aaudio_mmap, testCurrentPolicy) {
-    aaudio_policy_t policy = getSystemMMapPolicy();
+    aaudio_policy_t policy = (aaudio_policy_t) AAudioExtensions::getMMapPolicyProperty();
 
     // It must be one of these defined enum values.
     EXPECT_TRUE(policy == AAUDIO_UNSPECIFIED
@@ -78,19 +44,6 @@
     EXPECT_NE(AAUDIO_POLICY_ALWAYS, policy);
 }
 
-// Link to test functions in shared library.
-static void loadMMapTestFunctions() {
-    if (s_setMMapPolicy != nullptr) return; // already loaded
-
-    void *handle;
-    handle = dlopen("libaaudio.so", RTLD_NOW);
-    EXPECT_NE(nullptr, handle);
-    s_setMMapPolicy = (int (*)(int)) dlsym(handle, "AAudio_setMMapPolicy");
-    EXPECT_NE(nullptr, s_setMMapPolicy);
-    s_getMMapPolicy = (int (*)()) dlsym(handle, "AAudio_getMMapPolicy");
-    EXPECT_NE(nullptr, s_getMMapPolicy);
-}
-
 // An application should not be able to create an MMAP stream
 // by enabling MMAP when the system "aaudio.mmap_policy" says not to.
 TEST(test_aaudio_mmap, testElevatingMMapPolicy) {
@@ -98,12 +51,8 @@
     AAudioStreamBuilder *builder = nullptr;
     AAudioStream *stream = nullptr;
 
-    aaudio_policy_t policy = getSystemMMapPolicy();
-    bool mmapAllowed = (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
+    bool mmapAllowed = AAudioExtensions::getInstance().isMMapSupported();
     if (mmapAllowed) return;
-    // Try to enable MMAP when not allowed.
-
-    loadMMapTestFunctions();
 
     EXPECT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&builder));
 
@@ -111,10 +60,10 @@
     AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
 
     // Force policy to create an MMAP stream or fail.
-    aaudio_policy_t originalPolicy = s_getMMapPolicy();
-    s_setMMapPolicy(AAUDIO_POLICY_ALWAYS); // try to enable MMAP mode
+    aaudio_policy_t originalPolicy = AAudioExtensions::getInstance().getMMapPolicy();
+    AAudioExtensions::getInstance().setMMapPolicy(AAUDIO_POLICY_ALWAYS); // try to enable MMAP mode
     result = AAudioStreamBuilder_openStream(builder, &stream);
-    s_setMMapPolicy(originalPolicy);
+    AAudioExtensions::getInstance().setMMapPolicy(originalPolicy);
 
     // openStream should have failed.
     EXPECT_NE(AAUDIO_OK, result);
diff --git a/tests/tests/nativemedia/aaudio/jni/utils.cpp b/tests/tests/nativemedia/aaudio/jni/utils.cpp
index d843614..a9ed984 100644
--- a/tests/tests/nativemedia/aaudio/jni/utils.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/utils.cpp
@@ -149,7 +149,9 @@
     ASSERT_LE(mActual.channelCount, 16); // TODO what is min/max?
 
     mActual.dataFormat = AAudioStream_getFormat(mStream);
-    ASSERT_EQ(AAUDIO_FORMAT_PCM_I16, mActual.dataFormat);
+    if (mRequested.dataFormat != AAUDIO_FORMAT_UNSPECIFIED) {
+        ASSERT_EQ(mRequested.dataFormat, mActual.dataFormat);
+    }
 
     mActual.perfMode = AAudioStream_getPerformanceMode(mStream);
     if (mRequested.perfMode != AAUDIO_PERFORMANCE_MODE_NONE
@@ -192,17 +194,20 @@
     ASSERT_EQ(toState, state);
 }
 
-
 InputStreamBuilderHelper::InputStreamBuilderHelper(
-        aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
+        aaudio_sharing_mode_t requestedSharingMode,
+        aaudio_performance_mode_t requestedPerfMode,
+        aaudio_format_t requestedFormat)
         : StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
-            48000, 1, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
+            48000, 1, requestedFormat, requestedSharingMode, requestedPerfMode} {}
 
 
 OutputStreamBuilderHelper::OutputStreamBuilderHelper(
-        aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
+        aaudio_sharing_mode_t requestedSharingMode,
+        aaudio_performance_mode_t requestedPerfMode,
+        aaudio_format_t requestedFormat)
         : StreamBuilderHelper{AAUDIO_DIRECTION_OUTPUT,
-            48000, 2, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
+            48000, 2, requestedFormat, requestedSharingMode, requestedPerfMode} {}
 
 void OutputStreamBuilderHelper::initBuilder() {
     StreamBuilderHelper::initBuilder();
@@ -215,3 +220,51 @@
         ASSERT_GE(AAudioStream_getBufferCapacityInFrames(mStream), kBufferCapacityFrames);
     }
 }
+
+AAudioExtensions::AAudioExtensions()
+    : mMMapSupported(isPolicyEnabled(getMMapPolicyProperty()))
+    , mMMapExclusiveSupported(isPolicyEnabled(getIntegerProperty(
+            "aaudio.mmap_exclusive_policy", AAUDIO_POLICY_UNSPECIFIED))) {
+    loadLibrary();
+}
+
+int AAudioExtensions::getIntegerProperty(const char *name, int defaultValue) {
+    int result = defaultValue;
+    char valueText[PROP_VALUE_MAX] = {0};
+    if (__system_property_get(name, valueText) != 0) {
+        result = atoi(valueText);
+    }
+    return result;
+}
+
+// This should only be called once from the constructor.
+bool AAudioExtensions::loadLibrary() {
+    mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
+    if (mLibHandle == nullptr) {
+        //LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
+        return false;
+    }
+
+    mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
+            dlsym(mLibHandle, FUNCTION_IS_MMAP);
+    if (mAAudioStream_isMMap == nullptr) {
+        //LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
+        return false;
+    }
+
+    mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
+            dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY);
+    if (mAAudio_setMMapPolicy == nullptr) {
+        //LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__);
+        return false;
+    }
+
+    mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
+            dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY);
+    if (mAAudio_getMMapPolicy == nullptr) {
+        //LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__);
+        return false;
+    }
+    mFunctionsLoaded = true;
+    return mFunctionsLoaded;
+}
\ No newline at end of file
diff --git a/tests/tests/nativemedia/aaudio/jni/utils.h b/tests/tests/nativemedia/aaudio/jni/utils.h
index 4211410..f14e04d 100644
--- a/tests/tests/nativemedia/aaudio/jni/utils.h
+++ b/tests/tests/nativemedia/aaudio/jni/utils.h
@@ -16,7 +16,9 @@
 #ifndef CTS_MEDIA_TEST_AAUDIO_UTILS_H
 #define CTS_MEDIA_TEST_AAUDIO_UTILS_H
 
+#include <dlfcn.h>
 #include <map>
+#include <sys/system_properties.h>
 
 #include <aaudio/AAudio.h>
 
@@ -88,14 +90,16 @@
   public:
     InputStreamBuilderHelper(
             aaudio_sharing_mode_t requestedSharingMode,
-            aaudio_performance_mode_t requestedPerfMode);
+            aaudio_performance_mode_t requestedPerfMode,
+            aaudio_format_t requestedFormat = AAUDIO_FORMAT_PCM_FLOAT);
 };
 
 class OutputStreamBuilderHelper : public StreamBuilderHelper {
   public:
     OutputStreamBuilderHelper(
             aaudio_sharing_mode_t requestedSharingMode,
-            aaudio_performance_mode_t requestedPerfMode);
+            aaudio_performance_mode_t requestedPerfMode,
+            aaudio_format_t requestedFormat = AAUDIO_FORMAT_PCM_I16);
     void initBuilder();
     void createAndVerifyStream(bool *success);
 
@@ -103,4 +107,91 @@
     const int32_t kBufferCapacityFrames = 2000;
 };
 
+
+#define LIB_AAUDIO_NAME          "libaaudio.so"
+#define FUNCTION_IS_MMAP         "AAudioStream_isMMapUsed"
+#define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy"
+#define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy"
+
+enum {
+    AAUDIO_POLICY_UNSPECIFIED = 0,
+/* These definitions are from aaudio/AAudioTesting.h */
+    AAUDIO_POLICY_NEVER = 1,
+    AAUDIO_POLICY_AUTO = 2,
+    AAUDIO_POLICY_ALWAYS = 3
+};
+typedef int32_t aaudio_policy_t;
+
+/**
+ * Call some AAudio test routines that are not part of the normal API.
+ */
+class AAudioExtensions {
+public:
+    AAudioExtensions();
+
+    static bool isPolicyEnabled(int32_t policy) {
+        return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
+    }
+
+    static AAudioExtensions &getInstance() {
+        static AAudioExtensions instance;
+        return instance;
+    }
+
+    static int getMMapPolicyProperty() {
+        return getIntegerProperty("aaudio.mmap_policy", AAUDIO_POLICY_UNSPECIFIED);
+    }
+
+    aaudio_policy_t getMMapPolicy() {
+        if (!mFunctionsLoaded) return -1;
+        return mAAudio_getMMapPolicy();
+    }
+
+    int32_t setMMapPolicy(aaudio_policy_t policy) {
+        if (!mFunctionsLoaded) return -1;
+        return mAAudio_setMMapPolicy(policy);
+    }
+
+    bool isMMapUsed(AAudioStream *aaudioStream) {
+        if (!mFunctionsLoaded) return false;
+        return mAAudioStream_isMMap(aaudioStream);
+    }
+
+    int32_t setMMapEnabled(bool enabled) {
+        return setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER);
+    }
+
+    bool isMMapEnabled() {
+        return isPolicyEnabled(mAAudio_getMMapPolicy());
+    }
+
+    bool isMMapSupported() const {
+        return mMMapSupported;
+    }
+
+    bool isMMapExclusiveSupported() const {
+        return mMMapExclusiveSupported;
+    }
+
+private:
+
+    static int getIntegerProperty(const char *name, int defaultValue);
+
+    /**
+     * Load some AAudio test functions.
+     * This should only be called once from the constructor.
+     * @return true if it succeeds
+     */
+    bool loadLibrary();
+
+    bool      mFunctionsLoaded = false;
+    void     *mLibHandle = nullptr;
+    bool    (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
+    int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
+    aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr;
+
+    const bool   mMMapSupported;
+    const bool   mMMapExclusiveSupported;
+};
+
 #endif  // CTS_MEDIA_TEST_AAUDIO_UTILS_H
diff --git a/tests/tests/ndef/OWNERS b/tests/tests/ndef/OWNERS
index 8ffbd10..d92b2ab 100644
--- a/tests/tests/ndef/OWNERS
+++ b/tests/tests/ndef/OWNERS
@@ -1,3 +1,5 @@
 # Bug component: 48448
-rmojumder@google.com
+alisher@google.com
+jackcwyu@google.com
+georgekgchang@google.com
 zachoverflow@google.com
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..0a80047
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.NetworkRequest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+    private static final Executor INLINE_EXECUTOR = x -> x.run();
+    private static final NetworkRequest DEFAULT_REQUEST = new NetworkRequest.Builder().build();
+
+    private Context mContext;
+    private ConnectivityDiagnosticsManager mCdm;
+    private ConnectivityDiagnosticsCallback mCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mCdm = mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+
+        mCallback = new ConnectivityDiagnosticsCallback() {};
+    }
+
+    @Test
+    public void testRegisterConnectivityDiagnosticsCallback() {
+        mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+    }
+
+    @Test
+    public void testRegisterDuplicateConnectivityDiagnosticsCallback() {
+        mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+
+        try {
+            mCdm.registerConnectivityDiagnosticsCallback(
+                    DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+            fail("Registering the same callback twice should throw an IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testUnregisterConnectivityDiagnosticsCallback() {
+        mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+        mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+    }
+
+    @Test
+    public void testUnregisterUnknownConnectivityDiagnosticsCallback() {
+        // Expected to silently ignore the unregister() call
+        mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+    }
+}
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index c32a7a0..1cc49f9 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -86,7 +86,7 @@
     static final int CANCEL_RETRY_TIMES = 5;
     static final int QUERY_TIMES = 10;
     static final int NXDOMAIN = 3;
-    static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
+    static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
 
     private ContentResolver mCR;
     private ConnectivityManager mCM;
@@ -122,10 +122,15 @@
         mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
     }
 
-    private void restorePrivateDnsSetting() {
+    private void restorePrivateDnsSetting() throws InterruptedException {
         // restore private DNS setting
         Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
+        if ("hostname".equals(mOldMode)) {
+            Settings.Global.putString(
+                mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
+            mCtsNetUtils.awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
+                    mCM.getActiveNetwork(), mOldDnsSpecifier, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
+        }
     }
 
     private static String byteArrayToHexString(byte[] bytes) {
@@ -203,6 +208,7 @@
         private final CancellationSignal mCancelSignal;
         private int mRcode;
         private DnsAnswer mDnsAnswer;
+        private String mErrorMsg = null;
 
         VerifyCancelCallback(@NonNull String msg, @Nullable CancellationSignal cancel) {
             mMsg = msg;
@@ -228,14 +234,18 @@
         @Override
         public void onAnswer(@NonNull byte[] answer, int rcode) {
             if (mCancelSignal != null && mCancelSignal.isCanceled()) {
-                fail(mMsg + " should not have returned any answers");
+                mErrorMsg = mMsg + " should not have returned any answers";
+                mLatch.countDown();
+                return;
             }
 
             mRcode = rcode;
             try {
                 mDnsAnswer = new DnsAnswer(answer);
             } catch (ParseException | DnsParseException e) {
-                fail(mMsg + e.getMessage());
+                mErrorMsg = mMsg + e.getMessage();
+                mLatch.countDown();
+                return;
             }
             Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer));
             mLatch.countDown();
@@ -243,10 +253,12 @@
 
         @Override
         public void onError(@NonNull DnsResolver.DnsException error) {
-            fail(mMsg + error.getMessage());
+            mErrorMsg = mMsg + error.getMessage();
+            mLatch.countDown();
         }
 
         private void assertValidAnswer() {
+            assertNull(mErrorMsg);
             assertNotNull(mMsg + " No valid answer", mDnsAnswer);
             assertEquals(mMsg + " Unexpected error: reported rcode" + mRcode +
                     " blob's rcode " + mDnsAnswer.getRcode(), mRcode, mDnsAnswer.getRcode());
@@ -402,20 +414,18 @@
     public void doTestRawQueryNXDomainWithPrivateDns(Executor executor)
             throws InterruptedException {
         final String msg = "RawQuery " + TEST_NX_DOMAIN + " with private DNS";
-
         // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
         // b/144521720
         Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
         Settings.Global.putString(mCR,
                 Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
-
         for (Network network :  getTestableNetworks()) {
             final Network networkForPrivateDns =
                     (network != null) ? network : mCM.getActiveNetwork();
             assertNotNull("Can't find network to await private DNS on", networkForPrivateDns);
             mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
                     networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER,
-                    PRIVATE_DNS_SETTING_TIMEOUT_MS);
+                    PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
             mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
                     executor, null, callback);
@@ -508,6 +518,7 @@
         private final String mMsg;
         private final List<InetAddress> mAnswers;
         private final CancellationSignal mCancelSignal;
+        private String mErrorMsg = null;
 
         VerifyCancelInetAddressCallback(@NonNull String msg, @Nullable CancellationSignal cancel) {
             this.mMsg = msg;
@@ -541,10 +552,16 @@
             return false;
         }
 
+        public void assertNoError() {
+            assertNull(mErrorMsg);
+        }
+
         @Override
         public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) {
             if (mCancelSignal != null && mCancelSignal.isCanceled()) {
-                fail(mMsg + " should not have returned any answers");
+                mErrorMsg = mMsg + " should not have returned any answers";
+                mLatch.countDown();
+                return;
             }
             for (InetAddress addr : answerList) {
                 Log.d(TAG, "Reported addr: " + addr.toString());
@@ -556,7 +573,7 @@
 
         @Override
         public void onError(@NonNull DnsResolver.DnsException error) {
-            fail(mMsg + error.getMessage());
+            mErrorMsg = mMsg + error.getMessage();
         }
     }
 
@@ -601,6 +618,7 @@
 
             assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
                     callback.waitForAnswer());
+            callback.assertNoError();
             assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
         }
     }
@@ -644,6 +662,7 @@
 
             assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
                     callback.waitForAnswer());
+            callback.assertNoError();
             assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
             assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer());
         }
@@ -659,6 +678,7 @@
 
             assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
                     callback.waitForAnswer());
+            callback.assertNoError();
             assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
             assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer());
         }
@@ -671,7 +691,6 @@
         Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
         Settings.Global.putString(mCR,
                 Settings.Global.PRIVATE_DNS_SPECIFIER, INVALID_PRIVATE_DNS_SERVER);
-
         final String msg = "Test PrivateDnsBypass " + TEST_DOMAIN;
         for (Network network : testNetworks) {
             // This test cannot be ran with null network because we need to explicitly pass a
@@ -680,7 +699,7 @@
 
             // wait for private DNS setting propagating
             mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
-                    network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
+                    network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, false);
 
             final CountDownLatch latch = new CountDownLatch(1);
             final DnsResolver.Callback<List<InetAddress>> errorCallback =
@@ -712,6 +731,7 @@
 
             assertTrue(msg + " bypass private DNS round. No answer after " + TIMEOUT_MS + "ms.",
                     callback.waitForAnswer());
+            callback.assertNoError();
             assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
 
             // To ensure private DNS bypass still work even if passing null network.
@@ -724,6 +744,7 @@
 
             assertTrue(msg + " with null network bypass private DNS round. No answer after " +
                     TIMEOUT_MS + "ms.", callbackWithNullNetwork.waitForAnswer());
+            callbackWithNullNetwork.assertNoError();
             assertTrue(msg + " with null network returned 0 results",
                     !callbackWithNullNetwork.isAnswerEmpty());
 
@@ -745,6 +766,7 @@
 
                 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
                         callback.waitForAnswer());
+                callback.assertNoError();
                 assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
                 assertTrue(msg + " returned " + (queryV6 ? "Ipv4" : "Ipv6") + " results",
                         queryV6 ? !callback.hasIpv4Answer() : !callback.hasIpv6Answer());
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
index 766c55e..f123187 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -245,7 +245,7 @@
             for (Network network : getTestableNetworks()) {
               // Wait for private DNS setting to propagate.
               mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
-                        network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
+                        network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
               runResNnxDomainCheck(network.getNetworkHandle());
             }
         } finally {
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index e4c4b00..45b9d97 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -16,17 +16,15 @@
 
 package android.net.wifi.cts;
 
-import android.content.Context;
+import static com.google.common.truth.Truth.assertThat;
+
 import android.content.pm.PackageManager;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiEnterpriseConfig.Eap;
 import android.net.wifi.WifiEnterpriseConfig.Phase2;
-import android.net.wifi.WifiManager;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 
-import com.android.compatibility.common.util.SystemUtil;
-
 import java.io.ByteArrayInputStream;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
@@ -36,9 +34,7 @@
 
 @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
 public class WifiEnterpriseConfigTest extends AndroidTestCase {
-    private WifiManager mWifiManager;
 
-    private static final String SSID = "\"TestSSID\"";
     private static final String IDENTITY = "identity";
     private static final String PASSWORD = "password";
     private static final String SUBJECT_MATCH = "subjectmatch";
@@ -47,7 +43,11 @@
     private static final String PLMN = "plmn";
     private static final String REALM = "realm";
     private static final String ANON_IDENTITY = "anonidentity";
-    private static final int ENABLE_DELAY = 10000;
+    private static final String CERTIFICATE_ALIAS1 = "certificatealias1";
+    private static final String CERTIFICATE_ALIAS2 = "certificatealias2";
+    private static final String CA_PATH = "capath";
+    private static final String CLIENT_CERTIFICATE_ALIAS = "clientcertificatealias";
+    private static final String WAPI_CERT_SUITE = "wapicertsuite";
 
     /*
      * The keys and certificates below are generated with:
@@ -684,22 +684,6 @@
                 PackageManager.FEATURE_WIFI);
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        if(!hasWifi()) {
-            return;
-        }
-        mWifiManager = (WifiManager) mContext
-                .getSystemService(Context.WIFI_SERVICE);
-        assertNotNull(mWifiManager);
-        SystemUtil.runShellCommand("svc wifi enable");
-        Thread.sleep(ENABLE_DELAY);
-        if (hasWifi()) {
-            assertTrue(mWifiManager.isWifiEnabled());
-        }
-    }
-
     public void testSettersAndGetters() throws Exception {
         if (!hasWifi()) {
             return;
@@ -766,6 +750,7 @@
         assertTrue(testClientCertChain.length == 2);
         assertTrue(testClientCertChain[0] == testClientCert);
         assertTrue(testClientCertChain[1] == cert1);
+        assertSame(clientKey, config.getClientPrivateKey());
 
         config.setSubjectMatch(SUBJECT_MATCH);
         assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
@@ -793,4 +778,122 @@
         assertTrue(stringRepresentation.contains(identity));
         assertFalse(stringRepresentation.contains(password));
     }
+
+    public void testGetSetCaCertificateAliases() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+        config.setCaCertificateAliases(null);
+        assertThat(config.getCaCertificateAliases()).isNull();
+
+        config.setCaCertificateAliases(new String[]{CERTIFICATE_ALIAS1});
+        assertThat(config.getCaCertificateAliases()).isEqualTo(new String[]{CERTIFICATE_ALIAS1});
+
+        config.setCaCertificateAliases(new String[]{CERTIFICATE_ALIAS1, CERTIFICATE_ALIAS2});
+        assertThat(config.getCaCertificateAliases())
+                .isEqualTo(new String[]{CERTIFICATE_ALIAS1, CERTIFICATE_ALIAS2});
+    }
+
+    public void testGetSetCaPath() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+        config.setCaPath("");
+        assertThat(config.getCaPath()).isEmpty();
+
+        config.setCaPath(CA_PATH);
+        assertThat(config.getCaPath()).isEqualTo(CA_PATH);
+    }
+
+    public void testGetSetClientCertificateAlias() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+        config.setClientCertificateAlias("");
+        assertThat(config.getClientCertificateAlias()).isEmpty();
+
+        config.setClientCertificateAlias(CLIENT_CERTIFICATE_ALIAS);
+        assertThat(config.getClientCertificateAlias()).isEqualTo(CLIENT_CERTIFICATE_ALIAS);
+    }
+
+    public void testGetSetOcsp() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+        config.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
+        assertThat(config.getOcsp()).isEqualTo(WifiEnterpriseConfig.OCSP_NONE);
+
+        config.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+        assertThat(config.getOcsp())
+                .isEqualTo(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+
+        try {
+            config.setOcsp(-1);
+            fail("WifiEnterpriseConfig.setOcsp(-1) did not throw an IllegalArgumentException!");
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    public void testGetSetWapiCertSuite() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+        config.setWapiCertSuite("");
+        assertThat(config.getWapiCertSuite()).isEmpty();
+
+        config.setWapiCertSuite(WAPI_CERT_SUITE);
+        assertThat(config.getWapiCertSuite()).isEqualTo(WAPI_CERT_SUITE);
+    }
+
+    public void testIsAuthenticationSimBased() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+
+        config.setEapMethod(Eap.AKA);
+        assertThat(config.isAuthenticationSimBased()).isTrue();
+
+        config.setEapMethod(Eap.PWD);
+        assertThat(config.isAuthenticationSimBased()).isFalse();
+
+        config.setEapMethod(Eap.PEAP);
+        config.setPhase2Method(Phase2.SIM);
+        assertThat(config.isAuthenticationSimBased()).isTrue();
+
+        config.setEapMethod(Eap.PEAP);
+        config.setPhase2Method(Phase2.NONE);
+        assertThat(config.isAuthenticationSimBased()).isFalse();
+    }
+
+    public void testCopyConstructor() {
+        if (!hasWifi()) {
+            return;
+        }
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+        config.setEapMethod(Eap.WAPI_CERT);
+        config.setWapiCertSuite(WAPI_CERT_SUITE);
+        config.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+        config.setCaPath(CA_PATH);
+        config.setPassword(PASSWORD);
+        config.setRealm(REALM);
+
+        WifiEnterpriseConfig copy = new WifiEnterpriseConfig(config);
+        assertThat(copy.getEapMethod()).isEqualTo(Eap.WAPI_CERT);
+        assertThat(copy.getWapiCertSuite()).isEqualTo(WAPI_CERT_SUITE);
+        assertThat(copy.getOcsp())
+                .isEqualTo(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+        assertThat(copy.getCaPath()).isEqualTo(CA_PATH);
+        assertThat(copy.getPassword()).isEqualTo(PASSWORD);
+        assertThat(copy.getRealm()).isEqualTo(REALM);
+    }
 }
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
index d943231..6f94fea 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -16,11 +16,13 @@
 
 package android.net.wifi.cts;
 
+import static com.google.common.truth.Truth.assertThat;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
 import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
@@ -78,13 +80,13 @@
 
         mContext.registerReceiver(mReceiver, mIntentFilter);
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
-        assertNotNull(mWifiManager);
+        assertThat(mWifiManager).isNotNull();
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
         if (!mWifiManager.isWifiEnabled())
             setWifiEnabled(true);
         Thread.sleep(DURATION);
-        assertTrue(mWifiManager.isWifiEnabled());
+        assertThat(mWifiManager.isWifiEnabled()).isTrue();
         mMySync.expectedState = STATE_NULL;
     }
 
@@ -123,31 +125,19 @@
             // skip the test if WiFi is not supported
             return;
         }
+
+        // wait for Wifi to be connected
+        PollingCheck.check(
+                "Wifi not connected - Please ensure there is a saved network in range of this "
+                        + "device",
+                20000,
+                () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
+
         // this test case should in Wifi environment
         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
 
-        assertNotNull(wifiInfo);
-        assertNotNull(wifiInfo.toString());
-        SupplicantState.isValidState(wifiInfo.getSupplicantState());
-        WifiInfo.getDetailedStateOf(SupplicantState.DISCONNECTED);
-        String ssid = wifiInfo.getSSID();
-        if (!ssid.startsWith("0x") && !ssid.equals(WifiManager.UNKNOWN_SSID)) {
-            // Non-hex string should be quoted
-            assertTrue(ssid.charAt(0) == '"');
-            assertTrue(ssid.charAt(ssid.length() - 1) == '"');
-        }
+        testWifiInfoPropertiesWhileConnected(wifiInfo);
 
-        wifiInfo.getBSSID();
-        wifiInfo.getFrequency();
-        wifiInfo.getIpAddress();
-        wifiInfo.getLinkSpeed();
-        wifiInfo.getPasspointFqdn();
-        wifiInfo.getPasspointProviderFriendlyName();
-        wifiInfo.getTxLinkSpeedMbps();
-        wifiInfo.getRxLinkSpeedMbps();
-        wifiInfo.getRssi();
-        wifiInfo.getHiddenSSID();
-        wifiInfo.getMacAddress();
         setWifiEnabled(false);
 
         PollingCheck.check("getNetworkId not -1", 20000, new Callable<Boolean>() {
@@ -166,4 +156,55 @@
         });
     }
 
+    private void testWifiInfoPropertiesWhileConnected(WifiInfo wifiInfo) {
+        assertThat(wifiInfo).isNotNull();
+        assertThat(wifiInfo.toString()).isNotNull();
+        SupplicantState.isValidState(wifiInfo.getSupplicantState());
+        WifiInfo.getDetailedStateOf(SupplicantState.DISCONNECTED);
+        String ssid = wifiInfo.getSSID();
+        if (!ssid.startsWith("0x") && !ssid.equals(WifiManager.UNKNOWN_SSID)) {
+            // Non-hex string should be quoted
+            assertThat(ssid).startsWith("\"");
+            assertThat(ssid).endsWith("\"");
+        }
+
+        assertThat(wifiInfo.getBSSID()).isNotNull();
+        assertThat(wifiInfo.getFrequency()).isGreaterThan(0);
+        assertThat(wifiInfo.getMacAddress()).isNotNull();
+
+        wifiInfo.getRssi();
+        wifiInfo.getIpAddress();
+        wifiInfo.getHiddenSSID();
+        wifiInfo.getScore();
+
+        // null for saved networks
+        assertThat(wifiInfo.getRequestingPackageName()).isNull();
+        assertThat(wifiInfo.getPasspointFqdn()).isNull();
+        assertThat(wifiInfo.getPasspointProviderFriendlyName()).isNull();
+
+        // false for saved networks
+        assertThat(wifiInfo.isEphemeral()).isFalse();
+        assertThat(wifiInfo.isOsuAp()).isFalse();
+        assertThat(wifiInfo.isPasspointAp()).isFalse();
+
+        assertThat(wifiInfo.getWifiStandard()).isAnyOf(
+                ScanResult.WIFI_STANDARD_UNKNOWN,
+                ScanResult.WIFI_STANDARD_LEGACY,
+                ScanResult.WIFI_STANDARD_11N,
+                ScanResult.WIFI_STANDARD_11AC,
+                ScanResult.WIFI_STANDARD_11AX
+        );
+
+        assertThat(wifiInfo.getLostTxPacketsPerSecond()).isAtLeast(0.0);
+        assertThat(wifiInfo.getRetriedTxPacketsPerSecond()).isAtLeast(0.0);
+        assertThat(wifiInfo.getSuccessfulRxPacketsPerSecond()).isAtLeast(0.0);
+        assertThat(wifiInfo.getSuccessfulTxPacketsPerSecond()).isAtLeast(0.0);
+
+        // Can be -1 if link speed is unknown
+        assertThat(wifiInfo.getLinkSpeed()).isAtLeast(-1);
+        assertThat(wifiInfo.getTxLinkSpeedMbps()).isAtLeast(-1);
+        assertThat(wifiInfo.getRxLinkSpeedMbps()).isAtLeast(-1);
+        assertThat(wifiInfo.getMaxSupportedTxLinkSpeedMbps()).isAtLeast(-1);
+        assertThat(wifiInfo.getMaxSupportedRxLinkSpeedMbps()).isAtLeast(-1);
+    }
 }
diff --git a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
index f0c34e3..6214f89 100644
--- a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -56,6 +56,7 @@
     private static final String TAG = CtsNetUtils.class.getSimpleName();
     private static final int DURATION = 10000;
     private static final int SOCKET_TIMEOUT_MS = 2000;
+    private static final int PRIVATE_DNS_PROBE_MS = 1_000;
 
     public static final int HTTP_PORT = 80;
     public static final String TEST_HOST = "connectivitycheck.gstatic.com";
@@ -246,12 +247,16 @@
     }
 
     public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
-            @NonNull String server, int timeoutMs) throws InterruptedException {
+            @NonNull String server, int timeoutMs,
+            boolean requiresValidatedServers) throws InterruptedException {
         CountDownLatch latch = new CountDownLatch(1);
         NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
         NetworkCallback callback = new NetworkCallback() {
             @Override
             public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
+                if (requiresValidatedServers && lp.getValidatedPrivateDnsServers().isEmpty()) {
+                    return;
+                }
                 if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
                     latch.countDown();
                 }
@@ -260,6 +265,18 @@
         mCm.registerNetworkCallback(request, callback);
         assertTrue(msg, latch.await(timeoutMs, TimeUnit.MILLISECONDS));
         mCm.unregisterNetworkCallback(callback);
+        // Wait some time for NetworkMonitor's private DNS probe to complete. If we do not do
+        // this, then the test could complete before the NetworkMonitor private DNS probe
+        // completes. This would result in tearDown disabling private DNS, and the NetworkMonitor
+        // private DNS probe getting stuck because there are no longer any private DNS servers to
+        // query. This then results in the next test not being able to change the private DNS
+        // setting within the timeout, because the NetworkMonitor thread is blocked in the
+        // private DNS probe. There is no way to know when the probe has completed: because the
+        // network is likely already validated, there is no callback that we can listen to, so
+        // just sleep.
+        if (requiresValidatedServers) {
+            Thread.sleep(PRIVATE_DNS_PROBE_MS);
+        }
     }
 
     /**
diff --git a/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java b/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
index c64f07f..803f55e 100644
--- a/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
@@ -16,14 +16,11 @@
 
 package android.app.notification.legacy20.cts;
 
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-
 import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -64,7 +61,6 @@
     private PackageManager mPackageManager;
     final String NOTIFICATION_CHANNEL_ID = "LegacyNotificationManagerTest";
     private NotificationManager mNotificationManager;
-    private ActivityManager mActivityManager;
     private Context mContext;
 
     private TestNotificationListener mListener;
@@ -78,7 +74,6 @@
                 Context.NOTIFICATION_SERVICE);
         mNotificationManager.createNotificationChannel(new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mPackageManager = mContext.getPackageManager();
     }
 
@@ -92,10 +87,6 @@
 
     @Test
     public void testNotificationListener_cancelNotifications() throws Exception {
-        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
-            return;
-        }
-
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
index b999cfe..9049b3f 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
@@ -24,11 +24,8 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.TestCase.fail;
 
-import static org.junit.Assume.assumeFalse;
-
 import static java.lang.Thread.sleep;
 
-import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
 import android.app.Instrumentation;
 import android.app.NotificationManager;
@@ -62,7 +59,6 @@
     private static String TAG = "CpsTest";
 
     private NotificationManager mNm;
-    private ActivityManager mActivityManager;
     private Context mContext;
     private ZenModeBroadcastReceiver mModeReceiver;
     private IntentFilter mModeFilter;
@@ -71,9 +67,6 @@
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        assumeFalse(mActivityManager.isLowRamDevice());
-
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
         LegacyConditionProviderService.requestRebind(LegacyConditionProviderService.getId());
@@ -114,10 +107,6 @@
 
     @Test
     public void testUnboundCPSMaintainsCondition_addsNewRule() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // make sure service get bound
         pollForConnection(SecondaryConditionProviderService.class, true);
 
@@ -147,10 +136,6 @@
 
     @Test
     public void testUnboundCPSMaintainsCondition_otherConditionChanges() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // make sure both services get bound
         pollForConnection(LegacyConditionProviderService.class, true);
         pollForConnection(SecondaryConditionProviderService.class, true);
@@ -184,10 +169,6 @@
 
     @Test
     public void testUnboundCPSMaintainsCondition_otherProviderRuleChanges() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // make sure both services get bound
         pollForConnection(LegacyConditionProviderService.class, true);
         pollForConnection(SecondaryConditionProviderService.class, true);
@@ -219,10 +200,6 @@
 
     @Test
     public void testRequestRebindWhenLostAccess() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // make sure it gets bound
         pollForConnection(LegacyConditionProviderService.class, true);
 
@@ -250,10 +227,6 @@
 
     @Test
     public void testRequestRebindWhenStillHasAccess() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // make sure it gets bound
         pollForConnection(LegacyConditionProviderService.class, true);
 
@@ -276,12 +249,6 @@
 
     @Test
     public void testMethodsExistAndDoNotThrow() throws Exception {
-        // behavior is covered in cts verifier
-
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
-
         // make sure it gets bound
         pollForConnection(LegacyConditionProviderService.class, true);
 
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
index f2960a6..f5606de 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
@@ -65,7 +65,6 @@
 
     final String NOTIFICATION_CHANNEL_ID = "LegacyNotificationManagerTest";
     private NotificationManager mNotificationManager;
-    private ActivityManager mActivityManager;
     private Context mContext;
 
     private SecondaryNotificationListener mSecondaryListener;
@@ -82,7 +81,6 @@
                 Context.NOTIFICATION_SERVICE);
         mNotificationManager.createNotificationChannel(new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     @After
@@ -98,9 +96,6 @@
 
     @Test
     public void testPrePCannotToggleAlarmsAndMediaTest() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -141,9 +136,6 @@
 
     @Test
     public void testSetNotificationPolicy_preP_setOldFields() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -164,9 +156,6 @@
 
     @Test
     public void testSetNotificationPolicy_preP_setNewFields() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -187,9 +176,6 @@
 
     @Test
     public void testSuspendPackage() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -222,9 +208,6 @@
 
     @Test
     public void testSuspendedPackageSendNotification() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -255,9 +238,6 @@
 
     @Test
     public void testResetListenerHints_singleListener() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         Thread.sleep(500); // wait for listener to be allowed
@@ -277,9 +257,6 @@
 
     @Test
     public void testResetListenerHints_multiListener() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
         toggleListenerAccess(SecondaryNotificationListener.getId(),
@@ -307,9 +284,6 @@
 
     @Test
     public void testSetNotificationPolicy_preP_setOldNewFields() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
index 1747c17..a69c595 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
@@ -24,7 +24,6 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
-import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -43,6 +42,9 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import junit.framework.Assert;
 
 import org.junit.After;
@@ -57,9 +59,6 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
 @RunWith(AndroidJUnit4.class)
 public class NotificationAssistantServiceTest {
 
@@ -71,7 +70,6 @@
     private TestNotificationAssistant mNotificationAssistantService;
     private TestNotificationListener mNotificationListenerService;
     private NotificationManager mNotificationManager;
-    private ActivityManager mActivityManager;
     private StatusBarManager mStatusBarManager;
     private Context mContext;
     private UiAutomation mUi;
@@ -84,7 +82,6 @@
                 Context.NOTIFICATION_SERVICE);
         mNotificationManager.createNotificationChannel(new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
-        mActivityManager = mContext.getSystemService(ActivityManager.class);
         mStatusBarManager = (StatusBarManager) mContext.getSystemService(
                 Context.STATUS_BAR_SERVICE);
     }
@@ -92,18 +89,14 @@
     @After
     public void tearDown() throws IOException {
         if (mNotificationListenerService != null) mNotificationListenerService.resetData();
-        if (!mActivityManager.isLowRamDevice()) {
-            toggleListenerAccess(false);
-            toggleAssistantAccess(false);
-        }
+
+        toggleListenerAccess(false);
+        toggleAssistantAccess(false);
         mUi.dropShellPermissionIdentity();
     }
 
     @Test
     public void testOnNotificationEnqueued() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleListenerAccess(true);
         Thread.sleep(SLEEP_TIME);
 
@@ -138,9 +131,6 @@
 
     @Test
     public void testAdjustNotification_userSentimentKey() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -172,9 +162,6 @@
 
     @Test
     public void testAdjustNotification_importanceKey() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -205,9 +192,6 @@
 
     @Test
     public void testAdjustNotification_rankingScoreKey() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         try {
@@ -263,9 +247,6 @@
 
     @Test
     public void testAdjustNotification_smartActionKey() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -313,9 +294,6 @@
 
     @Test
     public void testAdjustNotification_smartReplyKey() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
         CharSequence smartReply = "Smart Reply!";
 
@@ -359,9 +337,6 @@
 
     @Test
     public void testAdjustNotification_importanceKey_notAllowed() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -394,9 +369,6 @@
 
     @Test
     public void testAdjustNotification_rankingScoreKey_notAllowed() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
@@ -442,9 +414,6 @@
 
     @Test
     public void testGetAllowedAssistantCapabilities_permission() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleAssistantAccess(false);
 
         try {
@@ -457,9 +426,6 @@
 
     @Test
     public void testGetAllowedAssistantCapabilities() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleAssistantAccess(true);
         Thread.sleep(SLEEP_TIME); // wait for assistant to be allowed
         mNotificationAssistantService = TestNotificationAssistant.getInstance();
@@ -489,9 +455,6 @@
 
     @Test
     public void testOnActionInvoked_methodExists() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
         final Intent intent = new Intent(Intent.ACTION_MAIN, Telephony.Threads.CONTENT_URI);
 
@@ -509,9 +472,6 @@
 
     @Test
     public void testOnNotificationDirectReplied_methodExists() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
         // This method has to exist and the call cannot fail
         mNotificationAssistantService.onNotificationDirectReplied("");
@@ -519,9 +479,6 @@
 
     @Test
     public void testOnNotificationExpansionChanged_methodExists() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
         // This method has to exist and the call cannot fail
         mNotificationAssistantService.onNotificationExpansionChanged("", true, true);
@@ -529,9 +486,6 @@
 
     @Test
     public void testOnNotificationVisibilityChanged() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -560,9 +514,6 @@
 
     @Test
     public void testOnNotificationsSeen() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -585,9 +536,6 @@
 
     @Test
     public void testOnPanelRevealedAndHidden() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
 
         mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -609,9 +557,6 @@
 
     @Test
     public void testOnSuggestedReplySent_methodExists() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         setUpListeners();
         // This method has to exist and the call cannot fail
         mNotificationAssistantService.onSuggestedReplySent("", "",
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
index 96c2b0d..5b70d19 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
@@ -17,7 +17,6 @@
 package android.app.notification.legacy29.cts;
 
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
-import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
 
 import static junit.framework.Assert.assertEquals;
@@ -25,7 +24,6 @@
 import static junit.framework.TestCase.assertNotNull;
 import static junit.framework.TestCase.assertNull;
 
-import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -61,7 +59,6 @@
     final String NOTIFICATION_CHANNEL_ID = "LegacyNoManTest29";
     private NotificationManager mNotificationManager;
     private Context mContext;
-    private ActivityManager mActivityManager;
 
     @Before
     public void setUp() throws Exception {
@@ -71,7 +68,6 @@
                 Context.NOTIFICATION_SERVICE);
         mNotificationManager.createNotificationChannel(new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     private void toggleNotificationPolicyAccess(String packageName,
@@ -158,9 +154,6 @@
 
     @Test
     public void testApi29CannotToggleConversationsTest() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
@@ -185,9 +178,6 @@
 
     @Test
     public void testApi29CannotToggleConversationsOffTest() throws Exception {
-        if (mActivityManager.isLowRamDevice()) {
-            return;
-        }
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 9639c0d..7c9ea67 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -47,6 +47,7 @@
     <uses-permission android:name="android.permission.POWER_SAVER" />
     <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
     <uses-permission android:name="android.permission.MANAGE_COMPANION_DEVICES" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.os.cts.permission.TEST_GRANTED" />
 
     <application
@@ -70,6 +71,8 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.os.cts.SimpleTestActivity" />
+
         <service
             android:name="android.os.cts.ParcelFileDescriptorPeer$Red"
             android:process=":red"
diff --git a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
index 13de572..932cbaf 100644
--- a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
+++ b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
@@ -17,6 +17,8 @@
 package android.os.cts;
 
 import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
 import static junit.framework.TestCase.fail;
 
 import static org.mockito.Mockito.reset;
@@ -123,4 +125,28 @@
         verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
                 .times(1)).onThermalStatusChanged(status);
     }
+
+    @Test
+    public void testGetThermalHeadroom() throws Exception {
+        float headroom = mPowerManager.getThermalHeadroom(0);
+        // If the device doesn't support thermal headroom, return early
+        if (Float.isNaN(headroom)) {
+            return;
+        }
+        assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+        assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+        // Sleep for a second before attempting to call again so as to not get rate limited
+        Thread.sleep(1000);
+        headroom = mPowerManager.getThermalHeadroom(5);
+        assertFalse("Expected data to still be available", Float.isNaN(headroom));
+        assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+        assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+        // Test rate limiting by spamming calls and ensuring that at least the last one fails
+        for (int i = 0; i < 20; ++i) {
+            headroom = mPowerManager.getThermalHeadroom(5);
+        }
+        assertTrue("Abusive calls get rate limited", Float.isNaN(headroom));
+    }
 }
diff --git a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
new file mode 100644
index 0000000..4242315
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+import android.app.Activity;
+
+public class SimpleTestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 2ffbbb7..1a4f0ef 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -16,17 +16,23 @@
 
 package android.os.cts;
 
+import static android.content.Context.WINDOW_SERVICE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.IBinder;
@@ -51,6 +57,8 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -610,6 +618,66 @@
         }
     }
 
+    @Test
+    public void testIncorrectContextUse_GetSystemService() throws Exception {
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder()
+                        .detectIncorrectContextUse()
+                        .penaltyLog()
+                        .build());
+
+        inspectViolation(
+                () -> getContext().getApplicationContext().getSystemService(WindowManager.class),
+                info -> assertThat(info.getStackTrace()).contains(
+                        "Tried to access visual service " + WINDOW_SERVICE));
+
+        final Display display = getContext().getSystemService(DisplayManager.class)
+                .getDisplay(DEFAULT_DISPLAY);
+        final Context visualContext = getContext().createDisplayContext(display)
+                .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
+        assertNoViolation(() -> visualContext.getSystemService(WINDOW_SERVICE));
+
+        Intent intent = new Intent(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                SimpleTestActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final Activity activity = InstrumentationRegistry.getInstrumentation()
+                .startActivitySync(intent);
+        assertNoViolation(() -> activity.getSystemService(WINDOW_SERVICE));
+    }
+
+    @Test
+    public void testIncorrectContextUse_GetDisplay() throws Exception {
+        StrictMode.setVmPolicy(
+                new StrictMode.VmPolicy.Builder()
+                        .detectIncorrectContextUse()
+                        .penaltyLog()
+                        .build());
+
+        final Display display = getContext().getSystemService(DisplayManager.class)
+                .getDisplay(DEFAULT_DISPLAY);
+
+        final Context displayContext = getContext().createDisplayContext(display);
+        assertNoViolation(displayContext::getDisplay);
+
+        final Context windowContext =
+                displayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
+        assertNoViolation(windowContext::getDisplay);
+
+        Intent intent = new Intent(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                SimpleTestActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final Activity activity = InstrumentationRegistry.getInstrumentation()
+                .startActivitySync(intent);
+        assertNoViolation(() -> activity.getDisplay());
+
+        try {
+            getContext().getApplicationContext().getDisplay();
+        } catch (UnsupportedOperationException e) {
+            return;
+        }
+        fail("Expected to get incorrect use exception from calling getDisplay() on Application");
+    }
+
     private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
             throws ExecutionException, InterruptedException, RemoteException {
         BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/AndroidManifest.xml b/tests/tests/packageinstaller/adminpackageinstaller/AndroidManifest.xml
index 6eabfb0..3867e9f 100755
--- a/tests/tests/packageinstaller/adminpackageinstaller/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/adminpackageinstaller/AndroidManifest.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -18,6 +17,7 @@
           package="android.packageinstaller.admin.cts" >
 
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application android:label="Cts Admin Package Installer Test" android:testOnly="true">
         <uses-library android:name="android.test.runner"/>
diff --git a/tests/tests/packageinstaller/install/AndroidManifest.xml b/tests/tests/packageinstaller/install/AndroidManifest.xml
index eeef252..4ece1a9 100644
--- a/tests/tests/packageinstaller/install/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/install/AndroidManifest.xml
@@ -18,6 +18,7 @@
           package="android.packageinstaller.install.cts" >
 
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application android:label="Cts Package Installer Tests">
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/packageinstaller/nopermission/AndroidManifest.xml b/tests/tests/packageinstaller/nopermission/AndroidManifest.xml
index 321066c..08a7db1 100755
--- a/tests/tests/packageinstaller/nopermission/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/nopermission/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.packageinstaller.nopermission.cts" >
 
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index a8bc1d0..e37b956 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -76,9 +76,4 @@
         <option name="package" value="android.permission.cts" />
         <option name="runtime-hint" value="13m" />
     </test>
-
-    <object type="module_controller"
-            class="com.android.tradefed.testtype.suite.module.TestFailureModuleController">
-        <option name="bugreportz-on-failure" value="true" />
-    </object>
 </configuration>
diff --git a/tests/tests/permission/telephony/OWNERS b/tests/tests/permission/telephony/OWNERS
index 423a16b..b19c963 100644
--- a/tests/tests/permission/telephony/OWNERS
+++ b/tests/tests/permission/telephony/OWNERS
@@ -1,4 +1,2 @@
 set noparent
-amitmahajan@google.com
-fionaxu@google.com
-jminjie@google.com
\ No newline at end of file
+include ../../telephony/OWNERS
\ No newline at end of file
diff --git a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
index b050ee0..b13a00a 100644
--- a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
@@ -200,6 +200,38 @@
         }
     }
 
+     /**
+     * Tests that isManualNetworkSelectionAllowed requires permission
+     * Expects a security exception since the caller does not have carrier privileges.
+     */
+    @Test
+    public void testIsManualNetworkSelectionAllowedWithoutPermission() {
+        if (!mHasTelephony) {
+            return;
+        }
+        try {
+            mTelephonyManager.isManualNetworkSelectionAllowed();
+            fail("Expected SecurityException. App does not have carrier privileges.");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Tests that getManualNetworkSelectionPlmn requires permission
+     * Expects a security exception since the caller does not have carrier privileges.
+     */
+    @Test
+    public void testGetManualNetworkSelectionPlmnWithoutPermission() {
+        if (!mHasTelephony) {
+            return;
+        }
+        try {
+            mTelephonyManager.getManualNetworkSelectionPlmn();
+            fail("Expected SecurityException. App does not have carrier privileges.");
+        } catch (SecurityException expected) {
+        }
+    }
+
     /**
      * Verify that Telephony related broadcasts are protected.
      */
@@ -307,6 +339,32 @@
     }
 
     /**
+     * Tests that getNetworkSelectionMode requires permission
+     * Expects a security exception since the caller does not have carrier privileges.
+     */
+    @Test
+    public void testGetNetworkSelectionModeWithoutPermission() {
+        if (!mHasTelephony) {
+            return;
+        }
+        assertThrowsSecurityException(() -> mTelephonyManager.getNetworkSelectionMode(),
+                "Expected SecurityException. App does not have carrier privileges.");
+    }
+
+    /**
+     * Tests that setNetworkSelectionModeAutomatic requires permission
+     * Expects a security exception since the caller does not have carrier privileges.
+     */
+    @Test
+    public void testSetNetworkSelectionModeAutomaticWithoutPermission() {
+        if (!mHasTelephony) {
+            return;
+        }
+        assertThrowsSecurityException(() -> mTelephonyManager.setNetworkSelectionModeAutomatic(),
+                "Expected SecurityException. App does not have carrier privileges.");
+    }
+
+    /**
      * Verify that setForbiddenPlmns requires Permission.
      * <p>
      * Requires Permission:
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 650432f..9bbc177 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -1197,6 +1197,15 @@
                 android:description="@string/permdesc_acceptHandovers"
                 android:protectionLevel="dangerous" />
 
+    <!-- Allows an application assigned to the Dialer role to be granted access to the telephony
+         call audio streams, both TX and RX.
+         <p>Protection level: signature|appop
+    -->
+    <permission android:name="android.permission.ACCESS_CALL_AUDIO"
+                android.label="@string/permlab_accessCallAudio"
+                android:description="@string/permdesc_accessCallAudio"
+                android:protectionLevel="signature|appop" />
+
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device microphone                        -->
     <!-- ====================================================================== -->
@@ -1947,6 +1956,18 @@
         android:description="@string/permdesc_modifyAudioSettings"
         android:protectionLevel="normal" />
 
+    <!-- ======================================== -->
+    <!-- Permissions for factory reset protection -->
+    <!-- ======================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows an application to set a factory reset protection (FRP) policy.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.MANAGE_FACTORY_RESET_PROTECTION"
+        android:protectionLevel="signature|privileged"/>
+
     <!-- ================================== -->
     <!-- Permissions for accessing hardware -->
     <!-- ================================== -->
@@ -2743,7 +2764,7 @@
     <permission android:name="android.permission.READ_DEVICE_CONFIG"
         android:protectionLevel="signature|preinstalled" />
 
-    <!-- @SystemApi @hide Allows an application to monitor config settings access.
+    <!-- @hide Allows an application to monitor config settings access.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
         android:protectionLevel="signature"/>
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
index 961d998..049f4aa 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
@@ -269,7 +269,8 @@
                         Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_PODCASTS,
                         Environment.DIRECTORY_ALARMS));
         final Set<String> allowedVideo = new HashSet<>(
-                Arrays.asList(Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_DCIM));
+                Arrays.asList(Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_DCIM,
+                Environment.DIRECTORY_PICTURES));
         final Set<String> allowedImages = new HashSet<>(
                 Arrays.asList(Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_DCIM));
         final Set<String> allowedDownloads = new HashSet<>(
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
index 061fe38..7228c7c 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
@@ -116,9 +116,9 @@
         assertEquals(1, mResolver.update(fileUri, values, null, null));
         assertStringColumn(fileUri, MediaColumns.DATA, updatedPath);
 
-        // check that inserting a duplicate entry fails
+        // check that inserting a duplicate entry updates previous entry.
         Uri foo = mResolver.insert(allFilesUri, values);
-        assertNull(foo);
+        assertEquals(foo, fileUri);
 
         // Delete the file and observe that the file count decreased.
         assertEquals(1, mResolver.delete(fileUri, null, null));
diff --git a/tests/tests/secure_element/access_control/OWNERS b/tests/tests/secure_element/access_control/OWNERS
index 853b7c3..6c4d2b3 100644
--- a/tests/tests/secure_element/access_control/OWNERS
+++ b/tests/tests/secure_element/access_control/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 456592
-zachoverflow@google.com
+alisher@google.com
 jackcwyu@google.com
-tokuda@google.com
 georgekgchang@google.com
-jimmychchang@google.com
+zachoverflow@google.com
diff --git a/tests/tests/secure_element/omapi/OWNERS b/tests/tests/secure_element/omapi/OWNERS
index 853b7c3..6c4d2b3 100644
--- a/tests/tests/secure_element/omapi/OWNERS
+++ b/tests/tests/secure_element/omapi/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 456592
-zachoverflow@google.com
+alisher@google.com
 jackcwyu@google.com
-tokuda@google.com
 georgekgchang@google.com
-jimmychchang@google.com
+zachoverflow@google.com
diff --git a/tests/tests/selinux/TEST_MAPPING b/tests/tests/selinux/TEST_MAPPING
index a8bae77..fb3c6c8 100644
--- a/tests/tests/selinux/TEST_MAPPING
+++ b/tests/tests/selinux/TEST_MAPPING
@@ -13,8 +13,10 @@
       "name": "CtsSelinuxTargetSdk28TestCases"
     },
     {
+      "name": "CtsSelinuxTargetSdk29TestCases"
+    },
+    {
       "name": "CtsSelinuxTargetSdkCurrentTestCases"
     }
   ]
 }
-
diff --git a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index 25a343d..6ec352c 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -8,6 +8,8 @@
 import java.util.Scanner;
 import java.io.File;
 import java.io.IOException;
+import java.net.NetworkInterface;
+import java.util.Collections;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
@@ -29,27 +31,6 @@
         }
     }
 
-    protected static void noExecuteOnly() throws IOException {
-        String[] maps = {"^[0-9a-z]+-[0-9a-z]+\\s+--xp.*\\/apex\\/com\\.android\\.runtime\\/.*",
-            "^[0-9a-z]+-[0-9a-z]+\\s+--xp.*\\/system\\/.*"};
-        for (String map : maps) {
-            final Pattern mapsPattern = Pattern.compile(map);
-            BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"));
-            String line;
-            try {
-                while ((line = reader.readLine()) != null) {
-                    Matcher m = mapsPattern.matcher(line);
-                    assertFalse("System provided libraries should be not be marked execute-only " +
-                           "for apps with targetSdkVersion<Q, but an execute-only segment was " +
-                           "found:\n" + line, m.matches());
-                }
-
-            } finally {
-                reader.close();
-            }
-        }
-    }
-
     protected static String getProperty(String property)
             throws IOException {
         Process process = new ProcessBuilder("getprop", property).start();
@@ -78,16 +59,36 @@
         }
     }
 
-    protected static void noNetlinkRouteGetlink() throws IOException {
-        assertEquals("RTM_GETLINK is not allowed on a netlink route sockets. Verify that the " +
-			"following patch has been applied to your kernel: " +
-			"https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
-                13, checkNetlinkRouteGetlink());
+    protected static void checkNetlinkRouteGetlink(boolean expectAllowed) throws IOException {
+        if (!expectAllowed) {
+            assertEquals(
+                    "RTM_GETLINK is not allowed on a netlink route sockets. Verify that the"
+                        + " following patch has been applied to your kernel: "
+                        + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
+                    13,
+                    checkNetlinkRouteGetlink());
+        } else {
+            assertEquals(
+                    "RTM_GETLINK should be allowed netlink route sockets for apps with "
+                            + "targetSdkVersion <= Q",
+                    -1,
+                    checkNetlinkRouteGetlink());
+        }
     }
 
-    protected static void noNetlinkRouteBind() throws IOException {
-        assertEquals("Bind() is not allowed on a netlink route sockets",
-                13, checkNetlinkRouteBind());
+    protected static void checkNetlinkRouteBind(boolean expectAllowed) throws IOException {
+        if (!expectAllowed) {
+            assertEquals(
+                    "Bind() is not allowed on a netlink route sockets",
+                    13,
+                    checkNetlinkRouteBind());
+        } else {
+            assertEquals(
+                    "Bind() should succeed for netlink route sockets for apps with "
+                            + "targetSdkVersion <= Q",
+                    -1,
+                    checkNetlinkRouteBind());
+        }
     }
 
     /**
@@ -167,6 +168,29 @@
         return true;
     }
 
+    /**
+     * Verify that apps having targetSdkVersion <= 29 are able to see MAC
+     * addresses of ethernet devices.
+     * The counterpart of this test (testing for targetSdkVersion > 29) is
+     * {@link libcore.java.net.NetworkInterfaceTest#testGetHardwareAddress_returnsNull()}.
+     */
+    protected static void checkNetworkInterface_returnsHardwareAddresses() throws Exception {
+        assertNotNull(NetworkInterface.getNetworkInterfaces());
+        for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+            if (isEthernet(nif.getName())) {
+                assertEquals(6, nif.getHardwareAddress().length);
+            }
+        }
+    }
+
+    /**
+     * Checks whether a network interface is an ethernet interface.
+     */
+    private static Pattern ethernetNamePattern = Pattern.compile("^(eth|wlan)[0-9]+$");
+    private static boolean isEthernet(String ifName) throws Exception {
+        return ethernetNamePattern.matcher(ifName).matches();
+    }
+
     private static final native int checkNetlinkRouteGetlink();
     private static final native int checkNetlinkRouteBind();
     private static final native String getFileContext(String path);
diff --git a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
index 2826421..1ed366e 100644
--- a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
@@ -77,4 +77,12 @@
             checkDex2oatAccess(false);
         }
     }
+
+    public void testNoNetlinkRouteGetlink() throws IOException {
+        checkNetlinkRouteGetlink(false);
+    }
+
+    public void testNoNetlinkRouteBind() throws IOException {
+        checkNetlinkRouteBind(false);
+    }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
index dddd3d9..ec2af2d 100644
--- a/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
@@ -68,8 +68,4 @@
     public void testDex2oat() throws Exception {
         checkDex2oatAccess(true);
     }
-
-    public void testNoExecuteOnly() throws IOException {
-        noExecuteOnly();
-    }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml b/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml
index d2addf8..b94757c 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk27/AndroidManifest.xml
@@ -22,6 +22,10 @@
          untrusted_app_27 selinux domain -->
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
 
+    <!-- INTERNET permission allows apps with targetSdkValue==27 to list the
+         device's network interfaces -->
+    <uses-permission android:name="android.permission.INTERNET" />
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
@@ -34,4 +38,3 @@
     </instrumentation>
 
 </manifest>
-
diff --git a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
index e7c0e67..a784464 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
@@ -65,7 +65,7 @@
         checkDex2oatAccess(true);
     }
 
-    public void testNoExecuteOnly() throws IOException {
-        noExecuteOnly();
+    public void testNetworkInterface() throws Exception {
+        checkNetworkInterface_returnsHardwareAddresses();
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml b/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml
index af86fa5..97d15c5 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml
+++ b/tests/tests/selinux/selinuxTargetSdk28/AndroidManifest.xml
@@ -22,6 +22,10 @@
          untrusted_app_28 selinux domain -->
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
 
+    <!-- INTERNET permission allows apps with targetSdkValue==28 to list the
+         device's network interfaces -->
+    <uses-permission android:name="android.permission.INTERNET" />
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
@@ -34,4 +38,3 @@
     </instrumentation>
 
 </manifest>
-
diff --git a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
index 1ab9706..880ae1a 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
@@ -65,7 +65,7 @@
         checkDex2oatAccess(true);
     }
 
-    public void testNoExecuteOnly() throws IOException {
-        noExecuteOnly();
+    public void testNetworkInterface() throws Exception {
+        checkNetworkInterface_returnsHardwareAddresses();
     }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk29/Android.bp b/tests/tests/selinux/selinuxTargetSdk29/Android.bp
new file mode 100644
index 0000000..bd62d00
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/Android.bp
@@ -0,0 +1,51 @@
+//
+// 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.
+
+android_test {
+    name: "CtsSelinuxTargetSdk29TestCases",
+    defaults: ["cts_defaults"],
+    compile_multilib: "both",
+    static_libs: [
+        "ctstestrunner-axt",
+        "compatibility-device-util-axt",
+    ],
+    libs: [
+        "android.test.base.stubs",
+        "android.test.runner.stubs",
+    ],
+    jni_libs: [
+        "libbase",
+        "libc++",
+        "libcrypto",
+        "libcts_jni",
+        "libctsselinux_jni",
+        "libnativehelper",
+        "libnativehelper_compat_libc++",
+        "libpackagelistparser",
+        "libpcre2",
+        "libselinux",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "common/**/*.java",
+    ],
+    platform_apis: true,
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    min_sdk_version: "21",
+}
diff --git a/tests/tests/selinux/selinuxTargetSdk29/AndroidManifest.xml b/tests/tests/selinux/selinuxTargetSdk29/AndroidManifest.xml
new file mode 100644
index 0000000..3e4e7c4
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.selinuxtargetsdk29.cts">
+
+    <!-- This app tests that apps with targetSdkValue==29 are placed in the
+         untrusted_app_29 selinux domain -->
+    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
+
+    <!-- INTERNET permission allows apps with targetSdkValue==29 to list the
+         device's network interfaces -->
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.selinuxtargetsdk29.cts"
+                     android:label="CTS tests for permissions enforce by selinux based on targetSdkVersion">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/selinux/selinuxTargetSdk29/AndroidTest.xml b/tests/tests/selinux/selinuxTargetSdk29/AndroidTest.xml
new file mode 100644
index 0000000..bed0f0a
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS Permission Selinux test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="security" />
+    <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="not-shardable" value="true" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsSelinuxTargetSdk29TestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.selinuxtargetsdk29.cts" />
+        <option name="runtime-hint" value="2m" />
+    </test>
+</configuration>
diff --git a/tests/tests/selinux/selinuxTargetSdk29/common b/tests/tests/selinux/selinuxTargetSdk29/common
new file mode 120000
index 0000000..581eb17
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/common
@@ -0,0 +1 @@
+../common/src
\ No newline at end of file
diff --git a/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
new file mode 100644
index 0000000..1f1eaaa
--- /dev/null
+++ b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.test.AndroidTestCase;
+import com.android.compatibility.common.util.PropertyUtil;
+import java.io.IOException;
+
+/**
+ * Verify the selinux domain for apps running with targetSdkVersion==29
+ */
+public class SELinuxTargetSdkTest extends SELinuxTargetSdkTestBase
+{
+    /**
+     * Verify that net.dns properties may not be read
+     */
+    public void testNoDns() throws IOException {
+        noDns();
+    }
+
+    public void testDex2oat() throws Exception {
+        /*
+         * Apps with a vendor image older than Q may access the dex2oat executable through
+         * selinux policy on the vendor partition because the permission was granted in public
+         * policy for appdomain.
+         */
+        if (PropertyUtil.isVendorApiLevelNewerThan(28)) {
+            checkDex2oatAccess(false);
+        }
+    }
+
+    public void testNetlinkRouteGetlinkSucceeds() throws IOException {
+        checkNetlinkRouteGetlink(true);
+    }
+
+    public void testNetlinkRouteBindSucceeds() throws IOException {
+        checkNetlinkRouteBind(true);
+    }
+
+    public void testCanNotExecuteFromHomeDir() throws Exception {
+        assertFalse(canExecuteFromHomeDir());
+    }
+
+    /**
+     * Verify that selinux context is the expected domain based on
+     * targetSdkVersion = 29
+     */
+    public void testAppDomainContext() throws IOException {
+        String context = "u:r:untrusted_app_29:s0:c[0-9]+,c[0-9]+,c[0-9]+,c[0-9]+";
+        String msg = "Untrusted apps with targetSdkVersion 29 " +
+            "must run in the untrusted_app selinux domain and use the levelFrom=all " +
+            "selector in SELinux seapp_contexts which adds four category types " +
+            "to the app's selinux context.\n" +
+            "Example expected value: u:r:untrusted_app:s0:c89,c256,c512,c768\n" +
+            "Actual value: ";
+        appDomainContext(context, msg);
+    }
+
+    /**
+     * Verify that selinux context is the expected type based on
+     * targetSdkVersion = 29
+     */
+    public void testAppDataContext() throws Exception {
+        String context = "u:object_r:app_data_file:s0:c[0-9]+,c[0-9]+,c[0-9]+,c[0-9]+";
+        String msg = "Untrusted apps with targetSdkVersion 29 " +
+            "must use the app_data_file selinux context and use the levelFrom=all " +
+            "selector in SELinux seapp_contexts which adds four category types " +
+            "to the app_data_file context.\n" +
+            "Example expected value: u:object_r:app_data_file:s0:c89,c256,c512,c768\n" +
+            "Actual value: ";
+        appDataContext(context, msg);
+    }
+
+    public void testNetworkInterface() throws Exception {
+        checkNetworkInterface_returnsHardwareAddresses();
+    }
+}
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
index 859864e..05fad31 100644
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
@@ -34,11 +34,11 @@
     }
 
     public void testNoNetlinkRouteGetlink() throws IOException {
-        noNetlinkRouteGetlink();
+        checkNetlinkRouteGetlink(false);
     }
 
     public void testNoNetlinkRouteBind() throws IOException {
-        noNetlinkRouteBind();
+        checkNetlinkRouteBind(false);
     }
 
     public void testCanNotExecuteFromHomeDir() throws Exception {
diff --git a/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java b/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
index 8a655cd..710ac94 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
@@ -54,9 +55,13 @@
     private static final Uri BASE_URI = Uri.parse("content://android.slice.cts/");
     private final Context mContext = InstrumentationRegistry.getContext();
     private final SliceManager mSliceManager = mContext.getSystemService(SliceManager.class);
+    private boolean isSliceDisabled = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED);
 
     @Test
     public void testProcess() {
+        if (isSliceDisabled) {
+            return;
+        }
         sFlag = false;
         mSliceManager.bindSlice(BASE_URI.buildUpon().appendPath("set_flag").build(),
                 Collections.emptySet());
@@ -71,6 +76,9 @@
 
     @Test
     public void testSliceUri() {
+        if (isSliceDisabled) {
+            return;
+        }
         Slice s = mSliceManager.bindSlice(BASE_URI,
                 Collections.emptySet());
         assertEquals(BASE_URI, s.getUri());
@@ -78,6 +86,9 @@
 
     @Test
     public void testSubSlice() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("subslice").build();
         Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
         assertEquals(uri, s.getUri());
@@ -93,6 +104,9 @@
 
     @Test
     public void testText() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("text").build();
         Slice s = mSliceManager.bindSlice(uri,
                 Collections.emptySet());
@@ -107,6 +121,9 @@
 
     @Test
     public void testIcon() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("icon").build();
         Slice s = mSliceManager.bindSlice(uri,
                 Collections.emptySet());
@@ -121,6 +138,9 @@
 
     @Test
     public void testAction() {
+        if (isSliceDisabled) {
+            return;
+        }
         sFlag = false;
         CountDownLatch latch = new CountDownLatch(1);
         BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -155,6 +175,9 @@
 
     @Test
     public void testInt() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("int").build();
         Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
         assertEquals(uri, s.getUri());
@@ -167,6 +190,9 @@
 
     @Test
     public void testTimestamp() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("timestamp").build();
         Slice s = mSliceManager.bindSlice(uri,
                 Collections.emptySet());
@@ -180,6 +206,9 @@
 
     @Test
     public void testHints() {
+        if (isSliceDisabled) {
+            return;
+        }
         // Note this tests that hints are propagated through to the client but not that any specific
         // hints have any effects.
         Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
@@ -194,6 +223,9 @@
 
     @Test
     public void testHasHints() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
         Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
 
@@ -203,6 +235,9 @@
 
     @Test
     public void testBundle() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("bundle").build();
         Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
         assertEquals(uri, s.getUri());
@@ -217,6 +252,9 @@
 
     @Test
     public void testGetDescendants() {
+        if (isSliceDisabled) {
+            return;
+        }
         Collection<Uri> allUris = mSliceManager.getSliceDescendants(BASE_URI);
         assertEquals(SliceProvider.PATHS.length, allUris.size());
         Iterator<Uri> it = allUris.iterator();
@@ -230,6 +268,9 @@
 
     @Test
     public void testGetSliceSpec() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon().appendPath("spec").build();
         Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
         assertEquals(new SliceSpec(SliceProvider.SPEC_TYPE, SliceProvider.SPEC_REV), s.getSpec());
diff --git a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
index 69d8d80..1e5c718 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
@@ -30,6 +30,7 @@
 import android.app.slice.SliceManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 
@@ -54,9 +55,14 @@
     private static final Uri BASE_URI = Uri.parse("content://android.slice.cts.local/main");
     private final Context mContext = InstrumentationRegistry.getContext();
     private final SliceManager mSliceManager = mContext.getSystemService(SliceManager.class);
+    private boolean isSliceDisabled = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED);
+
 
     @Before
     public void setup() {
+        if (isSliceDisabled) {
+            return;
+        }
         LocalSliceProvider.sProxy = mock(SliceProvider.class);
         try {
             mSliceManager.unpinSlice(BASE_URI);
@@ -66,6 +72,9 @@
 
     @After
     public void teardown() throws Exception {
+        if (isSliceDisabled) {
+            return;
+        }
         try {
             mSliceManager.unpinSlice(BASE_URI);
         } catch (Exception e) {
@@ -74,6 +83,9 @@
 
     @Test
     public void testPinSlice() throws Exception {
+        if (isSliceDisabled) {
+            return;
+        }
         mSliceManager.pinSlice(BASE_URI, Collections.emptySet());
 
         verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
@@ -81,6 +93,9 @@
 
     @Test
     public void testUnpinSlice() throws Exception {
+        if (isSliceDisabled) {
+            return;
+        }
         mSliceManager.pinSlice(BASE_URI, Collections.emptySet());
 
         verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
@@ -92,6 +107,9 @@
 
     @Test
     public void testPinList() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI;
         Uri longerUri = uri.buildUpon().appendPath("something").build();
         try {
@@ -111,6 +129,9 @@
 
     @Test
     public void testMapIntentToUri() {
+        if (isSliceDisabled) {
+            return;
+        }
         Intent intent = new Intent("android.slice.cts.action.TEST_ACTION");
         intent.setPackage("android.slice.cts");
         intent.putExtra("path", "intent");
@@ -127,6 +148,9 @@
 
     @Test
     public void testOnCreatePermissionSlice() {
+        if (isSliceDisabled) {
+            return;
+        }
         LocalSliceProvider.sAnswer = invocation -> {
             throw new SecurityException("No slices allowed");
         };
diff --git a/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java b/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
index 5081943..c36e7c9 100644
--- a/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
@@ -14,6 +14,7 @@
 
 package android.slice.cts;
 
+import android.content.pm.PackageManager;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
@@ -42,9 +43,13 @@
     private int mTestUid;
     private int mTestPid;
     private SliceManager mSliceManager;
+    private boolean isSliceDisabled = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED);
 
     @Before
     public void setup() throws NameNotFoundException {
+        if(isSliceDisabled) {
+          return;
+        }
         mSliceManager = mContext.getSystemService(SliceManager.class);
         mTestPkg = mContext.getPackageName();
         mTestUid = mContext.getPackageManager().getPackageUid(mTestPkg, 0);
@@ -53,11 +58,17 @@
 
     @After
     public void tearDown() {
+        if (isSliceDisabled) {
+            return;
+        }
         mSliceManager.revokeSlicePermission(mTestPkg, BASE_URI);
     }
 
     @Test
     public void testGrant() {
+        if (isSliceDisabled) {
+            return;
+        }
         assertEquals(PERMISSION_DENIED,
                 mSliceManager.checkSlicePermission(BASE_URI, mTestPid, mTestUid));
 
@@ -69,6 +80,9 @@
 
     @Test
     public void testGrantParent() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon()
                 .appendPath("something")
                 .build();
@@ -84,6 +98,9 @@
 
     @Test
     public void testGrantParentExpands() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon()
                 .appendPath("something")
                 .build();
@@ -110,6 +127,9 @@
 
     @Test
     public void testGrantChild() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon()
                 .appendPath("something")
                 .build();
@@ -126,6 +146,9 @@
 
     @Test
     public void testRevoke() {
+        if (isSliceDisabled) {
+            return;
+        }
         assertEquals(PERMISSION_DENIED,
                 mSliceManager.checkSlicePermission(BASE_URI, mTestPid, mTestUid));
 
@@ -142,6 +165,9 @@
 
     @Test
     public void testRevokeParent() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon()
                 .appendPath("something")
                 .build();
@@ -162,6 +188,9 @@
 
     @Test
     public void testRevokeChild() {
+        if (isSliceDisabled) {
+            return;
+        }
         Uri uri = BASE_URI.buildUpon()
                 .appendPath("something")
                 .build();
diff --git a/tests/tests/systemui/OWNERS b/tests/tests/systemui/OWNERS
index 583baa0..22ceb12 100644
--- a/tests/tests/systemui/OWNERS
+++ b/tests/tests/systemui/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 136515
+# Bug component: 181502
 evanlaird@google.com
 felkachang@google.com
 
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index 4423ed5..734d85f 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -798,8 +798,16 @@
         wcdma.getDbm();
 
         int asuLevel = wcdma.getAsuLevel();
-        assertTrue("getAsuLevel() out of range 0..96, 255, asuLevel=" + asuLevel,
-                asuLevel == 255 || (asuLevel >= 0 && asuLevel <= 96));
+        if (wcdma.getRscp() != CellInfo.UNAVAILABLE) {
+            assertTrue("getAsuLevel() out of range 0..96, 255), asuLevel=" + asuLevel,
+                    asuLevel == 255 || (asuLevel >= 0 && asuLevel <= 96));
+        } else if (wcdma.getRssi() != CellInfo.UNAVAILABLE) {
+            assertTrue("getAsuLevel() out of range 0..31, 99), asuLevel=" + asuLevel,
+                    asuLevel == 99 || (asuLevel >= 0 && asuLevel <= 31));
+        } else {
+            assertTrue("getAsuLevel() out of range 0..96, 255), asuLevel=" + asuLevel,
+                    asuLevel == 255);
+        }
 
         int level = wcdma.getLevel();
         assertTrue("getLevel() out of range [0,4], level=" + level, level >= 0 && level <= 4);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index b5f3417..dcdefb9 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -167,7 +167,8 @@
     @Test
     public void testGetActiveSubscriptionInfoForIcc() throws Exception {
         if (!isSupported()) return;
-        SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
+        SubscriptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+                (sm) -> sm.getActiveSubscriptionInfo(mSubId));
         assertNotNull(ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
                 (sm) -> sm.getActiveSubscriptionInfoForIcc(info.getIccId())));
     }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 4492561..de40198 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -168,6 +168,7 @@
     private static final int MIN_FPLMN_NUM = 3;
 
     private static final String TEST_FORWARD_NUMBER = "54321";
+    private static final String TESTING_PLMN = "12345";
 
     static {
         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
@@ -512,6 +513,11 @@
                 (tm) -> tm.getImei());
         ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
                 (tm) -> tm.getImei(mTelephonyManager.getSlotIndex()));
+        ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                (tm) -> tm.isManualNetworkSelectionAllowed());
+        ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                (tm) -> tm.getManualNetworkSelectionPlmn());
+
         mTelephonyManager.getPhoneCount();
         mTelephonyManager.getDataEnabled();
         mTelephonyManager.getNetworkSpecifier();
@@ -540,8 +546,6 @@
         }
 
         TelephonyManager.getDefaultRespondViaMessageApplication(getContext(), false);
-        ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-                (tm) -> tm.getPhoneCapability());
     }
 
     @Test
@@ -1764,6 +1768,52 @@
     }
 
     /**
+     * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
+     * The setting is not persisted selection
+     */
+    @Test
+    public void testGetManualNetworkSelectionPlmnNonPersisted() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                    (tm) -> tm.setNetworkSelectionModeManual(
+                     TESTING_PLMN/* operatorNumeric */, false /* persistSelection */));
+            String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                     (tm) -> tm.getManualNetworkSelectionPlmn());
+            assertEquals(TESTING_PLMN, plmn);
+        } finally {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    (tm) -> tm.setNetworkSelectionModeAutomatic());
+        }
+    }
+
+    /**
+     * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
+     * The setting is persisted selection
+     */
+    @Test
+    public void testGetManualNetworkSelectionPlmnPersisted() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                    (tm) -> tm.setNetworkSelectionModeManual(
+                     TESTING_PLMN/* operatorNumeric */, true /* persistSelection */));
+            String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                     (tm) -> tm.getManualNetworkSelectionPlmn());
+            assertEquals(TESTING_PLMN, plmn);
+        } finally {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    (tm) -> tm.setNetworkSelectionModeAutomatic());
+        }
+    }
+
+    /**
      * Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either
      * UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID.
      */
@@ -1819,6 +1869,27 @@
     }
 
     /**
+     * Tests that the device properly reports the contents of NetworkSelectionMode
+     */
+    @Test
+    public void testGetNetworkSelectionMode() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+                    (tm) -> tm.setNetworkSelectionModeAutomatic());
+        } catch (Exception e) {
+        }
+
+        int networkMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                (tm) -> tm.getNetworkSelectionMode());
+
+        assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode);
+    }
+
+    /**
      * Tests that the device properly sets the network selection mode to automatic.
      * Expects a security exception since the caller does not have carrier privileges.
      */
@@ -1855,6 +1926,19 @@
     }
 
     /**
+     * Tests that the device properly check whether selection mode was manual.
+     */
+    @Test
+    public void testIsManualNetworkSelectionAllowed() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
+            return;
+        }
+        assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                (tm) -> tm.isManualNetworkSelectionAllowed()));
+    }
+
+    /**
      * Construct a CallAttributes object and test getters.
      */
     @Test
diff --git a/tests/tests/text/Android.bp b/tests/tests/text/Android.bp
index f3d34bf..2909777 100644
--- a/tests/tests/text/Android.bp
+++ b/tests/tests/text/Android.bp
@@ -16,6 +16,7 @@
     name: "CtsTextTestCases",
     defaults: ["cts_defaults"],
     sdk_version: "test_current",
+    stl: "c++_shared",
 
     srcs: [
         "src/**/*.java",
@@ -38,7 +39,7 @@
 
     jni_libs: [
         "libctstext_jni",
-        "libc++",
+        "libnativehelper_compat_libc++",
     ],
     // Include both the 32 and 64 bit versions of libctstext_jni, where
     // applicable.
diff --git a/tests/tests/text/jni/Android.bp b/tests/tests/text/jni/Android.bp
index 8055f40..7821605 100644
--- a/tests/tests/text/jni/Android.bp
+++ b/tests/tests/text/jni/Android.bp
@@ -14,9 +14,10 @@
 
 cc_library {
     name: "libctstext_jni",
+    sdk_version: "current",
     srcs: [
         "CtsTextJniOnLoad.cpp",
         "android_text_format_cts_NativeTimeFunctions.cpp",
     ],
-    static_libs: ["libnativehelper"],
+    shared_libs: ["libnativehelper_compat_libc++"],
 }
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 72d3fd1..fa5043a 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -352,7 +352,7 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="android.view.cts.KeyEventInterceptTestActivity">
+        <activity android:name="android.view.cts.InputEventInterceptTestActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
diff --git a/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java b/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java
index c7f6591..fb1cb13 100644
--- a/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java
+++ b/tests/tests/view/src/android/view/cts/InputDeviceEnabledTest.java
@@ -23,9 +23,9 @@
 import android.hardware.input.InputManager;
 import android.view.InputDevice;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,12 +38,11 @@
 @RunWith(AndroidJUnit4.class)
 public class InputDeviceEnabledTest {
     private InputManager mInputManager;
-    private Instrumentation mInstrumentation;
 
     @Before
     public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mInputManager = mInstrumentation.getTargetContext().getSystemService(InputManager.class);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mInputManager = instrumentation.getTargetContext().getSystemService(InputManager.class);
         assertNotNull(mInputManager);
     }
 
diff --git a/tests/tests/view/src/android/view/cts/InputEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/InputEventInterceptTestActivity.java
new file mode 100644
index 0000000..e3bfe77
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/InputEventInterceptTestActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class InputEventInterceptTestActivity extends Activity {
+    final BlockingQueue<KeyEvent> mKeyEvents = new LinkedBlockingQueue<>();
+    final BlockingQueue<MotionEvent> mMotionEvents = new LinkedBlockingQueue<>();
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        mKeyEvents.add(event);
+        return true;
+    }
+
+    @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        mMotionEvents.add(event);
+        return true;
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        mMotionEvents.add(event);
+        return true;
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index 245dee8..abae9e7 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -24,10 +24,10 @@
 import android.os.SystemClock;
 import android.view.KeyEvent;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.PollingCheck;
 
@@ -56,12 +56,12 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class KeyEventInterceptTest {
-    private KeyEventInterceptTestActivity mActivity;
+    private InputEventInterceptTestActivity mActivity;
     private Instrumentation mInstrumentation;
 
     @Rule
-    public ActivityTestRule<KeyEventInterceptTestActivity> mActivityRule =
-            new ActivityTestRule<>(KeyEventInterceptTestActivity.class);
+    public ActivityTestRule<InputEventInterceptTestActivity> mActivityRule =
+            new ActivityTestRule<>(InputEventInterceptTestActivity.class);
 
     @Before
     public void setup() {
@@ -96,10 +96,10 @@
     }
 
     private void testKeyCodeHomeShortcut(int metaState) {
-        long downTime = SystemClock.uptimeMillis();
+        final long downTime = SystemClock.uptimeMillis();
         injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
                 KeyEvent.KEYCODE_ENTER, 0, metaState));
-        injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+        injectEvent(new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP,
                 KeyEvent.KEYCODE_ENTER, 0, metaState));
 
         assertKeyNotReceived();
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
deleted file mode 100644
index 2422a61..0000000
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.cts;
-
-import android.app.Activity;
-import android.view.KeyEvent;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-public class KeyEventInterceptTestActivity extends Activity {
-    final Queue<KeyEvent> mKeyEvents = new LinkedList<>();
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        mKeyEvents.add(event);
-        return true;
-    }
-}
diff --git a/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java b/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
new file mode 100644
index 0000000..fdda4e3
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InputDevice.SOURCE_JOYSTICK;
+import static android.view.KeyEvent.FLAG_CANCELED;
+import static android.view.KeyEvent.KEYCODE_A;
+import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
+import static android.view.MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Point;
+import android.hardware.input.InputManager;
+import android.os.SystemClock;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.VerifiedInputEvent;
+import android.view.VerifiedKeyEvent;
+import android.view.VerifiedMotionEvent;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test {@link android.hardware.input.InputManager#verifyInputEvent(InputEvent)} functionality.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VerifyInputEventTest {
+    private static final int NANOS_PER_MILLISECOND = 1000000;
+    private static final float STRICT_TOLERANCE = 0;
+    private static final int INJECTED_EVENT_DEVICE_ID = -1;
+
+    private InputManager mInputManager;
+    private UiAutomation mAutomation;
+    private InputEventInterceptTestActivity mActivity;
+
+    @Rule
+    public ActivityTestRule<InputEventInterceptTestActivity> mActivityRule =
+            new ActivityTestRule<>(InputEventInterceptTestActivity.class);
+
+    @Before
+    public void setup() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mInputManager = instrumentation.getTargetContext().getSystemService(InputManager.class);
+        assertNotNull(mInputManager);
+        mAutomation = instrumentation.getUiAutomation();
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(mActivity::hasWindowFocus);
+    }
+
+    @Test
+    public void testKeyEvent() {
+        final long downTime = SystemClock.uptimeMillis();
+        final int keyCode = KEYCODE_A;
+        KeyEvent downEvent = new KeyEvent(downTime, downTime,
+                KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */);
+        mAutomation.injectInputEvent(downEvent, true);
+        KeyEvent received = waitForKey();
+        VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+        assertNotNull(verified);
+        compareKeys(downEvent, verified);
+
+        // Send UP event for consistency
+        KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+                KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
+        mAutomation.injectInputEvent(upEvent, true);
+        waitForKey();
+    }
+
+    /**
+     * Try to verify an event that did not come from system, and therefore does not have an hmac
+     * set properly.
+     * Verification should fail.
+     */
+    @Test
+    public void testKeyEventWithoutHmac() {
+        final long downTime = SystemClock.uptimeMillis();
+        final int keyCode = KEYCODE_A;
+        KeyEvent downEvent = new KeyEvent(downTime, downTime,
+                KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */);
+        mAutomation.injectInputEvent(downEvent, true);
+        waitForKey(); // we will not be using the received event
+        VerifiedInputEvent verified = mInputManager.verifyInputEvent(downEvent);
+        assertNull(verified);
+
+        // Send UP event for consistency
+        KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+                KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
+        mAutomation.injectInputEvent(upEvent, true);
+        waitForKey();
+    }
+
+    /**
+     * Try to verify an event that came from system, but has been tempered with.
+     * Verification should fail.
+     */
+    @Test
+    public void testTamperedKeyEvent() {
+        final long downTime = SystemClock.uptimeMillis();
+        final int keyCode = KEYCODE_A;
+        KeyEvent downEvent = new KeyEvent(downTime, downTime,
+                KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */);
+        mAutomation.injectInputEvent(downEvent, true);
+        KeyEvent received = waitForKey();
+        received.setSource(SOURCE_JOYSTICK); // use the received event, but modify its source
+        VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+        assertNull(verified);
+
+        // Send UP event for consistency
+        KeyEvent upEvent = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+                KeyEvent.ACTION_UP, keyCode, 0 /* repeatCount */);
+        mAutomation.injectInputEvent(upEvent, true);
+        waitForKey();
+    }
+
+    @Test
+    public void testMotionEvent() {
+        final View view = mActivity.getWindow().getDecorView();
+        final Point point = getViewCenterOnScreen(view);
+        final long downTime = SystemClock.uptimeMillis();
+        MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+                point.x, point.y, 0 /*metaState*/);
+        mAutomation.injectInputEvent(downEvent, true);
+        MotionEvent received = waitForMotion();
+        VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+        assertNotNull(verified);
+
+        compareMotions(downEvent, verified);
+
+        // Send UP event for consistency
+        MotionEvent upEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP,
+                point.x, point.y, 0 /*metaState*/);
+        mAutomation.injectInputEvent(upEvent, true);
+        waitForMotion();
+    }
+
+    /**
+     * Try to verify an event that did not come from system, and therefore does not have an hmac
+     * set properly.
+     * Verification should fail.
+     */
+    @Test
+    public void testMotionEventWithoutHmac() {
+        final View view = mActivity.getWindow().getDecorView();
+        final Point point = getViewCenterOnScreen(view);
+        final long downTime = SystemClock.uptimeMillis();
+        MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+                point.x, point.y, 0 /*metaState*/);
+        mAutomation.injectInputEvent(downEvent, true);
+        waitForMotion(); // we will not be using the received event
+        VerifiedInputEvent verified = mInputManager.verifyInputEvent(downEvent);
+        assertNull(verified);
+
+        // Send UP event for consistency
+        MotionEvent upEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP,
+                point.x, point.y, 0 /*metaState*/);
+        mAutomation.injectInputEvent(upEvent, true);
+        waitForMotion();
+    }
+
+    /**
+     * Try to verify an event that came from system, but has been tempered with.
+     * Verification should fail.
+     */
+    @Test
+    public void testTamperedMotionEvent() {
+        final View view = mActivity.getWindow().getDecorView();
+        final Point point = getViewCenterOnScreen(view);
+        final long downTime = SystemClock.uptimeMillis();
+        MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+                point.x, point.y, 0 /*metaState*/);
+        mAutomation.injectInputEvent(downEvent, true);
+        MotionEvent received = waitForMotion();
+        // use the received event, by modify its action
+        received.setAction(MotionEvent.ACTION_CANCEL);
+        VerifiedInputEvent verified = mInputManager.verifyInputEvent(received);
+        assertNull(verified);
+
+        // Send UP event for consistency
+        MotionEvent upEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP,
+                point.x, point.y, 0 /*metaState*/);
+        mAutomation.injectInputEvent(upEvent, true);
+        waitForMotion();
+    }
+
+    private static Point getViewCenterOnScreen(View view) {
+        final int[] location = new int[2];
+        view.getLocationOnScreen(location);
+        final int width = view.getWidth();
+        final int height = view.getHeight();
+
+        return new Point(location[0] + width / 2, location[1] + height / 2);
+    }
+
+    private KeyEvent waitForKey() {
+        return mActivity.mKeyEvents.poll();
+    }
+
+    private MotionEvent waitForMotion() {
+        return mActivity.mMotionEvents.poll();
+    }
+
+    private static void compareKeys(KeyEvent keyEvent, VerifiedInputEvent verified) {
+        assertEquals(INJECTED_EVENT_DEVICE_ID, verified.getDeviceId());
+        assertEquals(keyEvent.getEventTime() * NANOS_PER_MILLISECOND,
+                verified.getEventTimeNanos());
+        assertEquals(keyEvent.getSource(), verified.getSource());
+        assertEquals(DEFAULT_DISPLAY, verified.getDisplayId());
+
+        assertTrue(verified instanceof VerifiedKeyEvent);
+        VerifiedKeyEvent verifiedKey = (VerifiedKeyEvent) verified;
+
+        assertEquals(keyEvent.getAction(), verifiedKey.getAction());
+        assertEquals(keyEvent.getDownTime() * NANOS_PER_MILLISECOND,
+                verifiedKey.getDownTimeNanos());
+        compareKeyFlags(keyEvent.getFlags(), verifiedKey);
+        assertEquals(keyEvent.getKeyCode(), verifiedKey.getKeyCode());
+        assertEquals(keyEvent.getScanCode(), verifiedKey.getScanCode());
+        assertEquals(keyEvent.getMetaState(), verifiedKey.getMetaState());
+        assertEquals(keyEvent.getRepeatCount(), verifiedKey.getRepeatCount());
+    }
+
+    private static void compareMotions(MotionEvent motionEvent, VerifiedInputEvent verified) {
+        assertEquals(INJECTED_EVENT_DEVICE_ID, verified.getDeviceId());
+        assertEquals(motionEvent.getEventTime() * NANOS_PER_MILLISECOND,
+                verified.getEventTimeNanos());
+        assertEquals(motionEvent.getSource(), verified.getSource());
+        assertEquals(DEFAULT_DISPLAY, verified.getDisplayId());
+
+        assertTrue(verified instanceof VerifiedMotionEvent);
+        VerifiedMotionEvent verifiedMotion = (VerifiedMotionEvent) verified;
+
+        assertEquals(motionEvent.getRawX(), verifiedMotion.getRawX(), STRICT_TOLERANCE);
+        assertEquals(motionEvent.getRawY(), verifiedMotion.getRawY(), STRICT_TOLERANCE);
+        assertEquals(motionEvent.getActionMasked(), verifiedMotion.getActionMasked());
+        assertEquals(motionEvent.getDownTime() * NANOS_PER_MILLISECOND,
+                verifiedMotion.getDownTimeNanos());
+        compareMotionFlags(motionEvent.getFlags(), verifiedMotion);
+        assertEquals(motionEvent.getMetaState(), verifiedMotion.getMetaState());
+        assertEquals(motionEvent.getButtonState(), verifiedMotion.getButtonState());
+    }
+
+    private static void compareKeyFlags(int expectedFlags, VerifiedKeyEvent verified) {
+        // Separately check the value of verifiable flags
+        assertFlag(expectedFlags, FLAG_CANCELED, verified);
+        // All other flags should be null, because they are not verifiable
+        for (int i = 0; i < Integer.SIZE; i++) {
+            int flag = 1 << i;
+            if (flag == FLAG_CANCELED) {
+                continue;
+            }
+            assertNull(verified.getFlag(flag));
+        }
+    }
+
+    private static void compareMotionFlags(int expectedFlags, VerifiedMotionEvent verified) {
+        // Separately check the value of verifiable flags
+        assertFlag(expectedFlags, FLAG_WINDOW_IS_OBSCURED, verified);
+        assertFlag(expectedFlags, FLAG_WINDOW_IS_PARTIALLY_OBSCURED, verified);
+        // All other flags should be null, because they are not verifiable
+        for (int i = 0; i < Integer.SIZE; i++) {
+            int flag = 1 << i;
+            if (flag == FLAG_WINDOW_IS_OBSCURED
+                    || flag == FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
+                continue;
+            }
+            assertNull(verified.getFlag(flag));
+        }
+    }
+
+    private static void assertFlag(int expectedFlags, int flag, VerifiedInputEvent verified) {
+        final Boolean actual;
+        if (verified instanceof VerifiedKeyEvent) {
+            actual = ((VerifiedKeyEvent) verified).getFlag(flag);
+        } else if (verified instanceof VerifiedMotionEvent) {
+            actual = ((VerifiedMotionEvent) verified).getFlag(flag);
+        } else {
+            fail("Unknown type of VerifiedInputEvent");
+            actual = null;
+        }
+        assertNotNull(actual);
+        boolean flagValue = (expectedFlags & flag) != 0;
+        assertEquals(flagValue, actual);
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 5980663..7419858 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -245,23 +245,30 @@
             return;
         }
 
-        assertTrue(mSettings.getAllowFileAccess());
+        // TODO(b/148840827): Uncomment default value assertion when a new version of WebView
+        // where the change happened is dropped in master.
+        // assertFalse("File access should be off by default", mSettings.getAllowFileAccess());
 
+        mSettings.setAllowFileAccess(true);
+        assertTrue("Explicitly setting file access to true should work",
+                mSettings.getAllowFileAccess());
         String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
-        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+        assertEquals("Loading files on the file system should work with file access enabled",
+                TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
 
         fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.BR_TAG_URL);
         mSettings.setAllowFileAccess(false);
-        assertFalse(mSettings.getAllowFileAccess());
+        assertFalse("Explicitly setting file access to false should work",
+                mSettings.getAllowFileAccess());
         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
-        // android_asset URLs should still be loaded when even with file access
-        // disabled.
-        assertEquals(TestHtmlConstants.BR_TAG_TITLE, mOnUiThread.getTitle());
+        assertEquals(
+                "android_asset URLs should still be loaded when even with file access disabled",
+                TestHtmlConstants.BR_TAG_TITLE, mOnUiThread.getTitle());
 
-        // Files on the file system should not be loaded.
         mOnUiThread.loadUrlAndWaitForCompletion(TestHtmlConstants.LOCAL_FILESYSTEM_URL);
-        assertEquals(TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
+        assertEquals("Files on the file system should not be loaded with file access disabled",
+                TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
     }
 
     public void testAccessCacheMode_defaultValue() throws Throwable {
@@ -1060,6 +1067,7 @@
         writeFile("target.html", target);
 
         mSettings.setJavaScriptEnabled(true);
+        mSettings.setAllowFileAccess(true);
         // disable universal access from files
         mSettings.setAllowUniversalAccessFromFileURLs(false);
         mSettings.setAllowFileAccessFromFileURLs(enableXHR);
diff --git a/tests/tests/widget29/AndroidTest.xml b/tests/tests/widget29/AndroidTest.xml
index 39d3911..f867df3 100644
--- a/tests/tests/widget29/AndroidTest.xml
+++ b/tests/tests/widget29/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWidgetTestCases29.apk" />