Merge "Switch to system user before CtsBackupTestCases and CtsBackupHostTestCases module" into qt-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2513993..aca54cd 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -16,6 +16,8 @@
                       tests/tests/content/
                       tests/tests/graphics/
                       tests/tests/hardware/
+                      tests/tests/permission2/
+                      tests/tests/permission/
                       tests/tests/preference/
                       tests/tests/print/
                       tests/tests/text/
diff --git a/apps/CameraITS/tests/dng_noise_model/dng_noise_model.py b/apps/CameraITS/tests/dng_noise_model/dng_noise_model.py
index e8c6d19..16f5fc0 100644
--- a/apps/CameraITS/tests/dng_noise_model/dng_noise_model.py
+++ b/apps/CameraITS/tests/dng_noise_model/dng_noise_model.py
@@ -12,25 +12,30 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.device
-import its.caps
-import its.objects
-import its.image
-import os.path
-from matplotlib import pylab
-import matplotlib
-import matplotlib.pyplot as plt
 import math
+import os.path
 import textwrap
-import time
+
+import its.caps
+import its.device
+import its.image
+import its.objects
+
+import matplotlib
+from matplotlib import pylab
+import matplotlib.pyplot as plt
 import numpy as np
-import scipy.stats
 import scipy.signal
+import scipy.stats
+
+BAYER_LIST = ['R', 'GR', 'GB', 'B']
+NAME = os.path.basename(__file__).split('.')[0]
+RTOL_EXP_GAIN = 0.97
 
 
-# Convert a 2D array a to a 4D array with dimensions [tile_size,
-# tile_size, row, col] where row, col are tile indices.
 def tile(a, tile_size):
+    """Convert a 2D array to 4D w/ dims [tile_size, tile_size, row, col] where row, col are tile indices.
+    """
     tile_rows, tile_cols = a.shape[0]/tile_size, a.shape[1]/tile_size
     a = a.reshape([tile_rows, tile_size, tile_cols, tile_size])
     a = a.transpose([1, 3, 0, 2])
@@ -38,10 +43,8 @@
 
 
 def main():
-    """Capture a set of raw images with increasing gains and measure the noise.
+    """Capture a set of raw images with increasing analog gains and measure the noise.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-    BAYER_LIST = ['R', 'GR', 'GB', 'B']
 
     # How many sensitivities per stop to sample.
     steps_per_stop = 2
@@ -88,13 +91,14 @@
         # Get basic properties we need.
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
         sens_max_analog = props['android.sensor.maxAnalogSensitivity']
+        sens_max_meas = sens_max_analog
         white_level = props['android.sensor.info.whiteLevel']
 
         print "Sensitivity range: [%f, %f]" % (sens_min, sens_max)
         print "Max analog sensitivity: %f" % (sens_max_analog)
 
         # Do AE to get a rough idea of where we are.
-        s_ae, e_ae, _, _, _  = \
+        s_ae, e_ae, _, _, _ = \
             cam.do_3a(get_results=True, do_awb=False, do_af=False)
         # Underexpose to get more data for low signal levels.
         auto_e = s_ae*e_ae/bracket_factor
@@ -106,7 +110,7 @@
         # an error.
         min_exposure_ns, max_exposure_ns = \
             props['android.sensor.info.exposureTimeRange']
-        if auto_e < min_exposure_ns*sens_max:
+        if auto_e < min_exposure_ns*sens_max_meas:
             raise its.error.Error("Scene is too bright to properly expose \
                                   at the highest sensitivity")
         if auto_e*bracket_factor > max_exposure_ns*sens_min:
@@ -119,23 +123,40 @@
         samples = [[], [], [], []]
         plots = []
         measured_models = [[], [], [], []]
-        while s <= sens_max + 1:
-            print "ISO %d" % round(s)
-            fig = plt.figure()
-            plt_s = fig.gca()
-            plt_s.set_title("ISO %d" % round(s))
-            plt_s.set_xlabel("Mean signal level")
-            plt_s.set_ylabel("Variance")
+        color_plane_plots = {}
+        while int(round(s)) <= sens_max_meas:
+            s_int = int(round(s))
+            print 'ISO %d' % s_int
+            fig, [[plt_r, plt_gr], [plt_gb, plt_b]] = plt.subplots(2, 2, figsize=(11, 11))
+            fig.gca()
+            color_plane_plots[s_int] = [plt_r, plt_gr, plt_gb, plt_b]
+            fig.suptitle('ISO %d' % s_int, x=0.54, y=0.99)
+            for i, plot in enumerate(color_plane_plots[s_int]):
+                plot.set_title('%s' % BAYER_LIST[i])
+                plot.set_xlabel('Mean signal level')
+                plot.set_ylabel('Variance')
 
             samples_s = [[], [], [], []]
             for b in range(0, bracket_stops + 1):
                 # Get the exposure for this sensitivity and exposure time.
                 e = int(math.pow(2, b)*auto_e/float(s))
-                req = its.objects.manual_capture_request(round(s), e, f_dist)
+                print 'exp %.3fms' % round(e*1.0E-6, 3)
+                req = its.objects.manual_capture_request(s_int, e, f_dist)
                 cap = cam.do_capture(req, cam.CAP_RAW)
                 planes = its.image.convert_capture_to_planes(cap, props)
+                e_read = cap['metadata']['android.sensor.exposureTime']
+                s_read = cap['metadata']['android.sensor.sensitivity']
+                e_err = 'e_write: %d, e_read: %d, RTOL: %.2f' % (
+                        e, e_read, RTOL_EXP_GAIN)
+                s_err = 's_write: %d, s_read: %d, RTOL: %.2f' % (
+                        s, s_read, RTOL_EXP_GAIN)
+                assert (1.0 >= e_read/float(e) >= RTOL_EXP_GAIN), e_err
+                assert (1.0 >= s_read/float(s_int) >= RTOL_EXP_GAIN), s_err
+                print 'ISO_write: %d, ISO_read: %d' %  (s_int, s_read)
 
                 for (pidx, p) in enumerate(planes):
+                    plot = color_plane_plots[s_int][pidx]
+
                     p = p.squeeze()
 
                     # Crop the plane to be a multiple of the tile size.
@@ -146,7 +167,7 @@
                     # to [0, 1], but without subtracting the black
                     # level.
                     black_level = its.image.get_black_level(
-                        pidx, props, cap["metadata"])
+                            pidx, props, cap["metadata"])
                     p *= white_level
                     p = (p - black_level)/(white_level - black_level)
 
@@ -165,39 +186,46 @@
                         if mean + 2*math.sqrt(var) < max_signal_level:
                             samples_e.append([mean, var])
 
-                    if len(samples_e) > 0:
+                    if samples_e:
                         means_e, vars_e = zip(*samples_e)
-                        plt_s.plot(means_e, vars_e, colors[b%len(colors)] + ',')
-
+                        color_plane_plots[s_int][pidx].plot(
+                                means_e, vars_e, colors[b%len(colors)] + '.',
+                                alpha=0.5)
                         samples_s[pidx].extend(samples_e)
 
             for (pidx, p) in enumerate(samples_s):
-                [S, O, R, p, stderr] = scipy.stats.linregress(samples_s[pidx])
-                measured_models[pidx].append([round(s), S, O])
-                print "Sensitivity %d: %e*y + %e (R=%f)" % (round(s), S, O, R)
+                [S, O, R, _, _] = scipy.stats.linregress(samples_s[pidx])
+                measured_models[pidx].append([s_int, S, O])
+                print "Sensitivity %d: %e*y + %e (R=%f)" % (s_int, S, O, R)
 
                 # Add the samples for this sensitivity to the global samples list.
-                samples[pidx].extend([(round(s), mean, var) for (mean, var) in samples_s[pidx]])
+                samples[pidx].extend([(s_int, mean, var) for (mean, var) in samples_s[pidx]])
 
-                # Add the linear fit to the plot for this sensitivity.
-                plt_s.plot([0, max_signal_level], [O, O + S*max_signal_level], 'rgkb'[pidx]+'--',
-                           label="Linear fit")
+                # Add the linear fit to subplot for this sensitivity.
+                # pylab.subplot(2, 2, pidx+1)
+                #pylab.plot([0, max_signal_level], [O, O + S*max_signal_level], 'rgkb'[pidx]+'--',
+                           #label="Linear fit")
+                color_plane_plots[s_int][pidx].plot([0, max_signal_level],
+                        [O, O + S*max_signal_level], 'rgkb'[pidx]+'--',
+                        label="Linear fit")
 
-            xmax = max([max([x for (x, _) in p]) for p in samples_s])*1.25
-            ymax = max([max([y for (_, y) in p]) for p in samples_s])*1.25
-            plt_s.set_xlim(xmin=0, xmax=xmax)
-            plt_s.set_ylim(ymin=0, ymax=ymax)
+                xmax = max([max([x for (x, _) in p]) for p in samples_s])*1.25
+                ymax = max([max([y for (_, y) in p]) for p in samples_s])*1.25
+                color_plane_plots[s_int][pidx].set_xlim(xmin=0, xmax=xmax)
+                color_plane_plots[s_int][pidx].set_ylim(ymin=0, ymax=ymax)
+                color_plane_plots[s_int][pidx].legend()
+                pylab.tight_layout()
 
-            fig.savefig("%s_samples_iso%04d.png" % (NAME, round(s)))
-            plots.append([round(s), fig])
+            fig.savefig('%s_samples_iso%04d.png' % (NAME, s_int))
+            plots.append([s_int, fig])
 
             # Move to the next sensitivity.
             s *= math.pow(2, 1.0/steps_per_stop)
 
-        (fig, (plt_S, plt_O)) = plt.subplots(2, 1)
+        # do model plots
+        (fig, (plt_S, plt_O)) = plt.subplots(2, 1, figsize=(11, 8.5))
         plt_S.set_title("Noise model")
         plt_S.set_ylabel("S")
-        plt_S.legend(loc=2)
         plt_O.set_xlabel("ISO")
         plt_O.set_ylabel("O")
 
@@ -256,26 +284,25 @@
             plt_O.loglog(sens, O_measured, 'rgkb'[pidx]+'+', basex=10, basey=10,
                          label="Measured")
             plt_O.loglog(sens, O_model, 'rgkb'[pidx]+'x', basex=10, basey=10, label="Model")
+        plt_S.legend()
+        plt_O.legend()
 
         fig.savefig("%s.png" % (NAME))
 
-        for [s, fig] in plots:
-            plt_s = fig.gca()
-
+        # add models to subplots and re-save
+        for [s, fig] in plots:  # re-step through figs...
             dg = max(s/sens_max_analog, 1)
+            fig.gca()
             for (pidx, p) in enumerate(measured_models):
                 S = A[pidx]*s + B[pidx]
                 O = C[pidx]*s*s + D[pidx]*dg*dg
-                plt_s.plot([0, max_signal_level], [O, O + S*max_signal_level], 'rgkb'[pidx]+'-',
-                           label="Model")
+                color_plane_plots[s][pidx].plot([0, max_signal_level],
+                        [O, O + S*max_signal_level], 'rgkb'[pidx]+'-',
+                        label="Model", alpha=0.5)
+                color_plane_plots[s][pidx].legend(loc='upper left')
+            fig.savefig('%s_samples_iso%04d.png' % (NAME, s))
 
-            plt_s.legend(loc=2)
-            plt.figure(fig.number)
-
-            # Re-save the plot with the global model.
-            fig.savefig("%s_samples_iso%04d.png" % (NAME, round(s)))
-
-          # Generate the noise model implementation.
+        # Generate the noise model implementation.
         A_array = ",".join([str(i) for i in A])
         B_array = ",".join([str(i) for i in B])
         C_array = ",".join([str(i) for i in C])
diff --git a/apps/CameraITS/tests/scene0/test_tonemap_curve.py b/apps/CameraITS/tests/scene0/test_tonemap_curve.py
index a1db43a..96a495a 100644
--- a/apps/CameraITS/tests/scene0/test_tonemap_curve.py
+++ b/apps/CameraITS/tests/scene0/test_tonemap_curve.py
@@ -131,6 +131,7 @@
         # YUV image
         req_yuv = its.objects.manual_capture_request(int(sens_min), exp)
         req_yuv['android.sensor.testPatternMode'] = PATTERN
+        req_yuv['android.distortionCorrection.mode'] = 0
         req_yuv['android.tonemap.mode'] = 0
         req_yuv['android.tonemap.curve'] = {
                 'red': tmap, 'green': tmap, 'blue': tmap}
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index a589c35..b5af652 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -82,6 +82,7 @@
             android:icon="@drawable/icon"
             android:debuggable="true"
             android:largeHeap="true"
+            android:requestLegacyExternalStorage="true"
             android:theme="@android:style/Theme.DeviceDefault">
 
         <provider android:name="android.location.cts.MmsPduProvider"
@@ -1373,141 +1374,6 @@
         </activity>
         -->
 
-        <activity android:name=".location.GpsTestActivity"
-                android:label="@string/location_gps_test"
-                android:configChanges="keyboardHidden|orientation|screenSize">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST" />
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware" />
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.GnssMeasurementsConstellationTestsActivity"
-            android:label="@string/location_gnss_constellation_type_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.GnssMeasurementRegistrationTestsActivity"
-            android:label="@string/location_gnss_reg_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.GnssMeasurementValuesTestsActivity"
-            android:label="@string/location_gnss_value_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.GnssPseudorangeVerificationTestsActivity"
-            android:label="@string/location_pseudorange_value_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.GnssTtffTestsActivity"
-            android:label="@string/location_gnss_ttff_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.EmergencyCallWifiTestsActivity"
-            android:label="@string/location_emergency_call_wifi_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features"
-                       android:value="android.hardware.location.gps:android.hardware.wifi:android.hardware.telephony"/>
-        </activity>
-
-        <activity android:name=".location.EmergencyCallMessageTestsActivity"
-            android:label="@string/location_emergency_call_message_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features"
-                       android:value="android.hardware.location.gps:android.hardware.wifi:android.hardware.telephony"/>
-        </activity>
-
-        <activity android:name=".location.EmergencyCallGNSSTestsActivity"
-            android:label="@string/location_emergency_call_gps_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features"
-                       android:value="android.hardware.location.gps:android.hardware.wifi:android.hardware.telephony"/>
-        </activity>
-
-        <activity android:name=".location.GnssMeasurementWhenNoLocationTestsActivity"
-            android:label="@string/location_gnss_measure_no_location_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <activity android:name=".location.GnssNavigationMessageTestsActivity"
-            android:label="@string/location_gnss_nav_msg_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity>
-
-        <!--  activity android:name=".location.GnssStatusTestsActivity"
-            android:label="@string/location_gnss_status_test"
-            android:screenOrientation="locked">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
-            <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
-        </activity -->
-
         <activity android:name=".location.LocationListenerActivity"
                 android:label="@string/location_listener_activity"
                 android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/res/layout/cf_main.xml b/apps/CtsVerifier/res/layout/cf_main.xml
index 1ff35cc..8a4fb7f 100644
--- a/apps/CtsVerifier/res/layout/cf_main.xml
+++ b/apps/CtsVerifier/res/layout/cf_main.xml
@@ -87,6 +87,11 @@
                 android:id="@+id/format_selection"
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"/>
+            <Button
+                android:id="@+id/next_button"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/cf_next_button" />
 
         </LinearLayout>
 
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 3ecfab7..4bf8b9f 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1318,10 +1318,12 @@
     the right view must be horizontally mirrored relative to the left
     view.\n - Note that the frame rate of the right view may be much
     lower than on the left; this is not an indication of a failed
-    test.
+    test.\n - The next button triggers the next resolution and format
+    combination of the current camera to show; this is optional.
     </string>
     <string name="cf_preview_label">Normal preview</string>
     <string name="cf_format_label">Processed callback data</string>
+    <string name="cf_next_button">Next</string>
 
     <!-- Strings for Camera Video -->
     <string name="record_button_text">Test</string>
@@ -2902,9 +2904,10 @@
     <string name="provisioning_byod_primary_location_when_work_disabled_instruction">
         This test verifies that location updates are still received by primary profile when location updates are disabled for managed profile apps through work profile location switch.\n
         1. Press the go button to go to the location settings page, set the work location switch disabled while the main location switch is still enabled.\n
-        2. Move your position a little bit, verify that location updates toast comes up.\n
+        2. Press home to go to the launcher.\n
+        3. Move your position a little bit, verify that location updates toast comes up.\n
         Please wait until the location updates or timeout toast message shows up before going back to the cts-verifier tests.\n
-        3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+        4. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
     </string>
 
     <string name="provisioning_byod_personal_ringtones">Personal ringtones</string>
@@ -4736,8 +4739,7 @@
     </string-array>
 
     <!-- Strings for setting and restoring default dialer for voicemail tests -->
-    <string name="voicemail_restore_default_dialer_description">Restore the default dialer setting</string>
-    <string name="voicemail_restore_default_dialer_no_default_description">Restore the default dialer by going to settings-> apps -> cogwheel -> Phone app</string>
+    <string name="voicemail_restore_default_dialer_description">Restore the default dialer setting by going to Default apps -> Phone app</string>
     <string name="voicemail_restore_default_dialer_button">Restore the default dialer"</string>
     <string name="voicemail_default_dialer_already_set">Default dialer already set</string>
     <string name="voicemail_default_dialer_already_restored">Default dialer already restored</string>
@@ -4990,6 +4992,10 @@
         BubbleMetadata is configured on the notification and user actions. Bubbles are special
         notifications that appear as a floating button on the screen, in addition to the notification
         in the notification shade.</string>
+    <string name="bubbles_notification_test_enable_bubbles_title">Enable Bubbles for CTS Verifier</string>
+    <string name="bubbles_notification_test_enable_bubbles_verify">Click the button below and enable
+        bubbles for the verifier app on the resulting screen, if they aren\'t enabled already.</string>
+    <string name="bubbles_notification_enable_bubbles_button">Enable bubbles for CTS Verifier</string>
     <string name="bubbles_notification_test_title_1">Step 1: send a bubble with notification</string>
     <string name="bubbles_notification_test_verify_1">Click the button below and verify that there is a
         bubble on the screen and a notification in the notification shade.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
index 72c54c2..9b54ed3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
@@ -94,9 +94,6 @@
             }
         });
 
-        mPolicyItems.add(new PasswordQualityPolicy(this));
-        mPolicyItems.add(new PasswordMinimumLengthPolicy(this));
-
         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)) {
             mPolicyItems.add(new MaximumFailedPasswordsForWipePolicy(this));
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java
index 0686944..32cc805 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java
@@ -136,7 +136,7 @@
         // Bluetooth
         mBTInputDeviceLbl.setText(mBTTestModule.getInputName());
         mBTOutputDeviceLbl.setText(mBTTestModule.getOutputName());
-        mBTTestBtn.setEnabled(mBTTestModule.isTestReady());
+        mBTTestBtn.setEnabled(mBTTestModule.isTestReady() && mUSBTestModule.isTestReady());
     }
 
     private boolean calcTestPassed() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
index 9c5b31d..3421f65 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
@@ -40,6 +40,7 @@
 import android.view.TextureView;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.Spinner;
@@ -51,6 +52,7 @@
 import java.util.HashSet;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Optional;
 import java.util.TreeSet;
 
 /**
@@ -99,14 +101,39 @@
     private boolean mProcessInProgress = false;
     private boolean mProcessingFirstFrame = false;
 
-    private TreeSet<String> mTestedCombinations = new TreeSet<String>();
-    private TreeSet<String> mUntestedCombinations = new TreeSet<String>();
+    private final TreeSet<CameraCombination> mTestedCombinations = new TreeSet<>(COMPARATOR);
+    private final TreeSet<CameraCombination> mUntestedCombinations = new TreeSet<>(COMPARATOR);
 
     private int mAllCombinationsSize = 0;
 
     // Menu to show the test progress
     private static final int MENU_ID_PROGRESS = Menu.FIRST + 1;
 
+    private class CameraCombination {
+        private final int cameraIndex;
+        private final int resolutionIndex;
+        private final int formatIndex;
+
+        private CameraCombination(int cameraIndex, int resolutionIndex, int formatIndex) {
+            this.cameraIndex = cameraIndex;
+            this.resolutionIndex = resolutionIndex;
+            this.formatIndex = formatIndex;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Camera %d, %dx%d, %s", cameraIndex,
+                mPreviewSizes.get(resolutionIndex).width,
+                mPreviewSizes.get(resolutionIndex).height,
+                mPreviewFormatNames.get(mPreviewFormats.get(formatIndex)));
+        }
+    }
+
+    private static final Comparator<CameraCombination> COMPARATOR =
+        Comparator.<CameraCombination, Integer>comparing(c -> c.cameraIndex)
+            .thenComparing(c -> c.resolutionIndex)
+            .thenComparing(c -> c.formatIndex);
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -130,7 +157,6 @@
         String[] cameraNames = new String[numCameras];
         for (int i = 0; i < numCameras; i++) {
             cameraNames[i] = "Camera " + i;
-            mUntestedCombinations.add("All combinations for Camera " + i + "\n");
         }
         mCameraSpinner = (Spinner) findViewById(R.id.cameras_selection);
         mCameraSpinner.setAdapter(
@@ -171,6 +197,35 @@
         yTotal.setConcat(y2r, yOffset);
 
         mYuv2RgbFilter = new ColorMatrixColorFilter(yTotal);
+
+        Button mNextButton = findViewById(R.id.next_button);
+        mNextButton.setOnClickListener(v -> {
+                setUntestedCombination();
+                startPreview();
+        });
+    }
+
+    /**
+     * Set an untested combination of resolution and format for the current camera.
+     * Triggered by next button click.
+     */
+    private void setUntestedCombination() {
+        Optional<CameraCombination> combination = mUntestedCombinations.stream().filter(
+            c -> c.cameraIndex == mCurrentCameraId).findFirst();
+        if (!combination.isPresent()) {
+            Toast.makeText(this, "All Camera " + mCurrentCameraId + " tests are done.",
+                Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        // There is untested combination for the current camera, set the next untested combination.
+        int mResolutionIndex = combination.get().resolutionIndex;
+        int mFormatIndex = combination.get().formatIndex;
+
+        mNextPreviewSize = mPreviewSizes.get(mResolutionIndex);
+        mResolutionSpinner.setSelection(mResolutionIndex);
+        mNextPreviewFormat = mPreviewFormats.get(mFormatIndex);
+        mFormatSpinner.setSelection(mFormatIndex);
     }
 
     @Override
@@ -183,13 +238,13 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         boolean ret = true;
         switch (item.getItemId()) {
-        case MENU_ID_PROGRESS:
-            showCombinationsDialog();
-            ret = true;
-            break;
-        default:
-            ret = super.onOptionsItemSelected(item);
-            break;
+            case MENU_ID_PROGRESS:
+                showCombinationsDialog();
+                ret = true;
+                break;
+            default:
+                ret = super.onOptionsItemSelected(item);
+                break;
         }
         return ret;
     }
@@ -222,17 +277,19 @@
     public String getTestDetails() {
         StringBuilder reportBuilder = new StringBuilder();
         reportBuilder.append("Tested combinations:\n");
-        for (String combination: mTestedCombinations) {
+        for (CameraCombination combination: mTestedCombinations) {
             reportBuilder.append(combination);
+            reportBuilder.append("\n");
         }
+
         reportBuilder.append("Untested combinations:\n");
-        for (String combination: mUntestedCombinations) {
+        for (CameraCombination combination: mUntestedCombinations) {
             reportBuilder.append(combination);
+            reportBuilder.append("\n");
         }
         return reportBuilder.toString();
     }
 
-
     public void onSurfaceTextureAvailable(SurfaceTexture surface,
             int width, int height) {
         mPreviewTexture = surface;
@@ -240,7 +297,7 @@
                 || mFormatView.getMeasuredHeight() != height) {
             mPreviewTexWidth = mFormatView.getMeasuredWidth();
             mPreviewTexHeight = mFormatView.getMeasuredHeight();
-         } else {
+        } else {
             mPreviewTexWidth = width;
             mPreviewTexHeight = height;
         }
@@ -310,8 +367,6 @@
 
             };
 
-
-
     private void setUpCamera(int id) {
         shutdownCamera();
 
@@ -331,7 +386,7 @@
                 if (lhs.height > rhs.height) return 1;
                 return 0;
             }
-        };
+        }
 
         SizeCompare s = new SizeCompare();
         TreeSet<Camera.Size> sortedResolutions = new TreeSet<Camera.Size>(s);
@@ -365,13 +420,11 @@
 
         // Update untested entries
 
-        mUntestedCombinations.remove("All combinations for Camera " + id + "\n");
-        for (Camera.Size previewSize: mPreviewSizes) {
-            for (int previewFormat: mPreviewFormats) {
-                String combination = "Camera " + id + ", "
-                        + previewSize.width + "x" + previewSize.height
-                        + ", " + mPreviewFormatNames.get(previewFormat)
-                        + "\n";
+        for (int resolutionIndex = 0; resolutionIndex < mPreviewSizes.size(); resolutionIndex++) {
+            for (int formatIndex = 0; formatIndex < mPreviewFormats.size(); formatIndex++) {
+                CameraCombination combination = new CameraCombination(
+                    id, resolutionIndex, formatIndex);
+
                 if (!mTestedCombinations.contains(combination)) {
                     mUntestedCombinations.add(combination);
                 }
@@ -571,14 +624,16 @@
                 mFormatView.setImageBitmap(mCallbackBitmap);
                 if (mProcessingFirstFrame) {
                     mProcessingFirstFrame = false;
-                    String combination = "Camera " + mCurrentCameraId + ", "
-                            + mPreviewSize.width + "x" + mPreviewSize.height
-                            + ", " + mPreviewFormatNames.get(mPreviewFormat)
-                            + "\n";
+
+                    CameraCombination combination = new CameraCombination(
+                        mCurrentCameraId,
+                        mResolutionSpinner.getSelectedItemPosition(),
+                        mFormatSpinner.getSelectedItemPosition());
+
                     mUntestedCombinations.remove(combination);
                     mTestedCombinations.add(combination);
 
-                    displayToast(combination.replace("\n", ""));
+                    displayToast(combination.toString());
 
                     if (mTestedCombinations.size() == mAllCombinationsSize) {
                         setPassButtonEnabled(true);
@@ -617,7 +672,8 @@
     }
 
     private void displayToast(String combination) {
-        Toast.makeText(this, "\"" + combination + "\"\n" + " has been tested.", Toast.LENGTH_LONG).show();
+        Toast.makeText(this, "\"" + combination + "\"\n" + " has been tested.", Toast.LENGTH_SHORT)
+            .show();
     }
 
     public void onPreviewFrame(byte[] data, Camera camera) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java
deleted file mode 100644
index 1e24e6a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.cts.verifier.location;
-
-import android.location.cts.GnssMeasurementValuesTest;
-import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Activity to execute CTS GnssMeasurementValuesTest while dialing emergency number.
- * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
- */
-public class EmergencyCallGNSSTestsActivity extends EmergencyCallBaseTestActivity {
-  // GNSS test has a longer timeout
-  private static final long PHONE_CALL_DURATION_MS = TimeUnit.MINUTES.toMillis(2);
-
-  public EmergencyCallGNSSTestsActivity() {
-    super(GnssMeasurementValuesTest.class);
-  }
-
-  @Override
-  protected long getPhoneCallDurationMs() {
-    return PHONE_CALL_DURATION_MS;
-  }
-
-  @Override
-  protected boolean showLocalNumberInputbox() {
-    return false;
-  }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java
deleted file mode 100644
index 7377065..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.cts.verifier.location;
-
-import android.location.cts.EmergencyCallMessageTest;
-import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Activity to execute CTS EmergencyCallMessageTest while dialing emergency number.
- * It is a wrapper for {@link EmergencyCallMessageTest} running with AndroidJUnitRunner.
- */
-public class EmergencyCallMessageTestsActivity extends EmergencyCallBaseTestActivity {
-  private static final long PHONE_CALL_DURATION_MS = TimeUnit.SECONDS.toMillis(35);
-  public EmergencyCallMessageTestsActivity() {
-    super(EmergencyCallMessageTest.class);
-  }
-
-  @Override
-  protected long getPhoneCallDurationMs() {
-    return PHONE_CALL_DURATION_MS;
-  }
-
-  @Override
-  protected boolean showLocalNumberInputbox() {
-    return true;
-  }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java
deleted file mode 100644
index 634863d..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.cts.verifier.location;
-
-import android.location.cts.EmergencyCallWifiTest;
-import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Activity to execute CTS EmergencyCallWifiTest while dialing emergency number.
- * It is a wrapper for {@link EmergencyCallWifiTest} running with AndroidJUnitRunner.
- */
-public class EmergencyCallWifiTestsActivity extends EmergencyCallBaseTestActivity {
-    private static final long PHONE_CALL_DURATION_MS = TimeUnit.SECONDS.toMillis(35);
-    public EmergencyCallWifiTestsActivity() {
-        super(EmergencyCallWifiTest.class);
-    }
-
-    @Override
-    protected long getPhoneCallDurationMs() {
-      return PHONE_CALL_DURATION_MS;
-    }
-
-    @Override
-    protected boolean showLocalNumberInputbox() {
-      return false;
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementRegistrationTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementRegistrationTestsActivity.java
deleted file mode 100644
index 56755cc..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementRegistrationTestsActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import android.location.cts.GnssMeasurementRegistrationTest;
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-
-/**
- * Activity to execute CTS Gnss Measurement tests.
- * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
- */
-public class GnssMeasurementRegistrationTestsActivity extends GnssCtsTestActivity {
-    public GnssMeasurementRegistrationTestsActivity() {
-        super(GnssMeasurementRegistrationTest.class);
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementValuesTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementValuesTestsActivity.java
deleted file mode 100644
index 4bdbddf..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementValuesTestsActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import android.location.cts.GnssMeasurementValuesTest;
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-
-/**
- * Activity to execute CTS Gnss Measurement tests.
- * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
- */
-public class GnssMeasurementValuesTestsActivity extends GnssCtsTestActivity {
-    public GnssMeasurementValuesTestsActivity() {
-        super(GnssMeasurementValuesTest.class);
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementWhenNoLocationTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementWhenNoLocationTestsActivity.java
deleted file mode 100644
index 1ee74c5..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementWhenNoLocationTestsActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import android.location.cts.GnssMeasurementWhenNoLocationTest;
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-
-/**
- * Activity to execute CTS GnssMeasurementWhenNoLocationTest.
- * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
- */
-public class GnssMeasurementWhenNoLocationTestsActivity extends GnssCtsTestActivity {
-    public GnssMeasurementWhenNoLocationTestsActivity() {
-        super(GnssMeasurementWhenNoLocationTest.class);
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementsConstellationTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementsConstellationTestsActivity.java
deleted file mode 100644
index c59346a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssMeasurementsConstellationTestsActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import android.location.cts.GnssMeasurementsConstellationTest;
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-
-/**
- * Activity to execute CTS Gnss ConstellationType tests.
- * It is a wrapper for {@link GnssConstellationTypeTest} running with AndroidJUnitRunner.
- */
-public class GnssMeasurementsConstellationTestsActivity extends GnssCtsTestActivity {
-    public GnssMeasurementsConstellationTestsActivity() {
-        super(GnssMeasurementsConstellationTest.class);
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssNavigationMessageTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssNavigationMessageTestsActivity.java
deleted file mode 100644
index a4c9c93..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssNavigationMessageTestsActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import android.location.cts.GnssNavigationMessageTest;
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-
-/**
- * Activity to execute CTS GnssNavigationMessageTest.
- * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
- */
-public class GnssNavigationMessageTestsActivity extends GnssCtsTestActivity {
-    public GnssNavigationMessageTestsActivity() {
-        super(GnssNavigationMessageTest.class);
-    }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssPseudorangeVerificationTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssPseudorangeVerificationTestsActivity.java
deleted file mode 100644
index e17b67a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssPseudorangeVerificationTestsActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-import android.location.cts.GnssPseudorangeVerificationTest;
-
-/**
- * Activity to execute CTS GnssPseudorangeVerificationTest.
- * It is a wrapper for {@link GnssPseudorangeVerificationTest} running with AndroidJUnitRunner.
- */
-
-public class GnssPseudorangeVerificationTestsActivity extends GnssCtsTestActivity {
-  public GnssPseudorangeVerificationTestsActivity() {
-    super(GnssPseudorangeVerificationTest.class);
-  }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssStatusTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssStatusTestsActivity.java
deleted file mode 100644
index b132c50..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssStatusTestsActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 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.verifier.location;
-
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-import android.location.cts.GnssStatusTest;
-
-/**
- * Activity to execute CTS GnssStatusTest.
- * It is a wrapper for {@link GnssStatusTest} running with AndroidJUnitRunner.
- */
-
-public class GnssStatusTestsActivity extends GnssCtsTestActivity {
-  public GnssStatusTestsActivity() {
-    super(GnssStatusTest.class);
-  }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssTtffTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssTtffTestsActivity.java
deleted file mode 100644
index 7b5b6fb..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssTtffTestsActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.cts.verifier.location;
-
-import com.android.cts.verifier.location.base.GnssCtsTestActivity;
-import android.location.cts.GnssTtffTests;
-
-/**
- * Activity to execute CTS GnssStatusTest.
- * It is a wrapper for {@link GnssTtffTests} running with AndroidJUnitRunner.
- */
-
-public class GnssTtffTestsActivity extends GnssCtsTestActivity {
-  public GnssTtffTestsActivity() {
-    super(GnssTtffTests.class);
-  }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
deleted file mode 100644
index 4909497..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.location;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.text.Layout;
-import android.text.Spannable;
-import android.text.style.ForegroundColorSpan;
-import android.util.Log;
-import android.view.View;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-interface PassFailLog {
-    public void log(String s);
-    public void pass();
-    public void fail(String s);
-}
-
-/**
- * CTS Verifier case for verifying GPS.
- */
-public class GpsTestActivity extends PassFailButtons.Activity implements PassFailLog {
-    private LocationManager mLocationManager;
-    private TextView mTextView;
-    private ScrollView mScrollView;
-
-    LocationVerifier mLocationVerifier;
-    private int mTestNumber;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mLocationManager = (LocationManager) getApplicationContext().getSystemService(
-                Context.LOCATION_SERVICE);
-
-        setContentView(R.layout.pass_fail_text);
-        setPassFailButtonClickListeners();
-        setInfoResources(R.string.location_gps_test, R.string.location_gps_test_info, -1);
-        mTextView = (TextView) findViewById(R.id.text);
-        mTextView.setTypeface(Typeface.MONOSPACE);
-        mTextView.setTextSize(15.0f);
-        mScrollView = (ScrollView) findViewById(R.id.scroll);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-
-        mTextView.setText("");
-        getPassButton().setEnabled(false);
-        mTestNumber = 0;
-        nextTest();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-
-        if (mLocationVerifier != null) {
-            mLocationVerifier.stop();
-        }
-    }
-
-    @Override
-    public String getTestDetails() {
-        return mTextView.getText().toString();
-    }
-
-    private void nextTest() {
-        mTestNumber++;
-        switch (mTestNumber) {
-            case 1:
-                // Test GPS with minTime = 0
-                mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 0, 8, false);
-                mLocationVerifier.start();
-                break;
-            case 2:
-                // Test GPS with minTime = 1s
-                mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 1 * 1000, 8, false);
-                mLocationVerifier.start();
-                break;
-            case 3:
-                // Test GPS with minTime = 5s
-                mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 5 * 1000, 8, false);
-                mLocationVerifier.start();
-                break;
-            case 4:
-                // Test GPS with minTime = 15s
-                mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 15 * 1000, 8, false);
-                mLocationVerifier.start();
-                break;
-            case 5:
-                log("All GPS tests complete", Color.GREEN);
-                getPassButton().setEnabled(true);
-                break;
-        }
-    }
-
-    @Override
-    public void log(String s) {
-        mTextView.append(s);
-        mTextView.append("\n");
-
-        // Scroll to bottom
-        mScrollView.post(new Runnable() {
-            @Override
-            public void run() {
-                mScrollView.fullScroll(View.FOCUS_DOWN);
-            }
-        });
-    }
-
-    private void log(String s, int color) {
-        int start = mTextView.getText().length();
-        mTextView.append(s);
-        mTextView.append("\n");
-        int end = mTextView.getText().length();
-        Spannable spanText = (Spannable) mTextView.getText();
-        spanText.setSpan(new ForegroundColorSpan(color), start, end, 0);
-    }
-
-    @Override
-    public void fail(String s) {
-        log(s, Color.RED);
-        mLocationVerifier = null;
-    }
-
-    @Override
-    public void pass() {
-        log("OK!\n", Color.GREEN);
-        try {
-            Thread.sleep(2000);
-            log("(Sleep 2 second) \n", Color.GREEN);
-        } catch (InterruptedException e) {
-            fail("unexpected InterruptedException when sleep");
-        }
-        mLocationVerifier = null;
-        nextTest();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
deleted file mode 100644
index ee4068f..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.location;
-
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-
-import java.util.List;
-import java.util.ArrayList;
-
-public class LocationVerifier implements Handler.Callback {
-
-    public static final String TAG = "CtsVerifierLocation";
-
-    private static final int MSG_TIMEOUT = 1;
-
-    /** Timing failures on first NUM_IGNORED_UPDATES updates are ignored. */
-    private static final int NUM_IGNORED_UPDATES = 2;
-
-    /* In active mode, the mean computed for the deltas should not be smaller
-     * than mInterval * ACTIVE_MIN_MEAN_RATIO */
-    private static final double ACTIVE_MIN_MEAN_RATIO = 0.75;
-
-    /* In passive mode, the mean computed for the deltas should not be smaller
-     * than mInterval * PASSIVE_MIN_MEAN_RATIO */
-    private static final double PASSIVE_MIN_MEAN_RATIO = 0.1;
-
-    /**
-     * The standard deviation computed for the deltas should not be bigger
-     * than mInterval * ALLOWED_STDEV_ERROR_RATIO
-     * or MIN_STDEV_MS, whichever is higher.
-     */
-    private static final double ALLOWED_STDEV_ERROR_RATIO = 0.50;
-    private static final long MIN_STDEV_MS = 1000;
-
-    private final LocationManager mLocationManager;
-    private final PassFailLog mCb;
-    private final String mProvider;
-    private final long mInterval;
-    private final long mTimeout;
-    private final Handler mHandler;
-    private final int mRequestedUpdates;
-    private final ActiveListener mActiveListener;
-    private final PassiveListener mPassiveListener;
-
-    private boolean isTestOutcomeSet = false;
-    private long mLastActiveTimestamp = -1;
-    private long mLastPassiveTimestamp = -1;
-    private int mNumActiveUpdates = 0;
-    private int mNumPassiveUpdates = 0;
-    private boolean mIsMockProvider = false;
-    private boolean mRunning = false;
-    private boolean mActiveLocationArrive = false;
-
-    private List<Long> mActiveDeltas = new ArrayList();
-    private List<Long> mPassiveDeltas = new ArrayList();
-
-    private class ActiveListener implements LocationListener {
-        @Override
-        public void onLocationChanged(Location location) {
-            if (!mRunning) return;
-
-            mActiveLocationArrive = true;
-            mNumActiveUpdates++;
-            scheduleTimeout();
-
-            long timestamp = location.getTime();
-            long delta = timestamp - mLastActiveTimestamp;
-            mLastActiveTimestamp = timestamp;
-
-            if (mNumActiveUpdates <= NUM_IGNORED_UPDATES ) {
-                mCb.log("(ignored) active " + mProvider + " update (" + delta + "ms)");
-                return;
-            }
-            if (location.isFromMockProvider() != mIsMockProvider) {
-                fail("location coming from \"" + mProvider +
-                        "\" provider reports isFromMockProvider() to be " +
-                        location.isFromMockProvider());
-            }
-
-            mActiveDeltas.add(delta);
-            mCb.log("active " + mProvider + " update (" + delta + "ms)");
-
-            if (mNumActiveUpdates >= mRequestedUpdates) {
-                assertMeanAndStdev(mProvider, mActiveDeltas, ACTIVE_MIN_MEAN_RATIO);
-                assertMeanAndStdev(LocationManager.PASSIVE_PROVIDER, mPassiveDeltas, PASSIVE_MIN_MEAN_RATIO);
-                pass();
-            }
-        }
-
-        @Override
-        public void onStatusChanged(String provider, int status, Bundle extras) { }
-        @Override
-        public void onProviderEnabled(String provider) { }
-        @Override
-        public void onProviderDisabled(String provider) { }
-    }
-
-    private void assertMeanAndStdev(String provider, List<Long> deltas, double minMeanRatio) {
-        double mean = computeMean(deltas);
-        double stdev = computeStdev(mean, deltas);
-
-        double minMean = mInterval * minMeanRatio;
-        if (mean < minMean) {
-            fail(provider + " provider mean too small: " + mean
-                 + " (min: " + minMean + ")");
-            return;
-        }
-
-        double maxStdev = Math.max(MIN_STDEV_MS, mInterval * ALLOWED_STDEV_ERROR_RATIO);
-        if (stdev > maxStdev) {
-            fail (provider + " provider stdev too big: "
-                  + stdev + " (max: " + maxStdev + ")");
-            return;
-        }
-
-        mCb.log(provider + " provider mean: " + mean);
-        mCb.log(provider + " provider stdev: " + stdev);
-    }
-
-    private double computeMean(List<Long> deltas) {
-        long accumulator = 0;
-        for (long d : deltas) {
-            accumulator += d;
-        }
-        return accumulator / deltas.size();
-    }
-
-    private double computeStdev(double mean, List<Long> deltas) {
-        double accumulator = 0;
-        for (long d : deltas) {
-            double diff = d - mean;
-            accumulator += diff * diff;
-        }
-        return Math.sqrt(accumulator / (deltas.size() - 1));
-    }
-
-    private class PassiveListener implements LocationListener {
-        @Override
-        public void onLocationChanged(Location location) {
-            if (!mRunning) return;
-            if (!location.getProvider().equals(mProvider)) return;
-
-            // When a test round start, passive listener shouldn't recevice location before active listener.
-            // If this situation occurs, we treat this location as overdue location.
-            // (The overdue location comes from previous test round, it occurs occasionally)
-            // We have to skip it to prevent wrong calculation of time interval.
-            if (!mActiveLocationArrive) {
-                mCb.log("ignoring passive " + mProvider + " update");
-                return;
-            }
-
-            mNumPassiveUpdates++;
-            long timestamp = location.getTime();
-            long delta = timestamp - mLastPassiveTimestamp;
-            mLastPassiveTimestamp = timestamp;
-
-            if (mNumPassiveUpdates <= NUM_IGNORED_UPDATES) {
-                mCb.log("(ignored) passive " + mProvider + " update (" + delta + "ms)");
-                return;
-            }
-            if (location.isFromMockProvider() != mIsMockProvider) {
-                fail("location coming from \"" + mProvider +
-                        "\" provider reports isFromMockProvider() to be " +
-                        location.isFromMockProvider());
-            }
-
-            mPassiveDeltas.add(delta);
-            mCb.log("passive " + mProvider + " update (" + delta + "ms)");
-        }
-
-        @Override
-        public void onStatusChanged(String provider, int status, Bundle extras) { }
-        @Override
-        public void onProviderEnabled(String provider) { }
-        @Override
-        public void onProviderDisabled(String provider) { }
-    }
-
-    public LocationVerifier(PassFailLog cb, LocationManager locationManager,
-            String provider, long requestedInterval, int numUpdates, boolean isMockProvider) {
-        mProvider = provider;
-        mInterval = requestedInterval;
-        // timeout at 60 seconds after interval time
-        mTimeout = requestedInterval + 60 * 1000;
-        mRequestedUpdates = numUpdates + NUM_IGNORED_UPDATES;
-        mLocationManager = locationManager;
-        mCb = cb;
-        mHandler = new Handler(this);
-        mActiveListener = new ActiveListener();
-        mPassiveListener = new PassiveListener();
-        mIsMockProvider = isMockProvider;
-    }
-
-    public void start() {
-        mRunning = true;
-        scheduleTimeout();
-        mLastActiveTimestamp = System.currentTimeMillis();
-        mLastPassiveTimestamp = mLastActiveTimestamp;
-        mCb.log("enabling passive listener");
-        mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0,
-                mPassiveListener);
-        mCb.log("enabling " + mProvider + " (minTime=" + mInterval + "ms)");
-        mLocationManager.requestLocationUpdates(mProvider, mInterval, 0,
-                mActiveListener);
-    }
-
-    public void stop() {
-        mRunning = false;
-        mCb.log("disabling " + mProvider);
-        mLocationManager.removeUpdates(mActiveListener);
-        mCb.log("disabling passive listener");
-        mLocationManager.removeUpdates(mPassiveListener);
-        mHandler.removeMessages(MSG_TIMEOUT);
-    }
-
-    private void pass() {
-        if (!isTestOutcomeSet) {
-            stop();
-            mCb.pass();
-            isTestOutcomeSet = true;
-        }
-    }
-
-    private void fail(String s) {
-        if (!isTestOutcomeSet) {
-            stop();
-            mCb.fail(s);
-            isTestOutcomeSet = true;
-        }
-    }
-
-    private void scheduleTimeout() {
-        mHandler.removeMessages(MSG_TIMEOUT);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), mTimeout);
-    }
-
-    @Override
-    public boolean handleMessage(Message msg) {
-        if (!mRunning) return true;
-        fail("timeout (" + mTimeout + "ms) waiting for " +
-                mProvider + " location change");
-        return true;
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
deleted file mode 100644
index e25f758..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.cts.verifier.location.base;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.location.cts.GnssTestCase;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.LayoutInflater;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-import android.widget.Toast;
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.TestResult;
-import java.util.concurrent.TimeUnit;
-
-/**
- * An Activity that allows Gnss CTS tests to be executed inside CtsVerifier.
- *
- * Sub-classes pass the test class as part of construction.
- * One JUnit test class is executed per Activity, the test class can still be executed outside
- * CtsVerifier.
- */
-public abstract class EmergencyCallBaseTestActivity extends GnssCtsTestActivity {
-    private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
-    private static final String defaultPhonePackageName = "com.google.android.dialer";
-
-    /**
-     * Constructor for a CTS test executor. It will execute a standalone CTS test class.
-     *
-     * @param testClass The test class to execute, it must be a subclass of {@link AndroidTestCase}.
-     */
-    protected EmergencyCallBaseTestActivity(Class<? extends GnssTestCase> testClass) {
-        super(testClass, R.layout.gnss_emergency_test);
-    }
-
-    protected abstract long getPhoneCallDurationMs();
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // skip current test if device doesn't support cellular
-        if (!EmergencyCallUtil.isPhoneDevice(this)) {
-          String skipInfo = getResources().getString(R.string.emergency_call_skip_info);
-          TestResult.setPassedResult(this, super.getClass().getName(), skipInfo);
-          Toast toast = Toast.makeText(getApplicationContext(), skipInfo, Toast.LENGTH_LONG);
-          toast.show();
-          this.finish();
-          return;
-        }
-        // override the test info
-        mTextView.setText(R.string.location_emergency_call_test_info);
-        EmergencyCallUtil.setDefaultDialer(this, this.getPackageName());
-        setPassFailButtonClickListeners();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        EmergencyCallUtil.setDefaultDialer(this, defaultPhonePackageName);
-    }
-
-    protected abstract boolean showLocalNumberInputbox();
-
-    @Override
-    public void onClick(View target) {
-
-        AlertDialog.Builder builder = new AlertDialog.Builder(this);
-        final FrameLayout frameView = new FrameLayout(this);
-        builder.setView(frameView);
-
-        final boolean enableLocalNumberInputBox = showLocalNumberInputbox();
-        final AlertDialog alertDialog = builder.create();
-        LayoutInflater inflater = alertDialog.getLayoutInflater();
-
-        View dialogView;
-        if (enableLocalNumberInputBox) {
-            dialogView =
-                inflater.inflate(R.layout.emergency_call_msg_test_confirm_dialog, frameView);
-        } else {
-            dialogView = inflater.inflate(R.layout.emergency_call_confirm_dialog, frameView);
-        }
-        final EditText targetNumberEditText =
-            (EditText) dialogView.findViewById(R.id.emergency_number);
-        final Button dialButton = (Button) dialogView.findViewById(R.id.dial_button);
-        dialButton.setOnClickListener(new Button.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (enableLocalNumberInputBox) {
-                    final EditText currentNumberEditText =
-                        (EditText) dialogView.findViewById(R.id.local_phone_number);
-                    String currentNumber = currentNumberEditText.getText().toString();
-                    // pass the number to cts tests for cts verifier UI, through System property.
-                    System.setProperty(PHONE_NUMBER_KEY, currentNumber);
-                }
-                int targetPhoneNumber =
-                    Integer.parseInt(targetNumberEditText.getText().toString());
-                long callDurationMs = EmergencyCallBaseTestActivity.this.getPhoneCallDurationMs();
-                EmergencyCallUtil.makePhoneCall(
-                    EmergencyCallBaseTestActivity.this, targetPhoneNumber);
-                EmergencyCallBaseTestActivity.super.onClick(target);
-                EmergencyCallUtil.endCallWithDelay(
-                    EmergencyCallBaseTestActivity.this.getApplicationContext(), callDurationMs);
-                alertDialog.dismiss();
-            }
-        });
-        alertDialog.show();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java
deleted file mode 100644
index 328498e..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.cts.verifier.location.base;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
-import android.text.InputType;
-import android.util.Log;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import com.android.cts.verifier.R;
-import java.lang.reflect.Method;
-import java.util.concurrent.TimeUnit;
-
-/**
- * The EmergencyCallUtil class provides util functions related to the emergency call.
- */
-public class EmergencyCallUtil {
-    private static final int REQUEST_CODE_SET_DEFAULT_DIALER = 1;
-    private static final String TAG = "EmergencyCallUtil";
-    private static final long WAIT_FOR_CONNECTION_MS = TimeUnit.SECONDS.toMillis(3);
-
-    /*
-     * This method is used to set default dialer app.
-     * To dial 911, it requires to set the dialer to be the system default dial app.
-     *
-     * @param activity current Activity.
-     * @param packageName dialer package name.
-     */
-    public static void setDefaultDialer(Activity activity, String packageName) {
-        final Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
-        intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
-        activity.startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER);
-    }
-
-    public static void makePhoneCall(Activity activity, int phoneNumber) {
-        Intent callIntent = new Intent(Intent.ACTION_CALL);
-        callIntent.setData(Uri.parse("tel:" + phoneNumber));
-        try {
-            callIntent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
-            activity.startActivityForResult(callIntent, REQUEST_CODE_SET_DEFAULT_DIALER);
-        } catch (SecurityException ex) {
-            Log.d(TAG, "Failed to make the phone call: " + ex.toString());
-        }
-        // sleep 3sec to make sure call is connected
-        activity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Thread.sleep(WAIT_FOR_CONNECTION_MS);
-                } catch (InterruptedException ex) {
-                    Log.d(TAG, "Failed to make the phone call: " + ex.toString());
-                }
-            }
-        });
-    }
-
-    public static void endCallWithDelay(Context context, long delayMs) {
-        Runnable runnable = new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Thread.sleep(delayMs);
-                    endCall(context);
-                } catch (InterruptedException ex) {
-                    Log.d(TAG, "Failed to make the phone call: " + ex.toString());
-                }
-            }
-        };
-        new Thread(runnable).start();
-    }
-
-    public static boolean isPhoneDevice(Activity activity) {
-        TelephonyManager tMgr =
-            (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
-        if(tMgr.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE){
-            return false;
-        }
-        return true;
-    }
-
-    private static void endCall(Context context) {
-        try {
-            TelephonyManager telephonyManager =
-                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-
-            Class<?> classTelephony = Class.forName(telephonyManager.getClass().getName());
-            Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
-            methodGetITelephony.setAccessible(true);
-
-            Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);
-
-            Class<?> telephonyInterfaceClass =
-                Class.forName(telephonyInterface.getClass().getName());
-            Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");
-
-            methodEndCall.invoke(telephonyInterface);
-
-        } catch (Exception e) {
-            Log.d(TAG, "Failed to cancel the call: " + e.toString());
-        }
-    }
-
-    private static String getCurrentPhoneNumber(Activity activity) {
-        TelephonyManager tMgr =
-            (TelephonyManager)activity.getSystemService(Context.TELEPHONY_SERVICE);
-        return tMgr.getLine1Number();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
index d5c81fe..fd1c457 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -29,6 +29,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
@@ -100,6 +101,7 @@
             runNextTestOrShowSummary();
         });
 
+        mTests.add(new EnableBubbleTest());
         mTests.add(new SendBubbleTest());
         mTests.add(new SuppressNotifTest());
         mTests.add(new AddNotifTest());
@@ -178,6 +180,38 @@
         }
     }
 
+    private class EnableBubbleTest extends BubblesTestStep {
+
+        @Override
+        public int getButtonText() {
+            return R.string.bubbles_notification_enable_bubbles_button;
+        }
+
+
+        @Override
+        public int getTestTitle() {
+            return R.string.bubbles_notification_test_enable_bubbles_title;
+        }
+
+        @Override
+        public int getTestDescription() {
+            return R.string.bubbles_notification_test_enable_bubbles_verify;
+        }
+
+        @Override
+        public void performTestAction() {
+            final String packageName = getApplicationContext().getPackageName();
+            final int appUid = getApplicationInfo().uid;
+            final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
+            intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+            intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            startActivity(intent);
+        }
+    }
+
     private class SendBubbleTest extends BubblesTestStep {
 
         @Override
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 a3de878..1b14d2b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -304,7 +304,14 @@
 
             if (!TextUtils.isEmpty(id)) {
                 AutomaticZenRule rule = mNm.getAutomaticZenRule(id);
-                if (Objects.equals(ruleToCreate, rule)) {
+                if (rule != null && ruleToCreate.getName().equals(rule.getName())
+                        && ruleToCreate.getOwner().equals(rule.getOwner())
+                        && ruleToCreate.getConditionId().equals(rule.getConditionId())
+                        && ruleToCreate.isEnabled() == rule.isEnabled()
+                        && ruleToCreate.getInterruptionFilter() == rule.getInterruptionFilter()
+                        && Objects.equals(ruleToCreate.getConfigurationActivity(),
+                        rule.getConfigurationActivity())
+                        && Objects.equals(ruleToCreate.getZenPolicy(), rule.getZenPolicy())) {
                     status = PASS;
                 } else {
                     logFail("created rule doesn't equal actual rule");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
index 4f92b64..b6116e2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
@@ -351,7 +351,7 @@
      *
      */
     private void initStoragePath() {
-        File rxcvRecDataDir = new File(Environment.getExternalStorageDirectory(),"RVCVRecData");
+        File rxcvRecDataDir = new File(getExternalFilesDir(null),"RVCVRecData");
 
         // Create the storage directory if it does not exist
         if (! rxcvRecDataDir.exists()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index c15d2ac..8bc9deb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -925,8 +925,7 @@
 
             if (OUTPUT_DEBUG_IMAGE) {
                 Calib3d.drawChessboardCorners(frame, patternSize, reprojCenters, true);
-                Imgcodecs.imwrite(Environment.getExternalStorageDirectory().getPath()
-                        + "/RVCVRecData/DebugCV/img" + i + ".png", frame);
+                Imgcodecs.imwrite(mPath + "/RVCVRecData/DebugCV/img" + i + ".png", frame);
             }
         }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/DefaultDialerChanger.java b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/DefaultDialerChanger.java
index a72ac2b..2588164 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/DefaultDialerChanger.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/DefaultDialerChanger.java
@@ -17,10 +17,12 @@
 package com.android.cts.verifier.voicemail;
 
 import android.app.Activity;
+import android.app.role.RoleManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -43,9 +45,6 @@
 
     private final ImageView mRestoreDefaultDialerImage;
     private final Button mRestoreDefaultDialerButton;
-    private final TextView mRestoreDefaultDialerText;
-
-    private String mOriginalDefaultDialer;
 
     private boolean mRestorePending;
 
@@ -55,24 +54,12 @@
         mSetDefaultDialerImage = (ImageView) mActivity.findViewById(R.id.set_default_dialer_image);
         mRestoreDefaultDialerImage = (ImageView) mActivity
                 .findViewById(R.id.restore_default_dialer_image);
-        mRestoreDefaultDialerText = (TextView) mActivity
-                .findViewById(R.id.restore_default_dialer_text);
 
         mSetDefaultDialerButton = (Button) mActivity.findViewById(R.id.set_default_dialer);
         mRestoreDefaultDialerButton = (Button) mActivity.findViewById(R.id.restore_default_dialer);
 
         final TelecomManager telecomManager = mActivity.getSystemService(TelecomManager.class);
-        mOriginalDefaultDialer = telecomManager.getDefaultDialerPackage();
-
-        updateSetDefaultDialerState(mOriginalDefaultDialer);
-        if (mOriginalDefaultDialer.equals(mActivity.getPackageName())) {
-            // The CTS verifier is already the default dialer (probably due to the tester exiting
-            // mid test. We don't know what the default dialer should be so just prompt the tester
-            // to restore it through settings, and remove the button.
-            mRestoreDefaultDialerText
-                    .setText(R.string.voicemail_restore_default_dialer_no_default_description);
-            mRestoreDefaultDialerButton.setVisibility(View.GONE);
-        }
+        updateSetDefaultDialerState(telecomManager.getDefaultDialerPackage());
 
         mSetDefaultDialerButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -84,9 +71,8 @@
                     return;
                 }
 
-                final Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
-                intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
-                        mActivity.getPackageName());
+                final RoleManager roleManager = mActivity.getSystemService(RoleManager.class);
+                final Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
                 mActivity.startActivityForResult(intent, 0);
             }
         });
@@ -94,16 +80,14 @@
         mRestoreDefaultDialerButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                if (telecomManager.getDefaultDialerPackage().equals(mOriginalDefaultDialer)) {
+                if (!telecomManager.getDefaultDialerPackage().equals(mActivity.getPackageName())) {
                     Toast.makeText(mActivity,
                             R.string.voicemail_default_dialer_already_restored, Toast.LENGTH_SHORT)
                             .show();
                     return;
                 }
 
-                final Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
-                intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
-                        mOriginalDefaultDialer);
+                final Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
                 mActivity.startActivityForResult(intent, 0);
             }
         });
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
index 5533d9b..26b0978 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
@@ -88,6 +88,8 @@
     private ConnectivityManager mCm;
     private CallbackUtils.NetworkCb mNetworkCb;
 
+    private static int sSDKLevel = android.os.Build.VERSION.SDK_INT;
+
     public DataPathInBandTestCase(Context context, boolean isSecurityOpen, boolean isPublish,
             boolean isUnsolicited) {
         super(context, isUnsolicited, false);
@@ -191,10 +193,12 @@
             Log.e(TAG, "executeTestSubscriber: received a null Network or NetworkCapabilities!?");
             return false;
         }
-        if (info.second.getNetworkSpecifier() != null) {
-            setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
-            Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
-            return false;
+        if (sSDKLevel <= android.os.Build.VERSION_CODES.P) {
+            if (info.second.getNetworkSpecifier() != null) {
+                setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
+                Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
+                return false;
+            }
         }
 
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
@@ -404,10 +408,12 @@
             Log.e(TAG, "executeTestPublisher: received a null Network or NetworkCapabilities!?");
             return false;
         }
-        if (info.second.getNetworkSpecifier() != null) {
-            setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
-            Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
-            return false;
+        if (sSDKLevel <= android.os.Build.VERSION_CODES.P) {
+            if (info.second.getNetworkSpecifier() != null) {
+                setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
+                Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
+                return false;
+            }
         }
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
         if (DBG) Log.d(TAG, "executeTestPublisher: network request granted - AVAILABLE");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
index 0d6ca8f..1c35d2a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
@@ -97,6 +97,8 @@
     private DiscoverySession mWifiAwareDiscoverySession;
     private byte[] mDiscoveryMac;
 
+    private static int sSDKLevel = android.os.Build.VERSION.SDK_INT;
+
     public DataPathOutOfBandTestCase(Context context, boolean isSecurityOpen,
             boolean isResponder) {
         super(context);
@@ -292,10 +294,12 @@
             Log.e(TAG, "executeTestResponder: network request rejected - ON_UNAVAILABLE");
             return false;
         }
-        if (info.second.getNetworkSpecifier() != null) {
-            setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
-            Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
-            return false;
+        if (sSDKLevel <= android.os.Build.VERSION_CODES.P){
+            if (info.second.getNetworkSpecifier() != null) {
+                setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
+                Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
+                return false;
+            }
         }
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
         if (DBG) Log.d(TAG, "executeTestResponder: network request granted - AVAILABLE");
@@ -431,10 +435,12 @@
             Log.e(TAG, "executeTestInitiator: network request rejected - ON_UNAVAILABLE");
             return false;
         }
-        if (info.second.getNetworkSpecifier() != null) {
-            setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
-            Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
-            return false;
+        if (sSDKLevel <= android.os.Build.VERSION_CODES.P){
+            if(info.second.getNetworkSpecifier() != null) {
+                setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak));
+                Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!");
+                return false;
+            }
         }
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
         if (DBG) Log.d(TAG, "executeTestInitiator: network request granted - AVAILABLE");
diff --git a/apps/OomCatcher/Android.mk b/apps/OomCatcher/Android.mk
index 7f47e03..e14cde5 100644
--- a/apps/OomCatcher/Android.mk
+++ b/apps/OomCatcher/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_SDK_VERSION := current
 
-LOCAL_COMPATIBILITY_SUITE := cts sts
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/apps/PermissionApp/AndroidManifest.xml b/apps/PermissionApp/AndroidManifest.xml
index f880933..be43556 100644
--- a/apps/PermissionApp/AndroidManifest.xml
+++ b/apps/PermissionApp/AndroidManifest.xml
@@ -20,6 +20,23 @@
 
     <uses-sdk android:minSdkVersion="23"/>
 
+    <permission android:name="com.android.cts.permissionapp.permA"
+                android:protectionLevel="dangerous"
+                android:label="@string/permA"
+                android:permissionGroup="com.android.cts.permissionapp.groupAB"
+                android:description="@string/permA" />
+    <permission android:name="com.android.cts.permissionapp.permB"
+                android:protectionLevel="dangerous"
+                android:label="@string/permB"
+                android:permissionGroup="com.android.cts.permissionapp.groupAB"
+                android:description="@string/permB" />
+
+    <permission-group android:description="@string/groupAB"
+                      android:label="@string/groupAB"
+                      android:name="com.android.cts.permissionapp.groupAB" />
+
+    <uses-permission android:name="com.android.cts.permissionapp.permA"/>
+    <uses-permission android:name="com.android.cts.permissionapp.permB"/>
     <uses-permission android:name="android.permission.READ_CONTACTS"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
 
diff --git a/apps/PermissionApp/res/values/strings.xml b/apps/PermissionApp/res/values/strings.xml
new file mode 100755
index 0000000..52435c6
--- /dev/null
+++ b/apps/PermissionApp/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permA">Permission A</string>
+    <string name="permB">Permission B</string>
+    <string name="groupAB">Group with A and B</string>
+</resources>
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java
index fdfdf42..9df0bf1 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java
@@ -71,6 +71,20 @@
         }
     }
 
+    /**
+     * Returns true if the app for the given package name is a privileged system app for this
+     * device
+     */
+    public static boolean isPrivilegedSystemApp(String packageName) {
+        try {
+            ApplicationInfo ai = getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.GET_META_DATA);
+            return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0) && ai.isPrivilegedApp();
+        } catch(PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /** Returns the version string of the package name, or null if the package can't be found */
     public static String getVersionString(String packageName) {
         try {
diff --git a/hostsidetests/appbinding/hostside/src/com/android/cts/appbinding/AppBindingHostTest.java b/hostsidetests/appbinding/hostside/src/com/android/cts/appbinding/AppBindingHostTest.java
index ed1b564..327c9ab 100644
--- a/hostsidetests/appbinding/hostside/src/com/android/cts/appbinding/AppBindingHostTest.java
+++ b/hostsidetests/appbinding/hostside/src/com/android/cts/appbinding/AppBindingHostTest.java
@@ -151,6 +151,16 @@
         runCommand("settings put global " + APP_BINDING_SETTING + " '" + settings + "'");
     }
 
+    private boolean isSmsCapable() throws Exception {
+        String output = runCommand("dumpsys phone");
+        if (output.contains("isSmsCapable=true")) {
+            CLog.d("Device is SMS capable");
+            return true;
+        }
+        CLog.d("Device is not SMS capable");
+        return false;
+    }
+
     private void setSmsApp(String pkg, int userId) throws Exception {
         runCommand("cmd phone sms set-default-app --user " + userId + " " + pkg,
                 "^SMS \\s app \\s set \\s to \\s " + Pattern.quote(pkg) + "$");
@@ -320,6 +330,11 @@
      * Install APK 1 and make it the default SMS app and make sure the service gets bound.
      */
     public void testSimpleBind1() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
     }
 
@@ -327,6 +342,11 @@
      * Install APK 2 and make it the default SMS app and make sure the service gets bound.
      */
     public void testSimpleBind2() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, USER_SYSTEM);
     }
 
@@ -334,6 +354,11 @@
      * Install APK B and make it the default SMS app and make sure the service gets bound.
      */
     public void testSimpleBindB() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_B, PACKAGE_B, SERVICE_1, USER_SYSTEM);
     }
 
@@ -341,6 +366,11 @@
      * APK 3 doesn't have a valid service to be bound.
      */
     public void testSimpleNotBound3() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckNotBound(APK_3, PACKAGE_A, USER_SYSTEM,
                 "must be protected with android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE");
     }
@@ -349,6 +379,11 @@
      * APK 4 doesn't have a valid service to be bound.
      */
     public void testSimpleNotBound4() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckNotBound(APK_4, PACKAGE_A, USER_SYSTEM, "More than one");
     }
 
@@ -356,6 +391,11 @@
      * APK 5 doesn't have a valid service to be bound.
      */
     public void testSimpleNotBound5() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckNotBound(APK_5, PACKAGE_A, USER_SYSTEM,
                 "Service with android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE not found");
     }
@@ -364,6 +404,11 @@
      * APK 6's service doesn't have android:process.
      */
     public void testSimpleNotBound6() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckNotBound(APK_6, PACKAGE_A, USER_SYSTEM,
                 "Service must not run on the main process");
     }
@@ -372,6 +417,11 @@
      * Make sure when the SMS app gets updated, the service still gets bound correctly.
      */
     public void testUpgrade() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         // Replace existing package without uninstalling.
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
         installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, USER_SYSTEM);
@@ -385,6 +435,11 @@
      * Make sure when the SMS app is uninstalled, the binding will be gone.
      */
     public void testUninstall() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         // Replace existing package without uninstalling.
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
         getDevice().uninstallPackage(PACKAGE_A);
@@ -404,6 +459,11 @@
      * Make sure when the SMS app changes, the service still gets bound correctly.
      */
     public void testSwitchDefaultApp() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
         installAndCheckBound(APK_B, PACKAGE_B, SERVICE_1, USER_SYSTEM);
         installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, USER_SYSTEM);
@@ -424,6 +484,11 @@
     }
 
     public void testSecondaryUser() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
 
         final int userId = getDevice().createUser("test-user");
@@ -469,6 +534,10 @@
     }
 
     public void testCrashAndAutoRebind() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
 
         updateConstants(
                 "service_reconnect_backoff_sec=5"
@@ -547,6 +616,11 @@
      * Test the feature flag.
      */
     public void testFeatureDisabled() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
 
         updateConstants("sms_service_enabled=false");
@@ -563,6 +637,11 @@
     }
 
     public void testOomAdjustment() throws Throwable {
+        if (!isSmsCapable()) {
+            // device not supporting sms. cannot run the test.
+            return;
+        }
+
         installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, USER_SYSTEM);
         assertOomAdjustment(PACKAGE_A, PACKAGE_A_PROC, 200);
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index 572164a..46ee46f 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -236,6 +236,10 @@
             new InstallMultiple().addApk(DECLARE_PERMISSION_COMPAT_APK).run();
 
             new InstallMultiple().addApk(PERMISSION_DIFF_CERT_APK).run();
+
+            // Enable alert window permission so it can start activity in background
+            enableAlertWindowAppOp(DECLARE_PERMISSION_PKG);
+
             runDeviceTests(PERMISSION_DIFF_CERT_PKG, null);
         } finally {
             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
@@ -253,6 +257,9 @@
             new InstallMultiple(false).addApk(DECLARE_PERMISSION_APK).run();
             new InstallMultiple(false).addApk(DUPLICATE_DECLARE_PERMISSION_APK).run();
 
+            // Enable alert window permission so it can start activity in background
+            enableAlertWindowAppOp(DECLARE_PERMISSION_PKG);
+
             runDeviceTests(DUPLICATE_DECLARE_PERMISSION_PKG, null);
 
             // make sure behavior is preserved after reboot
@@ -285,4 +292,14 @@
                         + " -S 1024 /data/local/tmp/foo.apk");
         assertTrue("Error text", output.contains("Error"));
     }
+
+    private void enableAlertWindowAppOp(String pkgName) throws Exception {
+        getDevice().executeShellCommand(
+                "appops set " + pkgName + " android:system_alert_window allow");
+        String result = "No operations.";
+        while (result.contains("No operations")) {
+            result = getDevice().executeShellCommand(
+                    "appops get " + pkgName + " android:system_alert_window");
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 577d91c..78f2fff 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -382,7 +382,7 @@
                 42, null, 0, 0, 0);
 
         device.waitForIdle();
-        device.findObject(new UiSelector().text("Allow")).click();
+        device.findObject(new UiSelector().textMatches("(?i:Allow)")).click();
 
         // Verify that we now have access
         final GetResultActivity.Result res = activity.getResult();
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
index a46f214..429ea8b 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <permission android:name="com.android.cts.permissionWithSignature"
         android:protectionLevel="signature" />
     <uses-permission android:name="com.android.cts.permissionWithSignature" />
+    <!-- To enable the app to start activities from the background. -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
     <!-- A permission this app will not hold. -->
     <permission android:name="com.android.cts.permissionNotUsedWithSignature"
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
index 23df503..93f6f19 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk
index 11f8674..2faf2d4 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk
index fc9f02e..f0cc8b6 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk
index bf49682..d7e0826 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk
Binary files differ
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.bp
index 0a6d58f..94b6232 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.bp
@@ -20,11 +20,11 @@
     static_libs: [
         "androidx.legacy_legacy-support-v4",
         "ctstestrunner-axt",
+        "compatibility-device-util-axt",
         "androidx.test.rules",
         "truth-prebuilt",
         "ub-uiautomator",
     ],
-    sdk_version: "current",
     min_sdk_version: "21",
     // tag this module as a cts test artifact
     test_suites: [
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/non_main.xml b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/non_main.xml
new file mode 100644
index 0000000..557ddce
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/non_main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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:id="@+id/user_textview2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
new file mode 100644
index 0000000..c807dd3
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 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 static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests the {@link CrossProfileApps#startActivity(ComponentName, UserHandle)} API.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrossProfileAppsStartActivityTest {
+    private static final String PARAM_TARGET_USER = "TARGET_USER";
+    private static final String ID_USER_TEXTVIEW =
+            "com.android.cts.crossprofileappstest:id/user_textview";
+    private static final String ID_USER_TEXTVIEW2 =
+            "com.android.cts.crossprofileappstest:id/user_textview2";
+    private static final long TIMEOUT_WAIT_UI = TimeUnit.SECONDS.toMillis(10);
+
+    private CrossProfileApps mCrossProfileApps;
+    private UserHandle mTargetUser;
+    private Context mContext;
+    private UiDevice mDevice;
+    private long mUserSerialNumber;
+
+    @Before
+    public void setupCrossProfileApps() {
+        mContext = InstrumentationRegistry.getContext();
+        mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
+    }
+
+    @Before
+    public void wakeupDeviceAndPressHome() throws Exception {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.wakeUp();
+        mDevice.pressMenu();
+        mDevice.pressHome();
+    }
+
+    @Before
+    public void readTargetUser() {
+        Context context = InstrumentationRegistry.getContext();
+        Bundle arguments = InstrumentationRegistry.getArguments();
+        UserManager userManager = context.getSystemService(UserManager.class);
+        mUserSerialNumber = Long.parseLong(arguments.getString(PARAM_TARGET_USER));
+        mTargetUser = userManager.getUserForSerialNumber(mUserSerialNumber);
+        assertNotNull(mTargetUser);
+    }
+
+    @After
+    public void pressHome() {
+        mDevice.pressHome();
+    }
+
+    @Test
+    public void testCanStartMainActivity() {
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+                    crossProfileApps -> mCrossProfileApps.startActivity(
+                            MainActivity.getComponentName(mContext), mTargetUser));
+
+            // Look for the text view to verify that MainActivity is started.
+            UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
+                    TIMEOUT_WAIT_UI);
+            assertNotNull("Failed to start main activity in target user", textView);
+            assertEquals("Main Activity is started in wrong user",
+                    String.valueOf(mUserSerialNumber), textView.getText());
+        } catch (Exception e) {
+            fail("unable to start main activity via CrossProfileApps#startActivity: " + e);
+        }
+    }
+
+    @Test
+    public void testCanStartNonMainActivity() {
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+                    crossProfileApps -> mCrossProfileApps.startActivity(
+                            NonMainActivity.getComponentName(mContext), mTargetUser));
+
+            // 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 non-main activity via CrossProfileApps#startActivity: " + e);
+        }
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testCannotStartNotExportedActivity() throws Exception {
+        mCrossProfileApps.startActivity(
+                NonExportedActivity.getComponentName(mContext), mTargetUser);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testCannotStartActivityInOtherPackage() throws Exception {
+        mCrossProfileApps.startMainActivity(new ComponentName(
+                "com.android.cts.launcherapps.simpleapp",
+                "com.android.cts.launcherapps.simpleapp.SimpleActivity"),
+                mTargetUser
+        );
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
index 56ec466..4131a98 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
@@ -18,10 +18,32 @@
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.widget.TextView;
 
 public class NonMainActivity extends Activity {
 
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.non_main);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        TextView textView = findViewById(R.id.user_textview2);
+        textView.setText(Long.toString(getCurrentUserSerialNumber()));
+    }
+
     public static ComponentName getComponentName(Context context) {
         return new ComponentName(context, NonMainActivity.class);
     }
+
+    private long getCurrentUserSerialNumber() {
+        UserManager userManager = getSystemService(UserManager.class);
+        return userManager.getSerialNumberForUser(Process.myUserHandle());
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/api29/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAdmin/api29/AndroidManifest.xml
index a0bf3c0..326e61f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/api29/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/api29/AndroidManifest.xml
@@ -17,8 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.deviceadmin29" >
 
-    <!-- STOPSHIP(b/114173216): Uncomment this once Q's API level is finalized -->
-    <!--<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="29"/>-->
+    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="29"/>
 
     <application
         android:testOnly="true">
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
index ca8e782..fefe9cd 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
@@ -48,6 +48,8 @@
     private static final String SIMPLE_PRE_M_APP_PACKAGE_NAME =
             "com.android.cts.launcherapps.simplepremapp";
     private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+    private static final String CUSTOM_PERM_A_NAME = "com.android.cts.permissionapp.permA";
+    private static final String CUSTOM_PERM_B_NAME = "com.android.cts.permissionapp.permB";
     private static final String DEVELOPMENT_PERMISSION = "android.permission.INTERACT_ACROSS_USERS";
 
     private static final String PERMISSIONS_ACTIVITY_NAME
@@ -159,6 +161,22 @@
         assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
     }
 
+    public void testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted() throws Exception {
+        assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                CUSTOM_PERM_A_NAME);
+        assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED,
+                CUSTOM_PERM_B_NAME);
+
+        /*
+         * CUSTOM_PERM_A_NAME and CUSTOM_PERM_B_NAME are in the same permission group and one is
+         * granted the other one is not.
+         *
+         * It should not be possible to get the permission that was denied via policy granted by
+         * requesting it.
+         */
+        assertPermissionRequest(PackageManager.PERMISSION_DENIED, null, CUSTOM_PERM_B_NAME);
+    }
+
     @Suppress // Flakey.
     public void testPermissionPrompts() throws Exception {
         // register a crash watcher
@@ -252,16 +270,21 @@
     }
 
     private void assertPermissionRequest(int expected, String buttonResource) throws Exception {
+        assertPermissionRequest(expected, buttonResource, PERMISSION_NAME);
+    }
+
+    private void assertPermissionRequest(int expected, String buttonResource, String permission)
+            throws Exception {
         Intent launchIntent = new Intent();
         launchIntent.setComponent(new ComponentName(PERMISSION_APP_PACKAGE_NAME,
                 PERMISSIONS_ACTIVITY_NAME));
-        launchIntent.putExtra(EXTRA_PERMISSION, PERMISSION_NAME);
+        launchIntent.putExtra(EXTRA_PERMISSION, permission);
         launchIntent.setAction(ACTION_REQUEST_PERMISSION);
         launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
         mContext.startActivity(launchIntent);
         pressPermissionPromptButton(buttonResource);
         assertEquals(expected, mReceiver.waitForBroadcast());
-        assertEquals(expected, mPackageManager.checkPermission(PERMISSION_NAME,
+        assertEquals(expected, mPackageManager.checkPermission(permission,
                 PERMISSION_APP_PACKAGE_NAME));
     }
 
@@ -286,11 +309,14 @@
     }
 
     private void assertSetPermissionGrantState(int value) throws Exception {
+        assertSetPermissionGrantState(value, PERMISSION_NAME);
+    }
+    private void assertSetPermissionGrantState(int value, String permission) throws Exception {
         mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME,
+                PERMISSION_APP_PACKAGE_NAME, permission,
                 value);
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+                PERMISSION_APP_PACKAGE_NAME, permission),
                 value);
     }
 
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
index e6bf9c0..1cbf5b2 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
@@ -53,14 +53,6 @@
                 InstallSystemUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND);
     }
 
-    public void testInstallUpdate_failWrongVersion() throws InterruptedException {
-        assertUpdateError(
-                "wrongVersion.zip",
-                isDeviceAB()
-                        ? InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION
-                        : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
-    }
-
     public void testInstallUpdate_failNoZipOtaFile() throws InterruptedException {
         assertUpdateError("notZip.zi",
                 isDeviceAB()
diff --git a/hostsidetests/devicepolicy/res/wrongVersion.zip b/hostsidetests/devicepolicy/res/wrongVersion.zip
deleted file mode 100644
index 3dfcfa8..0000000
--- a/hostsidetests/devicepolicy/res/wrongVersion.zip
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index 9a97b59..0c8bea3 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -21,6 +21,7 @@
     private static final String TEST_PACKAGE = "com.android.cts.crossprofileappstest";
     private static final String NON_TARGET_USER_TEST_CLASS = ".CrossProfileAppsNonTargetUserTest";
     private static final String TARGET_USER_TEST_CLASS = ".CrossProfileAppsTargetUserTest";
+    private static final String START_ACTIVITY_TEST_CLASS = ".CrossProfileAppsStartActivityTest";
     private static final String PARAM_TARGET_USER = "TARGET_USER";
     private static final String EXTRA_TEST_APK = "CtsCrossProfileAppsTests.apk";
     private static final String SIMPLE_APP_APK ="CtsSimpleApp.apk";
@@ -46,6 +47,7 @@
             installRequiredApps(mSecondaryUserId);
             mCanTestMultiUser = true;
         }
+        waitForBroadcastIdle();
     }
 
     private void installRequiredApps(int userId)
@@ -72,6 +74,13 @@
         verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
     }
 
+    public void testStartActivity() throws Exception {
+        if (!mHasManagedUserFeature) {
+            return;
+        }
+        verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, START_ACTIVITY_TEST_CLASS);
+    }
+
     public void testPrimaryUserToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 8de045c..56014ea 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -570,6 +570,16 @@
         executeDeviceTestMethod(".PermissionsTest", "testPermissionMixedPolicies");
     }
 
+    public void testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted()
+            throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppPermissionAppAsUser();
+        executeDeviceTestMethod(".PermissionsTest",
+                "testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted");
+    }
+
     // Test flakey; suppressed.
 //    public void testPermissionPrompts() throws Exception {
 //        if (!mHasFeature) {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index f26733c..a42799e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -143,6 +143,9 @@
     }
 
     public void testLockScreenInfo() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
         executeDeviceOwnerTest("LockScreenInfoTest");
         assertMetricsLogged(getDevice(), () -> {
             executeDeviceTestMethod(".LockScreenInfoTest", "testSetAndGetLockInfo");
@@ -152,7 +155,7 @@
     }
 
     public void testWifi() throws Exception {
-        if (!hasDeviceFeature("android.hardware.wifi")) {
+        if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
             return;
         }
         executeDeviceOwnerTest("WifiTest");
@@ -549,6 +552,8 @@
                 Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(1)));
         // Reboot the device, so the security event IDs are re-set.
         rebootAndWaitUntilReady();
+        // Make sure BOOT_COMPLETED is completed before proceeding.
+        waitForBroadcastIdle();
         // First batch after reboot: retrieve and verify the events.
         executeDeviceTestMethod(".NetworkLoggingTest", "testNetworkLoggingAndRetrieval",
                 Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(1)));
@@ -825,7 +830,6 @@
         // TODO(b/130210665): replace this with use of NotificationListenerService to dismiss the
         // bug report request
         rebootAndWaitUntilReady();
-
     }
 
     public void testBluetoothRestriction() throws Exception {
@@ -1028,7 +1032,6 @@
             return;
         }
 
-        pushUpdateFileToDevice("wrongVersion.zip");
         pushUpdateFileToDevice("notZip.zi");
         pushUpdateFileToDevice("empty.zip");
         pushUpdateFileToDevice("wrongPayload.zip");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index 96927a9..fdf063ea 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -39,11 +39,13 @@
 
     private boolean mRemoveOwnerInTearDown;
     private int mDeviceOwnerUserId;
+    private boolean mHasManagedUserFeature;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
+        mHasManagedUserFeature = hasDeviceFeature("android.software.managed_users");
         mRemoveOwnerInTearDown = false;
         mDeviceOwnerUserId = mPrimaryUserId;
     }
@@ -127,7 +129,7 @@
 
     // Checks restrictions for managed profile.
     public void testUserRestrictions_managedProfileOwnerOnly() throws Exception {
-        if (!mHasFeature || !mSupportsMultiUser) {
+        if (!mHasFeature || !mSupportsMultiUser || !mHasManagedUserFeature) {
             return;
         }
 
@@ -235,7 +237,7 @@
      * affect all users.
      */
     public void testUserRestrictions_ProfileGlobalRestrictionsAsPo() throws Exception {
-        if (!mHasFeature || !mSupportsMultiUser) {
+        if (!mHasFeature || !mSupportsMultiUser || !mHasManagedUserFeature) {
             return;
         }
         // Set PO on user 0
diff --git a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
index 7678096..f4362f0 100644
--- a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
+++ b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
@@ -578,10 +578,12 @@
         setupLayer(GLES_LAYER_B_LIB, GLES_LAYERS_APP);
 
         // Copy them over to our DEBUG app
-        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", "run-as", DEBUG_APP,
-                                  "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'");
-        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_B_LIB, "|", "run-as", DEBUG_APP,
-                                  "sh", "-c", "\'cat", ">", GLES_LAYER_B_LIB, ";", "chmod", "700", GLES_LAYER_B_LIB + "\'");
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|",
+            "run-as", DEBUG_APP, "--user", Integer.toString(mDevice.getCurrentUser()),
+            "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'");
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_B_LIB, "|",
+            "run-as", DEBUG_APP, "--user", Integer.toString(mDevice.getCurrentUser()),
+            "sh", "-c", "\'cat", ">", GLES_LAYER_B_LIB, ";", "chmod", "700", GLES_LAYER_B_LIB + "\'");
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
@@ -760,10 +762,12 @@
         setupLayer(GLES_LAYER_B_LIB, GLES_LAYERS_APP);
 
         // Copy them over to our DEBUG app
-        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", "run-as", DEBUG_APP,
-                                 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'");
-        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_B_LIB, "|", "run-as", DEBUG_APP,
-                                 "sh", "-c", "\'cat", ">", GLES_LAYER_B_LIB, ";", "chmod", "700", GLES_LAYER_B_LIB + "\'");
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|",
+            "run-as", DEBUG_APP, "--user", Integer.toString(mDevice.getCurrentUser()),
+            "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'");
+        mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_B_LIB, "|",
+            "run-as", DEBUG_APP, "--user", Integer.toString(mDevice.getCurrentUser()),
+            "sh", "-c", "\'cat", ">", GLES_LAYER_B_LIB, ";", "chmod", "700", GLES_LAYER_B_LIB + "\'");
 
         // Enable layerB with system properties
         mDevice.executeAdbCommand("shell", "setprop", "debug.gles.layers " + GLES_LAYER_B_LIB);
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 3d16367..24a96a7 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
@@ -482,7 +482,7 @@
             }
             List<ConformanceEntry> entries = mResults.get(mPath);
             for (ConformanceEntry ce : entries) {
-                if (!"true".equals(ce.mStatus)) {
+                if (!"true".equals(ce.mStatus) && !"unsupported".equals(ce.mStatus)) {
                     Assert.fail(ce.toString());
                 }
             }
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
index 0411f75..41cbeab 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
@@ -95,21 +95,4 @@
         Assert.assertTrue("User count should be greater than max users due to added guest user",
                 userCount > maxUsers);
     }
-
-    @Test
-    public void testCantAddMoreThanOneGuestUser() throws Exception {
-        if (!isAutomotiveDevice()) {
-            return;
-        }
-        boolean failedToCreateGuestUser = false;
-        if (getGuestUser() == -1) {
-            createGuestUser();
-        }
-        try {
-            createGuestUser();
-        } catch (IllegalStateException e) {
-            failedToCreateGuestUser = true;
-        }
-        Assert.assertTrue("Should failed creating second guest user", failedToCreateGuestUser);
-    }
 }
diff --git a/hostsidetests/os/src/android/os/cts/PowerManagerTests.java b/hostsidetests/os/src/android/os/cts/PowerManagerTests.java
index d0f4413..2322211 100644
--- a/hostsidetests/os/src/android/os/cts/PowerManagerTests.java
+++ b/hostsidetests/os/src/android/os/cts/PowerManagerTests.java
@@ -66,6 +66,8 @@
      */
     @Test
     public void testCachedProcessReleasesWakeLock() throws Exception {
+        // Turn screen on and unlock keyguard
+        mDevice.executeShellCommand("input keyevent 26; input keyevent 82");
         makeCachedProcess(PACKAGE_NAME);
         // Short wait before checking cached process
         Thread.sleep(1000);
@@ -73,9 +75,9 @@
         assertTrue(CACHED_PATTERN.matcher(processes).find());
 
         String wakelocks = mDevice.executeShellCommand(GET_WAKE_LOCKS_CMD);
-        assertTrue("Wake lock not acquired", WAKE_LOCK_ACQUIRED_PATTERN.matcher(wakelocks).find());
         assertFalse("Wake lock disabled early",
                 WAKE_LOCK_DISABLED_PATTERN.matcher(wakelocks).find());
+        assertTrue("Wake lock not acquired", WAKE_LOCK_ACQUIRED_PATTERN.matcher(wakelocks).find());
 
         // ActivityManager will inform PowerManager of processes with idle uids.
         // PowerManager will disable wakelocks held by such processes.
diff --git a/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java b/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java
index c7a75a7..1e8040d 100644
--- a/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java
+++ b/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java
@@ -292,4 +292,42 @@
 
         // At this point, the host test driver will reboot the device to complete the uninstall.
     }
+
+    /**
+     * Tests that apex update expires existing rollbacks for that apex.
+     * Enable rollback phase.
+     */
+    @Test
+    public void testApexRollbackExpirationEnableRollback() throws Exception {
+        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+
+        Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit();
+
+        // At this point, the host test driver will reboot the device and run
+        // testApexRollbackExpirationUpdateApex().
+    }
+
+    /**
+     * Tests that apex update expires existing rollbacks for that apex.
+     * Update apex phase.
+     */
+    @Test
+    public void testApexRollbackExpirationUpdateApex() throws Exception {
+        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(Utils.getAvailableRollback(TestApp.Apex)).isNotNull();
+        Install.single(TestApp.Apex3).setStaged().commit();
+
+        // At this point, the host test driver will reboot the device and run
+        // testApexRollbackExpirationConfirmExpiration().
+    }
+
+    /**
+     * Tests that apex update expires existing rollbacks for that apex.
+     * Confirm expiration phase.
+     */
+    @Test
+    public void testApexRollbackExpirationConfirmExpiration() throws Exception {
+        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        assertThat(Utils.getAvailableRollback(TestApp.Apex)).isNull();
+    }
 }
diff --git a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
index e9192dd..60e154c 100644
--- a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
+++ b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
@@ -147,4 +147,20 @@
         getDevice().reboot();
         run("testApexAndApkConfirmRollback");
     }
+
+    /**
+     * Tests that apex update expires existing rollbacks for that apex.
+     */
+    @Test
+    public void testApexRollbackExpiration() throws Exception {
+        assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
+
+        uninstallShimApexIfNecessary();
+        run("testApexRollbackExpirationEnableRollback");
+        getDevice().reboot();
+        run("testApexRollbackExpirationUpdateApex");
+        getDevice().reboot();
+        run("testApexRollbackExpirationConfirmExpiration");
+    }
+
 }
diff --git a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
index e81383b..19ad85f 100644
--- a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
@@ -178,13 +178,17 @@
         }
     }
 
-    private static final String QUALCOMM_SOC_FILE = "/sys/devices/soc0/chip_name";
-
     private String getHardware() throws Exception {
         String hardware = "DEFAULT";
-        /* lookup for Qualcomm devices */
-        if (doesFileExist(QUALCOMM_SOC_FILE)) {
-            hardware = mDevice.pullFileContents(QUALCOMM_SOC_FILE).trim();
+        String cpuInfo = mDevice.pullFileContents("/proc/cpuinfo");
+
+        for (String line : cpuInfo.split("\n")) {
+            /* Qualcomm SoCs */
+            if (line.startsWith("Hardware")) {
+                String[] hardwareLine = line.split(" ");
+                hardware = hardwareLine[hardwareLine.length - 1];
+                break;
+            }
         }
         /* TODO lookup other hardware as we get exemption requests. */
         return hardwareMitigations.containsKey(hardware) ? hardware : "DEFAULT";
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 919c3ad..95e1721 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -157,6 +157,11 @@
         <option name="push" value="CVE-2017-13253->/data/local/tmp/CVE-2017-13253" />
 
         <!--__________________-->
+        <!-- Bulletin 2018-06 -->
+        <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+        <option name="push" value="CVE-2018-9344->/data/local/tmp/CVE-2018-9344" />
+
+        <!--__________________-->
         <!-- Bulletin 2018-07 -->
         <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
         <option name="push" value="CVE-2018-9424->/data/local/tmp/CVE-2018-9424" />
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk
index e9ffee4..0bd5a7c 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
-LOCAL_COMPATIBILITY_SUITE := cts sts
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
 LOCAL_CTS_TEST_PACKAGE := android.security.cts
 
 LOCAL_ARM_MODE := arm
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/Android.mk
new file mode 100644
index 0000000..a806207
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2019 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
+
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2018-9344
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils \
+    libbinder \
+    libhidlbase \
+    libhardware \
+    android.hardware.cas.native@1.0 \
+    android.hardware.cas@1.0 \
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS = -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/poc.cpp
new file mode 100644
index 0000000..b5153e5
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/poc.cpp
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2019 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 <android/hardware/cas/1.0/IMediaCasService.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <binder/ProcessState.h>
+
+using namespace android;
+using hardware::hidl_vec;
+using hardware::Return;
+using namespace hardware::cas::V1_0;
+using android::hardware::Void;
+using android::hardware::cas::native::V1_0::IDescrambler;
+
+class MyMediaCasListener : public ICasListener {
+ public:
+  virtual Return<void> onEvent(int32_t, int32_t, const hidl_vec<uint8_t>&) override {
+    return Void();
+  }
+};
+
+static const int32_t sClearKeySystemId = 0xF6D8;
+static sp<IDescramblerBase> descramblerBase;
+
+static void* thread1(void*) {
+  Return<Status> returnStatus(Status::OK);
+  std::vector<uint8_t> sessionId;
+  sessionId.push_back(1);
+
+  returnStatus = descramblerBase->setMediaCasSession(sessionId);
+
+  return NULL;
+}
+
+int main() {
+  sp<ICas> cas;
+  Status status;
+  sp<IDescrambler> descrambler;
+  Return<Status> returnStatus(Status::OK);
+  Return<sp<IDescramblerBase>> returnDescrambler(NULL);
+  std::vector<uint8_t> sessionId;
+
+  android::ProcessState::self()->startThreadPool();
+
+  sp<IMediaCasService> casService = IMediaCasService::getService("default");
+  if (casService == NULL) {
+    return EXIT_FAILURE;
+  }
+
+  sp<ICasListener> listener;
+  cas = casService->createPlugin(sClearKeySystemId, listener);
+  if (cas == NULL) {
+    return EXIT_FAILURE;
+  }
+
+  auto returnVoid = cas->openSession(
+      [&status, &sessionId](Status _status,
+                            const hidl_vec<uint8_t>& _sessionId) {
+        status = _status;
+        sessionId = _sessionId;
+      });
+  if (!returnVoid.isOk() || status != Status::OK) {
+    return EXIT_FAILURE;
+  }
+
+  returnDescrambler = casService->createDescrambler(sClearKeySystemId);
+  if (!returnDescrambler.isOk()) {
+    return EXIT_FAILURE;
+  }
+
+  descramblerBase = (sp<IDescramblerBase>)returnDescrambler;
+
+  if (descramblerBase == NULL) {
+    return EXIT_FAILURE;
+  }
+
+  returnStatus = descramblerBase->setMediaCasSession(sessionId);
+
+  if (!returnStatus.isOk() || (Status)returnStatus != Status::OK) {
+    return EXIT_FAILURE;
+  }
+
+  descrambler = IDescrambler::castFrom(descramblerBase);
+
+  if (descrambler == NULL) {
+    return EXIT_FAILURE;
+  }
+
+  pthread_t pt;
+  pthread_create(&pt, NULL, thread1, NULL);
+
+  descramblerBase->release();
+
+  return EXIT_FAILURE;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java
index b1ed666..99a4692 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java
@@ -27,6 +27,7 @@
      */
     @SecurityTest(minPatchLevel = "2018-04")
     public void testPocCVE_2017_13286() throws Exception {
+        getOomCatcher().setHighMemoryTest();
         LaunchSomeWhere.launchSomeWhere("CVE_2017_13286", getDevice());
     }
 
@@ -36,6 +37,7 @@
      */
     @SecurityTest(minPatchLevel = "2018-04")
     public void testPocCVE_2017_13288() throws Exception {
+        getOomCatcher().setHighMemoryTest();
         LaunchSomeWhere.launchSomeWhere("CVE_2017_13288", getDevice());
     }
 
@@ -45,6 +47,7 @@
      */
     @SecurityTest(minPatchLevel = "2018-04")
     public void testPocCVE_2017_13289() throws Exception {
+        getOomCatcher().setHighMemoryTest();
         LaunchSomeWhere.launchSomeWhere("CVE_2017_13289", getDevice());
     }
 }
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java
index 9364d28..69a4ed5 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java
@@ -27,6 +27,7 @@
      */
     @SecurityTest(minPatchLevel = "2018-05")
     public void testPocCVE_2017_13315() throws Exception {
+        getOomCatcher().setHighMemoryTest();
         LaunchSomeWhere.launchSomeWhere("CVE_2017_13315", getDevice());
     }
 
@@ -36,6 +37,7 @@
      */
     @SecurityTest(minPatchLevel = "2018-05")
     public void testPocCVE_2017_13312() throws Exception {
+        getOomCatcher().setHighMemoryTest();
         LaunchSomeWhere.launchSomeWhere("CVE_2017_13312", getDevice());
     }
 }
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java
index a678ab3..9278af4 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java
@@ -45,4 +45,16 @@
         "pm list package com.emoji.keyboard.touchpal", getDevice());
     assertFalse(result.contains("com.emoji.keyboard.touchpal"));
   }
+
+    /**
+     *  b/73172817
+     */
+    @SecurityTest
+    public void testPocCVE_2018_9344() throws Exception {
+        AdbUtils.runCommandLine("logcat -c", getDevice());
+        AdbUtils.runPoc("CVE-2018-9344", getDevice(), 30);
+        String output = AdbUtils.runCommandLine("logcat -d", getDevice());
+        assertNotMatchesMultiLine(">>> /vendor/bin/hw/android.hardware.cas@1.0-service <<<" +
+                ".*?signal 11 \\(SIGSEGV\\)", output);
+    }
 }
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java
index 3ab1829..9ce7e39 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java
@@ -25,6 +25,8 @@
 import static org.junit.Assert.*;
 
 public class RegexUtils {
+    private static final int TIMEOUT_DURATION = 20 * 60_000; // 20 minutes
+    private static final int WARNING_THRESHOLD = 1000; // 1 second
     private static final int CONTEXT_RANGE = 100; // chars before/after matched input string
 
     public static void assertContains(String pattern, String input) throws Exception {
@@ -46,7 +48,7 @@
     private static void assertFind(
             String pattern, String input, boolean shouldFind, boolean multiline) {
         // The input string throws an error when used after the timeout
-        TimeoutCharSequence timedInput = new TimeoutCharSequence(input, 60_000); // 1 minute
+        TimeoutCharSequence timedInput = new TimeoutCharSequence(input, TIMEOUT_DURATION);
         Matcher matcher = null;
         if (multiline) {
             // DOTALL lets .* match line separators
@@ -62,7 +64,7 @@
             boolean found = matcher.find();
             long duration = System.currentTimeMillis() - start;
 
-            if (duration > 1000) { // one second
+            if (duration > WARNING_THRESHOLD) {
                 // Provide a warning to the test developer that their regex should be optimized.
                 CLog.logAndDisplay(LogLevel.WARN, "regex match took " + duration + "ms.");
             }
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index 68f06d5..479f18d 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -141,6 +141,7 @@
             if (deviceToReboot != null) {
                 deviceToReboot.nonBlockingReboot();
                 deviceToReboot.waitForDeviceAvailable();
+                updateKernelStartTime();
             }
         }
         fail("\"" + ptr + "\" is an exposed kernel pointer.");
@@ -192,6 +193,12 @@
         return Long.parseLong(uptime.substring(0, uptime.indexOf('.')));
     }
 
+    public void safeReboot() throws DeviceNotAvailableException {
+        getDevice().nonBlockingReboot();
+        getDevice().waitForDeviceAvailable();
+        updateKernelStartTime();
+    }
+
     /**
      * Allows a test to pass if called after a planned reboot.
      */
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index afe5737..2638729 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -49,6 +49,7 @@
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.time.Duration;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
@@ -319,18 +320,8 @@
         int sessionId = stageSingleApk(
                 "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
         assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
-        waitForIsReadyBroadcast(sessionId);
-        assertSessionReady(sessionId);
-        storeSessionId(sessionId);
-    }
-
-    @Test
-    public void testStagedInstallDowngrade_DowngradeNotRequested_Fails_VerifyPostReboot()
-            throws Exception {
-        int sessionId = retrieveLastSessionId();
-        assertSessionFailed(sessionId);
-        // INSTALL_REQUEST_DOWNGRADE wasn't set, so app shouldn't be downgraded.
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+        PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
+        assertThat(sessionInfo).isStagedSessionFailed();
     }
 
     @Test
@@ -614,6 +605,7 @@
     }
 
     private static StageSessionResult stageDowngradeSingleApk(String apkFileName) throws Exception {
+        Log.i(TAG, "Staging a downgrade of " + apkFileName);
         PackageInstaller packageInstaller = getPackageInstaller();
 
         Pair<Integer, PackageInstaller.Session> sessionPair =
@@ -625,6 +617,7 @@
     }
 
     private static StageSessionResult stageSingleApk(String apkFileName) throws Exception {
+        Log.i(TAG, "Staging an install of " + apkFileName);
         PackageInstaller packageInstaller = getPackageInstaller();
 
         Pair<Integer, PackageInstaller.Session> sessionPair =
@@ -647,6 +640,7 @@
     }
 
     private static StageSessionResult stageMultipleApks(String... apkFileNames) throws Exception {
+        Log.i(TAG, "Staging an install of " + Arrays.toString(apkFileNames));
         PackageInstaller packageInstaller = getPackageInstaller();
         int multiPackageSessionId = createStagedSession(packageInstaller, true, false, false);
         PackageInstaller.Session multiPackageSession = packageInstaller.openSession(
@@ -818,9 +812,7 @@
         try {
 
             PackageInstaller.SessionInfo info = waitForBroadcast(sessionId);
-            assertThat(info.isStagedSessionFailed()).isTrue();
-            assertThat(info.isStagedSessionReady()).isFalse();
-            assertThat(info.isStagedSessionApplied()).isFalse();
+            assertThat(info).isStagedSessionFailed();
         } catch (Exception e) {
             throw new AssertionError(e);
         }
@@ -830,10 +822,7 @@
         Log.i(TAG, "Waiting for session " + sessionId + " to be ready");
         try {
             PackageInstaller.SessionInfo info = waitForBroadcast(sessionId);
-            assertThat(info.isStagedSessionReady()).isTrue();
-            assertThat(info.isStagedSessionApplied()).isFalse();
-            assertWithMessage(info.getStagedSessionErrorMessage())
-                    .that(info.isStagedSessionFailed()).isFalse();
+            assertThat(info).isStagedSessionReady();
         } catch (Exception e) {
             throw new AssertionError(e);
         }
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index 249c36b..ace6138 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -140,8 +140,6 @@
     @Test
     public void testStagedInstallDowngrade_DowngradeNotRequested_Fails() throws Exception {
         runPhase("testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit");
-        getDevice().reboot();
-        runPhase("testStagedInstallDowngrade_DowngradeNotRequested_Fails_VerifyPostReboot");
     }
 
     @Test
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.bp b/hostsidetests/statsd/apps/statsdapp/Android.bp
index 20c1141..01b07ec 100644
--- a/hostsidetests/statsd/apps/statsdapp/Android.bp
+++ b/hostsidetests/statsd/apps/statsdapp/Android.bp
@@ -28,6 +28,7 @@
     name: "CtsStatsdApp",
     defaults: ["cts_defaults"],
     platform_apis: true,
+    min_sdk_version: "24",
     srcs: [
         "src/**/*.java",
         ":statslog-statsd-cts-java-gen",
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 9b1ce2b..f38bf33 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -199,6 +199,8 @@
         return StatsdConfig.newBuilder().setId(CONFIG_ID)
                 .addAllowedLogSource("AID_SYSTEM")
                 .addAllowedLogSource("AID_BLUETOOTH")
+                // TODO(b/134091167): Fix bluetooth source name issue in Auto platform.
+                .addAllowedLogSource("com.android.bluetooth")
                 .addAllowedLogSource(DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE);
     }
 
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index 50211c6..70777ef 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -121,6 +121,133 @@
         assertEquals(1, countData.getData(0).getBucketInfo(0).getCount());
     }
 
+    public void testEventCountWithConditionAndActivation() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+        int startMatcherId = 1;
+        int startMatcherLabel = 1;
+        int endMatcherId = 2;
+        int endMatcherLabel = 2;
+        int whatMatcherId = 3;
+        int whatMatcherLabel = 3;
+        int conditionId = 4;
+        int activationMatcherId = 5;
+        int activationMatcherLabel = 5;
+        int ttlSec = 5;
+
+        StatsdConfigProto.AtomMatcher whatMatcher =
+                MetricsUtils.appBreadcrumbMatcherWithLabel(whatMatcherId, whatMatcherLabel);
+
+        StatsdConfigProto.AtomMatcher predicateStartMatcher =
+                MetricsUtils.startAtomMatcherWithLabel(startMatcherId, startMatcherLabel);
+
+        StatsdConfigProto.AtomMatcher predicateEndMatcher =
+                MetricsUtils.stopAtomMatcherWithLabel(endMatcherId, endMatcherLabel);
+
+        StatsdConfigProto.AtomMatcher activationMatcher =
+                MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
+                                                           activationMatcherLabel);
+
+        StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder()
+                .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder()
+                        .setStart(startMatcherId)
+                        .setStop(endMatcherId)
+                        .setCountNesting(false))
+                .setId(conditionId)
+                .build();
+
+        StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+                .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
+                        .setId(MetricsUtils.COUNT_METRIC_ID)
+                        .setBucket(StatsdConfigProto.TimeUnit.CTS)
+                        .setWhat(whatMatcherId)
+                        .setCondition(conditionId)
+                )
+                .addAtomMatcher(whatMatcher)
+                .addAtomMatcher(predicateStartMatcher)
+                .addAtomMatcher(predicateEndMatcher)
+                .addAtomMatcher(activationMatcher)
+                .addPredicate(p)
+                .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
+                        .setMetricId(MetricsUtils.COUNT_METRIC_ID)
+                        .setActivationType(StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY)
+                        .addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
+                                .setAtomMatcherId(activationMatcherId)
+                                .setTtlSeconds(ttlSec)));
+
+        uploadConfig(builder);
+
+        // Activate the metric.
+        doAppBreadcrumbReported(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Set the condition to true.
+        doAppBreadcrumbReportedStart(startMatcherLabel);
+        Thread.sleep(10);
+
+        // Log an event that should be counted. Bucket 1 Count 1.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Log an event that should be counted. Bucket 1 Count 2.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Set the condition to false.
+        doAppBreadcrumbReportedStop(endMatcherLabel);
+        Thread.sleep(10);
+
+        // Log an event that should not be counted because condition is false.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Log an event that should not be counted.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Condition to true again.
+        doAppBreadcrumbReportedStart(startMatcherLabel);
+        Thread.sleep(10);
+
+        // Event should not be counted, metric is still not active.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Activate the metric.
+        doAppBreadcrumbReported(activationMatcherLabel);
+        Thread.sleep(10);
+
+        //  Log an event that should be counted.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Log an event that should not be counted.
+        doAppBreadcrumbReported(whatMatcherLabel);
+        Thread.sleep(10);
+
+        // Wait for the metrics to propagate to statsd.
+        Thread.sleep(2000);
+
+        StatsLogReport metricReport = getStatsLogReport();
+        assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertTrue(metricReport.hasCountMetrics());
+        assertFalse(metricReport.getIsActive());
+
+        StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
+        assertEquals(1, countData.getDataCount());
+        assertEquals(2, countData.getData(0).getBucketInfoCount());
+        assertEquals(2, countData.getData(0).getBucketInfo(0).getCount());
+        assertEquals(1, countData.getData(0).getBucketInfo(1).getCount());
+    }
+
     public void testPartialBucketCountMetric() throws Exception {
         if (statsdDisabled()) {
             return;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java
index 8e74386..75d86a8 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.metric;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.cts.statsd.atom.DeviceAtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -32,6 +34,9 @@
 import com.android.os.StatsLog.DurationBucketInfo;
 import com.android.os.StatsLog.StatsLogReport;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+import com.google.common.collect.Range;
 
 import java.util.List;
 
@@ -46,17 +51,19 @@
         if (statsdDisabled()) {
             return;
         }
-        // Add AtomMatcher's.
+
+        final int label = 1;
+        // Add AtomMatchers.
         AtomMatcher startAtomMatcher =
-            MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
+            MetricsUtils.startAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, label);
         AtomMatcher stopAtomMatcher =
-            MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
+            MetricsUtils.stopAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, label);
 
         StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
         builder.addAtomMatcher(startAtomMatcher);
         builder.addAtomMatcher(stopAtomMatcher);
 
-        // Add Predicate's.
+        // Add Predicates.
         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
@@ -79,28 +86,412 @@
         uploadConfig(builder);
 
         // Create AppBreadcrumbReported Start/Stop events.
-        doAppBreadcrumbReportedStart(1);
+        doAppBreadcrumbReportedStart(label);
         Thread.sleep(2000);
-        doAppBreadcrumbReportedStop(1);
+        doAppBreadcrumbReportedStop(label);
 
         // Wait for the metrics to propagate to statsd.
         Thread.sleep(2000);
 
         StatsLogReport metricReport = getStatsLogReport();
-        assertEquals(MetricsUtils.DURATION_METRIC_ID, metricReport.getMetricId());
-        assertTrue(metricReport.hasDurationMetrics());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertThat(metricReport.hasDurationMetrics()).isTrue();
         StatsLogReport.DurationMetricDataWrapper durationData
                 = metricReport.getDurationMetrics();
-        assertTrue(durationData.getDataCount() == 1);
-        assertTrue(durationData.getData(0).getBucketInfo(0).getDurationNanos() > 0);
-        assertTrue(durationData.getData(0).getBucketInfo(0).getDurationNanos() < 1e10);
+        assertThat(durationData.getDataCount()).isEqualTo(1);
+        assertThat(durationData.getData(0).getBucketInfo(0).getDurationNanos())
+                .isIn(Range.open(0L, (long)1e9));
+    }
+
+    public void testDurationMetricWithCondition() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        final int durationLabel = 1;
+        final int conditionLabel = 2;
+
+        // Add AtomMatchers.
+        AtomMatcher startAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, durationLabel);
+        AtomMatcher stopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
+        AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_B_MATCH_START_ID, conditionLabel);
+        AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID, conditionLabel);
+
+        StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+                .addAtomMatcher(startAtomMatcher)
+                .addAtomMatcher(stopAtomMatcher)
+                .addAtomMatcher(conditionStartAtomMatcher)
+                .addAtomMatcher(conditionStopAtomMatcher);
+
+        // Add Predicates.
+        SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+                .build();
+        Predicate predicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("Predicate"))
+                                  .setSimplePredicate(simplePredicate)
+                                  .build();
+
+        SimplePredicate conditionSimplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
+                .build();
+        Predicate conditionPredicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("ConditionPredicate"))
+                                  .setSimplePredicate(conditionSimplePredicate)
+                                  .build();
+
+        builder
+            .addPredicate(predicate)
+            .addPredicate(conditionPredicate);
+
+        // Add DurationMetric.
+        builder
+                .addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
+                        .setId(MetricsUtils.DURATION_METRIC_ID)
+                        .setWhat(predicate.getId())
+                        .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
+                        .setBucket(StatsdConfigProto.TimeUnit.CTS)
+                        .setCondition(conditionPredicate.getId())
+                );
+
+        // Upload config.
+        uploadConfig(builder);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Set the condition to true.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // Start counted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop counted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Set the condition to false.
+        doAppBreadcrumbReportedStop(conditionLabel);
+        Thread.sleep(10);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+        StatsLogReport metricReport = getStatsLogReport();
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertThat(metricReport.hasDurationMetrics()).isTrue();
+        StatsLogReport.DurationMetricDataWrapper durationData
+                = metricReport.getDurationMetrics();
+        assertThat(durationData.getDataCount()).isEqualTo(1);
+        long totalDuration = durationData.getData(0).getBucketInfoList().stream()
+                .mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
+                .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long)1e9)))
+                .sum();
+        assertThat(totalDuration).isIn(Range.open((long)2e9, (long)3e9));
+    }
+
+    public void testDurationMetricWithActivation() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        final int activationMatcherId = 5;
+        final int activationMatcherLabel = 5;
+        final int ttlSec = 5;
+        final int durationLabel = 1;
+
+        // Add AtomMatchers.
+        AtomMatcher startAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, durationLabel);
+        AtomMatcher stopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
+        StatsdConfigProto.AtomMatcher activationMatcher =
+                MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
+                                                           activationMatcherLabel);
+
+        StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+                .addAtomMatcher(startAtomMatcher)
+                .addAtomMatcher(stopAtomMatcher)
+                .addAtomMatcher(activationMatcher);
+
+        // Add Predicates.
+        SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+                .build();
+        Predicate predicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("Predicate"))
+                                  .setSimplePredicate(simplePredicate)
+                                  .build();
+        builder.addPredicate(predicate);
+
+        // Add DurationMetric.
+        builder
+                .addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
+                        .setId(MetricsUtils.DURATION_METRIC_ID)
+                        .setWhat(predicate.getId())
+                        .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
+                        .setBucket(StatsdConfigProto.TimeUnit.CTS)
+                )
+                .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
+                        .setMetricId(MetricsUtils.DURATION_METRIC_ID)
+                        .addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
+                                .setAtomMatcherId(activationMatcherId)
+                                .setActivationType(
+                                        StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY)
+                                .setTtlSeconds(ttlSec)));
+
+        // Upload config.
+        uploadConfig(builder);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Activate the metric.
+        doAppBreadcrumbReported(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Start counted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop counted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+        StatsLogReport metricReport = getStatsLogReport();
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertThat(metricReport.hasDurationMetrics()).isTrue();
+        StatsLogReport.DurationMetricDataWrapper durationData
+                = metricReport.getDurationMetrics();
+        assertThat(durationData.getDataCount()).isEqualTo(1);
+        long totalDuration = durationData.getData(0).getBucketInfoList().stream()
+                .mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
+                .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long)1e9)))
+                .sum();
+        assertThat(totalDuration).isIn(Range.open((long)2e9, (long)3e9));
+    }
+
+    public void testDurationMetricWithConditionAndActivation() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        final int durationLabel = 1;
+        final int conditionLabel = 2;
+        final int activationMatcherId = 5;
+        final int activationMatcherLabel = 5;
+        final int ttlSec = 5;
+
+        // Add AtomMatchers.
+        AtomMatcher startAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, durationLabel);
+        AtomMatcher stopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
+        AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_B_MATCH_START_ID, conditionLabel);
+        AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID, conditionLabel);
+        StatsdConfigProto.AtomMatcher activationMatcher =
+                MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
+                                                           activationMatcherLabel);
+
+        StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+                .addAtomMatcher(startAtomMatcher)
+                .addAtomMatcher(stopAtomMatcher)
+                .addAtomMatcher(conditionStartAtomMatcher)
+                .addAtomMatcher(conditionStopAtomMatcher)
+                .addAtomMatcher(activationMatcher);
+
+        // Add Predicates.
+        SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+                .build();
+        Predicate predicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("Predicate"))
+                                  .setSimplePredicate(simplePredicate)
+                                  .build();
+        builder.addPredicate(predicate);
+
+        SimplePredicate conditionSimplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
+                .build();
+        Predicate conditionPredicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("ConditionPredicate"))
+                                  .setSimplePredicate(conditionSimplePredicate)
+                                  .build();
+        builder.addPredicate(conditionPredicate);
+
+        // Add DurationMetric.
+        builder
+                .addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
+                        .setId(MetricsUtils.DURATION_METRIC_ID)
+                        .setWhat(predicate.getId())
+                        .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
+                        .setBucket(StatsdConfigProto.TimeUnit.CTS)
+                        .setCondition(conditionPredicate.getId())
+                )
+                .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
+                        .setMetricId(MetricsUtils.DURATION_METRIC_ID)
+                        .addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
+                                .setAtomMatcherId(activationMatcherId)
+                                .setActivationType(
+                                        StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY)
+                                .setTtlSeconds(ttlSec)));
+
+        // Upload config.
+        uploadConfig(builder);
+
+        // Activate the metric.
+        doAppBreadcrumbReported(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Set the condition to true.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // Start counted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop counted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Set the condition to false.
+        doAppBreadcrumbReportedStop(conditionLabel);
+        Thread.sleep(10);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+        //doAppBreadcrumbReported(99); // TODO: maybe remove?
+        //Thread.sleep(10);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Set condition to true again.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Activate the metric.
+        doAppBreadcrumbReported(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Start counted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop counted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Start uncounted duration.
+        doAppBreadcrumbReportedStart(durationLabel);
+        Thread.sleep(10);
+
+        Thread.sleep(2_000);
+
+        // Stop uncounted duration.
+        doAppBreadcrumbReportedStop(durationLabel);
+        Thread.sleep(10);
+
+        // Wait for the metrics to propagate to statsd.
+        Thread.sleep(2000);
+
+        StatsLogReport metricReport = getStatsLogReport();
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
+        assertThat(metricReport.hasDurationMetrics()).isTrue();
+        StatsLogReport.DurationMetricDataWrapper durationData
+                = metricReport.getDurationMetrics();
+        assertThat(durationData.getDataCount()).isEqualTo(1);
+        long totalDuration = durationData.getData(0).getBucketInfoList().stream()
+                .mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
+                .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long)1e9)))
+                .sum();
+        assertThat(totalDuration).isIn(Range.open((long)4e9, (long)5e9));
     }
 
     public void testDurationMetricWithDimension() throws Exception {
         if (statsdDisabled()) {
             return;
         }
-        // Add AtomMatcher's.
+        // Add AtomMatchers.
         AtomMatcher startAtomMatcherA =
             MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
         AtomMatcher stopAtomMatcherA =
@@ -116,7 +507,7 @@
         builder.addAtomMatcher(startAtomMatcherB);
         builder.addAtomMatcher(stopAtomMatcherB);
 
-        // Add Predicate's.
+        // Add Predicates.
         SimplePredicate simplePredicateA = SimplePredicate.newBuilder()
                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
@@ -178,15 +569,14 @@
         Thread.sleep(2000);
 
         StatsLogReport metricReport = getStatsLogReport();
-        assertEquals(MetricsUtils.DURATION_METRIC_ID, metricReport.getMetricId());
-        assertTrue(metricReport.hasDurationMetrics());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
+        assertThat(metricReport.hasDurationMetrics()).isTrue();
         StatsLogReport.DurationMetricDataWrapper durationData
                 = metricReport.getDurationMetrics();
-        assertTrue(durationData.getDataCount() == 1);
-        assertTrue(durationData.getData(0).getBucketInfoList().size() > 1);
+        assertThat(durationData.getDataCount()).isEqualTo(1);
+        assertThat(durationData.getData(0).getBucketInfoCount()).isGreaterThan(1);
         for (DurationBucketInfo bucketInfo : durationData.getData(0).getBucketInfoList()) {
-            assertTrue(bucketInfo.getDurationNanos() > 0);
-            assertTrue(bucketInfo.getDurationNanos() < 1e10);
+            assertThat(bucketInfo.getDurationNanos()).isIn(Range.openClosed(0L, (long)1e9));
         }
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
index 4435c26..4bd5a2a 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.metric;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.cts.statsd.atom.DeviceAtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -24,12 +26,16 @@
 import com.android.internal.os.StatsdConfigProto.FieldFilter;
 import com.android.internal.os.StatsdConfigProto.FieldMatcher;
 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
 import com.android.internal.os.StatsdConfigProto.MetricActivation;
 import com.android.internal.os.StatsdConfigProto.Predicate;
 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import com.android.os.AtomsProto.AppBreadcrumbReported;
 import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.GaugeBucketInfo;
 import com.android.os.StatsLog.GaugeMetricData;
 import com.android.os.StatsLog.StatsLogReport;
 import com.android.tradefed.log.LogUtil;
@@ -195,5 +201,146 @@
       assertFalse(metricReport.hasGaugeMetrics());
   }
 
+    public void testPulledGaugeMetricWithConditionAndActivation() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
 
+        final int conditionLabel = 2;
+        final int activationMatcherId = 5;
+        final int activationMatcherLabel = 5;
+        final int whatMatcherId = 8;
+        final int ttlSec = 5;
+
+        // Add AtomMatchers.
+        AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel);
+        AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel);
+        AtomMatcher activationMatcher =
+                MetricsUtils.startAtomMatcherWithLabel(
+                        activationMatcherId, activationMatcherLabel);
+        AtomMatcher whatMatcher =
+                MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
+
+        StatsdConfig.Builder builder = createConfigBuilder()
+                .addAtomMatcher(conditionStartAtomMatcher)
+                .addAtomMatcher(conditionStopAtomMatcher)
+                .addAtomMatcher(whatMatcher)
+                .addAtomMatcher(activationMatcher);
+
+        // Add Predicates.
+        SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+                .build();
+        Predicate predicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("Predicate"))
+                                  .setSimplePredicate(simplePredicate)
+                                  .build();
+        builder.addPredicate(predicate);
+
+        // Add GaugeMetric.
+        builder
+                .addGaugeMetric(GaugeMetric.newBuilder()
+                        .setId(MetricsUtils.GAUGE_METRIC_ID)
+                        .setWhat(whatMatcher.getId())
+                        .setBucket(TimeUnit.CTS)
+                        .setCondition(predicate.getId())
+                        .setGaugeFieldsFilter(
+                                FieldFilter.newBuilder().setIncludeAll(false).setFields(
+                                        FieldMatcher.newBuilder()
+                                                .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+                                )
+                        )
+                        .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId))
+                )
+                .addMetricActivation(MetricActivation.newBuilder()
+                        .setMetricId(MetricsUtils.GAUGE_METRIC_ID)
+                        .addEventActivation(EventActivation.newBuilder()
+                                .setAtomMatcherId(activationMatcherId)
+                                .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+                                .setTtlSeconds(ttlSec)
+                        )
+                );
+
+        uploadConfig(builder);
+
+        // Activate the metric.
+        doAppBreadcrumbReportedStart(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Set the condition to true.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // This value is collected.
+        doAppBreadcrumbReported(10);
+        Thread.sleep(10);
+
+        // Ignored; value already collected.
+        doAppBreadcrumbReported(20);
+        Thread.sleep(10);
+
+        // Set the condition to false.
+        doAppBreadcrumbReportedStop(conditionLabel);
+        Thread.sleep(10);
+
+        // Value not updated because condition is false.
+        doAppBreadcrumbReported(30);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Value not collected.
+        doAppBreadcrumbReported(40);
+        Thread.sleep(10);
+
+        // Condition to true again.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // Value not collected.
+        doAppBreadcrumbReported(50);
+        Thread.sleep(10);
+
+        // Activate the metric.
+        doAppBreadcrumbReportedStart(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Value collected.
+        doAppBreadcrumbReported(60);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Value not collected.
+        doAppBreadcrumbReported(70);
+        Thread.sleep(10);
+
+        // Wait for the metrics to propagate to statsd.
+        Thread.sleep(2000);
+
+        StatsLogReport metricReport = getStatsLogReport();
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+        assertThat(metricReport.hasGaugeMetrics()).isTrue();
+        assertThat(metricReport.getIsActive()).isFalse();
+
+        StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
+        assertThat(gaugeData.getDataCount()).isEqualTo(1);
+        assertThat(gaugeData.getData(0).getBucketInfoCount()).isEqualTo(2);
+
+        GaugeBucketInfo bucketInfo = gaugeData.getData(0).getBucketInfo(0);
+        MetricsUtils.assertBucketTimePresent(bucketInfo);
+        assertThat(bucketInfo.getAtomCount()).isEqualTo(1);
+        assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(10);
+
+        bucketInfo = gaugeData.getData(0).getBucketInfo(1);
+        MetricsUtils.assertBucketTimePresent(bucketInfo);
+        assertThat(bucketInfo.getAtomCount()).isEqualTo(1);
+        assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(60);
+    }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
index 5c0faf1..2677634 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
@@ -53,6 +53,10 @@
           .build();
     }
 
+    public static AtomMatcher startAtomMatcherWithLabel(int id, int label) {
+        return appBreadcrumbMatcherWithLabelAndState(id, label, AppBreadcrumbReported.State.START);
+    }
+
     public static AtomMatcher stopAtomMatcher(int id) {
       return AtomMatcher.newBuilder()
           .setId(id)
@@ -65,6 +69,10 @@
           .build();
     }
 
+    public static AtomMatcher stopAtomMatcherWithLabel(int id, int label) {
+        return appBreadcrumbMatcherWithLabelAndState(id, label, AppBreadcrumbReported.State.STOP);
+    }
+
     public static AtomMatcher unspecifiedAtomMatcher(int id) {
         return AtomMatcher.newBuilder()
                 .setId(id)
@@ -95,6 +103,22 @@
                 .build();
     }
 
+    public static AtomMatcher appBreadcrumbMatcherWithLabelAndState(int id, int label,
+            final AppBreadcrumbReported.State state) {
+
+        return AtomMatcher.newBuilder()
+                .setId(id)
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                        .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+                        .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+                                .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+                                .setEqInt(state.ordinal()))
+                        .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+                                .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+                                .setEqInt(label)))
+                .build();
+    }
+
     public static AtomMatcher simpleAtomMatcher(int id, int label) {
       return AtomMatcher.newBuilder()
           .setId(id)
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index 6c70b9e..25c06e3 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.metric;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.cts.statsd.atom.DeviceAtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto.ActivationType;
@@ -331,4 +333,144 @@
     assertFalse(metricReport.hasValueMetrics());
   }
 
+    public void testValueMetricWithConditionAndActivation() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        final int conditionLabel = 2;
+        final int activationMatcherId = 5;
+        final int activationMatcherLabel = 5;
+        final int whatMatcherId = 8;
+        final int ttlSec = 5;
+
+        // Add AtomMatchers.
+        AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel);
+        AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
+                APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel);
+        AtomMatcher activationMatcher =
+                MetricsUtils.startAtomMatcherWithLabel(
+                        activationMatcherId, activationMatcherLabel);
+        AtomMatcher whatMatcher =
+                MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
+
+        StatsdConfig.Builder builder = createConfigBuilder()
+                .addAtomMatcher(conditionStartAtomMatcher)
+                .addAtomMatcher(conditionStopAtomMatcher)
+                .addAtomMatcher(whatMatcher)
+                .addAtomMatcher(activationMatcher);
+
+        // Add Predicates.
+        SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+                .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+                .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+                .build();
+        Predicate predicate = Predicate.newBuilder()
+                                  .setId(MetricsUtils.StringToId("Predicate"))
+                                  .setSimplePredicate(simplePredicate)
+                                  .build();
+        builder.addPredicate(predicate);
+
+        // Add ValueMetric.
+        builder
+                .addValueMetric(ValueMetric.newBuilder()
+                        .setId(MetricsUtils.VALUE_METRIC_ID)
+                        .setWhat(whatMatcher.getId())
+                        .setBucket(TimeUnit.ONE_MINUTE)
+                        .setCondition(predicate.getId())
+                        .setValueField(FieldMatcher.newBuilder()
+                                .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+                                .addChild(FieldMatcher.newBuilder()
+                                        .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER))
+                        )
+                        .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId))
+                )
+                .addMetricActivation(MetricActivation.newBuilder()
+                        .setMetricId(MetricsUtils.VALUE_METRIC_ID)
+                        .addEventActivation(EventActivation.newBuilder()
+                                .setAtomMatcherId(activationMatcherId)
+                                .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+                                .setTtlSeconds(ttlSec)
+                        )
+                );
+
+        uploadConfig(builder);
+
+        // Activate the metric.
+        doAppBreadcrumbReportedStart(activationMatcherLabel);
+        Thread.sleep(10);
+
+        // Set the condition to true.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // Skipped due to unknown condition at start of bucket.
+        doAppBreadcrumbReported(10);
+        Thread.sleep(10);
+
+        // Skipped due to unknown condition at start of bucket.
+        doAppBreadcrumbReported(200);
+        Thread.sleep(10);
+
+        // Set the condition to false.
+        doAppBreadcrumbReportedStop(conditionLabel);
+        Thread.sleep(10);
+
+        // Log an event that should not be counted because condition is false.
+        doAppBreadcrumbReported(3_000);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Log an event that should not be counted.
+        doAppBreadcrumbReported(40_000);
+        Thread.sleep(10);
+
+        // Condition to true again.
+        doAppBreadcrumbReportedStart(conditionLabel);
+        Thread.sleep(10);
+
+        // Event should not be counted, metric is still not active.
+        doAppBreadcrumbReported(500_000);
+        Thread.sleep(10);
+
+        // Activate the metric.
+        doAppBreadcrumbReportedStart(activationMatcherLabel);
+        Thread.sleep(10);
+
+        //  Log an event that should be counted.
+        doAppBreadcrumbReported(6_000_000);
+        Thread.sleep(10);
+
+        // Let the metric deactivate.
+        Thread.sleep(ttlSec * 1000);
+
+        // Log an event that should not be counted.
+        doAppBreadcrumbReported(70_000_000);
+        Thread.sleep(10);
+
+        // Wait for the metrics to propagate to statsd.
+        Thread.sleep(2000);
+
+        StatsLogReport metricReport = getStatsLogReport();
+        LogUtil.CLog.d("Received the following data: " + metricReport.toString());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+        assertThat(metricReport.hasValueMetrics()).isTrue();
+        assertThat(metricReport.getIsActive()).isFalse();
+
+        StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
+        assertThat(valueData.getDataCount()).isEqualTo(1);
+        assertThat(valueData.getData(0).getBucketInfoCount()).isEqualTo(1);
+        long totalValue = valueData.getData(0).getBucketInfoList().stream()
+                .peek(MetricsUtils::assertBucketTimePresent)
+                .peek(bucketInfo -> assertThat(bucketInfo.getValuesCount()).isEqualTo(1))
+                .map(bucketInfo -> bucketInfo.getValues(0))
+                .peek(value -> assertThat(value.getIndex()).isEqualTo(0))
+                .mapToLong(value -> value.getValueLong())
+                .sum();
+        assertThat(totalValue).isEqualTo(6_000_000);
+    }
+
 }
diff --git a/hostsidetests/testharness/OWNERS b/hostsidetests/testharness/OWNERS
new file mode 100644
index 0000000..2e57a70
--- /dev/null
+++ b/hostsidetests/testharness/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 642746
+williamhester@google.com
diff --git a/hostsidetests/testharness/app/Android.mk b/hostsidetests/testharness/app/Android.mk
new file mode 100644
index 0000000..6396cc4
--- /dev/null
+++ b/hostsidetests/testharness/app/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2019 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_RESOURCE_DIR := res
+
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules \
+                               androidx.legacy_legacy-support-v4 \
+                               compatibility-device-util-axt
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_PACKAGE_NAME := CtsTestHarnessModeDeviceApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/testharness/app/AndroidManifest.xml b/hostsidetests/testharness/app/AndroidManifest.xml
new file mode 100755
index 0000000..421894d
--- /dev/null
+++ b/hostsidetests/testharness/app/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.testharness.app">
+
+    <application>
+        <activity android:name=".TestHarnessActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.testharness.app" />
+
+</manifest>
+
diff --git a/hostsidetests/testharness/app/res/layout/activity_test_harness.xml b/hostsidetests/testharness/app/res/layout/activity_test_harness.xml
new file mode 100644
index 0000000..2d50fbe
--- /dev/null
+++ b/hostsidetests/testharness/app/res/layout/activity_test_harness.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView android:id="@+id/text_view"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:text="This is some text."/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/testharness/app/src/android/testharness/app/TestHarnessActivity.java b/hostsidetests/testharness/app/src/android/testharness/app/TestHarnessActivity.java
new file mode 100644
index 0000000..833e7f5
--- /dev/null
+++ b/hostsidetests/testharness/app/src/android/testharness/app/TestHarnessActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 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.testharness.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.lang.Override;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * An activity that opens up the keyboard and contains methods for testing.
+ *
+ * <p>This is used to verify that calling {@code adb shell cmd testharness enable} wipes all apps
+ * and their private data directories.
+ */
+public class TestHarnessActivity extends Activity {
+    public static final String DIRTY_DEVICE_FILENAME = "dirty_device.txt";
+    private boolean mKeyboardVisible;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_test_harness);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        final TextView textView = findViewById(R.id.text_view);
+        textView.postDelayed(() -> {
+            getSystemService(InputMethodManager.class)
+                    .toggleSoftInputFromWindow(
+                            textView.getWindowToken(),
+                            InputMethodManager.SHOW_FORCED,
+                            0);
+            mKeyboardVisible = true;
+        }, 200);
+    }
+
+    boolean isKeyboardVisible() {
+        return mKeyboardVisible;
+    }
+
+    void dirtyDevice() {
+        try {
+            Path dirtyFile = getFilesDir().toPath().resolve(DIRTY_DEVICE_FILENAME);
+            Files.write(dirtyFile, "This device is dirty!".getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    boolean isDeviceClean() {
+        return !Files.exists(getFilesDir().toPath().resolve(DIRTY_DEVICE_FILENAME));
+    }
+}
diff --git a/hostsidetests/testharness/app/src/android/testharness/app/TestHarnessModeDeviceTest.java b/hostsidetests/testharness/app/src/android/testharness/app/TestHarnessModeDeviceTest.java
new file mode 100644
index 0000000..a0cb026
--- /dev/null
+++ b/hostsidetests/testharness/app/src/android/testharness/app/TestHarnessModeDeviceTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.testharness.app;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.app.Instrumentation;
+import android.app.ActivityManager;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+/** Device-side tests for Test Harness Mode */
+@RunWith(AndroidJUnit4.class)
+public class TestHarnessModeDeviceTest {
+    @Rule
+    public ActivityTestRule<TestHarnessActivity> mActivityRule =
+            new ActivityTestRule<>(android.testharness.app.TestHarnessActivity.class);
+
+    @Test
+    public void ensureActivityNotObscuredByKeyboardSetUpScreen() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        TestHarnessActivity activity = mActivityRule.getActivity();
+        PollingCheck.waitFor(activity::isKeyboardVisible);
+
+        UiDevice device = UiDevice.getInstance(instrumentation);
+        Assert.assertTrue(device.hasObject(By.res("android.testharness.app", "text_view")));
+    }
+
+    @Test
+    public void dirtyDevice() {
+        TestHarnessActivity activity = mActivityRule.getActivity();
+        PollingCheck.waitFor(activity::hasWindowFocus);
+
+        activity.dirtyDevice();
+    }
+
+    @Test
+    public void testDeviceIsClean() {
+        TestHarnessActivity activity = mActivityRule.getActivity();
+        PollingCheck.waitFor(activity::hasWindowFocus);
+
+        Assert.assertTrue(activity.isDeviceClean());
+    }
+
+    @Test
+    public void testDeviceInTestHarnessMode() {
+        Assert.assertTrue(ActivityManager.isRunningInUserTestHarness());
+    }
+}
diff --git a/hostsidetests/testharness/src/android/testharness/cts/TestHarnessModeDeviceTest.java b/hostsidetests/testharness/src/android/testharness/cts/TestHarnessModeDeviceTest.java
index 1b604e0..9fb73cd 100644
--- a/hostsidetests/testharness/src/android/testharness/cts/TestHarnessModeDeviceTest.java
+++ b/hostsidetests/testharness/src/android/testharness/cts/TestHarnessModeDeviceTest.java
@@ -100,6 +100,26 @@
                         .executeShellCommand("settings get global transition_animation_scale"));
     }
 
+    @Test
+    public void testHarnessModeRemovesInstalledAppsAndData() throws Exception {
+        installPackage("CtsTestHarnessModeDeviceApp.apk");
+        runTest("dirtyDevice");
+
+        enableTestHarnessModeAndWait();
+
+        installPackage("CtsTestHarnessModeDeviceApp.apk");
+        Assert.assertTrue(runTest("testDeviceInTestHarnessMode"));
+        Assert.assertTrue(runTest("ensureActivityNotObscuredByKeyboardSetUpScreen"));
+        Assert.assertTrue(runTest("testDeviceIsClean"));
+    }
+
+    private boolean runTest(String methodName) throws Exception {
+        return runDeviceTests(
+                "android.testharness.app",
+                "android.testharness.app.TestHarnessModeDeviceTest",
+                methodName);
+    }
+
     private void enableTestHarnessModeAndWait()
             throws InterruptedException, DeviceNotAvailableException {
         if (getDevice().executeShellV2Command("cmd testharness enable").getExitCode() != 0) {
@@ -109,6 +129,8 @@
         try {
             getDevice().waitForDeviceOnline(5 * ONE_MINUTE);
             getDevice().waitForBootComplete(ONE_MINUTE);
+
+            Thread.sleep(20 * 1000); // Wait 20 more seconds to ensure that the device has booted
         } catch (DeviceNotAvailableException e) {
             Assert.fail("Device did not come back online after 5 minutes. "
                     + "Did the ADB keys not get stored?");
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 23b0561..d76c718 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -20,7 +20,7 @@
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <application>
+    <application android:requestLegacyExternalStorage="true">
         <uses-library android:name="android.test.runner" />
         <activity android:name=".ThemeDeviceActivity"
                   android:screenOrientation="portrait">
diff --git a/hostsidetests/theme/assets/29/140dpi.zip b/hostsidetests/theme/assets/29/140dpi.zip
new file mode 100644
index 0000000..cb385f1
--- /dev/null
+++ b/hostsidetests/theme/assets/29/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/180dpi.zip b/hostsidetests/theme/assets/29/180dpi.zip
new file mode 100644
index 0000000..b034a7f
--- /dev/null
+++ b/hostsidetests/theme/assets/29/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/200dpi.zip b/hostsidetests/theme/assets/29/200dpi.zip
new file mode 100644
index 0000000..9a0ca5e
--- /dev/null
+++ b/hostsidetests/theme/assets/29/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/220dpi.zip b/hostsidetests/theme/assets/29/220dpi.zip
new file mode 100644
index 0000000..b65a035
--- /dev/null
+++ b/hostsidetests/theme/assets/29/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/260dpi.zip b/hostsidetests/theme/assets/29/260dpi.zip
index ba5d363..9cdefe7 100644
--- a/hostsidetests/theme/assets/29/260dpi.zip
+++ b/hostsidetests/theme/assets/29/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/280dpi.zip b/hostsidetests/theme/assets/29/280dpi.zip
index 34ea14c..2e39de5 100644
--- a/hostsidetests/theme/assets/29/280dpi.zip
+++ b/hostsidetests/theme/assets/29/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/300dpi.zip b/hostsidetests/theme/assets/29/300dpi.zip
index 7595d24..fba9c6c 100644
--- a/hostsidetests/theme/assets/29/300dpi.zip
+++ b/hostsidetests/theme/assets/29/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/340dpi.zip b/hostsidetests/theme/assets/29/340dpi.zip
index 8ce960c..72e6f8f 100644
--- a/hostsidetests/theme/assets/29/340dpi.zip
+++ b/hostsidetests/theme/assets/29/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/360dpi.zip b/hostsidetests/theme/assets/29/360dpi.zip
index aae3adc..3970139 100644
--- a/hostsidetests/theme/assets/29/360dpi.zip
+++ b/hostsidetests/theme/assets/29/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/400dpi.zip b/hostsidetests/theme/assets/29/400dpi.zip
index 363d602..510eb94d 100644
--- a/hostsidetests/theme/assets/29/400dpi.zip
+++ b/hostsidetests/theme/assets/29/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/420dpi.zip b/hostsidetests/theme/assets/29/420dpi.zip
index 0f2ce47..a457bda 100644
--- a/hostsidetests/theme/assets/29/420dpi.zip
+++ b/hostsidetests/theme/assets/29/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/440dpi.zip b/hostsidetests/theme/assets/29/440dpi.zip
index 2328c61..07355d1 100644
--- a/hostsidetests/theme/assets/29/440dpi.zip
+++ b/hostsidetests/theme/assets/29/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/560dpi.zip b/hostsidetests/theme/assets/29/560dpi.zip
index 5f1bb0b..6a85ad8 100644
--- a/hostsidetests/theme/assets/29/560dpi.zip
+++ b/hostsidetests/theme/assets/29/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/hdpi.zip b/hostsidetests/theme/assets/29/hdpi.zip
index 6d82318..e1a534a 100644
--- a/hostsidetests/theme/assets/29/hdpi.zip
+++ b/hostsidetests/theme/assets/29/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/ldpi.zip b/hostsidetests/theme/assets/29/ldpi.zip
index cc60027..5475608 100644
--- a/hostsidetests/theme/assets/29/ldpi.zip
+++ b/hostsidetests/theme/assets/29/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/mdpi.zip b/hostsidetests/theme/assets/29/mdpi.zip
index 66d41d4..67c5c03 100644
--- a/hostsidetests/theme/assets/29/mdpi.zip
+++ b/hostsidetests/theme/assets/29/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/tvdpi.zip b/hostsidetests/theme/assets/29/tvdpi.zip
index b43032f..60f5afe 100644
--- a/hostsidetests/theme/assets/29/tvdpi.zip
+++ b/hostsidetests/theme/assets/29/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/xhdpi.zip b/hostsidetests/theme/assets/29/xhdpi.zip
index 64905f3..d2895a1 100644
--- a/hostsidetests/theme/assets/29/xhdpi.zip
+++ b/hostsidetests/theme/assets/29/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/xxhdpi.zip b/hostsidetests/theme/assets/29/xxhdpi.zip
index b2cb422..637649a 100644
--- a/hostsidetests/theme/assets/29/xxhdpi.zip
+++ b/hostsidetests/theme/assets/29/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/xxxhdpi.zip b/hostsidetests/theme/assets/29/xxxhdpi.zip
index d00dbbd..9ab19b1 100644
--- a/hostsidetests/theme/assets/29/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/29/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/generate_images.py b/hostsidetests/theme/generate_images.py
index 4b1b581..b05d5f0 100755
--- a/hostsidetests/theme/generate_images.py
+++ b/hostsidetests/theme/generate_images.py
@@ -30,8 +30,12 @@
 # This dict should contain one entry for every density listed in CDD 7.1.1.3.
 CTS_THEME_dict = {
     120: "ldpi",
+    140: "140dpi",
     160: "mdpi",
+    180: "180dpi",
+    200: "200dpi",
     213: "tvdpi",
+    220: "220dpi",
     240: "hdpi",
     260: "260dpi",
     280: "280dpi",
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/UidCapTests.java b/tests/AlarmManager/src/android/alarmmanager/cts/UidCapTests.java
index de5481f..d6b5af9 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/UidCapTests.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/UidCapTests.java
@@ -97,7 +97,7 @@
                 mAlarmsSet.add(lastPi);
                 fail("Able to set an alarm of type " + type + " after reaching the limit of "
                         + limit);
-            } catch (UnsupportedOperationException e) {
+            } catch (IllegalStateException e) {
             }
         }
     }
diff --git a/tests/DropBoxManager/AndroidTest.xml b/tests/DropBoxManager/AndroidTest.xml
index af6f407..b1d45c9 100644
--- a/tests/DropBoxManager/AndroidTest.xml
+++ b/tests/DropBoxManager/AndroidTest.xml
@@ -19,7 +19,10 @@
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
-
+    <!-- Switch to system user before running this module since some tests only works for user 0 -->
+    <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
+        <option name="user-type" value="system" />
+    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsDropBoxManagerTestCases.apk" />
diff --git a/tests/JobScheduler/AndroidManifest.xml b/tests/JobScheduler/AndroidManifest.xml
index db0b6bb..4bd5208 100755
--- a/tests/JobScheduler/AndroidManifest.xml
+++ b/tests/JobScheduler/AndroidManifest.xml
@@ -27,7 +27,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
-    <application>
+    <application android:requestLegacyExternalStorage="true">
         <uses-library android:name="android.test.runner" />
 
         <service android:name="android.jobscheduler.MockJobService"
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
index b968811..09af06b 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
@@ -95,16 +95,16 @@
      * {@link JobParameters#isOverrideDeadlineExpired()} returns the correct value.
      */
     public void testJobParameters_unexpiredDeadline() throws Exception {
-
         JobInfo deadlineJob =
                 new JobInfo.Builder(UNEXPIRED_JOB_ID, kJobServiceComponent)
                         .setMinimumLatency(500L)
-                        .setRequiresCharging(true)
+                        .setRequiresStorageNotLow(true)
                         .build();
         kTestEnvironment.setExpectedExecutions(1);
+        setStorageState(true);
         mJobScheduler.schedule(deadlineJob);
-        // Run everything by pretending the device was just plugged in.
-        sendExpediteStableChargingBroadcast();
+        // Run everything by making storage state not-low.
+        setStorageState(false);
         assertTrue("Failed to execute non-deadline job", kTestEnvironment.awaitExecution());
         assertFalse("Job that ran early (unexpired) didn't have" +
                         " JobParameters#isOverrideDeadlineExpired=false",
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 3b70810..60ef850 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -422,6 +422,11 @@
                     " - Watches have different notification system.");
             return;
         }
+        if (pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE)) {
+            Log.i(LOG_TAG, "Skipping: testTypeNotificationStateChangedAccessibilityEvent" +
+                    " - Automotive handle notifications differently.");
+            return;
+        }
 
         String message = mActivity.getString(R.string.notification_message);
 
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
index 388fd0a..71c34c2 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.getActivityTitle;
@@ -275,8 +276,8 @@
                         autoCompleteTextView.setAdapter(adapter);
                         autoCompleteTextView.showDropDown();
                     }),
-                    filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_CHILDREN),
-                    TIMEOUT_ASYNC_PROCESSING);
+                    filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_CHILDREN,
+                            mActivityTitle.toString()), TIMEOUT_ASYNC_PROCESSING);
         } catch (TimeoutException exception) {
             throw new RuntimeException(
                     "Failed to get window changed event when showing dropdown", exception);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java
index 3da457a..790246e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AccessibilityEventFilterUtils.java
@@ -14,13 +14,20 @@
 
 package android.accessibilityservice.cts.utils;
 
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.both;
+
+import android.app.UiAutomation;
 import android.app.UiAutomation.AccessibilityEventFilter;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import androidx.annotation.NonNull;
 
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
 
+import java.util.List;
 import java.util.function.BiPredicate;
 
 /**
@@ -45,6 +52,13 @@
         return (both(new AccessibilityEventTypeMatcher(eventType)).and(matchResourceName))::matches;
     }
 
+    public static AccessibilityEventFilter filterWindowsChangeTypesAndWindowTitle(
+            @NonNull UiAutomation uiAutomation, int changeTypes, @NonNull String title) {
+        return allOf(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED),
+                new WindowChangesMatcher(changeTypes),
+                new WindowTitleMatcher(uiAutomation, title))::matches;
+    }
+
     public static class AccessibilityEventTypeMatcher extends TypeSafeMatcher<AccessibilityEvent> {
         private int mType;
 
@@ -125,4 +139,32 @@
             description.appendText("Matching to " + mDescription + " " + mProperty.toString());
         }
     }
+
+    public static class WindowTitleMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+        private final UiAutomation mUiAutomation;
+        private final String mTitle;
+
+        public WindowTitleMatcher(@NonNull UiAutomation uiAutomation, @NonNull String title) {
+            super();
+            mUiAutomation = uiAutomation;
+            mTitle = title;
+        }
+
+        @Override
+        protected boolean matchesSafely(AccessibilityEvent event) {
+            final List<AccessibilityWindowInfo> windows = mUiAutomation.getWindows();
+            final int eventWindowId = event.getWindowId();
+            for (AccessibilityWindowInfo info : windows) {
+                if (eventWindowId == info.getId() && mTitle.equals(info.getTitle())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("With window title " + mTitle);
+        }
+    }
 }
diff --git a/tests/app/DownloadManagerApi28Test/AndroidTest.xml b/tests/app/DownloadManagerApi28Test/AndroidTest.xml
index 8d93448..c07caaa 100644
--- a/tests/app/DownloadManagerApi28Test/AndroidTest.xml
+++ b/tests/app/DownloadManagerApi28Test/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for DownloadManagerApi28Test">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java b/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
index fd4cea7..bb46489 100644
--- a/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
+++ b/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
@@ -43,9 +43,7 @@
         File publicLocation = new File(
                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
                 "publicFile.bin");
-        if (publicLocation.exists()) {
-            assertTrue(publicLocation.delete());
-        }
+        deleteFromShell(publicLocation);
 
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
@@ -70,10 +68,8 @@
 
     @Test
     public void testSetDestinationUri_sdcardPath() throws Exception {
-        File path = new File("/sdcard/publicFile.bin");
-        if (path.exists()) {
-            assertTrue(path.delete());
-        }
+        final File path = new File("/sdcard/publicFile.bin");
+        deleteFromShell(path);
 
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
@@ -101,9 +97,7 @@
         File publicLocation = new File(
                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
                 "publicFile.bin");
-        if (publicLocation.exists()) {
-            assertTrue(publicLocation.delete());
-        }
+        deleteFromShell(publicLocation);
 
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
@@ -137,11 +131,12 @@
         };
 
         for (String path : filePaths) {
-            final String fileContents = path + "_" + System.nanoTime();
+            final String fileContents = "Test content:" + path + "_" + System.nanoTime();
 
+            final File file = new File(path);
             writeToFile(new File(path), fileContents);
 
-            final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+            final long id = mDownloadManager.addCompletedDownload(file.getName(), "Test desc", true,
                     "text/plain", path, fileContents.getBytes().length, true);
             final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
             assertEquals(fileContents, actualContents);
@@ -212,12 +207,13 @@
                 "/sdcard/file3.mp3",
         };
         for (String downloadLocation : downloadPath) {
-            final String fileContents = downloadLocation + "_" + System.nanoTime();
+            final String fileContents = "Test content:" + downloadLocation + "_" + System.nanoTime();
             final File file = new File(Uri.parse(downloadLocation).getPath());
             writeToFile(file, fileContents);
 
-            final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
-                    true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
+            final long downloadId = mDownloadManager.addCompletedDownload(file.getName(),
+                    "Test desc", true,
+                    "text/plain", downloadLocation, fileContents.getBytes().length, true);
             assertTrue(downloadId >= 0);
             final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
             mContext.grantUriPermission("com.android.shell", downloadUri,
diff --git a/tests/app/DownloadManagerLegacyTest/AndroidTest.xml b/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
index d225952..ae9708d 100644
--- a/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
+++ b/tests/app/DownloadManagerLegacyTest/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Config for DownloadManagerLegacyTest">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
index 43efb20..32d6fd9 100644
--- a/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
+++ b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
@@ -15,7 +15,6 @@
  */
 package android.app.cts;
 
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -31,7 +30,6 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
-import java.io.FileInputStream;
 
 @RunWith(AndroidJUnit4.class)
 public class DownloadManagerLegacyTest extends DownloadManagerTestBase {
@@ -44,11 +42,12 @@
         };
 
         for (String path : filePaths) {
-            final String fileContents = path + "_" + System.nanoTime();
+            final String fileContents = "Test content:" + path + "_" + System.nanoTime();
 
-            writeToFile(new File(path), fileContents);
+            final File file = new File(path);
+            writeToFile(file, fileContents);
 
-            final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+            final long id = mDownloadManager.addCompletedDownload(file.getName(), "Test desc", true,
                     "text/plain", path, fileContents.getBytes().length, true);
             final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
             assertEquals(fileContents, actualContents);
@@ -69,9 +68,9 @@
                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
                 "colors.txt");
         final String fileContents = "RED;GREEN;BLUE";
-        writeToFileFromShell(file, fileContents);
+        writeToFile(file, fileContents);
         try {
-            mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+            mDownloadManager.addCompletedDownload(file.getName(), "Test desc", true,
                     "text/plain", file.getPath(), fileContents.getBytes().length, true);
             fail(file + " is not valid for addCompletedDownload()");
         } catch (Exception e) {
@@ -85,25 +84,27 @@
      */
     @Test
     public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
-        final String[] downloadPath = new String[] {
+        final String[] downloadPaths = {
                 new File(Environment.getExternalStoragePublicDirectory(
-                        Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
-                "/sdcard/Download/file2.mp3",
+                        Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
+                "/sdcard/Download/file2.txt",
         };
-        for (String downloadLocation : downloadPath) {
-            final String fileContents = downloadLocation + "_" + System.nanoTime();
+        for (String downloadLocation : downloadPaths) {
+            final String fileContents =
+                    "Test content:" + downloadLocation + "_" + System.nanoTime();
             final File file = new File(downloadLocation);
             writeToFile(file, fileContents);
 
-            final long downloadId = mDownloadManager.addCompletedDownload("Test title", "Test desc",
+            final long downloadId = mDownloadManager.addCompletedDownload(
+                    file.getName(), "Test desc",
                     true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
             assertTrue(downloadId >= 0);
             final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
             mContext.grantUriPermission("com.android.shell", downloadUri,
                     Intent.FLAG_GRANT_READ_URI_PERMISSION);
             final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
-            assertArrayEquals(hash(new FileInputStream(file)),
-                    hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+            assertEquals(fileContents, readContentsFromUri(mediaStoreUri));
 
             // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
             assertRemoveDownload(downloadId, 0);
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 56badff..a576e62 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -401,6 +401,18 @@
                   android:resizeableActivity="true"
                   android:turnScreenOn="true"/>
 
+        <activity android:name="android.app.stubs.BubblesTestNotEmbeddableActivity"
+                  android:resizeableActivity="true"
+                  android:documentLaunchMode="always"
+                  android:exported="true"
+        />
+
+        <activity android:name="android.app.stubs.BubblesTestNotDocumentLaunchModeActivity"
+                  android:resizeableActivity="true"
+                  android:allowEmbedded="true"
+                  android:exported="true"
+        />
+
         <service android:name="android.app.stubs.BubblesTestService"
                  android:label="BubblesTestsService"
                  android:exported="true">
diff --git a/tests/app/app/src/android/app/stubs/BubblesTestActivity.java b/tests/app/app/src/android/app/stubs/BubblesTestActivity.java
index 5a80de4..36f51c3 100644
--- a/tests/app/app/src/android/app/stubs/BubblesTestActivity.java
+++ b/tests/app/app/src/android/app/stubs/BubblesTestActivity.java
@@ -56,7 +56,7 @@
     public void sendBubble(int i) {
         Context context = getApplicationContext();
 
-        final Intent intent = new Intent();
+        final Intent intent = new Intent(context, BubblesTestActivity.class);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
         intent.setAction(Intent.ACTION_MAIN);
diff --git a/tests/app/app/src/android/app/stubs/BubblesTestNotDocumentLaunchModeActivity.java b/tests/app/app/src/android/app/stubs/BubblesTestNotDocumentLaunchModeActivity.java
new file mode 100644
index 0000000..8601ed2
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/BubblesTestNotDocumentLaunchModeActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.stubs;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Used by NotificationManagerTest for testing policy around bubbles.
+ */
+public class BubblesTestNotDocumentLaunchModeActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/tests/app/app/src/android/app/stubs/BubblesTestNotEmbeddableActivity.java b/tests/app/app/src/android/app/stubs/BubblesTestNotEmbeddableActivity.java
new file mode 100644
index 0000000..ced310a
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/BubblesTestNotEmbeddableActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.stubs;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Used by NotificationManagerTest for testing policy around bubbles.
+ */
+public class BubblesTestNotEmbeddableActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
+
diff --git a/tests/app/app/src/android/app/stubs/BubblesTestService.java b/tests/app/app/src/android/app/stubs/BubblesTestService.java
index fadf00c..6ff579b 100644
--- a/tests/app/app/src/android/app/stubs/BubblesTestService.java
+++ b/tests/app/app/src/android/app/stubs/BubblesTestService.java
@@ -59,8 +59,9 @@
     }
 
     private Notification getNotificationForTest(final int testCase, final Context context) {
+        final Intent intent = new Intent(context, BubblesTestActivity.class);
         final PendingIntent pendingIntent =
-                PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0);
+                PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
         Notification.Builder nb = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
                 .setContentTitle("foofoo")
                 .setContentIntent(pendingIntent)
diff --git a/tests/app/src/android/app/cts/ActionBarTest.java b/tests/app/src/android/app/cts/ActionBarTest.java
index d6e41a0..eb9e591 100644
--- a/tests/app/src/android/app/cts/ActionBarTest.java
+++ b/tests/app/src/android/app/cts/ActionBarTest.java
@@ -28,6 +28,8 @@
 
 import java.util.concurrent.TimeUnit;
 
+import androidx.test.filters.FlakyTest;
+
 public class ActionBarTest extends ActivityInstrumentationTestCase2<ActionBarActivity> {
 
     private ActionBarActivity mActivity;
@@ -86,6 +88,7 @@
         assertEquals(t3, mBar.getTabAt(4));
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testOptionsMenuKey() throws Exception {
         boolean hasPermanentMenuKey = ViewConfiguration.get(getActivity()).hasPermanentMenuKey();
         if (!mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
diff --git a/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java b/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java
index 4192883..e6b178b 100644
--- a/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java
+++ b/tests/app/src/android/app/cts/ActivityKeyboardShortcutsTest.java
@@ -27,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.test.filters.FlakyTest;
+
 /**
  * Tests functionality in Activity related to Keyboard Shortcuts.
  */
@@ -51,6 +53,7 @@
      * Tests that requestShowKeyboardShortcuts fetches app specific shortcuts even when triggered
      * from an overflow menu (options menu in the test)
      */
+    @FlakyTest(bugId = 133760851)
     public void testRequestShowKeyboardShortcuts() throws InterruptedException {
         if (!keyboardShortcutsSupported()) {
             return;
diff --git a/tests/app/src/android/app/cts/AlertDialogTest.java b/tests/app/src/android/app/cts/AlertDialogTest.java
index 5c6a505..b2e2381 100644
--- a/tests/app/src/android/app/cts/AlertDialogTest.java
+++ b/tests/app/src/android/app/cts/AlertDialogTest.java
@@ -30,6 +30,7 @@
 import android.widget.Button;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
@@ -174,6 +175,7 @@
         assertTrue(mActivity.getDialog().isShowing());
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testCallback() {
         startDialogActivity(DialogStubActivity.TEST_ALERTDIALOG_CALLBACK);
@@ -201,6 +203,7 @@
         assertTrue(mActivity.onCancelCalled);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testAlertDialogNotCancelable() throws Exception {
         startDialogActivity(DialogStubActivity.TEST_ALERTDIALOG_NOT_CANCELABLE);
diff --git a/tests/app/src/android/app/cts/AlertDialog_BuilderCursorTest.java b/tests/app/src/android/app/cts/AlertDialog_BuilderCursorTest.java
index 75017c1..28e5ace 100644
--- a/tests/app/src/android/app/cts/AlertDialog_BuilderCursorTest.java
+++ b/tests/app/src/android/app/cts/AlertDialog_BuilderCursorTest.java
@@ -34,6 +34,7 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.PollingCheck;
@@ -204,6 +205,7 @@
         verifyNoMoreInteractions(mOnClickListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testSetMultiChoiceItemsWithParamCursor() throws Throwable {
         mCursor = mDatabase.query("test", mProjectionWithChecked,
                 null, null, null, null, null);
diff --git a/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java b/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
index fceae14..b3404b3 100644
--- a/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
+++ b/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
@@ -62,6 +62,8 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
+import androidx.test.filters.FlakyTest;
+
 @SmallTest
 @RunWith(JUnit4.class)
 public class AlertDialog_BuilderTest  {
@@ -121,6 +123,7 @@
         assertEquals(20, ta.getInt(0, 0));
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetIconWithParamInt() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -134,6 +137,7 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetIconWithParamDrawable() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -147,6 +151,7 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetIconAttribute() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -160,6 +165,7 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetPositiveButtonWithParamInt() throws Throwable {
        runOnUiThread(new Runnable() {
@@ -182,6 +188,7 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetPositiveButtonWithParamCharSequence() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -203,6 +210,7 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetNegativeButtonWithParamCharSequence() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -224,6 +232,7 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetNegativeButtonWithParamInt() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -245,6 +254,7 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetNeutralButtonWithParamInt() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -266,6 +276,7 @@
         verifyNoMoreInteractions(mOnDismissListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetNeutralButtonWithParamCharSequence() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -326,6 +337,7 @@
         testCancelable(false);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetOnCancelListener() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -341,6 +353,7 @@
         verifyNoMoreInteractions(mOnCancelListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetOnDismissListener() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -418,6 +431,7 @@
         assertEquals(expect[0], mListView.getItemAtPosition(0));
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetAdapter() throws Throwable {
         final ListAdapter adapter = new AdapterTest();
@@ -483,6 +497,7 @@
         assertEquals(items[0], mListView.getItemAtPosition(0));
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetSingleChoiceItemsWithParamInt() throws Throwable {
         final CharSequence[] items = mContext.getResources().getTextArray(
@@ -506,6 +521,7 @@
         verifyNoMoreInteractions(mOnClickListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetSingleChoiceItemsWithParamCharSequence() throws Throwable {
         final CharSequence[] items = mContext.getResources().getTextArray(
@@ -552,6 +568,7 @@
         verifyNoMoreInteractions(mOnClickListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetOnItemSelectedListener() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -570,6 +587,7 @@
         verifyNoMoreInteractions(mOnItemSelectedListener);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetView() throws Throwable {
         final View view = new View(mContext);
@@ -603,6 +621,7 @@
         assertNotNull(mView.findViewById(R.id.username_edit));
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetViewById() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -619,6 +638,7 @@
         assertNotNull(mView.findViewById(R.id.username_edit));
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetCustomTitle() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -632,6 +652,7 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetInverseBackgroundForced() throws Throwable {
         runOnUiThread(new Runnable() {
@@ -659,6 +680,7 @@
         assertTrue(mDialog.isShowing());
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testShow() throws Throwable {
         runOnUiThread(new Runnable() {
diff --git a/tests/app/src/android/app/cts/DialogTest.java b/tests/app/src/android/app/cts/DialogTest.java
index 4530492..6cf088e 100755
--- a/tests/app/src/android/app/cts/DialogTest.java
+++ b/tests/app/src/android/app/cts/DialogTest.java
@@ -59,6 +59,7 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.FlakyTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -126,6 +127,7 @@
         assertTextAppearanceStyle(ta);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testConstructor_protectedCancellable() {
         startDialogActivity(DialogStubActivity.TEST_PROTECTED_CANCELABLE);
@@ -150,6 +152,7 @@
         assertTrue(mActivity.onCancelListenerCalled);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testConstructor_protectedNotCancellableEsc() {
         startDialogActivity(DialogStubActivity.TEST_PROTECTED_NOT_CANCELABLE);
@@ -180,6 +183,7 @@
                 ta.getInt(R.styleable.TextAppearance_textStyle, defValue));
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testOnStartCreateStop(){
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
@@ -279,6 +283,7 @@
         TestDialog.onRestoreInstanceStateObserver.await();
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testGetCurrentFocus() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
@@ -469,6 +474,7 @@
         return event;
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testTouchEvent() {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
@@ -603,6 +609,7 @@
         assertTrue(d.isOnContentChangedCalled);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testOnWindowFocusChanged() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
@@ -626,6 +633,7 @@
         }.run();
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testDispatchKeyEvent() {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
@@ -753,6 +761,7 @@
     public void testOnSearchRequested() {
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testTakeKeyEvents() throws Throwable {
         startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
@@ -842,6 +851,7 @@
         assertEquals(d.getWindow().getLayoutInflater(), d.getLayoutInflater());
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetCancellable_true() {
         startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME);
@@ -875,6 +885,7 @@
         assertFalse(d.isShowing());
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetCancellableEsc_false() {
         startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME);
@@ -924,6 +935,7 @@
         assertFalse(mOnCancelListenerCalled);
     }
 
+    @FlakyTest(bugId = 133760851)
     @Test
     public void testSetCancelMessage() throws Exception {
         mCalledCallback = false;
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index 4d365a4..e1a4591 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -218,21 +218,15 @@
     @Test
     public void testDownloadManagerDestination() throws Exception {
         File uriLocation = new File(mContext.getExternalFilesDir(null), "uriFile.bin");
-        if (uriLocation.exists()) {
-            assertTrue(uriLocation.delete());
-        }
+        deleteFromShell(uriLocation);
 
         File extFileLocation = new File(mContext.getExternalFilesDir(null), "extFile.bin");
-        if (extFileLocation.exists()) {
-            assertTrue(extFileLocation.delete());
-        }
+        deleteFromShell(extFileLocation);
 
         File publicLocation = new File(
                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                 "publicFile.bin");
-        if (publicLocation.exists()) {
-            assertTrue(publicLocation.delete());
-        }
+        deleteFromShell(publicLocation);
 
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
@@ -276,15 +270,11 @@
     public void testDownloadManagerDestinationExtension() throws Exception {
         String noExt = "noiseandchirps";
         File noExtLocation = new File(mContext.getExternalFilesDir(null), noExt);
-        if (noExtLocation.exists()) {
-            assertTrue(noExtLocation.delete());
-        }
+        deleteFromShell(noExtLocation);
 
         String wrongExt = "noiseandchirps.wrong";
         File wrongExtLocation = new File(mContext.getExternalFilesDir(null), wrongExt);
-        if (wrongExtLocation.exists()) {
-            assertTrue(wrongExtLocation.delete());
-        }
+        deleteFromShell(wrongExtLocation);
 
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
@@ -319,9 +309,7 @@
         final File documentsFile = new File(
                 Environment.getExternalStoragePublicDirectory("TestDir"),
                 "uriFile.bin");
-        if (documentsFile.exists()) {
-            assertTrue(documentsFile.delete());
-        }
+        deleteFromShell(documentsFile);
 
         final Request badRequest = new Request(getGoodUrl());
         badRequest.setDestinationUri(Uri.fromFile(documentsFile));
@@ -480,9 +468,7 @@
     @Test
     public void testDownloadNotVisibleInUi() throws Exception {
         File uriLocation = new File(mContext.getExternalFilesDir(null), "uriFile.bin");
-        if (uriLocation.exists()) {
-            assertTrue(uriLocation.delete());
-        }
+        deleteFromShell(uriLocation);
 
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
@@ -516,7 +502,7 @@
 
         writeToFile(file, fileContents);
 
-        final long id = mDownloadManager.addCompletedDownload("Test title", "Test desc", true,
+        final long id = mDownloadManager.addCompletedDownload(file.getName(), "Test desc", true,
                 "text/plain", file.getPath(), fileContents.getBytes().length, true);
         final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
         assertEquals(fileContents, actualContents);
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
index 71eb4e9a..93ca1c9 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTestBase.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -131,6 +131,13 @@
         }
     }
 
+    protected static String readContentsFromUri(Uri uri) throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) {
+            return readFromInputStream(inputStream);
+        }
+    }
+
     protected static String readFromRawFile(String filePath) throws Exception {
         Log.d(TAG, "Reading form file: " + filePath);
         return runShellCommand("cat " + filePath);
@@ -160,16 +167,62 @@
         return new File(baseDir, fileName);
     }
 
+    protected static void deleteFromShell(File file) {
+        runShellCommand("rm " + file);
+    }
+
     protected static void writeToFile(File file, String contents) throws Exception {
+        file.getParentFile().mkdirs();
+        file.delete();
+
         try (final PrintWriter out = new PrintWriter(file)) {
             out.print(contents);
         }
+
+        final String actual;
+        try (FileInputStream fis = new FileInputStream(file)) {
+            actual = readFromInputStream(fis);
+        }
+        assertEquals(contents, actual);
     }
 
     protected static void writeToFileFromShell(File file, String contents) throws Exception {
-        final String cmd = "echo \"" + contents + "\" > " + file;
-        final String res = runShellCommand(cmd);
+        runShellCommand("mkdir -p " + file.getParentFile());
+        runShellCommand("rm " + file);
+
+        final String cmd = "dd of=" + file.getAbsolutePath();
+        final ParcelFileDescriptor[] pfds = InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation().executeShellCommandRw(cmd);
+        try (final PrintWriter out =
+                     new PrintWriter(new ParcelFileDescriptor.AutoCloseOutputStream(pfds[1]))) {
+            out.print(contents);
+        }
+
+        final String res;
+        try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfds[0])) {
+            res = readFromInputStream(fis);
+        }
         Log.d(TAG, "Output of '" + cmd + "': '" + res + "'");
+        runShellCommand("sync");
+
+        assertFileContents(file, contents);
+    }
+
+    private static String readFromInputStream(InputStream inputStream) throws Exception {
+        final StringBuffer res = new StringBuffer();
+        final byte[] buf = new byte[512];
+        int bytesRead;
+        while ((bytesRead = inputStream.read(buf)) != -1) {
+            res.append(new String(buf, 0, bytesRead));
+        }
+        return res.toString();
+    }
+
+    protected static void assertFileContents(File file, String contents) {
+        final String cmd = "cat " + file.getAbsolutePath();
+        final String output = runShellCommand(cmd);
+        Log.d(TAG, "Output of '" + cmd + "': '" + output + "'");
+        assertEquals(contents, output);
     }
 
     protected void clearDownloads() {
diff --git a/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/app/src/android/app/cts/InstrumentationTest.java
index c51549c..396c20d 100644
--- a/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -54,6 +54,8 @@
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.SystemUtil;
 
+import androidx.test.filters.FlakyTest;
+
 public class InstrumentationTest extends InstrumentationTestCase {
 
     private static final int WAIT_TIME = 1000;
@@ -103,6 +105,7 @@
                 "\nINSTRUMENTATION_CODE: -1\n", result);
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testWildcardProcessInstrumentation() throws Exception {
         String cmd = "am instrument -w android.app.cts/.WildcardProcessInstrumentation";
         String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
@@ -204,6 +207,7 @@
         assertEquals(threadAllocCount, Debug.getThreadAllocCount());
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testSendTrackballEventSync() throws Exception {
         long now = SystemClock.uptimeMillis();
         MotionEvent orig = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
@@ -217,12 +221,14 @@
         assertEquals(orig.getDownTime(), motionEvent.getDownTime());
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testCallApplicationOnCreate() throws Exception {
         InstrumentationTestStub ca = new InstrumentationTestStub();
         mInstrumentation.callApplicationOnCreate(ca);
         assertTrue(ca.mIsOnCreateCalled);
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testContext() throws Exception {
         Context c1 = mInstrumentation.getContext();
         Context c2 = mInstrumentation.getTargetContext();
@@ -262,6 +268,7 @@
         assertTrue(mActivity.isOnNewIntentCalled());
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testCallActivityOnResume() throws Throwable {
         mActivity.setOnResume(false);
         runTestOnUiThread(new Runnable() {
@@ -327,6 +334,7 @@
         }
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testCallActivityOnSaveInstanceState() throws Throwable {
         final Bundle bundle = new Bundle();
         mActivity.setOnSaveInstanceState(false);
@@ -410,6 +418,7 @@
         assertEquals(KeyEvent.KEYCODE_0, mActivity.getKeyDownList().get(0).getKeyCode());
     }
 
+    @FlakyTest(bugId = 133760851)
     @UiThreadTest
     public void testNewActivity() throws Exception {
         Intent intent = new Intent();
@@ -459,6 +468,7 @@
         assertEquals(KeyEvent.KEYCODE_0, mActivity.getKeyUpCode());
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testCallActivityOnRestart() throws Exception {
         mActivity.setOnRestart(false);
         mInstrumentation.callActivityOnRestart(mActivity);
@@ -473,6 +483,7 @@
         assertTrue(mActivity.isOnStop());
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testCallActivityOnUserLeaving() throws Exception {
         assertFalse(mActivity.isOnLeave());
         mInstrumentation.callActivityOnUserLeaving(mActivity);
diff --git a/tests/app/src/android/app/cts/LauncherActivityTest.java b/tests/app/src/android/app/cts/LauncherActivityTest.java
index 6ab4608..9f5fb23 100644
--- a/tests/app/src/android/app/cts/LauncherActivityTest.java
+++ b/tests/app/src/android/app/cts/LauncherActivityTest.java
@@ -24,6 +24,8 @@
 
 import java.util.List;
 
+import androidx.test.filters.FlakyTest;
+
 public class LauncherActivityTest
         extends ActivityInstrumentationTestCase2<LauncherActivityStub> {
 
@@ -41,6 +43,7 @@
         mActivity = getActivity();
     }
 
+    @FlakyTest(bugId = 133760851)
     public void testLaunchActivity() throws Throwable {
         runTestOnUiThread(new Runnable() {
             public void run() {
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 559a7bf..3513f21 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -51,6 +51,8 @@
 import android.app.UiAutomation;
 import android.app.stubs.AutomaticZenRuleActivity;
 import android.app.stubs.BubblesTestActivity;
+import android.app.stubs.BubblesTestNotDocumentLaunchModeActivity;
+import android.app.stubs.BubblesTestNotEmbeddableActivity;
 import android.app.stubs.BubblesTestService;
 import android.app.stubs.R;
 import android.app.stubs.TestNotificationListener;
@@ -180,6 +182,14 @@
         }
     }
 
+    private void toggleBubbleSetting(boolean enabled) throws InterruptedException {
+        SystemUtil.runWithShellPermissionIdentity(() ->
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
+        Thread.sleep(500); // wait for ranking update
+
+    }
+
     private void insertSingleContact(String name, String phone, String email, boolean starred) {
         final ArrayList<ContentProviderOperation> operationList =
                 new ArrayList<ContentProviderOperation>();
@@ -367,7 +377,7 @@
 
     private void sendAndVerifyBubble(final int id, Notification.Builder builder,
             Notification.BubbleMetadata data, boolean shouldBeBubble) {
-        final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI);
+        final Intent intent = new Intent(mContext, BubblesTestActivity.class);
 
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -1072,11 +1082,7 @@
         }
 
         // turn on bubbles globally
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.NOTIFICATION_BUBBLES, 1));
-
-        Thread.sleep(500); // wait for ranking update
+        toggleBubbleSetting(true);
 
         assertEquals(1, Settings.Secure.getInt(
                 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BUBBLES));
@@ -1102,25 +1108,21 @@
             }
 
             // turn off bubbles globally
-            SystemUtil.runWithShellPermissionIdentity(() ->
-                    Settings.Secure.putInt(mContext.getContentResolver(),
-                            Settings.Secure.NOTIFICATION_BUBBLES, 0));
-
-            Thread.sleep(500); // wait for ranking update
+            toggleBubbleSetting(false);
 
             rankingMap = mListener.mRankingMap;
             outRanking = new NotificationListenerService.Ranking();
             for (String key : rankingMap.getOrderedKeys()) {
                 if (key.contains(mListener.getPackageName())) {
+                    rankingMap.getRanking(key, outRanking);
                     assertFalse(outRanking.canBubble());
                 }
             }
 
             mListener.resetData();
         } finally {
-            SystemUtil.runWithShellPermissionIdentity(() ->
-                    Settings.Secure.putInt(mContext.getContentResolver(),
-                            Settings.Secure.NOTIFICATION_BUBBLES, 0));
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
         }
     }
 
@@ -2398,24 +2400,267 @@
                 badNumberString);
     }
 
-    public void testNotificationManagerBubblePolicy_flagForMessage_failsNoRemoteInput() {
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
-                .setContentTitle("foo")
-                .setStyle(new Notification.MessagingStyle(person)
-                        .setConversationTitle("Bubble Chat")
-                        .addMessage("Hello?",
-                                SystemClock.currentThreadTimeMillis() - 300000, person)
-                        .addMessage("Is it me you're looking for?",
-                                SystemClock.currentThreadTimeMillis(), person)
-                )
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-        sendAndVerifyBubble(1, nb, null /* use default metadata */, false);
+    public void testNotificationManagerBubblePolicy_flagForMessage_failsNoRemoteInput()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Person person = new Person.Builder()
+                    .setName("bubblebot")
+                    .build();
+            Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                    .setContentTitle("foo")
+                    .setStyle(new Notification.MessagingStyle(person)
+                            .setConversationTitle("Bubble Chat")
+                            .addMessage("Hello?",
+                                    SystemClock.currentThreadTimeMillis() - 300000, person)
+                            .addMessage("Is it me you're looking for?",
+                                    SystemClock.currentThreadTimeMillis(), person)
+                    )
+                    .setSmallIcon(android.R.drawable.sym_def_app_icon);
+            sendAndVerifyBubble(1, nb, null /* use default metadata */, false);
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
     }
 
-    public void testNotificationManagerBubblePolicy_flagForMessage_succeeds() {
+    public void testNotificationManagerBubblePolicy_flagForMessage_succeeds()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Person person = new Person.Builder()
+                    .setName("bubblebot")
+                    .build();
+
+            RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel(
+                    "reply").build();
+            PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+            Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+            Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                    inputIntent).addRemoteInput(remoteInput)
+                    .build();
+
+            Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                    .setContentTitle("foo")
+                    .setStyle(new Notification.MessagingStyle(person)
+                            .setConversationTitle("Bubble Chat")
+                            .addMessage("Hello?",
+                                    SystemClock.currentThreadTimeMillis() - 300000, person)
+                            .addMessage("Is it me you're looking for?",
+                                    SystemClock.currentThreadTimeMillis(), person)
+                    )
+                    .setActions(replyAction)
+                    .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+            boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+            sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble);
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_flagForPhonecall() throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
+            serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_SUCCESS);
+            mContext.startService(serviceIntent);
+
+            boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, shouldBeBubble)) {
+                fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
+            }
+
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoPerson()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
+            serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_PERSON);
+            mContext.startService(serviceIntent);
+
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, false /* shouldBeBubble */)) {
+                fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+                        + " or it was a bubble when it shouldn't be");
+            }
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoForeground()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Person person = new Person.Builder()
+                    .setName("bubblebot")
+                    .build();
+            Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                    .setContentTitle("foo")
+                    .setCategory(CATEGORY_CALL)
+                    .addPerson(person)
+                    .setSmallIcon(android.R.drawable.sym_def_app_icon);
+            sendAndVerifyBubble(1, nb, null /* use default metadata */, false /* shouldBeBubble */);
+
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoCategory()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
+            serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_CATEGORY);
+            mContext.startService(serviceIntent);
+
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, false /* shouldBeBubble */)) {
+                fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+                        + " or it was a bubble when it shouldn't be");
+            }
+
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+
+    }
+
+    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoMetadata()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
+            serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_BUBBLE_METADATA);
+            mContext.startService(serviceIntent);
+
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, false /* shouldBeBubble */)) {
+                fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+                        + " or it was a bubble when it shouldn't be");
+            }
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_noFlagForAppNotForeground()
+            throws InterruptedException {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            sendAndVerifyBubble(1, null /* use default notif */, null /* use default metadata */,
+                    false /* shouldBeBubble */);
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_flagForAppForeground() throws Exception {
+        try {
+            // turn on bubbles globally
+            toggleBubbleSetting(true);
+
+            final CountDownLatch latch = new CountDownLatch(2);
+            BroadcastReceiver receiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    latch.countDown();
+                }
+            };
+            IntentFilter filter = new IntentFilter(BubblesTestActivity.BUBBLE_ACTIVITY_OPENED);
+            mContext.registerReceiver(receiver, filter);
+
+            // Start & get the activity
+            BubblesTestActivity a = (BubblesTestActivity) launchSendBubbleActivity();
+
+            // Make sure device is unlocked
+            KeyguardManager keyguardManager =
+                    (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+            keyguardManager.requestDismissKeyguard(a, new KeyguardManager.KeyguardDismissCallback() {
+                @Override
+                public void onDismissSucceeded() {
+                    latch.countDown();
+                }
+            });
+            try {
+                latch.await(100, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+            // Should be foreground now
+            a.sendBubble(1);
+
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, true /* shouldBeBubble */)) {
+                fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
+            }
+
+            // Make ourselves not foreground
+            HomeHelper homeHelper = new HomeHelper();
+            homeHelper.goHome();
+
+            // The notif should be allowed to update as a bubble
+            a.sendBubble(2);
+
+            boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, shouldBeBubble)) {
+                fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
+            }
+
+            // Cancel the notif
+            cancelAndPoll(BUBBLE_NOTIF_ID);
+
+            // Send it again when not foreground, this should not be a bubble & just be a notif
+            a.sendBubble(3);
+            if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
+                    true /* shouldExist */, false /* shouldBeBubble */)) {
+                fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+                        + " or it was a bubble when it shouldn't be");
+            }
+
+            mContext.unregisterReceiver(receiver);
+            homeHelper.close();
+        } finally {
+            // turn off bubbles globally
+            toggleBubbleSetting(false);
+        }
+    }
+
+    public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() throws Exception {
         Person person = new Person.Builder()
                 .setName("bubblebot")
                 .build();
@@ -2439,138 +2684,51 @@
                 .setActions(replyAction)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
-        sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble);
+        final Intent intent = new Intent(mContext, BubblesTestNotEmbeddableActivity.class);
+        final PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        Notification.BubbleMetadata.Builder metadataBuilder =
+                new Notification.BubbleMetadata.Builder()
+                        .setIntent(pendingIntent)
+                        .setIcon(Icon.createWithResource(mContext, R.drawable.black));
+
+        sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
     }
 
-    public void testNotificationManagerBubblePolicy_flagForPhonecall() {
-        Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
-        serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_SUCCESS);
-        mContext.startService(serviceIntent);
-
-        boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, shouldBeBubble)) {
-            fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
-        }
-    }
-
-    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoPerson() {
-        Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
-        serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_PERSON);
-        mContext.startService(serviceIntent);
-
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, false /* shouldBeBubble */)) {
-            fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
-                    + " or it was a bubble when it shouldn't be");
-        }
-    }
-
-    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoForeground() {
+    public void testNotificationManagerBubblePolicy_noFlag_notDocumentLaunchModeAlways() throws Exception {
         Person person = new Person.Builder()
                 .setName("bubblebot")
                 .build();
+
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+
         Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                 .setContentTitle("foo")
-                .setCategory(CATEGORY_CALL)
-                .addPerson(person)
+                .setStyle(new Notification.MessagingStyle(person)
+                        .setConversationTitle("Bubble Chat")
+                        .addMessage("Hello?",
+                                SystemClock.currentThreadTimeMillis() - 300000, person)
+                        .addMessage("Is it me you're looking for?",
+                                SystemClock.currentThreadTimeMillis(), person)
+                )
+                .setActions(replyAction)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
-        sendAndVerifyBubble(1, nb, null /* use default metadata */, false /* shouldBeBubble */);
-    }
 
-    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoCategory() {
-        Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
-        serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_CATEGORY);
-        mContext.startService(serviceIntent);
+        final Intent intent = new Intent(mContext, BubblesTestNotDocumentLaunchModeActivity.class);
+        final PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, intent, 0);
 
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, false /* shouldBeBubble */)) {
-            fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
-                    + " or it was a bubble when it shouldn't be");
-        }
-    }
+        Notification.BubbleMetadata.Builder metadataBuilder =
+                new Notification.BubbleMetadata.Builder()
+                        .setIntent(pendingIntent)
+                        .setIcon(Icon.createWithResource(mContext, R.drawable.black));
 
-    public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoMetadata() {
-        Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
-        serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_BUBBLE_METADATA);
-        mContext.startService(serviceIntent);
-
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, false /* shouldBeBubble */)) {
-            fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
-                    + " or it was a bubble when it shouldn't be");
-        }
-    }
-
-    public void testNotificationManagerBubblePolicy_noFlagForAppNotForeground() {
-        sendAndVerifyBubble(1, null /* use default notif */, null /* use default metadata */,
-                false /* shouldBeBubble */);
-    }
-
-    public void testNotificationManagerBubblePolicy_flagForAppForeground() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(2);
-        BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-            }
-        };
-        IntentFilter filter = new IntentFilter(BubblesTestActivity.BUBBLE_ACTIVITY_OPENED);
-        mContext.registerReceiver(receiver, filter);
-
-        // Start & get the activity
-        BubblesTestActivity a = (BubblesTestActivity) launchSendBubbleActivity();
-
-        // Make sure device is unlocked
-        KeyguardManager keyguardManager =
-                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        keyguardManager.requestDismissKeyguard(a, new KeyguardManager.KeyguardDismissCallback() {
-            @Override
-            public void onDismissSucceeded() {
-                latch.countDown();
-            }
-        });
-        try {
-            latch.await(100, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        // Should be foreground now
-        a.sendBubble(1);
-
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, true /* shouldBeBubble */)) {
-            fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
-        }
-
-        // Make ourselves not foreground
-        HomeHelper homeHelper = new HomeHelper();
-        homeHelper.goHome();
-
-        // The notif should be allowed to update as a bubble
-        a.sendBubble(2);
-
-        boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
-
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, shouldBeBubble)) {
-            fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
-        }
-
-        // Cancel the notif
-        cancelAndPoll(BUBBLE_NOTIF_ID);
-
-        // Send it again when not foreground, this should not be a bubble & just be a notif
-        a.sendBubble(3);
-        if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                true /* shouldExist */, false /* shouldBeBubble */)) {
-            fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
-                    + " or it was a bubble when it shouldn't be");
-        }
-
-        mContext.unregisterReceiver(receiver);
-        homeHelper.close();
+        sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
     }
 }
diff --git a/tests/app/src/android/app/cts/StatusBarManagerTest.java b/tests/app/src/android/app/cts/StatusBarManagerTest.java
index 4ccc921..11eb922 100644
--- a/tests/app/src/android/app/cts/StatusBarManagerTest.java
+++ b/tests/app/src/android/app/cts/StatusBarManagerTest.java
@@ -57,6 +57,7 @@
 
     @After
     public void tearDown() {
+        mStatusBarManager.setDisabledForSetup(false);
         getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
     }
 
diff --git a/tests/app/src/android/app/cts/TileServiceTest.java b/tests/app/src/android/app/cts/TileServiceTest.java
index ee67ffb..35faf28 100644
--- a/tests/app/src/android/app/cts/TileServiceTest.java
+++ b/tests/app/src/android/app/cts/TileServiceTest.java
@@ -20,10 +20,15 @@
 import android.app.Dialog;
 import android.app.UiAutomation;
 import android.app.stubs.TestTileService;
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.service.quicksettings.Tile;
 import android.service.quicksettings.TileService;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
 import android.test.AndroidTestCase;
 
 import androidx.test.filters.FlakyTest;
@@ -43,12 +48,26 @@
     // Number of times to check before failing. This is set so the maximum wait time is about 4s,
     // as some tests were observed to take around 3s.
     private static final long CHECK_RETRIES = 15;
+    // Timeout to wait for launcher
+    private static final long TIMEOUT = 8000;
 
     private TileService mTileService;
+    private Intent homeIntent;
+    private String mLauncherPackage;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+
+        mLauncherPackage = mContext.getPackageManager().resolveActivity(homeIntent,
+                PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
+
+        // Wait for home
+        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        device.pressHome();
+        device.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
     }
 
     @Override
diff --git a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
index eae6016..dbc5f38 100644
--- a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
+++ b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
@@ -275,10 +275,12 @@
 
     private void setService(String service) {
         Log.d(TAG, "Setting app prediction service to " + service);
+        int userId = android.os.Process.myUserHandle().getIdentifier();
         if (service != null) {
-            runShellCommand("cmd app_prediction set temporary-service 0 " + service + " 12000");
+            runShellCommand("cmd app_prediction set temporary-service "
+                    + userId + " " + service + " 12000");
         } else {
-            runShellCommand("cmd app_prediction set temporary-service 0");
+            runShellCommand("cmd app_prediction set temporary-service " + userId);
         }
     }
 
diff --git a/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java b/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java
index 5e41cf4..45a7247 100644
--- a/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java
+++ b/tests/attentionservice/src/android/attentionservice/cts/CtsAttentionServiceDeviceTest.java
@@ -21,9 +21,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.platform.test.annotations.AppModeFull;
 import android.provider.DeviceConfig;
 import android.service.attention.AttentionService;
+import android.text.TextUtils;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -45,6 +48,8 @@
     private static final String SERVICE_ENABLED = "service_enabled";
     private static final String FAKE_SERVICE_PACKAGE =
             CtsTestAttentionService.class.getPackage().getName();
+    private final boolean isTestable =
+            !TextUtils.isEmpty(getAttentionServiceComponent());
 
     @Rule
     public final DeviceConfigStateChangerRule mLookAllTheseRules =
@@ -55,6 +60,7 @@
 
     @Before
     public void setUp() {
+        assumeTrue("Feature not available on this device. Skipping test.", isTestable);
         clearTestableAttentionService();
         CtsTestAttentionService.reset();
         bindToTestService();
diff --git a/tests/autofillservice/res/layout/view_attribute_test_activity.xml b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
index f2b7c3b..268a064 100644
--- a/tests/autofillservice/res/layout/view_attribute_test_activity.xml
+++ b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
@@ -71,21 +71,21 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:paddingTop="23px"
-                android:paddingBottom="31px"
+                android:paddingBottom="11px"
                 android:paddingLeft="37px"
                 android:paddingRight="29px"
                 android:orientation="vertical"
                 android:background="#00F"
                 android:importantForAutofill="yes">
                 <EditText android:id="@+id/tripleNestedView"
-                    android:layout_height="41px"
+                    android:layout_height="11px"
                     android:layout_width="43px"
                     android:background="#FF0"
                     android:importantForAutofill="yes" />
             </LinearLayout>
 
             <EditText android:id="@+id/secondDoubleNestedView"
-                android:layout_height="47px"
+                android:layout_height="17px"
                 android:layout_width="53px"
                 android:background="#F0F"
                 android:importantForAutofill="yes" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 3413422..31fd320 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -1414,6 +1414,21 @@
     }
 
     /**
+     * Asserts {@link View#isAutofilled()} state of the given view, waiting if necessarity to avoid
+     * race conditions.
+     */
+    public static void assertViewAutofillState(@NonNull View view, boolean expected)
+            throws Exception {
+        Timeouts.FILL_TIMEOUT.run("assertViewAutofillState(" + view + ", " + expected + ")",
+                () -> {
+                    final boolean actual = view.isAutofilled();
+                    Log.v(TAG, "assertViewAutofillState(): expected=" + expected + ", actual="
+                            + actual);
+                    return actual == expected ? "not_used" : null;
+                });
+    }
+
+    /**
      * Allows the test to draw overlaid windows.
      *
      * <p>Should call {@link #disallowOverlays()} afterwards.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index e4d77bc..b7241b3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -32,6 +32,7 @@
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertTextOnly;
 import static android.autofillservice.cts.Helper.assertValue;
+import static android.autofillservice.cts.Helper.assertViewAutofillState;
 import static android.autofillservice.cts.Helper.disallowOverlays;
 import static android.autofillservice.cts.Helper.dumpStructure;
 import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
@@ -985,6 +986,7 @@
 
         // Check the results.
         mActivity.assertAutoFilled();
+        assertViewAutofillState(mActivity.getPassword(), true);
 
         // Try to login, it will fail.
         final String loginMessage = mActivity.tapLogin();
@@ -993,6 +995,7 @@
 
         // Set right password...
         mActivity.onPassword((v) -> v.setText("dude"));
+        assertViewAutofillState(mActivity.getPassword(), false);
 
         // ... and try again
         final String expectedMessage = getWelcomeMessage("dude");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 40a9589..2e56a02 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -1024,6 +1024,7 @@
         try {
             file = Helper.createTestFile("screenshot.png");
             if (file != null) {
+                Log.i(TAG, "Taking screenshot on " + file);
                 final Bitmap screenshot = takeScreenshot();
                 Helper.dumpBitmap(screenshot, file);
             }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index be03b84..5d03c03 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -26,6 +26,7 @@
 import android.view.autofill.AutofillValue;
 import android.widget.EditText;
 
+
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -187,15 +188,14 @@
                     // check size of outerView
                     AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
                             "outerView");
-
                     // The size of the view should include all paddings and size of all children
                     assertThat(outerView.getHeight()).isEqualTo(
                             2             // outerView.top
                                     + 11  // nestedView.top
                                     + 23  // doubleNestedView.top
-                                    + 41  // tripleNestedView.height
-                                    + 47  // secondDoubleNestedView.height
-                                    + 31  // doubleNestedView.bottom
+                                    + 11  // tripleNestedView.height
+                                    + 17  // secondDoubleNestedView.height
+                                    + 11  // doubleNestedView.bottom
                                     + 17  // nestedView.bottom
                                     + 5); // outerView.bottom
                     assertThat(outerView.getWidth()).isEqualTo(
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
index dea07e3..beb598e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedLoginActivityTest.java
@@ -21,6 +21,7 @@
 import static android.autofillservice.cts.Helper.ID_USERNAME;
 import static android.autofillservice.cts.Helper.assertHasFlags;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.assertViewAutofillState;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
 import static android.autofillservice.cts.UiBot.LANDSCAPE;
@@ -214,6 +215,74 @@
     }
 
     @Test
+    @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
+    public void testAutoFill_notImportantForAutofill_thenManualRequest() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set IMPORTANT_FOR_AUTOFILL_NO
+        mActivity.onUsername((v) -> v.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO));
+
+        // Set expectations
+        final EditText username = mActivity.getUsername();
+        final AutofillValue expectedFocusedValue = username.getAutofillValue();
+        final AutofillId expectedFocusedId = username.getAutofillId();
+        sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.assertOnFillRequestNotCalled();
+        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request, mActivity, expectedFocusedId, expectedFocusedValue);
+
+        // Make sure standard Autofill UI is not shown.
+        mUiBot.assertNoDatasetsEver();
+
+        // Make sure Augmented Autofill UI is not shown.
+        mAugmentedUiBot.assertUiNeverShown();
+
+        // Try again, forcing it
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("The Dude"))
+                        .build())
+                .build());
+        mActivity.expectAutoFill("dude", "sweet");
+        mActivity.forceAutofillOnUsername();
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+        assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
+        mAugmentedUiBot.assertUiNeverShown();
+
+        mUiBot.selectDataset("The Dude");
+        mActivity.assertAutoFilled();
+
+        // Now force save to make sure the values changes are notified
+        mActivity.onUsername((v) -> v.setText("malkovich"));
+        mActivity.onPassword((v) -> v.setText("malkovich"));
+        final String expectedMessage = getWelcomeMessage("malkovich");
+        final String actualMessage = mActivity.tapLogin();
+        assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+
+        // Assert the snack bar is shown and tap "Save".
+        mUiBot.updateForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        sReplier.assertNoUnhandledSaveRequests();
+        assertThat(saveRequest.datasetIds).isNull();
+
+        // Assert value of expected fields - should not be sanitized.
+        final ViewNode usernameNode = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
+        assertTextAndValue(usernameNode, "malkovich");
+        final ViewNode passwordNode = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
+        assertTextAndValue(passwordNode, "malkovich");
+    }
+
+    @Test
     public void testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField() throws Exception {
         // Set services
         enableService();
@@ -349,6 +418,72 @@
 
     @Test
     @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
+    public void testCancellationSignalCalled_retriggerAugmentedAutofill() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText username = mActivity.getUsername();
+        final AutofillId usernameId = username.getAutofillId();
+        final AutofillValue expectedFocusedValue = username.getAutofillValue();
+        sReplier.addResponse(NO_RESPONSE);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude")
+                        .setField(mActivity.getPassword().getAutofillId(), "sweet")
+                        .build(), usernameId)
+                .build());
+
+        final OneTimeCancellationSignalListener listener =
+                new OneTimeCancellationSignalListener(AUGMENTED_FILL_TIMEOUT.ms() + 5000);
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
+        mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+        final CancellationSignal cancellationSignal = request.cancellationSignal;
+
+        assertThat(cancellationSignal).isNotNull();
+        cancellationSignal.setOnCancelListener(listener);
+
+        // Move focus away to make sure Augmented Autofill UI is gone.
+        mActivity.clearFocus();
+        mAugmentedUiBot.assertUiGone();
+
+        // Set expectations for username again.
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude")
+                        .setField(mActivity.getPassword().getAutofillId(), "sweet")
+                        .build(), usernameId)
+                .build());
+
+        // Tap on username again
+        mActivity.onUsername(View::requestFocus);
+        final AugmentedFillRequest request2 = sAugmentedReplier.getNextFillRequest();
+
+        // Assert first request cancelled
+        listener.assertOnCancelCalled();
+
+        // Assert request
+        assertBasicRequestInfo(request2, mActivity, usernameId, expectedFocusedValue);
+        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+        // ...and autofill this time
+        mActivity.expectAutoFill("dude", "sweet");
+        ui.click();
+        mActivity.assertAutoFilled();
+        mAugmentedUiBot.assertUiGone();
+    }
+
+    @Test
+    @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
     public void testAugmentedAutoFill_multipleRequests() throws Exception {
         // Set services
         enableService();
@@ -418,6 +553,50 @@
     }
 
     @Test
+    @AppModeFull(reason = "testAutoFill_mainServiceReturnedNull_augmentedAutofillOneField enough")
+    public void testAugmentedAutoFill_thenEditField() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText username = mActivity.getUsername();
+        final AutofillId usernameId = username.getAutofillId();
+        final AutofillValue expectedFocusedValue = username.getAutofillValue();
+        sReplier.addResponse(NO_RESPONSE);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude")
+                        .build(), usernameId)
+                .build());
+        mActivity.expectAutoFill("dude");
+
+        // Trigger autofill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+        final AugmentedFillRequest request = sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request, mActivity, usernameId, expectedFocusedValue);
+
+        // Make sure standard Autofill UI is not shown.
+        mUiBot.assertNoDatasetsEver();
+
+        // Make sure Augmented Autofill UI is shown.
+        final UiObject2 ui = mAugmentedUiBot.assertUiShown(usernameId, "Augment Me");
+
+        // Autofill
+        ui.click();
+        mActivity.assertAutoFilled();
+        mAugmentedUiBot.assertUiGone();
+        assertViewAutofillState(mActivity.getUsername(), true);
+
+        // Now change value and make sure autofill status is changed
+        mActivity.onUsername((v) -> v.setText("I AM GROOT"));
+        assertViewAutofillState(mActivity.getUsername(), false);
+    }
+
+    @Test
     public void testAugmentedAutoFill_callback() throws Exception {
         // Set services
         enableService();
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index d1bae57..7705d19 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -29,7 +29,10 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.camera.cts" />
         <option name="runtime-hint" value="12m7s" />
-        <!-- test-timeout unit is ms, value = 33 min -->
-        <option name="test-timeout" value="2000000" />
+        <!-- test-timeout unit is ms, value = 400 min -->
+        <!-- This (host side) timeout value needs to be at least as large as any
+              single test class/method (device side) timeout value set by timeout
+              annotation -->
+        <option name="test-timeout" value="24000000" />
     </test>
 </configuration>
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 48ddd3d..e1b7110 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -130,7 +130,6 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        String[] ids = mCameraManager.getCameraIdList();
         mCharacteristics = new ArrayList<>();
         for (int i = 0; i < mAllCameraIds.length; i++) {
             mCharacteristics.add(mAllStaticInfo.get(mAllCameraIds[i]).getCharacteristics());
@@ -148,7 +147,8 @@
      */
     public void testAvailableStreamConfigs() throws Exception {
         int counter = 0;
-        for (CameraCharacteristics c : mCharacteristics) {
+        for (String id : mAllCameraIds) {
+            CameraCharacteristics c = mAllStaticInfo.get(id).getCharacteristics();
             StreamConfigurationMap config =
                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
             assertNotNull(String.format("No stream configuration map found for: ID %s",
@@ -168,6 +168,7 @@
 
             boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
                     && arrayContains(outputFormats, ImageFormat.Y8);
+            boolean isHiddenPhysicalCamera = !arrayContains(mCameraIds, id);
             boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
 
             assertArrayContains(
@@ -283,8 +284,10 @@
             boolean isExternalCamera = (hwLevel ==
                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
             Size maxVideoSize = null;
-            if (isExternalCamera) {
-                // TODO: for now, use FULLHD 30 as largest possible video size
+            if (isExternalCamera || isHiddenPhysicalCamera) {
+                // TODO: for now, use FULLHD 30 as largest possible video size for external camera.
+                // For hidden physical camera, since we don't require CamcorderProfile to be
+                // available, use FULLHD 30 as maximum video size as well.
                 List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(
                         mAllCameraIds[counter], mCameraManager, FULLHD);
                 for (Size sz : videoSizes) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
index 060c022..0cb5c1b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
@@ -66,7 +66,12 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mFilePath = mContext.getExternalFilesDir(null).getPath();
+
+        File filesDir = mContext.getPackageManager().isInstantApp()
+                ? mContext.getFilesDir()
+                : mContext.getExternalFilesDir(null);
+
+        mFilePath = filesDir.getPath();
     }
 
     @Override
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index be932b7..750e715 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -1182,7 +1182,7 @@
     private boolean deviceHasBattery() {
         final Intent batteryInfo = mContext.registerReceiver(null,
                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-        return batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
+        return batteryInfo != null && batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
     }
 
     private double getScreenSizeInInches() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index dc3eaba..b7510e6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -17,7 +17,6 @@
 package android.hardware.camera2.cts;
 
 import static android.hardware.camera2.cts.CameraTestUtils.*;
-import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.*;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
@@ -39,15 +38,12 @@
 import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
-import android.media.CamcorderProfile;
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.ImageWriter;
 import android.util.Log;
 import android.util.Size;
-import android.view.Display;
 import android.view.Surface;
-import android.view.WindowManager;
 
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 
@@ -1381,12 +1377,14 @@
             Log.i(TAG, String.format(
                     "Testing Camera %s for abandoning surface of a repeating request", id));
 
-            openDevice(id);
-            if (!mStaticInfo.isColorOutputSupported()) {
+            StaticMetadata staticInfo = mAllStaticInfo.get(id);
+            if (!staticInfo.isColorOutputSupported()) {
                 Log.i(TAG, "Camera " + id + " does not support color output, skipping");
                 continue;
             }
 
+            openDevice(id);
+
             try {
 
                 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
@@ -1791,437 +1789,4 @@
         return precaptureComplete;
     }
 
-    /**
-     * Sanity check the configuration tables.
-     */
-    private void sanityCheckConfigurationTables(final int[][][] tables) throws Exception {
-        int tableIdx = 0;
-        for (int[][] table : tables) {
-            int rowIdx = 0;
-            for (int[] row : table) {
-                assertTrue(String.format("Odd number of entries for table %d row %d: %s ",
-                                tableIdx, rowIdx, Arrays.toString(row)),
-                        (row.length % 2) == 0);
-                for (int i = 0; i < row.length; i += 2) {
-                    int format = row[i];
-                    int maxSize = row[i + 1];
-                    assertTrue(String.format("table %d row %d index %d format not valid: %d",
-                                    tableIdx, rowIdx, i, format),
-                            format == PRIV || format == JPEG || format == YUV || format == RAW);
-                    assertTrue(String.format("table %d row %d index %d max size not valid: %d",
-                                    tableIdx, rowIdx, i + 1, maxSize),
-                            maxSize == PREVIEW || maxSize == RECORD ||
-                            maxSize == MAXIMUM || maxSize == VGA);
-                }
-                rowIdx++;
-            }
-            tableIdx++;
-        }
-    }
-
-    /**
-     * Simple holder for resolutions to use for different camera outputs and size limits.
-     */
-    static class MaxStreamSizes {
-        // Format shorthands
-        static final int PRIV = ImageFormat.PRIVATE;
-        static final int JPEG = ImageFormat.JPEG;
-        static final int YUV  = ImageFormat.YUV_420_888;
-        static final int RAW  = ImageFormat.RAW_SENSOR;
-        static final int Y8   = ImageFormat.Y8;
-        static final int HEIC = ImageFormat.HEIC;
-
-        // Max resolution indices
-        static final int PREVIEW = 0;
-        static final int RECORD  = 1;
-        static final int MAXIMUM = 2;
-        static final int VGA = 3;
-        static final int RESOLUTION_COUNT = 4;
-
-        public MaxStreamSizes(StaticMetadata sm, String cameraId, Context context) {
-            Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE,
-                    StaticMetadata.StreamDirection.Output);
-            Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
-                    StaticMetadata.StreamDirection.Output);
-            Size[] y8Sizes = sm.getAvailableSizesForFormatChecked(ImageFormat.Y8,
-                    StaticMetadata.StreamDirection.Output);
-            Size[] jpegSizes = sm.getJpegOutputSizesChecked();
-            Size[] rawSizes = sm.getRawOutputSizesChecked();
-            Size[] heicSizes = sm.getHeicOutputSizesChecked();
-
-            Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
-
-            maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null;
-
-            StreamConfigurationMap configs = sm.getCharacteristics().get(
-                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-            if (sm.isColorOutputSupported()) {
-                maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize);
-                maxYuvSizes[PREVIEW]  = getMaxSize(yuvSizes, maxPreviewSize);
-                maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize);
-
-                if (sm.isExternalCamera()) {
-                    maxPrivSizes[RECORD] = getMaxExternalRecordingSize(cameraId, configs);
-                    maxYuvSizes[RECORD]  = getMaxExternalRecordingSize(cameraId, configs);
-                    maxJpegSizes[RECORD] = getMaxExternalRecordingSize(cameraId, configs);
-                } else {
-                    maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId);
-                    maxYuvSizes[RECORD]  = getMaxRecordingSize(cameraId);
-                    maxJpegSizes[RECORD] = getMaxRecordingSize(cameraId);
-                }
-
-                maxPrivSizes[MAXIMUM] = CameraTestUtils.getMaxSize(privSizes);
-                maxYuvSizes[MAXIMUM] = CameraTestUtils.getMaxSize(yuvSizes);
-                maxJpegSizes[MAXIMUM] = CameraTestUtils.getMaxSize(jpegSizes);
-
-                // Must always be supported, add unconditionally
-                final Size vgaSize = new Size(640, 480);
-                maxPrivSizes[VGA] = vgaSize;
-                maxYuvSizes[VGA] = vgaSize;
-                maxJpegSizes[VGA] = vgaSize;
-
-                if (sm.isMonochromeWithY8()) {
-                    maxY8Sizes[PREVIEW]  = getMaxSize(y8Sizes, maxPreviewSize);
-                    if (sm.isExternalCamera()) {
-                        maxY8Sizes[RECORD]  = getMaxExternalRecordingSize(cameraId, configs);
-                    } else {
-                        maxY8Sizes[RECORD]  = getMaxRecordingSize(cameraId);
-                    }
-                    maxY8Sizes[MAXIMUM] = CameraTestUtils.getMaxSize(y8Sizes);
-                    maxY8Sizes[VGA] = vgaSize;
-                }
-
-                if (sm.isHeicSupported()) {
-                    maxHeicSizes[PREVIEW] = getMaxSize(heicSizes, maxPreviewSize);
-                    maxHeicSizes[RECORD] = getMaxRecordingSize(cameraId);
-                    maxHeicSizes[MAXIMUM] = CameraTestUtils.getMaxSize(heicSizes);
-                    maxHeicSizes[VGA] = vgaSize;
-                }
-            }
-
-            Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE);
-            maxInputPrivSize = privInputSizes != null ?
-                    CameraTestUtils.getMaxSize(privInputSizes) : null;
-            Size[] yuvInputSizes = configs.getInputSizes(ImageFormat.YUV_420_888);
-            maxInputYuvSize = yuvInputSizes != null ?
-                    CameraTestUtils.getMaxSize(yuvInputSizes) : null;
-            Size[] y8InputSizes = configs.getInputSizes(ImageFormat.Y8);
-            maxInputY8Size = y8InputSizes != null ?
-                    CameraTestUtils.getMaxSize(y8InputSizes) : null;
-        }
-
-        public final Size[] maxPrivSizes = new Size[RESOLUTION_COUNT];
-        public final Size[] maxJpegSizes = new Size[RESOLUTION_COUNT];
-        public final Size[] maxYuvSizes = new Size[RESOLUTION_COUNT];
-        public final Size[] maxY8Sizes = new Size[RESOLUTION_COUNT];
-        public final Size[] maxHeicSizes = new Size[RESOLUTION_COUNT];
-        public final Size maxRawSize;
-        // TODO: support non maximum reprocess input.
-        public final Size maxInputPrivSize;
-        public final Size maxInputYuvSize;
-        public final Size maxInputY8Size;
-
-        static public String configToString(int[] config) {
-            StringBuilder b = new StringBuilder("{ ");
-            for (int i = 0; i < config.length; i += 2) {
-                int format = config[i];
-                int sizeLimit = config[i + 1];
-
-                appendFormatSize(b, format, sizeLimit);
-                b.append(" ");
-            }
-            b.append("}");
-            return b.toString();
-        }
-
-        static public String reprocessConfigToString(int[] reprocessConfig) {
-            // reprocessConfig[0..1] is the input configuration
-            StringBuilder b = new StringBuilder("Input: ");
-            appendFormatSize(b, reprocessConfig[0], reprocessConfig[1]);
-
-            // reprocessConfig[0..1] is also output configuration to be captured as reprocess input.
-            b.append(", Outputs: { ");
-            for (int i = 0; i < reprocessConfig.length; i += 2) {
-                int format = reprocessConfig[i];
-                int sizeLimit = reprocessConfig[i + 1];
-
-                appendFormatSize(b, format, sizeLimit);
-                b.append(" ");
-            }
-            b.append("}");
-            return b.toString();
-        }
-
-        static private void appendFormatSize(StringBuilder b, int format, int Size) {
-            switch (format) {
-                case PRIV:
-                    b.append("[PRIV, ");
-                    break;
-                case JPEG:
-                    b.append("[JPEG, ");
-                    break;
-                case YUV:
-                    b.append("[YUV, ");
-                    break;
-                case Y8:
-                    b.append("[Y8, ");
-                    break;
-                case RAW:
-                    b.append("[RAW, ");
-                    break;
-                default:
-                    b.append("[UNK, ");
-                    break;
-            }
-
-            switch (Size) {
-                case PREVIEW:
-                    b.append("PREVIEW]");
-                    break;
-                case RECORD:
-                    b.append("RECORD]");
-                    break;
-                case MAXIMUM:
-                    b.append("MAXIMUM]");
-                    break;
-                case VGA:
-                    b.append("VGA]");
-                    break;
-                default:
-                    b.append("UNK]");
-                    break;
-            }
-        }
-    }
-
-    private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
-            List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
-            List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
-            List<ImageReader> rawTargets, List<ImageReader> heicTargets,
-            List<OutputConfiguration> outputConfigs, int numBuffers,
-            int overrideStreamIndex, List<String> overridePhysicalCameraIds,
-            List<Size> overridePhysicalCameraSizes) {
-
-        ImageDropperListener imageDropperListener = new ImageDropperListener();
-
-        for (int i = 0; i < configs.length; i += 2) {
-            int format = configs[i];
-            int sizeLimit = configs[i + 1];
-            Surface newSurface;
-
-            int numConfigs = 1;
-            if (overrideStreamIndex == i && overridePhysicalCameraIds != null &&
-                    overridePhysicalCameraIds.size() > 1) {
-                numConfigs = overridePhysicalCameraIds.size();
-            }
-            for (int j = 0; j < numConfigs; j++) {
-                switch (format) {
-                    case PRIV: {
-                        Size targetSize = (numConfigs == 1) ? maxSizes.maxPrivSizes[sizeLimit] :
-                                overridePhysicalCameraSizes.get(j);
-                        SurfaceTexture target = new SurfaceTexture(/*random int*/1);
-                        target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
-                        OutputConfiguration config = new OutputConfiguration(new Surface(target));
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        privTargets.add(target);
-                        break;
-                    }
-                    case JPEG: {
-                        Size targetSize = (numConfigs == 1) ? maxSizes.maxJpegSizes[sizeLimit] :
-                                overridePhysicalCameraSizes.get(j);
-                        ImageReader target = ImageReader.newInstance(
-                            targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        jpegTargets.add(target);
-                        break;
-                    }
-                    case HEIC: {
-                        Size targetSize = (numConfigs == 1) ? maxSizes.maxHeicSizes[sizeLimit] :
-                                overridePhysicalCameraSizes.get(j);
-                        ImageReader target = ImageReader.newInstance(
-                            targetSize.getWidth(), targetSize.getHeight(), HEIC, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        heicTargets.add(target);
-                        break;
-                    }
-                    case YUV: {
-                        Size targetSize = (numConfigs == 1) ? maxSizes.maxYuvSizes[sizeLimit] :
-                                overridePhysicalCameraSizes.get(j);
-                        ImageReader target = ImageReader.newInstance(
-                            targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        yuvTargets.add(target);
-                        break;
-                    }
-                    case Y8: {
-                        Size targetSize = (numConfigs == 1) ? maxSizes.maxY8Sizes[sizeLimit] :
-                                overridePhysicalCameraSizes.get(j);
-                        ImageReader target = ImageReader.newInstance(
-                            targetSize.getWidth(), targetSize.getHeight(), Y8, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        y8Targets.add(target);
-                        break;
-                    }
-                    case RAW: {
-                        Size targetSize = (numConfigs == 1) ? maxSizes.maxRawSize :
-                                overridePhysicalCameraSizes.get(j);
-                        // targetSize could be null in the logical camera case where only
-                        // physical camera supports RAW stream.
-                        if (targetSize != null) {
-                            ImageReader target = ImageReader.newInstance(
-                                targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
-                            target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                            OutputConfiguration config =
-                                    new OutputConfiguration(target.getSurface());
-                            if (numConfigs > 1) {
-                                config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                            }
-                            outputConfigs.add(config);
-                            rawTargets.add(target);
-                        }
-                        break;
-                    }
-                    default:
-                        fail("Unknown output format " + format);
-                }
-            }
-        }
-    }
-
-    private static Size getMaxRecordingSize(String cameraId) {
-        int id = Integer.valueOf(cameraId);
-
-        int quality =
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_2160P) ?
-                    CamcorderProfile.QUALITY_2160P :
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_1080P) ?
-                    CamcorderProfile.QUALITY_1080P :
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_720P) ?
-                    CamcorderProfile.QUALITY_720P :
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_480P) ?
-                    CamcorderProfile.QUALITY_480P :
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QVGA) ?
-                    CamcorderProfile.QUALITY_QVGA :
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_CIF) ?
-                    CamcorderProfile.QUALITY_CIF :
-                CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QCIF) ?
-                    CamcorderProfile.QUALITY_QCIF :
-                    -1;
-
-        assertTrue("No recording supported for camera id " + cameraId, quality != -1);
-
-        CamcorderProfile maxProfile = CamcorderProfile.get(id, quality);
-        return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
-    }
-
-    private static Size getMaxExternalRecordingSize(
-            String cameraId, StreamConfigurationMap config) {
-        final Size FULLHD = new Size(1920, 1080);
-
-        Size[] videoSizeArr = config.getOutputSizes(android.media.MediaRecorder.class);
-        List<Size> sizes = new ArrayList<Size>();
-        for (Size sz: videoSizeArr) {
-            if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
-                sizes.add(sz);
-            }
-        }
-        List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
-        for (Size sz : videoSizes) {
-            long minFrameDuration = config.getOutputMinFrameDuration(
-                    android.media.MediaRecorder.class, sz);
-            // Give some margin for rounding error
-            if (minFrameDuration > (1e9 / 30.1)) {
-                Log.i(TAG, "External camera " + cameraId + " has max video size:" + sz);
-                return sz;
-            }
-        }
-        fail("Camera " + cameraId + " does not support any 30fps video output");
-        return FULLHD; // doesn't matter what size is returned here
-    }
-
-    /**
-     * Get maximum size in list that's equal or smaller to than the bound.
-     * Returns null if no size is smaller than or equal to the bound.
-     */
-    private static Size getMaxSize(Size[] sizes, Size bound) {
-        if (sizes == null || sizes.length == 0) {
-            throw new IllegalArgumentException("sizes was empty");
-        }
-
-        Size sz = null;
-        for (Size size : sizes) {
-            if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
-
-                if (sz == null) {
-                    sz = size;
-                } else {
-                    long curArea = sz.getWidth() * (long) sz.getHeight();
-                    long newArea = size.getWidth() * (long) size.getHeight();
-                    if ( newArea > curArea ) {
-                        sz = size;
-                    }
-                }
-            }
-        }
-
-        assertTrue("No size under bound found: " + Arrays.toString(sizes) + " bound " + bound,
-                sz != null);
-
-        return sz;
-    }
-
-    private static Size getMaxPreviewSize(Context context, String cameraId) {
-        try {
-            WindowManager windowManager =
-                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-            Display display = windowManager.getDefaultDisplay();
-
-            int width = display.getWidth();
-            int height = display.getHeight();
-
-            if (height > width) {
-                height = width;
-                width = display.getHeight();
-            }
-
-            CameraManager camMgr =
-                (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-            List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
-                cameraId, camMgr, PREVIEW_SIZE_BOUND);
-
-            if (orderedPreviewSizes != null) {
-                for (Size size : orderedPreviewSizes) {
-                    if (width >= size.getWidth() &&
-                        height >= size.getHeight())
-                        return size;
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString());
-        }
-        return PREVIEW_SIZE_BOUND;
-    }
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 168260b..b56fcbf 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -56,9 +56,6 @@
     private static final float MIN_FPS_FOR_FULL_DEVICE = 20.0f;
     private String mCameraId;
 
-    // Last defined capability enum, for iterating over all of them
-    private static final int LAST_CAPABILITY_ENUM = REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
-
     /**
      * Test the available capability for different hardware support level devices.
      */
@@ -172,7 +169,7 @@
             List<Integer> availableCaps = mStaticInfo.getAvailableCapabilitiesChecked();
 
             for (Integer capability = REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
-                    capability <= LAST_CAPABILITY_ENUM; capability++) {
+                    capability <= StaticMetadata.LAST_CAPABILITY_ENUM; capability++) {
                 boolean isCapabilityAvailable = availableCaps.contains(capability);
                 validateCapability(capability, isCapabilityAvailable);
             }
@@ -424,6 +421,9 @@
             case REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME:
                 // Tested in ExtendedCameraCharacteristicsTest
                 return;
+            case REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA:
+                // No other restrictions with other metadata keys which  are reliably testable.
+                return;
             default:
                 capabilityName = "Unknown";
                 assertTrue(String.format("Unknown capability set: %d", capability),
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 80d570b..4cd0046 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -48,6 +48,7 @@
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 
+import java.io.File;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -115,7 +116,12 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         mCollector = new CameraErrorCollector();
-        mDebugFileNameBase = getContext().getExternalFilesDir(null).getPath();
+
+        File filesDir = mContext.getPackageManager().isInstantApp()
+                ? mContext.getFilesDir()
+                : mContext.getExternalFilesDir(null);
+
+        mDebugFileNameBase = filesDir.getPath();
 
         mAllStaticInfo = new HashMap<String, StaticMetadata>();
         List<String> hiddenPhysicalIds = new ArrayList<>();
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index 672a01e..c345b41 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -324,8 +324,9 @@
 
     protected StaticMetadata getStaticInfo(String cameraId) {
         CameraHolder camera = getCameraHolder(cameraId);
-        assertTrue("Camera is not openned", camera.isOpened());
-        return camera.getStaticInfo();
+        StaticMetadata staticInfo = camera.getStaticInfo();
+        assertNotNull("Camera " + cameraId + " static info is null", staticInfo);
+        return staticInfo;
     }
 
     protected List<Size> getOrderedPreviewSizes(String cameraId) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index b2b7828..1f8b792 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -59,6 +59,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -135,7 +136,12 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         mCollector = new CameraErrorCollector();
-        mDebugFileNameBase = mContext.getExternalFilesDir(null).getPath();
+
+        File filesDir = mContext.getPackageManager().isInstantApp()
+                ? mContext.getFilesDir()
+                : mContext.getExternalFilesDir(null);
+
+        mDebugFileNameBase = filesDir.getPath();
 
         mAllStaticInfo = new HashMap<String, StaticMetadata>();
         List<String> hiddenPhysicalIds = new ArrayList<>();
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index 51605f0..b6fe28a 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -150,6 +150,7 @@
      */
     private void initializeMessageLooper(final int cameraId) throws IOException {
         final ConditionVariable startDone = new ConditionVariable();
+        final CameraCtsActivity activity = mActivityRule.getActivity();
         new Thread() {
             @Override
             public void run() {
@@ -161,7 +162,7 @@
                 mLooper = Looper.myLooper();
                 try {
                     mIsExternalCamera = CameraUtils.isExternal(
-                            mActivityRule.getActivity().getApplicationContext(), cameraId);
+                            activity.getApplicationContext(), cameraId);
                 } catch (Exception e) {
                     Log.e(TAG, "Unable to query external camera!" + e);
                 }
@@ -185,8 +186,13 @@
             fail("initializeMessageLooper: start timeout");
         }
         assertNotNull("Fail to open camera.", mCamera);
-        mCamera.setPreviewDisplay(mActivityRule.getActivity().getSurfaceView().getHolder());
-        mJpegPath = mActivityRule.getActivity().getExternalFilesDir(null).getPath() + "/test.jpg";
+        mCamera.setPreviewDisplay(activity.getSurfaceView().getHolder());
+
+        File parent = activity.getPackageManager().isInstantApp()
+                ? activity.getFilesDir()
+                : activity.getExternalFilesDir(null);
+
+        mJpegPath = parent.getPath() + "/test.jpg";
     }
 
     /*
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java
index 1f12cea..186706b 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java
@@ -58,7 +58,12 @@
         mErrorServiceConnection.start();
 
         mMediaRecorder = new MediaRecorder();
-        mOutputPath = new File(getExternalFilesDir(null), "record.out").getAbsolutePath();
+
+        File filesDir = getPackageManager().isInstantApp()
+                ? getFilesDir()
+                : getExternalFilesDir(null);
+
+        mOutputPath = new File(filesDir, "record.out").getAbsolutePath();
     }
 
     @Override
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 6af6be5..d622c23 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -73,6 +73,10 @@
     private final CheckLevel mLevel;
     private final CameraErrorCollector mCollector;
 
+    // Last defined capability enum, for iterating over all of them
+    public static final int LAST_CAPABILITY_ENUM =
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
+
     // Access via getAeModeName() to account for vendor extensions
     public static final String[] AE_MODE_NAMES = new String[] {
         "AE_MODE_OFF",
@@ -1958,7 +1962,7 @@
 
         checkArrayValuesInRange(key, availableCaps,
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
+                LAST_CAPABILITY_ENUM);
         capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
         return capList;
     }
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index 9a9c743..e7f2e68 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -15,8 +15,8 @@
  */
 package android.contentcaptureservice.cts;
 
+import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
 import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
-import static android.contentcaptureservice.cts.Helper.SYSTEM_SERVICE_NAME;
 import static android.contentcaptureservice.cts.Helper.resetService;
 import static android.contentcaptureservice.cts.Helper.sContext;
 import static android.contentcaptureservice.cts.Helper.setService;
@@ -70,7 +70,7 @@
     protected final String mTag = getClass().getSimpleName();
 
     private final RequiredServiceRule mRequiredServiceRule =
-            new RequiredServiceRule(SYSTEM_SERVICE_NAME);
+            new RequiredServiceRule(CONTENT_CAPTURE_MANAGER_SERVICE);
 
     private final DeviceConfigStateChangerRule mVerboseLoggingRule =
             new DeviceConfigStateChangerRule(
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CanaryTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CanaryTest.java
index f1fb439..bf65516 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CanaryTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CanaryTest.java
@@ -15,8 +15,8 @@
  */
 package android.contentcaptureservice.cts;
 
+import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
 import static android.contentcaptureservice.cts.Helper.RESOURCE_STRING_SERVICE_NAME;
-import static android.contentcaptureservice.cts.Helper.SYSTEM_SERVICE_NAME;
 import static android.contentcaptureservice.cts.Helper.getInternalString;
 import static android.contentcaptureservice.cts.Helper.sContext;
 
@@ -43,9 +43,9 @@
 
     @Test
     public void logHasService() {
-        final boolean hasService = RequiredServiceRule.hasService(SYSTEM_SERVICE_NAME);
-        Log.d(TAG, "has " + SYSTEM_SERVICE_NAME + ": " + hasService);
-        assumeTrue("device doesn't have service " + SYSTEM_SERVICE_NAME, hasService);
+        final boolean hasService = RequiredServiceRule.hasService(CONTENT_CAPTURE_MANAGER_SERVICE);
+        Log.d(TAG, "has " + CONTENT_CAPTURE_MANAGER_SERVICE + ": " + hasService);
+        assumeTrue("device doesn't have service " + CONTENT_CAPTURE_MANAGER_SERVICE, hasService);
     }
 
     @Test
@@ -54,9 +54,9 @@
         final String enableSettings = new DeviceConfigStateManager(sContext,
                 DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED).get();
-        final boolean hasService = RequiredServiceRule.hasService(SYSTEM_SERVICE_NAME);
+        final boolean hasService = RequiredServiceRule.hasService(CONTENT_CAPTURE_MANAGER_SERVICE);
         Log.d(TAG, "Service resource: '" + serviceName + "' Settings: '" + enableSettings
-                + "' Has '" + SYSTEM_SERVICE_NAME + "': " + hasService);
+                + "' Has '" + CONTENT_CAPTURE_MANAGER_SERVICE + "': " + hasService);
 
         // We're only asserting when the OEM defines a service
         assumeTrue("service resource (" + serviceName + ") is not defined",
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
index aa0bfb1..8e97aac 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -59,8 +59,6 @@
 
     public static final long MY_EPOCH = SystemClock.uptimeMillis();
 
-    public static final String SYSTEM_SERVICE_NAME = "content_capture";
-
     public static final String RESOURCE_STRING_SERVICE_NAME = "config_defaultContentCaptureService";
 
     public static final Context sContext = getInstrumentation().getTargetContext();
diff --git a/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java b/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java
index ead3fa8..c9f7fbb 100644
--- a/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java
+++ b/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java
@@ -150,12 +150,14 @@
      */
     private static void setService(@NonNull String service) {
         Log.d(TAG, "Setting service to " + service);
+        int userId = android.os.Process.myUserHandle().getIdentifier();
         runShellCommand(
-                "cmd content_suggestions set temporary-service 0 %s 120000", service);
+                "cmd content_suggestions set temporary-service %d %s 120000", userId, service);
     }
 
     private static void resetService() {
         Log.d(TAG, "Resetting service");
-        runShellCommand("cmd content_suggestions set temporary-service 0");
+        int userId = android.os.Process.myUserHandle().getIdentifier();
+        runShellCommand("cmd content_suggestions set temporary-service %d", userId);
     }
 }
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index 3ed4cfe..77fa6dd 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -61,6 +61,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -178,6 +179,7 @@
                 APP_A_FOREGROUND_ACTIVITY);
     }
 
+    @Ignore // test temporarily disabled due to bg activity start grace period introduction
     @Test
     public void testActivityNotBlockedwhenForegroundActivityLaunchInSameTask() throws Exception {
         // Start foreground activity, and foreground activity able to launch background activity
@@ -208,6 +210,7 @@
                 APP_A_FOREGROUND_ACTIVITY);
     }
 
+    @Ignore // test temporarily disabled due to bg activity start grace period introduction
     @Test
     public void testActivityNotBlockedWhenForegroundActivityLaunchInDifferentTask()
             throws Exception {
@@ -240,6 +243,7 @@
         assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
     }
 
+    @Ignore // test temporarily disabled due to bg activity start grace period introduction
     @Test
     @FlakyTest(bugId = 130800326)
     public void testActivityBlockedWhenForegroundActivityRestartsItself() throws Exception {
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 ee0d12c..d3aabb7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -45,6 +45,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.content.ComponentName;
@@ -595,6 +596,10 @@
      */
     @Test
     public void testStackFocusSwitchOnTouchEvent() throws Exception {
+        // If config_perDisplayFocusEnabled, the focus will not move even if touching on
+        // the Activity in the different display.
+        assumeFalse(perDisplayFocusEnabled());
+
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
@@ -630,6 +635,8 @@
      */
     @Test
     public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
+        assumeFalse(perDisplayFocusEnabled());
+
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
@@ -712,6 +719,8 @@
      */
     @Test
     public void testSecondaryDisplayFocus() throws Exception {
+        assumeFalse(perDisplayFocusEnabled());
+
         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
             launchActivity(TEST_ACTIVITY);
             mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index d662bd2..07a8dba 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -36,6 +36,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.app.Activity;
@@ -477,6 +478,10 @@
     @Test
     @FlakyTest(bugId = 131005232)
     public void testImeWindowCanSwitchWhenTopFocusedDisplayChange() throws Exception {
+        // If config_perDisplayFocusEnabled, the focus will not move even if touching on
+        // the Activity in the different display.
+        assumeFalse(perDisplayFocusEnabled());
+
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
              final TestActivitySession<ImeTestActivity> imeTestActivitySession = new
                      TestActivitySession<>();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
index 615e572..9a6d134 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
@@ -40,6 +40,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -124,6 +125,12 @@
                 .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
     }
 
+    /** Checks if per-display-focus is enabled in the device. */
+    private static boolean perDisplayFocusEnabled() {
+        return getInstrumentation().getTargetContext().getResources()
+                    .getBoolean(android.R.bool.config_perDisplayFocusEnabled);
+    }
+
     /**
      * Test the following conditions:
      * - Each display can have a focused window at the same time.
@@ -141,6 +148,8 @@
         sendAndAssertTargetConsumedKey(primaryActivity, KEYCODE_1, DEFAULT_DISPLAY);
 
         assumeTrue(supportsMultiDisplay());
+        // If config_perDisplayFocusEnabled, tapping on a display will not move the focus.
+        assumeFalse(perDisplayFocusEnabled());
         try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
             final int secondaryDisplayId = displaySession.createDisplay(
                     getInstrumentation().getTargetContext()).getDisplayId();
@@ -149,15 +158,7 @@
             sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_2, INVALID_DISPLAY);
             sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_3, secondaryDisplayId);
 
-            final boolean perDisplayFocusEnabled = getInstrumentation()
-                    .getTargetContext().getResources()
-                    .getBoolean(android.R.bool.config_perDisplayFocusEnabled);
-            if (perDisplayFocusEnabled) {
-                primaryActivity.assertWindowFocusState(true /* hasFocus */);
-                sendAndAssertTargetConsumedKey(primaryActivity, KEYCODE_4, DEFAULT_DISPLAY);
-            } else {
-                primaryActivity.waitAndAssertWindowFocusState(false /* hasFocus */);
-            }
+            primaryActivity.waitAndAssertWindowFocusState(false /* hasFocus */);
 
             // Press display-unspecified keys and a display-specified key but not release them.
             sendKey(ACTION_DOWN, KEYCODE_5, INVALID_DISPLAY);
@@ -174,9 +175,7 @@
             // key events sent to secondary activity would be cancelled.
             secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_5, FLAG_CANCELED);
             secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_7, FLAG_CANCELED);
-            if (!perDisplayFocusEnabled) {
-                secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_6, FLAG_CANCELED);
-            }
+            secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_6, FLAG_CANCELED);
             assertEquals(secondaryActivity.getLogTag() + " must only receive expected events.",
                     0 /* expected event count */, secondaryActivity.getKeyEventCount());
 
@@ -192,11 +191,7 @@
     @FlakyTest(bugId = 131005232)
     public void testMovingDisplayToTopByKeyEvent() throws InterruptedException {
         assumeTrue(supportsMultiDisplay());
-
-        if (getInstrumentation().getTargetContext().getResources().getBoolean(
-                android.R.bool.config_perDisplayFocusEnabled)) {
-            return;
-        }
+        assumeFalse(perDisplayFocusEnabled());
 
         final PrimaryActivity primaryActivity = startActivity(PrimaryActivity.class,
                 DEFAULT_DISPLAY);
@@ -245,6 +240,7 @@
         primaryActivity.waitAndAssertPointerCaptureState(true /* hasCapture */);
 
         assumeTrue(supportsMultiDisplay());
+        assumeFalse(perDisplayFocusEnabled());
         try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
             final int secondaryDisplayId = displaySession.createDisplay(
                     getInstrumentation().getTargetContext()).getDisplayId();
@@ -297,11 +293,7 @@
     @Test
     public void testTapFocusableWindow() throws InterruptedException {
         assumeTrue(supportsMultiDisplay());
-        // Ensure per display focus is disabled before proceeding with this test
-        if (getInstrumentation().getTargetContext().getResources().getBoolean(
-                android.R.bool.config_perDisplayFocusEnabled)) {
-            return;
-        }
+        assumeFalse(perDisplayFocusEnabled());
 
         PrimaryActivity primaryActivity = startActivity(PrimaryActivity.class, DEFAULT_DISPLAY);
 
@@ -326,11 +318,7 @@
     @FlakyTest(bugId = 130467737)
     public void testTapNonFocusableWindow() throws InterruptedException {
         assumeTrue(supportsMultiDisplay());
-        // Ensure per display focus is disabled before proceeding with this test
-        if (getInstrumentation().getTargetContext().getResources().getBoolean(
-                android.R.bool.config_perDisplayFocusEnabled)) {
-            return;
-        }
+        assumeFalse(perDisplayFocusEnabled());
 
         PrimaryActivity primaryActivity = startActivity(PrimaryActivity.class, DEFAULT_DISPLAY);
 
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 d6a31dc..4368e87 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
@@ -912,6 +912,11 @@
         return display != null && display.getState() == Display.STATE_ON;
     }
 
+    protected static boolean perDisplayFocusEnabled() {
+        return getInstrumentation().getTargetContext().getResources()
+                .getBoolean(android.R.bool.config_perDisplayFocusEnabled);
+    }
+
     /**
      * Test @Rule class that disables screen doze settings before each test method running and
      * restoring to initial values after test method finished.
diff --git a/tests/providerui/Android.mk b/tests/providerui/Android.mk
index be1c226..d02a099 100644
--- a/tests/providerui/Android.mk
+++ b/tests/providerui/Android.mk
@@ -40,6 +40,6 @@
 
 LOCAL_PACKAGE_NAME := CtsProviderUiTestCases
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/providerui/AndroidManifest.xml b/tests/providerui/AndroidManifest.xml
index 3e7329c..8434397 100644
--- a/tests/providerui/AndroidManifest.xml
+++ b/tests/providerui/AndroidManifest.xml
@@ -19,6 +19,9 @@
           package="android.providerui.cts">
 
     <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/tests/providerui/res/raw/text.txt b/tests/providerui/res/raw/text.txt
new file mode 100644
index 0000000..2a02d41
--- /dev/null
+++ b/tests/providerui/res/raw/text.txt
@@ -0,0 +1 @@
+TEST
diff --git a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
index b5f30b6..7746c98 100644
--- a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
+++ b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
@@ -25,13 +25,17 @@
 import static android.Manifest.permission.RECORD_AUDIO;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 
-import static android.media.MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO;
-import static android.media.MediaMetadataRetriever.METADATA_KEY_LOCATION;
-import static android.media.MediaMetadataRetriever.METADATA_KEY_DURATION;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
 import android.content.ContentResolver;
-import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.UriPermission;
@@ -40,11 +44,11 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
+import android.content.res.AssetFileDescriptor;
 import android.media.ExifInterface;
-import android.media.MediaMetadataRetriever;
-import android.media.MediaScannerConnection;
 import android.net.Uri;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.os.storage.StorageManager;
@@ -57,7 +61,7 @@
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
+import android.system.Os;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -65,26 +69,39 @@
 import androidx.core.content.FileProvider;
 import androidx.test.InstrumentationRegistry;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 
-public class MediaStoreUiTest extends InstrumentationTestCase {
+@RunWith(Parameterized.class)
+public class MediaStoreUiTest {
     private static final String TAG = "MediaStoreUiTest";
 
     private static final int REQUEST_CODE = 42;
-    private static final String CONTENT = "Test";
 
+    private Instrumentation mInstrumentation;
+    private Context mContext;
     private UiDevice mDevice;
     private GetResultActivity mActivity;
 
@@ -92,16 +109,28 @@
     private Uri mMediaStoreUri;
     private String mTargetPackageName;
 
-    @Override
-    public void setUp() throws Exception {
-        mDevice = UiDevice.getInstance(getInstrumentation());
+    @Parameter(0)
+    public String mVolumeName;
 
-        final Context context = getInstrumentation().getContext();
-        mActivity = launchActivity(context.getPackageName(), GetResultActivity.class, null);
+    @Parameters
+    public static Iterable<? extends Object> data() {
+        return MediaStore.getExternalVolumeNames(InstrumentationRegistry.getTargetContext());
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = InstrumentationRegistry.getTargetContext();
+        mDevice = UiDevice.getInstance(mInstrumentation);
+
+        final Intent intent = new Intent(mContext, GetResultActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mActivity = (GetResultActivity) mInstrumentation.startActivitySync(intent);
+        mInstrumentation.waitForIdleSync();
         mActivity.clearResult();
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
         if (mFile != null) {
             mFile.delete();
@@ -118,6 +147,7 @@
         mActivity.finish();
     }
 
+    @Test
     public void testGetDocumentUri() throws Exception {
         if (!supportsHardware()) return;
 
@@ -130,20 +160,22 @@
         assertNotNull(docUri);
 
         final ContentResolver resolver = mActivity.getContentResolver();
-        try (ParcelFileDescriptor fd = resolver.openFileDescriptor(docUri, "rw")) {
-            // Test reading
-            try (final BufferedReader reader =
-                         new BufferedReader(new FileReader(fd.getFileDescriptor()))) {
-                assertEquals(CONTENT, reader.readLine());
-            }
 
-            // Test writing
-            try (final OutputStream out = new FileOutputStream(fd.getFileDescriptor())) {
-                out.write(CONTENT.getBytes());
-            }
+        // Test reading
+        final byte[] expected = "TEST".getBytes();
+        final byte[] actual = new byte[4];
+        try (ParcelFileDescriptor fd = resolver.openFileDescriptor(docUri, "r")) {
+            Os.read(fd.getFileDescriptor(), actual, 0, actual.length);
+            assertArrayEquals(expected, actual);
+        }
+
+        // Test writing
+        try (ParcelFileDescriptor fd = resolver.openFileDescriptor(docUri, "wt")) {
+            Os.write(fd.getFileDescriptor(), expected, 0, expected.length);
         }
     }
 
+    @Test
     public void testGetDocumentUri_ThrowsWithoutPermission() throws Exception {
         if (!supportsHardware()) return;
 
@@ -157,18 +189,22 @@
         }
     }
 
+    @Test
     public void testGetDocumentUri_Symmetry() throws Exception {
         if (!supportsHardware()) return;
 
         prepareFile();
 
         final Uri treeUri = acquireAccess(mFile, Environment.DIRECTORY_DOCUMENTS);
+        Log.v(TAG, "Tree " + treeUri);
         assertNotNull(treeUri);
 
         final Uri docUri = MediaStore.getDocumentUri(mActivity, mMediaStoreUri);
+        Log.v(TAG, "Document " + docUri);
         assertNotNull(docUri);
 
         final Uri mediaUri = MediaStore.getMediaUri(mActivity, docUri);
+        Log.v(TAG, "Media " + mediaUri);
         assertNotNull(mediaUri);
 
         assertEquals(mMediaStoreUri, mediaUri);
@@ -185,8 +221,7 @@
     private void maybeGrantRuntimePermission(String pkg, Set<String> requested, String permission)
             throws NameNotFoundException {
         // We only need to grant dangerous permissions
-        final Context context = getInstrumentation().getContext();
-        if ((context.getPackageManager().getPermissionInfo(permission, 0).getProtection()
+        if ((mContext.getPackageManager().getPermissionInfo(permission, 0).getProtection()
                 & PermissionInfo.PROTECTION_DANGEROUS) == 0) {
             return;
         }
@@ -197,18 +232,11 @@
         }
     }
 
-    private void maybeRevokeRuntimePermission(String pkg, Set<String> requested, String permission)
-            throws NameNotFoundException {
-        if (requested.contains(permission)) {
-            InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                    .revokeRuntimePermission(pkg, permission);
-        }
-    }
-
     /**
      * Verify that whoever handles {@link MediaStore#ACTION_IMAGE_CAPTURE} can
      * correctly write the contents into a passed {@code content://} Uri.
      */
+    @Test
     public void testImageCaptureWithInadequeteLocationPermissions() throws Exception {
         Set<String> perms = new HashSet<>();
         perms.add(ACCESS_COARSE_LOCATION);
@@ -225,15 +253,14 @@
             throws Exception {
         assertFalse("testImageCaptureWithoutLocation should not be passed ACCESS_FINE_LOCATION",
                 locationPermissions.contains(ACCESS_FINE_LOCATION));
-        final Context context = getInstrumentation().getContext();
-        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
             Log.d(TAG, "Skipping due to lack of camera");
             return;
         }
 
         String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
 
-        final File targetDir = new File(context.getFilesDir(), "debug");
+        final File targetDir = new File(mContext.getFilesDir(), "debug");
         final File target = new File(targetDir, timeStamp  + "capture.jpg");
 
         targetDir.mkdirs();
@@ -241,21 +268,30 @@
 
         final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
         intent.putExtra(MediaStore.EXTRA_OUTPUT,
-                FileProvider.getUriForFile(context, "android.providerui.cts.fileprovider", target));
+                FileProvider.getUriForFile(mContext, "android.providerui.cts.fileprovider", target));
 
         // Figure out who is going to answer the phone
-        final ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0);
-        final String pkg = ri.activityInfo.packageName;
+        final ResolveInfo ri = mContext.getPackageManager().resolveActivity(intent, 0);
+        final String answeringPkg = ri.activityInfo.packageName;
         Log.d(TAG, "We're probably launching " + ri);
 
-        final PackageInfo pi = context.getPackageManager().getPackageInfo(pkg,
+        final PackageInfo pi = mContext.getPackageManager().getPackageInfo(answeringPkg,
                 PackageManager.GET_PERMISSIONS);
-        final Set<String> req = new HashSet<>();
-        req.addAll(Arrays.asList(pi.requestedPermissions));
+        final Set<String> answeringReq = new HashSet<>();
+        answeringReq.addAll(Arrays.asList(pi.requestedPermissions));
+        // Grant the 'answering' app all the permissions they might want.
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, CAMERA);
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, ACCESS_FINE_LOCATION);
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, ACCESS_COARSE_LOCATION);
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, ACCESS_BACKGROUND_LOCATION);
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, RECORD_AUDIO);
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, READ_EXTERNAL_STORAGE);
+        maybeGrantRuntimePermission(answeringPkg, answeringReq, WRITE_EXTERNAL_STORAGE);
+        SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
 
-        grantRequisitePermissions(pkg, req, locationPermissions);
+        grantSelfRequisitePermissions(locationPermissions);
 
-        Result result = getImageCaptureIntentResult(intent, pkg);
+        Result result = getImageCaptureIntentResult(intent, answeringPkg);
 
         assertTrue("exists", target.exists());
         assertTrue("has data", target.length() > 65536);
@@ -270,31 +306,20 @@
         Boolean hasLocation = exif.getLatLong(latLong);
         assertTrue("Should not contain location information latitude: " + latLong[0] +
                 " longitude: " + latLong[1], !hasLocation);
-        revokeRequisitePermissions(pkg, req, locationPermissions);
     }
 
-    private void grantRequisitePermissions(String pkg, Set<String> req,
-            Set<String> locationPermissions) throws Exception {
-        // Grant them all the permissions they might want.
-        maybeGrantRuntimePermission(pkg, req, CAMERA);
-        maybeGrantRuntimePermission(pkg, req, RECORD_AUDIO);
-        maybeGrantRuntimePermission(pkg, req, READ_EXTERNAL_STORAGE);
-        maybeGrantRuntimePermission(pkg, req, WRITE_EXTERNAL_STORAGE);
-        SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
-        for (String perm : locationPermissions) {
-            maybeGrantRuntimePermission(pkg, req, perm);
-        }
-    }
-
-    private void revokeRequisitePermissions(String pkg, Set<String> req, Set<String> perms)
+    private void grantSelfRequisitePermissions(Set<String> locationPermissions)
             throws Exception {
-        // So that the other tests don't start with this permission granted.
-        for (String perm : perms) {
-            maybeRevokeRuntimePermission(pkg, req, perm);
+        String selfPkg = mContext.getPackageName();
+        for (String perm : locationPermissions) {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .grantRuntimePermission(selfPkg, perm);
+            assertTrue("Permission " + perm + "could not be granted",
+                    mContext.checkSelfPermission(perm) == PackageManager.PERMISSION_GRANTED);
         }
     }
 
-    private Result getImageCaptureIntentResult(Intent intent, String pkg)
+    private Result getImageCaptureIntentResult(Intent intent, String answeringPkg)
             throws Exception {
 
         mActivity.startActivityForResult(intent, REQUEST_CODE);
@@ -319,12 +344,12 @@
         // Hrm, that didn't work; let's try an alternative approach of digging
         // around for a shutter button
         if (result == null) {
-            maybeClick(new UiSelector().resourceId(pkg + ":id/shutter_button"));
+            maybeClick(new UiSelector().resourceId(answeringPkg + ":id/shutter_button"));
             mDevice.waitForIdle();
             SystemClock.sleep(5 * DateUtils.SECOND_IN_MILLIS);
-            maybeClick(new UiSelector().resourceId(pkg + ":id/shutter_button"));
+            maybeClick(new UiSelector().resourceId(answeringPkg + ":id/shutter_button"));
             mDevice.waitForIdle();
-            maybeClick(new UiSelector().resourceId(pkg + ":id/done_button"));
+            maybeClick(new UiSelector().resourceId(answeringPkg + ":id/done_button"));
             mDevice.waitForIdle();
 
             result = mActivity.getResult(15, TimeUnit.SECONDS);
@@ -333,10 +358,10 @@
 
         // Grr, let's try hunting around even more
         if (result == null) {
-            maybeClick(By.pkg(pkg).descContains("Capture"));
+            maybeClick(By.pkg(answeringPkg).descContains("Capture"));
             mDevice.waitForIdle();
             SystemClock.sleep(5 * DateUtils.SECOND_IN_MILLIS);
-            maybeClick(By.pkg(pkg).descContains("Done"));
+            maybeClick(By.pkg(answeringPkg).descContains("Done"));
             mDevice.waitForIdle();
 
             result = mActivity.getResult(15, TimeUnit.SECONDS);
@@ -356,40 +381,20 @@
     }
 
     private boolean supportsHardware() {
-        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        final PackageManager pm = mContext.getPackageManager();
         return !pm.hasSystemFeature("android.hardware.type.television")
                 && !pm.hasSystemFeature("android.hardware.type.watch");
     }
 
     private void prepareFile() throws Exception {
-        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+        final File dir = new File(MediaStore.getVolumePath(mVolumeName),
+                Environment.DIRECTORY_DOCUMENTS);
+        final File file = new File(dir, "cts" + System.nanoTime() + ".txt");
 
-        final File documents =
-                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
-        documents.mkdirs();
-        assertTrue(documents.isDirectory());
+        mFile = stageFile(R.raw.text, file);
+        mMediaStoreUri = MediaStore.scanFile(mContext, mFile);
 
-        mFile = new File(documents, "test.jpg");
-        try (OutputStream os = new FileOutputStream(mFile)) {
-            os.write(CONTENT.getBytes());
-        }
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        MediaScannerConnection.scanFile(
-                mActivity,
-                new String[]{ mFile.getAbsolutePath() },
-                new String[]{ "image/jpeg" },
-                (String path, Uri uri) -> onScanCompleted(uri, latch)
-        );
-        assertTrue(
-                "MediaScanner didn't finish scanning in 30s.", latch.await(30, TimeUnit.SECONDS));
-    }
-
-    private void onScanCompleted(Uri uri, CountDownLatch latch) {
-        final String volumeName = MediaStore.getVolumeName(uri);
-        mMediaStoreUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri(volumeName),
-                ContentUris.parseId(uri));
-        latch.countDown();
+        Log.v(TAG, "Staged " + mFile + " as " + mMediaStoreUri);
     }
 
     private Uri acquireAccess(File file, String directoryName) {
@@ -445,4 +450,66 @@
         final ResolveInfo ri = pm.resolveActivity(intent, 0);
         return ri.activityInfo.packageName;
     }
+
+    // TODO: replace with ProviderTestUtils
+    static String executeShellCommand(String command) throws IOException {
+        return executeShellCommand(command,
+                InstrumentationRegistry.getInstrumentation().getUiAutomation());
+    }
+
+    // TODO: replace with ProviderTestUtils
+    static String executeShellCommand(String command, UiAutomation uiAutomation)
+            throws IOException {
+        Log.v(TAG, "$ " + command);
+        ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command.toString());
+        BufferedReader br = null;
+        try (InputStream in = new FileInputStream(pfd.getFileDescriptor());) {
+            br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+            String str = null;
+            StringBuilder out = new StringBuilder();
+            while ((str = br.readLine()) != null) {
+                Log.v(TAG, "> " + str);
+                out.append(str);
+            }
+            return out.toString();
+        } finally {
+            if (br != null) {
+                br.close();
+            }
+        }
+    }
+
+    // TODO: replace with ProviderTestUtils
+    static File stageFile(int resId, File file) throws IOException {
+        // The caller may be trying to stage into a location only available to
+        // the shell user, so we need to perform the entire copy as the shell
+        if (FileUtils.contains(Environment.getStorageDirectory(), file)) {
+            executeShellCommand("mkdir -p " + file.getParent());
+
+            final Context context = InstrumentationRegistry.getTargetContext();
+            try (AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId)) {
+                final File source = ParcelFileDescriptor.getFile(afd.getFileDescriptor());
+                final long skip = afd.getStartOffset();
+                final long count = afd.getLength();
+
+                executeShellCommand(String.format("dd bs=1 if=%s skip=%d count=%d of=%s",
+                        source.getAbsolutePath(), skip, count, file.getAbsolutePath()));
+
+                // Force sync to try updating other views
+                executeShellCommand("sync");
+            }
+        } else {
+            final File dir = file.getParentFile();
+            dir.mkdirs();
+            if (!dir.exists()) {
+                throw new FileNotFoundException("Failed to create parent for " + file);
+            }
+            final Context context = InstrumentationRegistry.getTargetContext();
+            try (InputStream source = context.getResources().openRawResource(resId);
+                    OutputStream target = new FileOutputStream(file)) {
+                FileUtils.copy(source, target);
+            }
+        }
+        return file;
+    }
 }
diff --git a/tests/tests/assist/Android.mk b/tests/tests/assist/Android.mk
index 36ec735..3357334 100644
--- a/tests/tests/assist/Android.mk
+++ b/tests/tests/assist/Android.mk
@@ -32,7 +32,7 @@
 
 LOCAL_PACKAGE_NAME := CtsAssistTestCases
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index 937d8ce..349574b 100755
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -74,10 +74,13 @@
     public static final String APP_3P_HASDRAWED = ACTION_PREFIX + "app_3p_hasDrawed";
     public static final String TEST_ACTIVITY_LOADED = ACTION_PREFIX + "test_activity_hasResumed";
 
-    /** Two second timeout for getting back assist context */
-    public static final int TIMEOUT_MS = 2 * 1000;
-    /** Four second timeout for an activity to resume */
-    public static final int ACTIVITY_ONRESUME_TIMEOUT_MS = 4000;
+    // Notice: timeout belows have to be long because some devices / form factors (like car) are
+    // slower.
+
+    /** Timeout for getting back assist context */
+    public static final int TIMEOUT_MS = 4 * 1_000;
+    /** Timeout for an activity to resume */
+    public static final int ACTIVITY_ONRESUME_TIMEOUT_MS = 8 * 1_000;
 
     public static final String EXTRA_REGISTER_RECEIVER = "register_receiver";
 
@@ -86,6 +89,14 @@
     public static final String EXTRA_CONTENT_VIEW_WIDTH = "extra_content_view_width";
     public static final String EXTRA_DISPLAY_POINT = "extra_display_point";
 
+    /*
+     * Extras used to pass RemoteCallback objects responsible for IPC between test, app, and
+     * service.
+     */
+    public static final String EXTRA_CALLBACK_CONTEXT_READY = "extra_callback_context_ready";
+    public static final String EXTRA_CALLBACK_ACTIVITY_RESUMED = "extra_callback_activity_resumed";
+    public static final String EXTRA_CALLBACK_ACTIVITY_DRAWED = "extra_callback_activity_drawed";
+
     /** Test name suffixes */
     public static final String ASSIST_STRUCTURE = "ASSIST_STRUCTURE";
     public static final String DISABLE_CONTEXT = "DISABLE_CONTEXT";
diff --git a/tests/tests/assist/service/Android.mk b/tests/tests/assist/service/Android.mk
index f989776..822c422 100644
--- a/tests/tests/assist/service/Android.mk
+++ b/tests/tests/assist/service/Android.mk
@@ -30,6 +30,6 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionService.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionService.java
index 708cf9a..3aa8261 100644
--- a/tests/tests/assist/service/src/android/assist/service/MainInteractionService.java
+++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionService.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
@@ -102,14 +103,17 @@
     private class MainInteractionServiceBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            Log.i(MainInteractionService.TAG, "Recieved broadcast to start session now.");
+            Log.i(TAG, "Received broadcast to start session now: " + intent);
             if (intent.getAction().equals(Utils.BROADCAST_INTENT_START_ASSIST)) {
                 String testCaseName = intent.getStringExtra(Utils.TESTCASE_TYPE);
-                Log.i(MainInteractionService.TAG, "trying to start 3p activity: " + testCaseName);
+                Log.i(TAG, "trying to start 3p activity: " + testCaseName);
                 Bundle extras = intent.getExtras();
                 if (extras == null) {
                     extras = new Bundle();
                 }
+                RemoteCallback contextReadycallback = intent
+                        .getParcelableExtra(Utils.EXTRA_CALLBACK_CONTEXT_READY);
+                extras.putParcelable(Utils.EXTRA_CALLBACK_CONTEXT_READY, contextReadycallback);
                 if (testCaseName.equals(Utils.SCREENSHOT)) {
                     try {
                         // extra info to pass along to 3p activity.
diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
index 96b9873..068a4a8 100644
--- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
@@ -28,13 +28,13 @@
 
 import android.graphics.Point;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import android.service.voice.VoiceInteractionSession;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.Display;
 import android.view.ViewTreeObserver;
 
 import java.io.ByteArrayOutputStream;
@@ -55,10 +55,10 @@
     private int mCurColor;
     private int mDisplayHeight;
     private int mDisplayWidth;
-    private Bitmap mScreenshot;
     private BroadcastReceiver mReceiver;
     private String mTestName;
     private View mContentView;
+    private RemoteCallback mContextReadyCallback;
 
     MainInteractionSession(Context context) {
         super(context);
@@ -109,6 +109,7 @@
         mCurColor = args.getInt(Utils.SCREENSHOT_COLOR_KEY);
         mDisplayHeight = args.getInt(Utils.DISPLAY_HEIGHT_KEY);
         mDisplayWidth = args.getInt(Utils.DISPLAY_WIDTH_KEY);
+        mContextReadyCallback = args.getParcelable(Utils.EXTRA_CALLBACK_CONTEXT_READY);
         super.onShow(args, showFlags);
         if (mContentView == null) return; // Happens when ui is not enabled.
         mContentView.getViewTreeObserver().addOnPreDrawListener(
@@ -214,11 +215,17 @@
         } else if (!hasReceivedScreenshot) {
             Log.i(TAG, "waiting for screenshot before broadcasting results");
         } else {
-            Intent intent = new Intent(Utils.BROADCAST_ASSIST_DATA_INTENT);
-            intent.putExtras(mAssistData);
-            Log.i(TAG,
-                    "broadcasting: " + intent.toString() + ", Bundle = " + mAssistData.toString());
-            mContext.sendBroadcast(intent);
+            if (mContextReadyCallback != null) {
+                Log.i(TAG, "maybeBroadcastResults(): calling callback " + mContextReadyCallback);
+                mContextReadyCallback.sendResult(mAssistData);
+            } else {
+                // TODO(b/133431034): should only use callbacks
+                Log.w(TAG, "maybeBroadcastResults(): no callback; broadcasting instead");
+                Intent intent = new Intent(Utils.BROADCAST_ASSIST_DATA_INTENT);
+                intent.putExtras(mAssistData);
+                Log.i(TAG, "broadcasting: " + intent+ ", Bundle = " + mAssistData);
+                mContext.sendBroadcast(intent);
+            }
 
             hasReceivedAssistData = false;
             hasReceivedScreenshot = false;
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index 378e39c..70669e5 100755
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -35,14 +35,7 @@
     private static final String TAG = "AssistStructureTest";
     private static final String TEST_CASE_TYPE = Utils.ASSIST_STRUCTURE;
 
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-    private CountDownLatch mHasDrawedLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
-
-    public AssistStructureTest() {
-        super();
-    }
+    private final CountDownLatch mHasDrawedLatch = new CountDownLatch(1);
 
     @Override
     protected void setUp() throws Exception {
@@ -93,13 +86,13 @@
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
             return;
         }
-        mTestActivity.start3pApp(TEST_CASE_TYPE);
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        start3pApp(TEST_CASE_TYPE, mHasDrawedLatch);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
         waitForOnResume();
         waitForOnDraw();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         getInstrumentation().waitForIdleSync();
         runTestOnUiThread(new Runnable() {
             @Override
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index b9fe602..438d905 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,6 +16,8 @@
 
 package android.assist.cts;
 
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
 import android.app.ActivityManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -30,6 +32,7 @@
 import android.graphics.Point;
 import android.os.Bundle;
 import android.os.LocaleList;
+import android.os.RemoteCallback;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.ActivityInstrumentationTestCase2;
@@ -61,7 +64,7 @@
     private static boolean mFirstTest = true;
 
     protected ActivityManager mActivityManager;
-    protected TestStartActivity mTestActivity;
+    private TestStartActivity mTestActivity;
     protected AssistContent mAssistContent;
     protected AssistStructure mAssistStructure;
     protected boolean mScreenshot;
@@ -69,7 +72,10 @@
     protected BroadcastReceiver mReceiver;
     protected Bundle mAssistBundle;
     protected Context mContext;
-    protected CountDownLatch mLatch, mScreenshotLatch, mHasResumedLatch;
+    protected final CountDownLatch mReadyLatch = new CountDownLatch(1);
+    protected final CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mAssistDataReceivedLatch;
+
     protected boolean mScreenshotMatches;
     private Point mDisplaySize;
     private String mTestName;
@@ -103,6 +109,8 @@
         mReceiver = new TestResultsReceiver();
         mContext.registerReceiver(mReceiver,
             new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
+
+        prepareDevice();
     }
 
     @Override
@@ -116,6 +124,28 @@
         super.tearDown();
     }
 
+    private void prepareDevice() throws Exception {
+        Log.d(TAG, "prepareDevice()");
+
+        // Unlock screen.
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+        // Dismiss keyguard, in case it's set as "Swipe to unlock".
+        runShellCommand("wm dismiss-keyguard");
+    }
+
+    protected void startTest(String testName) {
+        mTestActivity.startTest(testName);
+    }
+
+    protected void start3pApp(String testName) {
+        start3pApp(testName, /* hasDrawedLatch= */ null);
+    }
+
+    protected void start3pApp(String testName, CountDownLatch hasDrawedLatch) {
+        mTestActivity.start3pApp(testName, mHasResumedLatch, hasDrawedLatch);
+    }
+
     /**
      * Starts the shim service activity
      */
@@ -144,17 +174,26 @@
     /**
      * Send broadcast to MainInteractionService to start a session
      */
-    protected void startSession() {
-        startSession(mTestName, new Bundle());
+    protected CountDownLatch startSession() {
+        return startSession(mTestName, new Bundle());
     }
 
-    protected void startSession(String testName, Bundle extras) {
+    protected CountDownLatch startSession(String testName, Bundle extras) {
         Intent intent = new Intent(Utils.BROADCAST_INTENT_START_ASSIST);
         Log.i(TAG, "passed in class test name is: " + testName);
         intent.putExtra(Utils.TESTCASE_TYPE, testName);
         addDimensionsToIntent(intent);
         intent.putExtras(extras);
+        mAssistDataReceivedLatch = new CountDownLatch(1);
+        final RemoteCallback contextReadyCallback = new RemoteCallback((result) -> {
+            Log.v(TAG, "Service called contextReady callback for " + testName + " RESULT: " + result);
+            setAssistResults(result);
+            mAssistDataReceivedLatch.countDown();
+        });
+        intent.putExtra(Utils.EXTRA_CALLBACK_CONTEXT_READY, contextReadyCallback);
+
         mContext.sendBroadcast(intent);
+        return mAssistDataReceivedLatch;
     }
 
     /**
@@ -174,14 +213,13 @@
      * Called after startTestActivity. Includes check for receiving context.
      */
     protected boolean waitForBroadcast() throws Exception {
-        mTestActivity.start3pApp(mTestName);
-        mTestActivity.startTest(mTestName);
-        return waitForContext();
+        mAssistDataReceivedLatch = new CountDownLatch(1);
+        start3pApp(mTestName);
+        startTest(mTestName);
+        return waitForContext(mAssistDataReceivedLatch);
     }
 
-    protected boolean waitForContext() throws Exception {
-        mLatch = new CountDownLatch(1);
-
+    protected boolean waitForContext(CountDownLatch sessionLatch) throws Exception {
         if (mReceiver != null) {
             mContext.unregisterReceiver(mReceiver);
         }
@@ -189,7 +227,7 @@
         mContext.registerReceiver(mReceiver,
                 new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
 
-        if (!mLatch.await(Utils.getAssistDataTimeout(mTestName), TimeUnit.MILLISECONDS)) {
+        if (!sessionLatch.await(Utils.getAssistDataTimeout(mTestName), TimeUnit.MILLISECONDS)) {
             fail("Fail to receive broadcast in " + Utils.getAssistDataTimeout(mTestName) + "msec");
         }
         Log.i(TAG, "Received broadcast with all information.");
@@ -432,10 +470,6 @@
         Log.i(TAG, "setFeaturesEnabled(" + structure + ", " + screenshot + ")");
         SettingsUtils.syncSet(mContext, ASSIST_STRUCTURE_ENABLED, structure.value);
         SettingsUtils.syncSet(mContext, ASSIST_SCREENSHOT_ENABLED, screenshot.value);
-        // TODO: figure out a better way to wait until it's done
-        Log.v(TAG, "sleeping 2s");
-        SystemClock.sleep(2_000);
-        Log.v(TAG, "woke up");
     }
 
     /**
@@ -507,27 +541,18 @@
         }
     }
 
+    // TODO(b/133431034): remove?
     class TestResultsReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) {
                 Log.i(TAG, "Received broadcast with assist data.");
                 Bundle assistData = intent.getExtras();
-                AssistTestBase.this.mAssistBundle = assistData.getBundle(Utils.ASSIST_BUNDLE_KEY);
-                AssistTestBase.this.mAssistStructure = assistData.getParcelable(
-                        Utils.ASSIST_STRUCTURE_KEY);
-                AssistTestBase.this.mAssistContent = assistData.getParcelable(
-                        Utils.ASSIST_CONTENT_KEY);
+                AssistTestBase.this.setAssistResults(assistData);
 
-                AssistTestBase.this.mScreenshot =
-                        assistData.getBoolean(Utils.ASSIST_SCREENSHOT_KEY, false);
-
-                AssistTestBase.this.mScreenshotMatches = assistData.getBoolean(
-                        Utils.COMPARE_SCREENSHOT_KEY, false);
-
-                if (mLatch != null) {
+                if (mAssistDataReceivedLatch != null) {
                     Log.i(AssistTestBase.TAG, "counting down latch. received assist data.");
-                    mLatch.countDown();
+                    mAssistDataReceivedLatch.countDown();
                 }
             } else if (intent.getAction().equals(Utils.APP_3P_HASRESUMED)) {
                 if (mHasResumedLatch != null) {
@@ -537,6 +562,17 @@
         }
     }
 
+    protected void setAssistResults(Bundle assistData) {
+        mAssistBundle = assistData.getBundle(Utils.ASSIST_BUNDLE_KEY);
+        mAssistStructure = assistData.getParcelable(Utils.ASSIST_STRUCTURE_KEY);
+        mAssistContent = assistData.getParcelable(Utils.ASSIST_CONTENT_KEY);
+
+        mScreenshot =assistData.getBoolean(Utils.ASSIST_SCREENSHOT_KEY, false);
+
+        mScreenshotMatches = assistData.getBoolean(Utils.COMPARE_SCREENSHOT_KEY, false);
+    }
+
+
     protected enum StructureEnabled {
         TRUE("1"), FALSE("0");
 
diff --git a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
index afbd4aa..cb09270 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
@@ -30,15 +30,12 @@
 /** Test verifying the Content View of the Assistant */
 public class AssistantContentViewTest extends AssistTestBase {
     private static final String TAG = "ContentViewTest";
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mContentViewLatch, mReadyLatch;
+    private final CountDownLatch mContentViewLatch = new CountDownLatch(1);
     private Intent mIntent;
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mContentViewLatch = new CountDownLatch(1);
-        mReadyLatch = new CountDownLatch(1);
         setUpAndRegisterReceiver();
         startTestActivity(Utils.VERIFY_CONTENT_VIEW);
     }
@@ -76,7 +73,7 @@
           Log.d(TAG, "Not running assist tests on low-RAM device.");
           return;
         }
-        mTestActivity.startTest(Utils.VERIFY_CONTENT_VIEW);
+        startTest(Utils.VERIFY_CONTENT_VIEW);
         waitForAssistantToBeReady(mReadyLatch);
         startSession();
         waitForContentView();
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 7d60417..90788a0 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -56,25 +56,38 @@
         waitForBroadcast();
 
         logContextAndScreenshotSetting();
-
         verifyAssistDataNullness(true, true, true, true);
+    }
 
+    // TODO(b/133379285) need to figure out a way to finish the activity so this class can run
+    // multiple tests
+    public void disabled_TestContextOff() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
+            Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
+            return;
+        }
         // Screenshot off, context on
         Log.i(TAG, "DisableContext: Screenshot OFF, Context ON");
         setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.FALSE);
         waitForBroadcast();
 
         logContextAndScreenshotSetting();
-
         verifyAssistDataNullness(false, false, false, true);
+    }
 
+    // TODO(b/133379285) need to figure out a way to finish the activity so this class can run
+    // multiple tests
+    public void disabled_testScreenshotOff() throws Exception {
+        if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
+            Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
+            return;
+        }
         // Context off, screenshot on
         Log.i(TAG, "DisableContext: Screenshot ON, Context OFF");
         setFeaturesEnabled(StructureEnabled.FALSE, ScreenshotEnabled.TRUE);
         waitForBroadcast();
 
         logContextAndScreenshotSetting();
-
         verifyAssistDataNullness(true, true, true, true);
     }
 }
diff --git a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
index 5717fcc..b5e799e 100644
--- a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
+++ b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
@@ -31,14 +31,6 @@
     private static final String TAG = "ExtraAssistDataTest";
     private static final String TEST_CASE_TYPE = Utils.EXTRA_ASSIST;
 
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
-
-    public ExtraAssistDataTest() {
-        super();
-    }
-
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -71,12 +63,12 @@
             Log.d(TAG, "Not running assist tests on low-RAM device.");
             return;
         }
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
-        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        start3pApp(TEST_CASE_TYPE);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         verifyAssistDataNullness(false, false, false, false);
 
         Log.i(TAG, "assist bundle is: " + Utils.toBundleString(mAssistBundle));
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index 43364f8..2d5a989 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -35,13 +35,6 @@
 
     private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
 
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
-    public FlagSecureTest() {
-        super();
-    }
-
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -81,12 +74,12 @@
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
             return;
         }
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
-        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        start3pApp(TEST_CASE_TYPE);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         verifyAssistDataNullness(false, false, false, false);
         // verify that we have only the root window and not its children.
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), true);
diff --git a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
index c0397d8..e42b0d0 100644
--- a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
@@ -32,10 +32,8 @@
     private static final String TAG = "FocusChangeTest";
     private static final String TEST_CASE_TYPE = Utils.FOCUS_CHANGE;
 
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasGainedFocusLatch = new CountDownLatch(1);
-    private CountDownLatch mHasLostFocusLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
+    private final CountDownLatch mHasGainedFocusLatch = new CountDownLatch(1);
+    private final CountDownLatch mHasLostFocusLatch = new CountDownLatch(1);
 
     @Override
     public void setUp() throws Exception {
@@ -85,9 +83,9 @@
             Log.d(TAG, "Not running assist tests on low-RAM device.");
             return;
         }
-        mTestActivity.startTest(Utils.FOCUS_CHANGE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
-        mTestActivity.start3pApp(Utils.FOCUS_CHANGE);
+        start3pApp(TEST_CASE_TYPE);
         waitToGainFocus();
         startSession();
         waitToLoseFocus();
diff --git a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
index 3926091..cf2a47c 100644
--- a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -34,14 +34,6 @@
     private static final String TAG = "LargeViewHierarchyTest";
     private static final String TEST_CASE_TYPE = Utils.LARGE_VIEW_HIERARCHY;
 
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
-
-    public LargeViewHierarchyTest() {
-        super();
-    }
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -81,12 +73,12 @@
             Log.d(TAG, "Not running assist tests on low-RAM device.");
             return;
         }
-        mTestActivity.start3pApp(TEST_CASE_TYPE);
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        start3pApp(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         verifyAssistDataNullness(false, false, false, false);
 
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index a4a1606..037d534 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -30,22 +30,20 @@
 
 public class LifecycleTest extends AssistTestBase {
     private static final String TAG = "LifecycleTest";
-    private static final String action_hasResumed = Utils.LIFECYCLE_HASRESUMED;
-    private static final String action_hasFocus = Utils.LIFECYCLE_HASFOCUS;
-    private static final String action_lostFocus = Utils.LIFECYCLE_LOSTFOCUS;
-    private static final String action_onPause = Utils.LIFECYCLE_ONPAUSE;
-    private static final String action_onStop = Utils.LIFECYCLE_ONSTOP;
-    private static final String action_onDestroy = Utils.LIFECYCLE_ONDESTROY;
+    private static final String ACTION_HAS_RESUMED = Utils.LIFECYCLE_HASRESUMED;
+    private static final String ACTION_HAS_FOCUS = Utils.LIFECYCLE_HASFOCUS;
+    private static final String ACTION_LOST_FOCUS = Utils.LIFECYCLE_LOSTFOCUS;
+    private static final String ACTION_ON_PAUSE = Utils.LIFECYCLE_ONPAUSE;
+    private static final String ACTION_ON_STOP = Utils.LIFECYCLE_ONSTOP;
+    private static final String ACTION_ON_DESTROY = Utils.LIFECYCLE_ONDESTROY;
 
     private static final String TEST_CASE_TYPE = Utils.LIFECYCLE;
 
     private BroadcastReceiver mLifecycleTestBroadcastReceiver;
-    private CountDownLatch mHasResumedLatch;
-    private CountDownLatch mHasFocusLatch;
-    private CountDownLatch mLostFocusLatch;
-    private CountDownLatch mActivityLifecycleLatch;
-    private CountDownLatch mDestroyLatch;
-    private CountDownLatch mReadyLatch;
+    private final CountDownLatch mHasFocusLatch = new CountDownLatch(1);
+    private final CountDownLatch mLostFocusLatch = new CountDownLatch(1);
+    private final CountDownLatch mActivityLifecycleLatch = new CountDownLatch(1);
+    private final CountDownLatch mDestroyLatch = new CountDownLatch(1);
     private boolean mLostFocusIsLifecycle;
 
     @Override
@@ -53,20 +51,14 @@
         super.setUp();
         mLifecycleTestBroadcastReceiver = new LifecycleTestReceiver();
         IntentFilter filter = new IntentFilter();
-        filter.addAction(action_hasResumed);
-        filter.addAction(action_hasFocus);
-        filter.addAction(action_lostFocus);
-        filter.addAction(action_onPause);
-        filter.addAction(action_onStop);
-        filter.addAction(action_onDestroy);
+        filter.addAction(ACTION_HAS_RESUMED);
+        filter.addAction(ACTION_HAS_FOCUS);
+        filter.addAction(ACTION_LOST_FOCUS);
+        filter.addAction(ACTION_ON_PAUSE);
+        filter.addAction(ACTION_ON_STOP);
+        filter.addAction(ACTION_ON_DESTROY);
         filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
         mContext.registerReceiver(mLifecycleTestBroadcastReceiver, filter);
-        mHasResumedLatch = new CountDownLatch(1);
-        mHasFocusLatch = new CountDownLatch(1);
-        mLostFocusLatch = new CountDownLatch(1);
-        mActivityLifecycleLatch = new CountDownLatch(1);
-        mDestroyLatch = new CountDownLatch(1);
-        mReadyLatch = new CountDownLatch(1);
         mLostFocusIsLifecycle = false;
         startTestActivity(TEST_CASE_TYPE);
     }
@@ -78,12 +70,6 @@
             mContext.unregisterReceiver(mLifecycleTestBroadcastReceiver);
             mLifecycleTestBroadcastReceiver = null;
         }
-        mHasResumedLatch = null;
-        mHasFocusLatch = null;
-        mLostFocusLatch = null;
-        mActivityLifecycleLatch = null;
-        mDestroyLatch = null;
-        mReadyLatch = null;
     }
 
     private void waitForOnResume() throws Exception {
@@ -125,13 +111,13 @@
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
             return;
         }
-        mTestActivity.startTest(Utils.LIFECYCLE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
-        mTestActivity.start3pApp(Utils.LIFECYCLE);
+        start3pApp(TEST_CASE_TYPE);
         waitForOnResume();
         waitForHasFocus();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         // Since there is no UI, focus should not be lost.  We are counting focus lost as
         // a lifecycle event in this case.
         // Do this after waitForContext(), since we don't start looking for context until
@@ -148,13 +134,13 @@
             return;
         }
         mLostFocusIsLifecycle = true;
-        mTestActivity.startTest(Utils.LIFECYCLE_NOUI);
+        startTest(Utils.LIFECYCLE_NOUI);
         waitForAssistantToBeReady(mReadyLatch);
-        mTestActivity.start3pApp(Utils.LIFECYCLE_NOUI);
+        start3pApp(Utils.LIFECYCLE_NOUI);
         waitForOnResume();
         waitForHasFocus();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         // Do this after waitForContext(), since we don't start looking for context until
         // calling the above (RACY!!!).
         waitAndSeeIfLifecycleMethodsAreTriggered();
@@ -166,21 +152,21 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(action_hasResumed) && mHasResumedLatch != null) {
+            if (action.equals(ACTION_HAS_RESUMED) && mHasResumedLatch != null) {
                 mHasResumedLatch.countDown();
-            } else if (action.equals(action_hasFocus) && mHasFocusLatch != null) {
+            } else if (action.equals(ACTION_HAS_FOCUS) && mHasFocusLatch != null) {
                 mHasFocusLatch.countDown();
-            } else if (action.equals(action_lostFocus) && mLostFocusLatch != null) {
+            } else if (action.equals(ACTION_LOST_FOCUS) && mLostFocusLatch != null) {
                 if (mLostFocusIsLifecycle) {
                     mActivityLifecycleLatch.countDown();
                 } else {
                     mLostFocusLatch.countDown();
                 }
-            } else if (action.equals(action_onPause) && mActivityLifecycleLatch != null) {
+            } else if (action.equals(ACTION_ON_PAUSE) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
-            } else if (action.equals(action_onStop) && mActivityLifecycleLatch != null) {
+            } else if (action.equals(ACTION_ON_STOP) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
-            } else if (action.equals(action_onDestroy) && mActivityLifecycleLatch != null) {
+            } else if (action.equals(ACTION_ON_DESTROY) && mActivityLifecycleLatch != null) {
                 mActivityLifecycleLatch.countDown();
                 mDestroyLatch.countDown();
             } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
diff --git a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
index 9835689..e24ef54 100644
--- a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
+++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
@@ -34,16 +34,10 @@
     private static final String TEST_CASE_TYPE = Utils.SCREENSHOT;
 
     private BroadcastReceiver mScreenshotActivityReceiver;
-    private CountDownLatch mHasResumedLatch, mReadyLatch;
-
-    public ScreenshotTest() {
-        super();
-    }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mReadyLatch = new CountDownLatch(1);
         // set up receiver
         mScreenshotActivityReceiver = new ScreenshotTestReceiver();
         IntentFilter filter = new IntentFilter();
@@ -69,7 +63,7 @@
             return;
         }
         Log.i(TAG, "Starting screenshot test");
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         Log.i(TAG, "start waitForAssistantToBeReady()");
         waitForAssistantToBeReady(mReadyLatch);
 
@@ -84,7 +78,7 @@
             return;
         }
         Log.i(TAG, "Starting screenshot test");
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         Log.i(TAG, "start waitForAssistantToBeReady()");
         waitForAssistantToBeReady(mReadyLatch);
 
@@ -99,7 +93,7 @@
             return;
         }
         Log.i(TAG, "Starting screenshot test");
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         Log.i(TAG, "start waitForAssistantToBeReady()");
         waitForAssistantToBeReady(mReadyLatch);
 
@@ -109,15 +103,14 @@
     }
 
     private void waitForActivityResumeAndAssist(int color) throws Exception {
-        mHasResumedLatch = new CountDownLatch(1);
         Bundle extras = new Bundle();
         extras.putInt(Utils.SCREENSHOT_COLOR_KEY, color);
-        startSession(TEST_CASE_TYPE, extras);
+        final CountDownLatch latch = startSession(TEST_CASE_TYPE, extras);
         Log.i(TAG, "waiting for onResume() before continuing.");
         if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
             fail("activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
         }
-        waitForContext();
+        waitForContext(latch);
     }
 
     private class ScreenshotTestReceiver extends BroadcastReceiver {
diff --git a/tests/tests/assist/src/android/assist/cts/TestStartActivity.java b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
index acc0291..80c6e48 100644
--- a/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
+++ b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import android.util.Log;
 import android.view.View;
 import android.webkit.WebView;
@@ -29,6 +30,8 @@
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import java.util.concurrent.CountDownLatch;
+
 public class TestStartActivity extends Activity {
     static final String TAG = "TestStartActivity";
 
@@ -90,19 +93,22 @@
         startActivity(intent);
     }
 
-    public void start3pApp(String testCaseName) {
+    public void start3pApp(String testCaseName, CountDownLatch resumedLatch,
+            CountDownLatch drawedLatch) {
+        final RemoteCallback resumedCallback = new RemoteCallback((result) -> {
+            Log.v(TAG, "Testapp called resumed callback for " + testCaseName);
+            resumedLatch.countDown();
+        });
+        final RemoteCallback drawedCallback = new RemoteCallback((result) -> {
+            Log.v(TAG, "Testapp called drawed callback for " + testCaseName);
+            drawedLatch.countDown();
+        });
         Intent intent = new Intent();
         intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
         intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
         intent.setComponent(Utils.getTestAppComponent(testCaseName));
-        startActivity(intent);
-    }
-
-    public void start3pAppWithColor(String testCaseName, int color) {
-        Intent intent = new Intent();
-        intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
-        intent.putExtra(Utils.SCREENSHOT_COLOR_KEY, color);
-        intent.setComponent(Utils.getTestAppComponent(testCaseName));
+        intent.putExtra(Utils.EXTRA_CALLBACK_ACTIVITY_RESUMED, resumedCallback);
+        intent.putExtra(Utils.EXTRA_CALLBACK_ACTIVITY_DRAWED, drawedCallback);
         startActivity(intent);
     }
 
diff --git a/tests/tests/assist/src/android/assist/cts/TextViewTest.java b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
index 8b36d54..a3ef572 100644
--- a/tests/tests/assist/src/android/assist/cts/TextViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
@@ -34,14 +34,6 @@
     private static final String TAG = "TextViewTest";
     private static final String TEST_CASE_TYPE = Utils.TEXTVIEW;
 
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
-
-    public TextViewTest() {
-        super();
-    }
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -81,15 +73,15 @@
             Log.d(TAG, "Not running assist tests on low-RAM device.");
             return;
         }
-        mTestActivity.start3pApp(TEST_CASE_TYPE);
+        start3pApp(TEST_CASE_TYPE);
         scrollTestApp(0, 0, true, false);
 
         // Verify that the text view contains the right text
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch1 = startSession();
+        waitForContext(latch1);
         verifyAssistDataNullness(false, false, false, false);
 
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
@@ -98,26 +90,26 @@
         // Verify that the scroll position of the text view is accurate after scrolling.
         scrollTestApp(10, 50, true /* scrollTextView */, false /* scrollScrollView */);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch2 = startSession();
+        waitForContext(latch2);
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
 
         scrollTestApp(-1, -1, true, false);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch3 = startSession();
+        waitForContext(latch3);
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
 
         scrollTestApp(0, 0, true, true);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch4 = startSession();
+        waitForContext(latch4);
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
 
         scrollTestApp(10, 50, false, true);
         waitForOnResume();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch5 = startSession();
+        waitForContext(latch5);
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
     }
 
diff --git a/tests/tests/assist/src/android/assist/cts/WebViewTest.java b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
index 18ca0c0..a38362d 100644
--- a/tests/tests/assist/src/android/assist/cts/WebViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -37,14 +37,7 @@
     private static final String TEST_CASE_TYPE = Utils.WEBVIEW;
 
     private boolean mWebViewSupported;
-    private BroadcastReceiver mReceiver;
-    private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-    private CountDownLatch mTestWebViewLatch = new CountDownLatch(1);
-    private CountDownLatch mReadyLatch = new CountDownLatch(1);
-
-    public WebViewTest() {
-        super();
-    }
+    private final CountDownLatch mTestWebViewLatch = new CountDownLatch(1);
 
     @Override
     protected void setUp() throws Exception {
@@ -96,13 +89,13 @@
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
             return;
         }
-        mTestActivity.start3pApp(TEST_CASE_TYPE);
-        mTestActivity.startTest(TEST_CASE_TYPE);
+        start3pApp(TEST_CASE_TYPE);
+        startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
         waitForOnResume();
         waitForTestActivity();
-        startSession();
-        waitForContext();
+        final CountDownLatch latch = startSession();
+        waitForContext(latch);
         verifyAssistDataNullness(false, false, false, false);
         verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
                 false /*FLAG_SECURE set*/);
diff --git a/tests/tests/assist/testapp/Android.mk b/tests/tests/assist/testapp/Android.mk
index f179ec3..9482510 100644
--- a/tests/tests/assist/testapp/Android.mk
+++ b/tests/tests/assist/testapp/Android.mk
@@ -30,6 +30,6 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/assist/testapp/src/android/assist/testapp/TestApp.java b/tests/tests/assist/testapp/src/android/assist/testapp/TestApp.java
index 3649f81..7865c4d 100755
--- a/tests/tests/assist/testapp/src/android/assist/testapp/TestApp.java
+++ b/tests/tests/assist/testapp/src/android/assist/testapp/TestApp.java
@@ -20,6 +20,7 @@
 import android.assist.common.Utils;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -29,11 +30,15 @@
     static final String TAG = "TestApp";
 
     private String mTestCaseName;
+    private RemoteCallback mResumedCallback;
+    private RemoteCallback mDrawedCallback;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Log.i(TAG, "TestApp created");
+        Log.i(TAG, "TestApp created: " + getIntent());
+        mResumedCallback = getIntent().getParcelableExtra(Utils.EXTRA_CALLBACK_ACTIVITY_RESUMED);
+        mDrawedCallback = getIntent().getParcelableExtra(Utils.EXTRA_CALLBACK_ACTIVITY_DRAWED);
         mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
         switch (mTestCaseName) {
             case Utils.LARGE_VIEW_HIERARCHY:
@@ -54,14 +59,29 @@
             @Override
             public void onGlobalLayout() {
                 layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+                if (mResumedCallback != null) {
+                    Log.i(TAG, "onGlobalLayout(): notifying callback");
+                    mResumedCallback.sendResult(/* result= */ null);
+                } else {
+                    // TODO(b/133431034): should only use callbacks
+                    Log.w(TAG, "No resume callback onGlobalLayout(); broadcasting instead");
+                    sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+                }
             }
         });
     }
 
+    @Override
     public void onEnterAnimationComplete() {
         Log.i(TAG, "TestApp onEnterAnimationComplete ");
-        sendBroadcast(new Intent(Utils.APP_3P_HASDRAWED));
+        if (mDrawedCallback != null) {
+            Log.i(TAG, "onEnterAnimationComplete(): notifying callback");
+            mDrawedCallback.sendResult(/* result= */ null);
+        } else {
+            // TODO(b/133431034): should only use callbacks
+            Log.w(TAG, "No resume callback onEnterAnimationComplete(); broadcasting instead");
+            sendBroadcast(new Intent(Utils.APP_3P_HASDRAWED));
+        }
     }
 
 }
\ No newline at end of file
diff --git a/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java b/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
new file mode 100644
index 0000000..ca7364b
--- /dev/null
+++ b/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.car.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.car.Car;
+import android.car.drivingstate.CarUxRestrictions;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.platform.test.annotations.RequiresDevice;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RequiresDevice
+@RunWith(AndroidJUnit4.class)
+public class CarUxRestrictionsManagerTest extends CarApiTestBase {
+    private CarUxRestrictionsManager mManager;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mManager = (CarUxRestrictionsManager)
+                getCar().getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+        assertNotNull(mManager);
+    }
+
+    @Test
+    public void testCarUxRestrictionsBuilder() {
+        int maxContentDepth = 1;
+        int maxCumulativeContentItems = 1;
+        int maxStringLength = 1;
+        CarUxRestrictions.Builder builder = new CarUxRestrictions.Builder(
+                true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED, 0L);
+        builder.setMaxContentDepth(maxContentDepth);
+        builder.setMaxCumulativeContentItems(maxCumulativeContentItems);
+        builder.setMaxStringLength(maxStringLength);
+
+        CarUxRestrictions restrictions = builder.build();
+
+        assertTrue(restrictions.toString(),
+                restrictions.isRequiresDistractionOptimization());
+        assertEquals(restrictions.toString(),
+                restrictions.getActiveRestrictions(),
+                CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
+        assertEquals(restrictions.toString(),
+                restrictions.getMaxContentDepth(), maxContentDepth);
+        assertEquals(restrictions.toString(),
+                restrictions.getMaxCumulativeContentItems(), maxCumulativeContentItems);
+        assertEquals(restrictions.toString(),
+                restrictions.getMaxRestrictedStringLength(), maxStringLength);
+    }
+
+    @Test
+    public void testCarUxRestrictions_isSameRestrictions() {
+        CarUxRestrictions.Builder oneBuilder = new CarUxRestrictions.Builder(
+                true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED, 0L);
+        CarUxRestrictions.Builder anotherBuilder = new CarUxRestrictions.Builder(
+                true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED, 0L);
+
+        assertTrue(oneBuilder.build().isSameRestrictions(anotherBuilder.build()));
+    }
+
+    @Test
+    public void testRegisterListener_noCrash() {
+        mManager.registerListener(restrictions -> {});
+        mManager.unregisterListener();
+    }
+}
diff --git a/tests/tests/carrierapi/AndroidManifest.xml b/tests/tests/carrierapi/AndroidManifest.xml
index ea3c053..978651f 100644
--- a/tests/tests/carrierapi/AndroidManifest.xml
+++ b/tests/tests/carrierapi/AndroidManifest.xml
@@ -19,6 +19,7 @@
     android:targetSandboxVersion="2">
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
 
diff --git a/tests/tests/carrierapi/OWNERS b/tests/tests/carrierapi/OWNERS
index 5617896..619d676 100644
--- a/tests/tests/carrierapi/OWNERS
+++ b/tests/tests/carrierapi/OWNERS
@@ -1,2 +1,5 @@
 # Bug component: 20868
-yinxu@google.com
\ No newline at end of file
+yinxu@google.com
+hallliu@google.com
+mpq@google.com
+refuhoo@google.com
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 44d49f1..7bc308b 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -38,6 +38,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.provider.Telephony;
 import android.provider.VoicemailContract;
@@ -52,10 +53,14 @@
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
 
@@ -119,6 +124,8 @@
     private static final String NUMBER_A = "1234567890";
     private static final String NUMBER_B = "0987654321";
 
+    private static final int DSDS_PHONE_COUNT = 2;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -771,6 +778,129 @@
     }
 
     /**
+     * This test verifies that {@link SubscriptionManager#createSubscriptionGroup(List)} correctly
+     * create a group with the given subscription id.
+     *
+     * This also verifies that
+     * {@link SubscriptionManager#removeSubscriptionsFromGroup(List, ParcelUuid)} correctly remove
+     * the given subscription group.
+     */
+    public void testCreateAndRemoveSubscriptionGroup() {
+        if (!hasCellular) return;
+        // Set subscription group with current sub Id.
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
+        List<Integer> subGroup = Arrays.asList(subId);
+        ParcelUuid uuid = mSubscriptionManager.createSubscriptionGroup(subGroup);
+
+        // Getting subscriptions in group.
+        List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
+
+        try {
+            assertEquals(1, infoList.size());
+            assertEquals(uuid, infoList.get(0).getGroupUuid());
+            assertEquals(subId, infoList.get(0).getSubscriptionId());
+        } finally {
+            // Verify that the given subGroup has been removed.
+            mSubscriptionManager.removeSubscriptionsFromGroup(subGroup, uuid);
+            infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
+            assertTrue(infoList.isEmpty());
+        }
+    }
+
+    public void testAddSubscriptionToExistingGroupForMultipleSims() {
+        if (!hasCellular || mTelephonyManager.getPhoneCount() < DSDS_PHONE_COUNT) return;
+
+        // Set subscription group with current sub Id.
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        ParcelUuid uuid = mSubscriptionManager.createSubscriptionGroup(Arrays.asList(subId));
+
+        try {
+            // Get all active subscriptions.
+            List<SubscriptionInfo> activeSubInfos =
+                    mSubscriptionManager.getActiveSubscriptionInfoList();
+
+            // Verify that the device has at least two active subscriptions.
+            assertTrue(activeSubInfos.size() >= DSDS_PHONE_COUNT);
+
+            List<Integer> activeSubGroup = getSubscriptionIdList(activeSubInfos);
+            activeSubGroup.removeIf(id -> id == subId);
+
+            mSubscriptionManager.addSubscriptionsIntoGroup(activeSubGroup, uuid);
+
+            List<Integer> infoList =
+                    getSubscriptionIdList(mSubscriptionManager.getSubscriptionsInGroup(uuid));
+            activeSubGroup.add(subId);
+            assertEquals(activeSubGroup.size(), infoList.size());
+            assertTrue(activeSubGroup.containsAll(infoList));
+        } finally {
+            removeSubscriptionsFromGroup(uuid);
+        }
+    }
+
+    /**
+     * This test verifies that
+     * {@link SubscriptionManager#addSubscriptionsIntoGroup(List, ParcelUuid)}} correctly add some
+     * additional subscriptions to the existing group.
+     *
+     * This test required the device has more than one subscription.
+     */
+    public void testAddSubscriptionToExistingGroupForEsim() {
+        if (!hasCellular) return;
+
+        // Set subscription group with current sub Id.
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        ParcelUuid uuid = mSubscriptionManager.createSubscriptionGroup(Arrays.asList(subId));
+
+        try {
+            // Get all accessible eSim subscription.
+            List<SubscriptionInfo> accessibleSubInfos =
+                    mSubscriptionManager.getAccessibleSubscriptionInfoList();
+            if (accessibleSubInfos != null && accessibleSubInfos.size() > 1) {
+                List<Integer> accessibleSubGroup = getSubscriptionIdList(accessibleSubInfos);
+                accessibleSubGroup.removeIf(id -> id == subId);
+
+                mSubscriptionManager.addSubscriptionsIntoGroup(accessibleSubGroup, uuid);
+
+                List<Integer> infoList =
+                        getSubscriptionIdList(mSubscriptionManager.getSubscriptionsInGroup(uuid));
+                accessibleSubGroup.add(subId);
+                assertEquals(accessibleSubGroup.size(), infoList.size());
+                assertTrue(accessibleSubGroup.containsAll(infoList));
+            }
+        } finally {
+            removeSubscriptionsFromGroup(uuid);
+        }
+    }
+
+    /**
+     * This test verifies that {@link SubscriptionManager#setOpportunistic(boolean, int)} correctly
+     * set the opportunistic property of the given subscription.
+     */
+    public void testOpportunistic() {
+        if (!hasCellular) return;
+
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
+        boolean oldOpportunistic = info.isOpportunistic();
+        boolean newOpportunistic = !oldOpportunistic;
+
+        try {
+            // Mark the given subscription as opportunistic subscription.
+            boolean successed = mSubscriptionManager.setOpportunistic(newOpportunistic, subId);
+            assertTrue(successed);
+
+            // Verify that the given subscription is opportunistic subscription.
+            info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
+            assertEquals(newOpportunistic, info.isOpportunistic());
+        } finally {
+            // Set back to original opportunistic property.
+            mSubscriptionManager.setOpportunistic(oldOpportunistic, subId);
+            info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
+            assertEquals(oldOpportunistic, info.isOpportunistic());
+        }
+    }
+
+    /**
      * This test verifies that {@link TelephonyManager#iccExchangeSimIO(int, int, int, int, int,
      * String)} correctly transmits iccIO commands to the UICC card. First, the MF is selected via a
      * SELECT apdu via the basic channel, then a STATUS AT-command is sent.
@@ -836,6 +966,24 @@
         assertArrayEquals(STATUS_NORMAL, response.getSelectResponse());
     }
 
+    private void removeSubscriptionsFromGroup(ParcelUuid uuid) {
+        List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
+        if (!infoList.isEmpty()) {
+            mSubscriptionManager.removeSubscriptionsFromGroup(
+                    getSubscriptionIdList(infoList),
+                    uuid);
+        }
+        infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
+        assertTrue(infoList.isEmpty());
+    }
+
+    private List<Integer> getSubscriptionIdList(List<SubscriptionInfo> subInfoList) {
+        if (subInfoList == null || subInfoList.isEmpty()) return Collections.EMPTY_LIST;
+        return subInfoList.stream()
+                .map(info -> info.getSubscriptionId())
+                .collect(Collectors.toList());
+    }
+
     /**
      * Checks whether the a {@code fcpTemplate} contains the given {@code fileId}.
      *
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
index d8218de..8c70904 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
@@ -15,13 +15,15 @@
  */
 package android.carrierapi.cts;
 
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -42,7 +44,6 @@
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyScanManager;
-import android.util.ArraySet;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -56,7 +57,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -82,6 +82,7 @@
     private NetworkScan mNetworkScan;
     private NetworkScanRequest mNetworkScanRequest;
     private NetworkScanCallbackImpl mNetworkScanCallback;
+    private static final int MAX_CELLINFO_WAIT_MILLIS = 5000; // 5 seconds
     private static final int MAX_INIT_WAIT_MS = 60000; // 60 seconds
     private Object mLock = new Object();
     private boolean mReady;
@@ -113,9 +114,14 @@
 
     @Before
     public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getContext();
         mTelephonyManager = (TelephonyManager)
-                InstrumentationRegistry.getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        mPackageManager = InstrumentationRegistry.getContext().getPackageManager();
+                context.getSystemService(Context.TELEPHONY_SERVICE);
+        mPackageManager = context.getPackageManager();
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+                context.getPackageName(), ACCESS_FINE_LOCATION);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+                context.getPackageName(), ACCESS_BACKGROUND_LOCATION);
         mTestHandlerThread = new NetworkScanHandlerThread(TAG);
         mTestHandlerThread.start();
     }
@@ -160,8 +166,11 @@
                     switch (msg.what) {
                         case EVENT_NETWORK_SCAN_START:
                             Log.d(TAG, "request network scan");
-                            InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                                    .adoptShellPermissionIdentity();
+                            boolean useShellIdentity = (Boolean) msg.obj;
+                            if (useShellIdentity) {
+                                InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                                        .adoptShellPermissionIdentity();
+                            }
                             try {
                                 mNetworkScan = mTelephonyManager.requestNetworkScan(
                                         mNetworkScanRequest,
@@ -175,8 +184,10 @@
                                 mNetworkScanStatus = EVENT_SCAN_DENIED;
                                 setReady(true);
                             } finally {
-                                InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                                        .dropShellPermissionIdentity();
+                                if (useShellIdentity) {
+                                    InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                                            .dropShellPermissionIdentity();
+                                }
                             }
                             break;
                         default:
@@ -211,6 +222,22 @@
         }
     }
 
+    private class CellInfoResultsCallback extends TelephonyManager.CellInfoCallback {
+        public List<CellInfo> cellInfo;
+
+        @Override
+        public synchronized void onCellInfo(List<CellInfo> cellInfo) {
+            this.cellInfo = cellInfo;
+            notifyAll();
+        }
+
+        public synchronized void wait(int millis) throws InterruptedException {
+            if (cellInfo == null) {
+                super.wait(millis);
+            }
+        }
+    }
+
     private List<RadioAccessSpecifier> getRadioAccessSpecifier(List<CellInfo> allCellInfo) {
         List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
         List<Integer> lteChannels = new ArrayList<>();
@@ -263,29 +290,38 @@
         if (!mTelephonyManager.hasCarrierPrivileges()) {
             fail("This test requires a SIM card with carrier privilege rule on it.");
         }
-        mNetworkScanRequest = buildNetworkScanRequest(true);
-        mNetworkScanCallback = new NetworkScanCallbackImpl();
-        Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START);
-        setReady(false);
-        startNetworkScan.sendToTarget();
-        waitUntilReady();
+        boolean isLocationSwitchOn = getAndSetLocationSwitch(true);
+        try {
+            mNetworkScanRequest = buildNetworkScanRequest(true);
+            mNetworkScanCallback = new NetworkScanCallbackImpl();
+            Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START, false);
+            setReady(false);
+            startNetworkScan.sendToTarget();
+            waitUntilReady();
 
-        Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
-        assertTrue("The final scan status is not ScanCompleted or ScanError with an error "
-                        + "code ERROR_MODEM_UNAVAILABLE or ERROR_UNSUPPORTED",
-                isScanStatusValid());
+            Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
+            assertTrue("The final scan status is " + mNetworkScanStatus + " with error code "
+                            + mErrorCode + ", not ScanCompleted"
+                            + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
+                            + " ERROR_UNSUPPORTED",
+                    isScanStatusValid());
+        } finally {
+            getAndSetLocationSwitch(isLocationSwitchOn);
+        }
     }
+
     @Test
     public void testRequestNetworkScanLocationOffPass() {
-        requestNetworkScanLocationOffHelper(false);
+        requestNetworkScanLocationOffHelper(false, true);
     }
 
     @Test
     public void testRequestNetworkScanLocationOffFail() {
-        requestNetworkScanLocationOffHelper(true);
+        requestNetworkScanLocationOffHelper(true, true);
     }
 
-    public void requestNetworkScanLocationOffHelper(boolean includeBandsAndChannels) {
+    public void requestNetworkScanLocationOffHelper(boolean includeBandsAndChannels,
+            boolean useSpecialScanPermission) {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             // Checks whether the cellular stack should be running on this device.
             Log.e(TAG, "No cellular support, the test will be skipped.");
@@ -300,7 +336,8 @@
         boolean isLocationSwitchOn = getAndSetLocationSwitch(false);
         try {
             mNetworkScanCallback = new NetworkScanCallbackImpl();
-            Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START);
+            Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START,
+                    useSpecialScanPermission);
             setReady(false);
             startNetworkScan.sendToTarget();
             waitUntilReady();
@@ -312,8 +349,10 @@
             }
 
             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
-            assertTrue("The final scan status is not ScanCompleted or ScanError with an error "
-                            + "code ERROR_MODEM_UNAVAILABLE or ERROR_UNSUPPORTED",
+            assertTrue("The final scan status is " + mNetworkScanStatus + " with error code "
+                            + mErrorCode + ", not ScanCompleted"
+                            + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
+                            + " ERROR_UNSUPPORTED",
                     isScanStatusValid());
         } finally {
             getAndSetLocationSwitch(isLocationSwitchOn);
@@ -322,7 +361,7 @@
 
     private NetworkScanRequest buildNetworkScanRequest(boolean includeBandsAndChannels) {
         // Make sure that there should be at least one entry.
-        List<CellInfo> allCellInfo = mTelephonyManager.getAllCellInfo();
+        List<CellInfo> allCellInfo = getCellInfo();
         List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
 
         if (allCellInfo != null && allCellInfo.size() != 0) {
@@ -337,14 +376,20 @@
 
         Log.d(TAG, "number of radioAccessSpecifier: " + radioAccessSpecifier.size());
         if (radioAccessSpecifier.isEmpty()) {
+            // Put in some arbitrary bands and channels so that we trip the location check if needed
+            int[] fakeBands = includeBandsAndChannels
+                    ? new int[] { AccessNetworkConstants.EutranBand.BAND_5 }
+                    : null;
+            int[] fakeChannels = includeBandsAndChannels ? new int[] { 2400 } : null;
+
             RadioAccessSpecifier gsm = new RadioAccessSpecifier(
                     AccessNetworkConstants.AccessNetworkType.GERAN,
                     null /* bands */,
                     null /* channels */);
             RadioAccessSpecifier lte = new RadioAccessSpecifier(
                     AccessNetworkConstants.AccessNetworkType.EUTRAN,
-                    null /* bands */,
-                    null /* channels */);
+                    fakeBands /* bands */,
+                    fakeChannels /* channels */);
             RadioAccessSpecifier wcdma = new RadioAccessSpecifier(
                     AccessNetworkConstants.AccessNetworkType.UTRAN,
                     null /* bands */,
@@ -366,6 +411,17 @@
 
     }
 
+    private List<CellInfo> getCellInfo() {
+        CellInfoResultsCallback resultsCallback = new CellInfoResultsCallback();
+        mTelephonyManager.requestCellInfoUpdate(r -> r.run(), resultsCallback);
+        try {
+            resultsCallback.wait(MAX_CELLINFO_WAIT_MILLIS);
+        } catch (InterruptedException ex) {
+            fail("CellInfoCallback was interrupted: " + ex);
+        }
+        return resultsCallback.cellInfo;
+    }
+
     @Test
     public void testNetworkScanPermission() {
         PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
@@ -561,6 +617,5 @@
         p.setDataPosition(0);
         NetworkScanRequest newnsr = NetworkScanRequest.CREATOR.createFromParcel(p);
         assertTrue(networkScanRequest.equals(newnsr));
-
     }
 }
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index a8f7a3c..7e5d2e9 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -34,6 +34,9 @@
     <uses-permission android:name="android.content.cts.permission.TEST_GRANTED" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 
+    <!-- Used for ContextTest#testCreatePackageContextAsUser -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
     <!-- Used for PackageManager test, don't delete this INTERNET permission -->
     <uses-permission android:name="android.permission.INTERNET" />
 
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 4228c26..418c201 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -40,7 +40,6 @@
         <option name="test-file-name" value="CtsContentTestCases.apk" />
         <option name="test-file-name" value="CtsContentDirectBootUnawareTestApp.apk" />
         <option name="test-file-name" value="CtsContentPartiallyDirectBootAwareTestApp.apk" />
-        <option name="test-file-name" value="CtsContentDirectBootAwareTestApp.apk" />
         <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
         <option name="test-file-name" value="CtsBinderPermissionTestService.apk" />
     </target_preparer>
diff --git a/tests/tests/content/DirectBootAwareTestApp/AndroidManifest.xml b/tests/tests/content/DirectBootAwareTestApp/AndroidManifest.xml
deleted file mode 100644
index b4753b9..0000000
--- a/tests/tests/content/DirectBootAwareTestApp/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2019 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.content.cts.directbootaware">
-
-    <application
-        android:directBootAware="true"
-        android:hasCode="false"
-        android:label="Direct Boot Aware Test App" />
-</manifest>
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index a9a705a..ddedb84 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -16,9 +16,12 @@
 
 package android.content.cts;
 
+import static com.android.compatibility.common.util.RequiredServiceRule.hasService;
+
 import android.app.DownloadManager;
 import android.app.SearchManager;
 import android.content.ContentUris;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -410,7 +413,9 @@
     }
 
     public void testPowerUsageSummarySettings() {
-        assertCanBeHandled(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+        if (isHandheld()) {
+            assertCanBeHandled(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+        }
     }
 
     public void testEasyConnectIntent() {
@@ -430,12 +435,18 @@
     }
 
     public void testNotificationPolicyDetailIntent() {
+        if (!isHandheld()) {
+            return;
+        }
+
         Intent intent = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS)
                 .setData(Uri.parse("package:android.content.cts"));
         assertCanBeHandled(intent);
     }
 
     public void testRequestEnableContentCaptureIntent() {
+        if (!hasService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)) return;
+
         Intent intent = new Intent(Settings.ACTION_REQUEST_ENABLE_CONTENT_CAPTURE);
         assertCanBeHandled(intent);
     }
@@ -444,4 +455,14 @@
         Intent intent = new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS);
         assertCanBeHandled(intent);
     }
+
+    private boolean isHandheld() {
+        // handheld nature is not exposed to package manager, for now
+        // we check for touchscreen and NOT watch, NOT tv and NOT car
+        PackageManager pm = getContext().getPackageManager();
+        return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
+                && !pm.hasSystemFeature(pm.FEATURE_WATCH)
+                && !pm.hasSystemFeature(pm.FEATURE_TELEVISION)
+                && !pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE);
+    }
 }
diff --git a/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java b/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
index 728bcbe..7260c3c 100644
--- a/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
@@ -57,8 +57,6 @@
             "android.content.cts.directbootunaware";
     private static final String PARTIALLY_DIRECT_BOOT_AWARE_PACKAGE_NAME =
             "android.content.cts.partiallydirectbootaware";
-    private static final String DIRECT_BOOT_AWARE_PACKAGE_NAME =
-            "android.content.cts.directbootaware";
 
     private ApplicationInfo mApplicationInfo;
     private String mPackageName;
@@ -217,11 +215,4 @@
                 PARTIALLY_DIRECT_BOOT_AWARE_PACKAGE_NAME, 0);
         assertTrue(applicationInfo.isEncryptionAware());
     }
-
-    @Test
-    public void testDirectBootAwareAppIsEncryptionAware() throws Exception {
-        ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(
-                DIRECT_BOOT_AWARE_PACKAGE_NAME, 0);
-        assertTrue(applicationInfo.isEncryptionAware());
-    }
 }
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 44624aa..2c4d0067 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -55,6 +55,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.platform.test.annotations.AppModeFull;
 import android.text.TextUtils;
 import android.util.Log;
@@ -608,7 +609,10 @@
 
     @Test
     public void testGetPackageUid() throws NameNotFoundException {
-        assertEquals(1000, mPackageManager.getPackageUid("android", 0));
+        int userId = InstrumentationRegistry.getContext().getUserId();
+        int expectedUid = UserHandle.getUid(userId, 1000);
+
+        assertEquals(expectedUid, mPackageManager.getPackageUid("android", 0));
 
         int uid = mPackageManager.getApplicationInfo("com.android.cts.ctsshim", 0 /*flags*/).uid;
         assertEquals(uid, mPackageManager.getPackageUid("com.android.cts.ctsshim", 0));
diff --git a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiTests.java b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiTests.java
index aa958d1..f2fad8c 100644
--- a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiTests.java
+++ b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiTests.java
@@ -517,8 +517,7 @@
      */
     @Test
     public void getPropertiesString_empty() {
-        DeviceConfig.setProperty(
-                NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
+        setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
         final Properties properties =
                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
         final String result = properties.getString(KEY1, DEFAULT_VALUE);
@@ -532,7 +531,7 @@
      */
     @Test
     public void getPropertiesString_nullDefault() {
-        DeviceConfig.setProperty(NAMESPACE1, KEY1, DEFAULT_VALUE, /*makeDefault=*/false);
+        setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, DEFAULT_VALUE);
         final Properties properties =
                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
         final String result = properties.getString(KEY1, null);
@@ -558,8 +557,7 @@
      */
     @Test
     public void getPropertiesBoolean_empty() {
-        DeviceConfig.setProperty(
-                NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE), /*makeDefault=*/false);
+        setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE));
         final Properties properties =
                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
         final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_TRUE);
@@ -600,8 +598,7 @@
      */
     @Test
     public void getPropertiesInt_empty() {
-        DeviceConfig.setProperty(
-                NAMESPACE1, KEY1, String.valueOf(VALID_INT), /*makeDefault=*/false);
+        setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_INT));
         final Properties properties =
                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
 
@@ -643,8 +640,7 @@
      */
     @Test
     public void getPropertiesLong_empty() {
-        DeviceConfig.setProperty(
-                NAMESPACE1, KEY1, String.valueOf(VALID_LONG), /*makeDefault=*/false);
+        setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_LONG));
         final Properties properties =
                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
 
@@ -686,8 +682,7 @@
      */
     @Test
     public void getPropertiesFloat_empty() {
-        DeviceConfig.setProperty(
-                NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT), /*makeDefault=*/false);
+        setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT));
         final Properties properties =
                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
         final float result = properties.getFloat(KEY1, DEFAULT_FLOAT);
@@ -958,8 +953,8 @@
         return propertiesUpdate.properties;
     }
 
-    private static void nullifyProperty(String namespace, String key) {
-        DeviceConfig.setProperty(namespace, key, null, false);
+    private void nullifyProperty(String namespace, String key) {
+        setPropertiesAndAssertSuccessfulChange(namespace, key, null);
     }
 
     private static void deletePropertyThrowShell(String namespace, String key) {
diff --git a/tests/tests/gesture/AndroidTest.xml b/tests/tests/gesture/AndroidTest.xml
index 10d1e6a..0c1c200 100644
--- a/tests/tests/gesture/AndroidTest.xml
+++ b/tests/tests/gesture/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsGestureTestCases.apk" />
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
index 3eb7065..8eb1966 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
@@ -311,7 +311,7 @@
     }
 
     if (outPreTransformHint) {
-        *outPreTransformHint = preTransform;
+        *outPreTransformHint = surfaceCapabilities.currentTransform;
     }
 
     const uint32_t queueFamilyIndex = mDeviceInfo->queueFamilyIndex();
@@ -875,7 +875,10 @@
             .pImageIndices = &nextIndex,
             .pResults = nullptr,
     };
-    VK_CALL(vkQueuePresentKHR(mDeviceInfo->queue(), &presentInfo));
+    VkResult ret = vkQueuePresentKHR(mDeviceInfo->queue(), &presentInfo);
+    if (ret == VK_SUBOPTIMAL_KHR) {
+        return VK_TEST_SUCCESS_SUBOPTIMAL;
+    }
 
-    return VK_TEST_SUCCESS;
+    return ret == VK_SUCCESS ? VK_TEST_SUCCESS : VK_TEST_ERROR;
 }
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
index 600b375..743c124 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
@@ -28,6 +28,7 @@
     VK_TEST_SUCCESS = 0,
     VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED = 1,
     VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED = 2,
+    VK_TEST_SUCCESS_SUBOPTIMAL = 3,
 } VkTestResult;
 
 class DeviceInfo {
diff --git a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
index 508c777..c9f557f 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
@@ -63,7 +63,12 @@
            "Failed to initialize Vulkan renderer");
 
     for (uint32_t i = 0; i < 120; ++i) {
-        ASSERT(renderer.drawFrame() == VK_TEST_SUCCESS, "Failed to draw frame");
+        ret = renderer.drawFrame();
+        if (setPreTransform || preTransformHint == 0x1 /*VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR*/) {
+            ASSERT(ret == VK_TEST_SUCCESS, "Failed to draw frame");
+        } else {
+            ASSERT(ret == VK_TEST_SUCCESS_SUBOPTIMAL, "Failed to draw suboptimal frame");
+        }
     }
 
     ASSERT(validatePixelValues(env, setPreTransform, preTransformHint), "Not properly rotated");
diff --git a/tests/tests/graphics/src/android/graphics/cts/CameraVulkanGpuTest.java b/tests/tests/graphics/src/android/graphics/cts/CameraVulkanGpuTest.java
index b310978..483a17a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CameraVulkanGpuTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CameraVulkanGpuTest.java
@@ -15,13 +15,20 @@
  */
 package android.graphics.cts;
 
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraCharacteristics;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.PropertyUtil;
+
+import java.util.ArrayList;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,6 +48,32 @@
             return;
         }
 
+        if(PropertyUtil.getFirstApiLevel() < 28){
+            // HAL3 is not required for P upgrade devices.
+            return;
+        }
+
+        CameraManager camMgr =
+                (CameraManager) InstrumentationRegistry.getContext().
+                        getSystemService(Context.CAMERA_SERVICE);
+        try {
+            String[] cameraIds = camMgr.getCameraIdList();
+            ArrayList<String> mToBeTestedCameraIds = new ArrayList<String>();
+            for (String id : cameraIds) {
+                CameraCharacteristics characteristics = camMgr.getCameraCharacteristics(id);
+                int hwLevel =
+                        characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+                if (hwLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
+                    mToBeTestedCameraIds.add(id);
+                }
+            }
+
+            // skip the test if all camera devices are legacy
+            if (mToBeTestedCameraIds.size() == 0) return;
+        } catch (IllegalArgumentException e) {
+            // This is the exception that should be thrown in this case.
+        }
+
         loadCameraAndVerifyFrameImport(InstrumentationRegistry.getContext().getAssets());
     }
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java b/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java
index db6910e..b7e6781 100644
--- a/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java
@@ -191,6 +191,20 @@
     }
 
     @Test
+    public void testSetConvexPathConcave() {
+        Outline outline = new Outline();
+        Path path = new Path();
+        path.addRect(0, 0, 100, 10, Path.Direction.CW);
+        path.addRect(0, 0, 10, 100, Path.Direction.CW);
+        assertFalse(path.isConvex()); // path is concave
+
+        assertTrue(outline.isEmpty());
+        // As of Q, this no longer throws an exception
+        outline.setConvexPath(path);
+        assertFalse(outline.isEmpty());
+    }
+
+    @Test
     public void testGetRectRadius() {
         Outline outline = new Outline();
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableScaleTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableScaleTest.java
deleted file mode 100644
index 29afaaf..0000000
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableScaleTest.java
+++ /dev/null
@@ -1,106 +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.graphics.drawable.cts;
-
-import android.app.Activity;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.graphics.cts.R;
-import android.view.PixelCopy;
-import android.widget.ImageView;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.compatibility.common.util.SynchronousPixelCopy;
-import com.android.compatibility.common.util.WidgetTestUtils;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-@MediumTest
-public class VectorDrawableScaleTest {
-    private static final boolean DBG_SCREENSHOT = false;
-    @Rule
-    public final ActivityTestRule<DrawableStubActivity> mActivityRule =
-            new ActivityTestRule<>(DrawableStubActivity.class);
-
-    private Activity mActivity = null;
-    private Resources mResources = null;
-
-    public VectorDrawableScaleTest() throws Throwable {
-    }
-
-    @Before
-    public void setup() {
-        mActivity = mActivityRule.getActivity();
-        mResources = mActivity.getResources();
-    }
-
-    @Test
-    public void testVectorDrawableInImageView() throws Throwable {
-        mActivityRule.runOnUiThread(() -> {
-            mActivity.setContentView(R.layout.vector_drawable_scale_layout);
-        });
-
-        Bitmap screenShot = null;
-        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
-                mActivity.findViewById(R.id.scaletest),
-                () -> setupImageViews());
-        final Rect srcRect = new Rect();
-        mActivityRule.runOnUiThread(() -> {
-            mActivity.findViewById(R.id.imageview1).getGlobalVisibleRect(srcRect);
-        });
-
-        screenShot = takeScreenshot(srcRect);
-        if (DBG_SCREENSHOT) {
-            String outputFolder = mActivity.getExternalFilesDir(null).getAbsolutePath();
-            DrawableTestUtils.saveVectorDrawableIntoPNG(screenShot, outputFolder, "scale");
-        }
-
-        Bitmap golden = BitmapFactory.decodeResource(mResources,
-                R.drawable.vector_drawable_scale_golden);
-        DrawableTestUtils.compareImages("vectorDrawableScale", screenShot, golden,
-                DrawableTestUtils.PIXEL_ERROR_THRESHOLD,
-                DrawableTestUtils.PIXEL_ERROR_COUNT_THRESHOLD,
-                DrawableTestUtils.PIXEL_ERROR_TOLERANCE);
-    }
-
-    // Setup 2 imageviews, one big and one small. The purpose of this test is to make sure that the
-    // imageview with smaller scale will not affect the appearance in the imageview with larger
-    // scale.
-    private void setupImageViews() {
-        ImageView imageView = (ImageView) mActivity.findViewById(R.id.imageview1);
-        imageView.setImageResource(R.drawable.vector_icon_create);
-        imageView = (ImageView) mActivity.findViewById(R.id.imageview2);
-        imageView.setImageResource(R.drawable.vector_icon_create);
-    }
-
-    // Copy the source rectangle from the screen into the returned bitmap.
-    private Bitmap takeScreenshot(Rect srcRect) {
-        SynchronousPixelCopy copy = new SynchronousPixelCopy();
-        Bitmap dest = Bitmap.createBitmap(
-                srcRect.width(), srcRect.height(), Bitmap.Config.ARGB_8888);
-        int copyResult = copy.request(mActivity.getWindow(), srcRect, dest);
-        Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
-        return dest;
-    }
-}
diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
index 3b6251d..37a29ad 100644
--- a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
+++ b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
@@ -52,9 +52,11 @@
 abstract class BlockCipherTestBase extends AndroidTestCase {
 
     private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
+    private static final int LARGE_MESSAGE_SIZE = 100 * 1024;
 
     private KeyStore mAndroidKeyStore;
     private int mNextKeyId;
+    private SecureRandom mRand = new SecureRandom();
 
     @Override
     protected void setUp() throws Exception {
@@ -1249,6 +1251,20 @@
                 subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
     }
 
+    public void testVeryLargeBlock() throws Exception {
+        createCipher();
+        Key key = importKey(getKatKey());
+        init(Cipher.ENCRYPT_MODE, key, getKatAlgorithmParameterSpec());
+        byte[] largeMessage = new byte[LARGE_MESSAGE_SIZE];
+        mRand.nextBytes(largeMessage);
+        byte[] ciphertext = doFinal(largeMessage);
+        assertEquals(getExpectedCiphertextLength(LARGE_MESSAGE_SIZE), ciphertext.length);
+
+        init(Cipher.DECRYPT_MODE, key, getKatAlgorithmParameterSpec());
+        byte[] plaintext = doFinal(ciphertext);
+        assertTrue(Arrays.equals(largeMessage, plaintext));
+    }
+
     protected void createCipher() throws NoSuchAlgorithmException,
             NoSuchPaddingException, NoSuchProviderException  {
         mCipher = Cipher.getInstance(getTransformation(), EXPECTED_PROVIDER_NAME);
@@ -1318,16 +1334,21 @@
     }
 
     private int getExpectedCiphertextLength(int plaintextLength) {
+        int authTagLength = 0;
+        if (isAuthenticatedCipher()) {
+            authTagLength = getKatAuthenticationTagLengthBytes();
+        }
+
         int blockSize = getBlockSize();
         if (isStreamCipher()) {
             // Padding not supported for stream ciphers
             assertFalse(isPaddingEnabled());
-            return plaintextLength;
+            return plaintextLength + authTagLength;
         } else {
             if (isPaddingEnabled()) {
-                return ((plaintextLength / blockSize) + 1) * blockSize;
+                return ((plaintextLength / blockSize) + 1) * blockSize + authTagLength;
             } else {
-                return ((plaintextLength + blockSize - 1) / blockSize) * blockSize;
+                return ((plaintextLength + blockSize - 1) / blockSize) * blockSize + authTagLength;
             }
         }
     }
diff --git a/tests/tests/location/src/android/location/cts/GnssLocationUpdateIntervalTest.java b/tests/tests/location/src/android/location/cts/GnssLocationUpdateIntervalTest.java
new file mode 100644
index 0000000..106df1f
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssLocationUpdateIntervalTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+import android.location.Location;
+import android.location.LocationManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test the {@link Location} update interval.
+ *
+ * Test steps:
+ * 1. Register for location updates with a specific interval.
+ * 2. Wait for {@link #LOCATION_TO_COLLECT_COUNT} locations.
+ * 3. Compute the deltas between location update timestamps.
+ * 4. Compute mean and stddev and assert that they are within expected thresholds.
+ */
+public class GnssLocationUpdateIntervalTest extends GnssTestCase {
+
+    private static final String TAG = "GnssLocationUpdateIntervalTest";
+
+    private static final int LOCATION_TO_COLLECT_COUNT = 8;
+    private static final int TIMEOUT_IN_SEC = 120;
+
+    // Minimum time interval between fixes in milliseconds.
+    private static final int[] FIX_INTERVALS_MILLIS = {0, 1000, 5000, 15000};
+
+    private static final int MSG_TIMEOUT = 1;
+
+    // Timing failures on first NUM_IGNORED_UPDATES updates are ignored.
+    private static final int NUM_IGNORED_UPDATES = 2;
+
+    // In active mode, the mean computed for the deltas should not be smaller
+    // than mInterval * ACTIVE_MIN_MEAN_RATIO
+    private static final double ACTIVE_MIN_MEAN_RATIO = 0.75;
+
+    // In passive mode, the mean computed for the deltas should not be smaller
+    // than mInterval * PASSIVE_MIN_MEAN_RATIO
+    private static final double PASSIVE_MIN_MEAN_RATIO = 0.1;
+
+    /**
+     * The standard deviation computed for the deltas should not be bigger
+     * than mInterval * ALLOWED_STDEV_ERROR_RATIO
+     * or MIN_STDEV_MS, whichever is higher.
+     */
+    private static final double ALLOWED_STDEV_ERROR_RATIO = 0.50;
+    private static final long MIN_STDEV_MS = 1000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTestLocationManager = new TestLocationManager(getContext());
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testLocationUpdatesAtVariousIntervals() throws Exception {
+        if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, true)) {
+            return;
+        }
+
+        for (int fixIntervalMillis : FIX_INTERVALS_MILLIS) {
+            testLocationUpdatesAtInterval(fixIntervalMillis);
+        }
+    }
+
+    private void testLocationUpdatesAtInterval(int fixIntervalMillis) throws Exception {
+        Log.i(TAG, "testLocationUpdatesAtInterval, fixIntervalMillis: " + fixIntervalMillis);
+        TestLocationListener activeLocationListener = new TestLocationListener(
+                LOCATION_TO_COLLECT_COUNT);
+        TestLocationListener passiveLocationListener = new TestLocationListener(
+                LOCATION_TO_COLLECT_COUNT);
+        mTestLocationManager.requestLocationUpdates(activeLocationListener, fixIntervalMillis);
+        mTestLocationManager.requestPassiveLocationUpdates(passiveLocationListener,
+                fixIntervalMillis);
+        try {
+            boolean success = activeLocationListener.await(
+                    (fixIntervalMillis * LOCATION_TO_COLLECT_COUNT) + TIMEOUT_IN_SEC);
+            assertTrue("Time elapsed without getting enough location fixes."
+                            + " Possibly, the test has been run deep indoors."
+                            + " Consider retrying test outdoors.",
+                    success);
+        } finally {
+            mTestLocationManager.removeLocationUpdates(activeLocationListener);
+            mTestLocationManager.removeLocationUpdates(passiveLocationListener);
+        }
+
+        List<Location> activeLocations = activeLocationListener.getReceivedLocationList();
+        List<Location> passiveLocations = passiveLocationListener.getReceivedLocationList();
+        validateLocationUpdateInterval(activeLocations, passiveLocations, fixIntervalMillis);
+    }
+
+    private static void validateLocationUpdateInterval(List<Location> activeLocations,
+            List<Location> passiveLocations, int fixIntervalMillis) {
+        // For active locations, consider all fixes.
+        long minFirstFixTimestamp = 0;
+        List<Long> activeDeltas = getTimeBetweenFixes(LocationManager.GPS_PROVIDER,
+                activeLocations, minFirstFixTimestamp);
+
+        // When a test round starts, passive listener shouldn't receive location before active
+        // listener. If this situation occurs, we treat this location as overdue location.
+        // (The overdue location comes from previous test round, it occurs occasionally)
+        // We have to skip it to prevent wrong calculation of time interval.
+        minFirstFixTimestamp = activeLocations.get(0).getTime();
+        List<Long> passiveDeltas = getTimeBetweenFixes(LocationManager.PASSIVE_PROVIDER,
+                passiveLocations, minFirstFixTimestamp);
+
+        SoftAssert softAssert = new SoftAssert(TAG);
+        assertMeanAndStdev(softAssert, LocationManager.GPS_PROVIDER, fixIntervalMillis,
+                activeDeltas, ACTIVE_MIN_MEAN_RATIO);
+        assertMeanAndStdev(softAssert, LocationManager.PASSIVE_PROVIDER, fixIntervalMillis,
+                passiveDeltas, PASSIVE_MIN_MEAN_RATIO);
+        softAssert.assertAll();
+    }
+
+    private static List<Long> getTimeBetweenFixes(String provider, List<Location> locations,
+            long minFixTimestampMillis) {
+        List<Long> deltas = new ArrayList(locations.size());
+        long lastFixTimestamp = -1;
+        int i = 0;
+        for (Location location : locations) {
+            if (!location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
+                continue;
+            }
+
+            final long fixTimestamp = location.getTime();
+            if (fixTimestamp < minFixTimestampMillis) {
+                Log.i(TAG, provider + " provider, ignoring location update from an earlier test");
+                continue;
+            }
+
+            ++i;
+            final long delta = fixTimestamp - lastFixTimestamp;
+            lastFixTimestamp = fixTimestamp;
+            if (i <= NUM_IGNORED_UPDATES) {
+                Log.i(TAG, provider + " provider, ignoring location update with delta: "
+                        + delta + " msecs");
+                continue;
+            }
+
+            deltas.add(delta);
+        }
+
+        return deltas;
+    }
+
+    private static void assertMeanAndStdev(SoftAssert softAssert, String provider,
+            int fixIntervalMillis, List<Long> deltas, double minMeanRatio) {
+        double mean = computeMean(deltas);
+        double stdev = computeStdev(mean, deltas);
+
+        double minMean = fixIntervalMillis * minMeanRatio;
+        softAssert.assertTrue(provider + " provider mean too small: " + mean
+                + " (min: " + minMean + ")", mean >= minMean);
+
+        double maxStdev = Math.max(MIN_STDEV_MS, fixIntervalMillis * ALLOWED_STDEV_ERROR_RATIO);
+        softAssert.assertTrue(provider + " provider stdev too big: "
+                + stdev + " (max: " + maxStdev + ")", stdev <= maxStdev);
+        Log.i(TAG, provider + " provider mean: " + mean);
+        Log.i(TAG, provider + " provider stdev: " + stdev);
+    }
+
+    private static double computeMean(List<Long> deltas) {
+        long accumulator = 0;
+        for (long d : deltas) {
+            accumulator += d;
+        }
+        return accumulator / deltas.size();
+    }
+
+    private static double computeStdev(double mean, List<Long> deltas) {
+        double accumulator = 0;
+        for (long d : deltas) {
+            double diff = d - mean;
+            accumulator += diff * diff;
+        }
+        return Math.sqrt(accumulator / (deltas.size() - 1));
+    }
+}
diff --git a/tests/tests/location/src/android/location/cts/TestLocationListener.java b/tests/tests/location/src/android/location/cts/TestLocationListener.java
index ba4a664..ea8db06 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationListener.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationListener.java
@@ -22,6 +22,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -30,14 +31,15 @@
 class TestLocationListener implements LocationListener {
     private volatile boolean mProviderEnabled;
     private volatile boolean mLocationReceived;
+
     // Timeout in sec for count down latch wait
     private static final int TIMEOUT_IN_SEC = 120;
     private final CountDownLatch mCountDownLatch;
-    private List<Location>  mLocationList = null;
+    private ConcurrentLinkedQueue<Location> mLocationList = null;
 
     TestLocationListener(int locationToCollect) {
         mCountDownLatch = new CountDownLatch(locationToCollect);
-        mLocationList = new ArrayList<>();
+        mLocationList = new ConcurrentLinkedQueue<>();
     }
 
     @Override
@@ -69,13 +71,21 @@
         return TestUtils.waitFor(mCountDownLatch, TIMEOUT_IN_SEC);
     }
 
+    public boolean await(int timeInSec) throws InterruptedException {
+        return TestUtils.waitFor(mCountDownLatch, timeInSec);
+    }
+
     /**
-     * Get the list of locations received
+     * Get the list of locations received.
      *
-     * @return mLocationList, List of {@link Location}.
+     * Makes a copy of {@code mLocationList}. New locations received after this call is
+     * made are not reflected in the returned list so that the returned list can be safely
+     * iterated without getting a ConcurrentModificationException. Occasionally,
+     * even after calling TestLocationManager.removeLocationUpdates(), the location listener
+     * can receive one or two location updates.
      */
     public List<Location> getReceivedLocationList(){
-        return mLocationList;
+        return new ArrayList(mLocationList);
     }
 
     /**
diff --git a/tests/tests/location/src/android/location/cts/TestLocationManager.java b/tests/tests/location/src/android/location/cts/TestLocationManager.java
index 0d146dd..0447109 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationManager.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationManager.java
@@ -141,6 +141,22 @@
     }
 
     /**
+     * See {@code LocationManager#requestLocationUpdates}.
+     *
+     * @param locationListener location listener for request
+     */
+    public void requestPassiveLocationUpdates(LocationListener locationListener, int minTimeMsec) {
+        if (mLocationManager.getProvider(LocationManager.PASSIVE_PROVIDER) != null) {
+            Log.i(TAG, "Request Passive Location updates.");
+            mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+                    minTimeMsec,
+                    0 /* minDistance */,
+                    locationListener,
+                    Looper.getMainLooper());
+        }
+    }
+
+    /**
      * See {@link android.location.LocationManager#sendExtraCommand}.
      *
      * @param command name of the command to send to the provider.
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
index 2c119cc..6db5494 100644
--- a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -570,8 +570,8 @@
     const AMediaDrmKeyStatus* keysStatus, size_t numKeys, bool hasNewUsableKey) {
 
     gOnKeyChangeListenerOK = false;
-    if (numKeys != 2) {
-        ALOGE("Expects 2 keys, received %zd keys", numKeys);
+    if (numKeys != 3) {
+        ALOGE("Expects 3 keys, received %zd keys", numKeys);
         return;
     }
 
diff --git a/tests/tests/media/src/android/media/cts/AudioFocusTest.java b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
index bacc1cf..f156ada 100644
--- a/tests/tests/media/src/android/media/cts/AudioFocusTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
@@ -16,12 +16,15 @@
 
 package android.media.cts;
 
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
 import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.util.Log;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
 
@@ -168,25 +171,52 @@
     }
 
     public void testAudioFocusRequestGainLossTransientDuck() throws Exception {
+        if (hasAutomotiveFeature(getContext())) {
+            Log.i(TAG,"Test testAudioFocusRequestGainLossTransientDuck "
+                    + "skipped: not required for Auto platform");
+            return;
+        }
+
         final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
         doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, attributes,
                 false /*no handler*/);
     }
 
     public void testAudioFocusRequestGainLossTransientDuckHandler() throws Exception {
+        if (hasAutomotiveFeature(getContext())) {
+            Log.i(TAG,"Test testAudioFocusRequestGainLossTransientDuckHandler "
+                    + "skipped: not required for Auto platform");
+            return;
+        }
+
         final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
         doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, attributes,
                 true /*with handler*/);
     }
 
     public void testAudioFocusRequestForceDuckNotA11y() throws Exception {
+        if (hasAutomotiveFeature(getContext())) {
+            Log.i(TAG,"Test testAudioFocusRequestForceDuckNotA11y "
+                    + "skipped: not required for Auto platform");
+            return;
+        }
+
         // verify a request that is "force duck"'d still causes loss of focus because it doesn't
         // come from an A11y service, and requests are from same uid
-        final AudioAttributes[] attributes = { ATTR_MEDIA, ATTR_A11Y };
+        final AudioAttributes[] attributes = {ATTR_MEDIA, ATTR_A11Y};
         doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, attributes,
                 false /*no handler*/, true /* forceDucking */);
     }
 
+    /**
+     * Determine if automotive feature is available
+     * @param context context to query
+     * @return true if automotive feature is available
+     */
+    private static boolean hasAutomotiveFeature(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
     //-----------------------------------
     // Test utilities
 
diff --git a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
index dd96c2c..6abf63d 100644
--- a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
+++ b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
@@ -58,9 +58,9 @@
         CamcorderProfile.QUALITY_HIGH_SPEED_1080P,
         CamcorderProfile.QUALITY_HIGH_SPEED_2160P
     };
-    private static final int LAST_QUALITY = CamcorderProfile.QUALITY_2160P;
-    private static final int LAST_TIMELAPSE_QUALITY = CamcorderProfile.QUALITY_TIME_LAPSE_2160P;
-    private static final int LAST_HIGH_SPEED_QUALITY = CamcorderProfile.QUALITY_HIGH_SPEED_2160P;
+    private static final int LAST_QUALITY = CamcorderProfile.QUALITY_2K;
+    private static final int LAST_TIMELAPSE_QUALITY = CamcorderProfile.QUALITY_TIME_LAPSE_2K;
+    private static final int LAST_HIGH_SPEED_QUALITY = CamcorderProfile.QUALITY_HIGH_SPEED_4KDCI;
     private static final Integer[] UNKNOWN_QUALITIES = {
         LAST_QUALITY + 1, // Unknown normal profile quality
         LAST_TIMELAPSE_QUALITY + 1, // Unknown timelapse profile quality
diff --git a/tests/tests/media/src/android/media/cts/HeifWriterTest.java b/tests/tests/media/src/android/media/cts/HeifWriterTest.java
index 7a1805f..79eda3b 100644
--- a/tests/tests/media/src/android/media/cts/HeifWriterTest.java
+++ b/tests/tests/media/src/android/media/cts/HeifWriterTest.java
@@ -30,6 +30,7 @@
 import android.graphics.Rect;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.MediaMetadataRetriever;
@@ -225,18 +226,28 @@
             return;
         }
 
-        MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
-        assertNotNull("HEIC full-frame image encoder without HEVC fallback");
+        final MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
 
-        MediaCodecInfo.CodecCapabilities caps =
-                encoder.getCodecInfo().getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_HEVC);
-        assertTrue("HEVC fallback doesn't support tile size " + GRID_WIDTH + "x" + GRID_HEIGHT,
-                caps.getVideoCapabilities().isSizeSupported(GRID_WIDTH, GRID_HEIGHT));
-
-        MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
-        assertTrue("HEVC fallback doesn't support CQ mode",
-                encoderCaps.isBitrateModeSupported(
-                        MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ));
+        boolean fallbackFound = false;
+        for (MediaCodecInfo info : mcl.getCodecInfos()) {
+            if (!info.isEncoder() || !info.isHardwareAccelerated()) {
+                continue;
+            }
+            MediaCodecInfo.CodecCapabilities caps = null;
+            try {
+                caps = info.getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_HEVC);
+            } catch (IllegalArgumentException e) { // mime is not supported
+                continue;
+            }
+            if (caps.getVideoCapabilities().isSizeSupported(GRID_WIDTH, GRID_HEIGHT) &&
+                    caps.getEncoderCapabilities().isBitrateModeSupported(
+                            MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
+                fallbackFound = true;
+                Log.d(TAG, "found fallback on " + info.getName());
+                // not breaking here so that we can log what's available by running this test
+            }
+        }
+        assertTrue("HEIC full-frame image encoder without HEVC fallback", fallbackFound);
     }
 
     private static boolean canEncodeHeic() {
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 0ce4803..29cec3f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -315,7 +315,8 @@
         PackageManager pm = getContext().getPackageManager();
         return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
                 && !pm.hasSystemFeature(pm.FEATURE_WATCH)
-                && !pm.hasSystemFeature(pm.FEATURE_TELEVISION);
+                && !pm.hasSystemFeature(pm.FEATURE_TELEVISION)
+                && !pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE);
     }
 
     // Find whether the given codec can be found using MediaCodecList.find methods.
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
index 89d3404..15340e7 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
@@ -17,6 +17,7 @@
 
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaDrm;
+import android.media.MediaDrm.KeyStatus;
 import android.media.MediaDrm.MediaDrmStateException;
 import android.media.MediaDrmException;
 import android.media.MediaFormat;
@@ -290,6 +291,28 @@
                                 }
                             }
                         }, null);
+                    mDrm.setOnKeyStatusChangeListener(new MediaDrm.OnKeyStatusChangeListener() {
+                            @Override
+                            public void onKeyStatusChange(MediaDrm md, byte[] sessionId,
+                                    List<KeyStatus> keyInformation, boolean hasNewUsableKey) {
+                                Log.d(TAG, "onKeyStatusChange");
+                                assertTrue(md == mDrm);
+                                assertTrue(Arrays.equals(sessionId, mSessionId));
+                                assertTrue(hasNewUsableKey);
+
+                                assertEquals(3, keyInformation.size());
+                                KeyStatus keyStatus = keyInformation.get(0);
+                                assertTrue(Arrays.equals(keyStatus.getKeyId(), new byte[] {0xa, 0xb, 0xc}));
+                                assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_USABLE);
+                                keyStatus = keyInformation.get(1);
+                                assertTrue(Arrays.equals(keyStatus.getKeyId(), new byte[] {0xd, 0xe, 0xf}));
+                                assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_EXPIRED);
+                                keyStatus = keyInformation.get(2);
+                                assertTrue(Arrays.equals(keyStatus.getKeyId(), new byte[] {0x0, 0x1, 0x2}));
+                                assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_USABLE_IN_FUTURE);
+
+                            }
+                        }, null);
 
                     mLock.notify();
                 }
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
index 91a6acf..f01665a 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
@@ -183,6 +183,12 @@
         playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
     }
 
+    protected void playLiveAudioOnlyTest(
+            Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
+            int playTime) throws Exception {
+        playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime);
+    }
+
     protected void playVideoWithRetries(
             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
             Integer width, Integer height, int playTime) throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index 1eba46f..e7d562c 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -75,6 +75,7 @@
         Arrays.asList(AudioDeviceInfo.TYPE_BUILTIN_MIC));
 
     private boolean mRoutingChanged;
+    private boolean mRoutingChangedDetected;
     private AudioManager mAudioManager;
     private File mOutFile;
     private Looper mRoutingChangedLooper;
@@ -673,6 +674,7 @@
 
         mRoutingChanged = false;
         mRoutingChangedLooper = null;
+        mRoutingChangedDetected = false;
         // Create MediaPlayer in another thread to make sure there is a looper active for events.
         Thread t = new Thread() {
             @Override
@@ -699,7 +701,16 @@
                         for (AudioDeviceInfo device : devices) {
                             if (routedDevice.getId() != device.getId()) {
                                 mediaPlayer.setPreferredDevice(device);
-                                break;
+                                try {
+                                    Thread.sleep(WAIT_ROUTING_CHANGE_TIME_MS);
+                                } catch (Exception e) {
+                                }
+                                AudioDeviceInfo currentRoutedDevice = mediaPlayer.getRoutedDevice();
+                                if (currentRoutedDevice != null
+                                        && currentRoutedDevice.getId() != routedDevice.getId()) {
+                                    mRoutingChangedDetected = true;
+                                    break;
+                                }
                             }
                         }
                     }
@@ -719,7 +730,8 @@
             mRoutingChangedLooper = null;
         }
         t.join();
-        assertTrue("Routing changed callback has not been called", mRoutingChanged);
+        assertTrue("Routing changed callback has not been called",
+                (mRoutingChanged || !mRoutingChangedDetected));
     }
 
     public void test_mediaPlayer_incallMusicRoutingPermissions() {
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 8f63a75..789d1ba 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -187,7 +187,7 @@
 
         // Play stream for 60 seconds
         // limit rate to workaround multiplication overflow in framework
-        localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS);
+        localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS, false /*isAudioOnly*/);
     }
 
     public void testHlsWithHeadersCookies() throws Exception {
@@ -215,7 +215,7 @@
 
         // Play stream for 60 seconds
         // limit rate to workaround multiplication overflow in framework
-        localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS);
+        localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS, false /*isAudioOnly*/);
     }
 
     public void testHlsSampleAes_bbb_audio_only_overridable() throws Exception {
@@ -228,7 +228,7 @@
             // if url override provided
             playLiveAudioOnlyTest(mInputUrl, 60 * 1000);
         } else {
-            localHlsTest("audio_only/index.m3u8", 60 * 1000, -1);
+            localHlsTest("audio_only/index.m3u8", 60 * 1000, -1, true /*isAudioOnly*/);
         }
 
     }
@@ -239,7 +239,7 @@
         }
 
         // Play stream for 60 seconds
-        localHlsTest("unmuxed_1500k/index.m3u8", 60 * 1000, -1);
+        localHlsTest("unmuxed_1500k/index.m3u8", 60 * 1000, -1, false /*isAudioOnly*/);
     }
 
 
@@ -379,21 +379,21 @@
         if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
             return; // skip
         }
-        localHlsTest("hls.m3u8", false, false);
+        localHlsTest("hls.m3u8", false, false, false /*isAudioOnly*/);
     }
 
     public void testPlayHlsStreamWithQueryString() throws Throwable {
         if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
             return; // skip
         }
-        localHlsTest("hls.m3u8", true, false);
+        localHlsTest("hls.m3u8", true, false, false /*isAudioOnly*/);
     }
 
     public void testPlayHlsStreamWithRedirect() throws Throwable {
         if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
             return; // skip
         }
-        localHlsTest("hls.m3u8", false, true);
+        localHlsTest("hls.m3u8", false, true, false /*isAudioOnly*/);
     }
 
     public void testPlayHlsStreamWithTimedId3() throws Throwable {
@@ -576,19 +576,19 @@
         worker.quit();
     }
 
-    private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
-            throws Exception {
-        localHlsTest(name, null, null, appendQueryString, redirect, 10, -1);
+    private void localHlsTest(final String name, boolean appendQueryString,
+            boolean redirect, boolean isAudioOnly) throws Exception {
+        localHlsTest(name, null, null, appendQueryString, redirect, 10, -1, isAudioOnly);
     }
 
-    private void localHlsTest(final String name, int playTime, int bitsPerMs)
+    private void localHlsTest(final String name, int playTime, int bitsPerMs, boolean isAudioOnly)
             throws Exception {
-        localHlsTest(name, null, null, false, false, playTime, bitsPerMs);
+        localHlsTest(name, null, null, false, false, playTime, bitsPerMs, isAudioOnly);
     }
 
     private void localHlsTest(String name, Map<String, String> headers, List<HttpCookie> cookies,
-            boolean appendQueryString, boolean redirect, int playTime, int bitsPerMs)
-            throws Exception {
+            boolean appendQueryString, boolean redirect, int playTime, int bitsPerMs,
+            boolean isAudioOnly) throws Exception {
         if (bitsPerMs >= 0) {
             mServer = new CtsTestServer(mContext) {
                 @Override
@@ -609,8 +609,11 @@
             if (appendQueryString) {
                 stream_url += "?foo=bar/baz";
             }
-
-            playLiveVideoTest(Uri.parse(stream_url), headers, cookies, playTime);
+            if (isAudioOnly) {
+                playLiveAudioOnlyTest(Uri.parse(stream_url), headers, cookies, playTime);
+            } else {
+                playLiveVideoTest(Uri.parse(stream_url), headers, cookies, playTime);
+            }
         } finally {
             mServer.shutdown();
         }
diff --git a/tests/tests/net/Android.bp b/tests/tests/net/Android.bp
new file mode 100644
index 0000000..b6ea4af
--- /dev/null
+++ b/tests/tests/net/Android.bp
@@ -0,0 +1,65 @@
+// Copyright (C) 2008 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: "CtsNetTestCases",
+    defaults: ["cts_defaults"],
+
+    // Include both the 32 and 64 bit versions
+    compile_multilib: "both",
+
+    libs: [
+        "voip-common",
+        "org.apache.http.legacy",
+        "android.test.base.stubs",
+    ],
+
+    jni_libs: [
+        "libcts_jni",
+        "libnativedns_jni",
+        "libnativemultinetwork_jni",
+        "libnativehelper_compat_libc++",
+    ],
+
+    // include CtsTestServer as a temporary hack to free net.cts from cts.stub.
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    static_libs: [
+        "FrameworksNetCommonTests",
+        "core-tests-support",
+        "compatibility-device-util-axt",
+        "cts-net-utils",
+        "ctstestrunner-axt",
+        "ctstestserver",
+        "mockwebserver",
+        "junit",
+        "junit-params",
+        "truth-prebuilt",
+    ],
+
+    // uncomment when b/13249961 is fixed
+    // sdk_version: "current",
+    platform_apis: true,
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+
+}
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
deleted file mode 100644
index 0497470..0000000
--- a/tests/tests/net/Android.mk
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (C) 2008 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Include both the 32 and 64 bit versions
-LOCAL_MULTILIB := both
-
-LOCAL_JAVA_LIBRARIES := \
-    voip-common \
-    org.apache.http.legacy \
-    android.test.base.stubs \
-
-
-LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni \
-                              libnativemultinetwork_jni libnativehelper_compat_libc++
-
-# include CtsTestServer as a temporary hack to free net.cts from cts.stub.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsNetTestCases
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    FrameworksNetCommonTests \
-    core-tests-support \
-    compatibility-device-util-axt \
-    ctstestrunner-axt \
-    ctstestserver \
-    mockwebserver \
-    junit \
-    junit-params \
-    truth-prebuilt \
-
-
-# uncomment when b/13249961 is fixed
-#LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/net/api23Test/Android.bp b/tests/tests/net/api23Test/Android.bp
index 48161cf..ffe854e 100644
--- a/tests/tests/net/api23Test/Android.bp
+++ b/tests/tests/net/api23Test/Android.bp
@@ -31,6 +31,7 @@
     static_libs: [
         "core-tests-support",
         "compatibility-device-util-axt",
+        "cts-net-utils",
         "ctstestrunner-axt",
         "ctstestserver",
         "mockwebserver",
diff --git a/tests/tests/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java b/tests/tests/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java
index f38490e..cdb66e3 100644
--- a/tests/tests/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java
+++ b/tests/tests/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java
@@ -25,58 +25,33 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.State;
-import android.net.NetworkRequest;
-import android.net.wifi.WifiManager;
+import android.net.cts.util.CtsNetUtils;
 import android.os.Looper;
-import android.system.Os;
-import android.system.OsConstants;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class ConnectivityManagerApi23Test extends AndroidTestCase {
     private static final String TAG = ConnectivityManagerApi23Test.class.getSimpleName();
-
-    private static final String TEST_HOST = "connectivitycheck.gstatic.com";
-    private static final int SOCKET_TIMEOUT_MS = 2000;
     private static final int SEND_BROADCAST_TIMEOUT = 30000;
-    private static final int HTTP_PORT = 80;
     // Intent string to get the number of wifi CONNECTIVITY_ACTION callbacks the test app has seen
     public static final String GET_WIFI_CONNECTIVITY_ACTION_COUNT =
             "android.net.cts.appForApi23.getWifiConnectivityActionCount";
     // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
-    private static final String NETWORK_CALLBACK_ACTION =
-            "ConnectivityManagerTest.NetworkCallbackAction";
-    private static final String HTTP_REQUEST =
-            "GET /generate_204 HTTP/1.0\r\n" +
-                    "Host: " + TEST_HOST + "\r\n" +
-                    "Connection: keep-alive\r\n\r\n";
 
     private Context mContext;
-    private ConnectivityManager mCm;
-    private WifiManager mWifiManager;
     private PackageManager mPackageManager;
+    private CtsNetUtils mCtsNetUtils;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         Looper.prepare();
         mContext = getContext();
-        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mPackageManager = mContext.getPackageManager();
+        mCtsNetUtils = new CtsNetUtils(mContext);
     }
 
     /**
@@ -89,7 +64,7 @@
         }
         ConnectivityReceiver.prepare();
 
-        toggleWifi();
+        mCtsNetUtils.toggleWifi();
 
         // The connectivity broadcast has been sent; push through a terminal broadcast
         // to wait for in the receive to confirm it didn't see the connectivity change.
@@ -112,7 +87,7 @@
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
         Thread.sleep(200);
 
-        toggleWifi();
+        mCtsNetUtils.toggleWifi();
 
         Intent getConnectivityCount = new Intent(GET_WIFI_CONNECTIVITY_ACTION_COUNT);
         assertEquals(2, sendOrderedBroadcastAndReturnResultCode(
@@ -130,7 +105,7 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mContext.registerReceiver(receiver, filter);
 
-        toggleWifi();
+        mCtsNetUtils.toggleWifi();
         Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
         finalIntent.setClass(mContext, ConnectivityReceiver.class);
         mContext.sendBroadcast(finalIntent);
@@ -138,19 +113,6 @@
         assertTrue(ConnectivityReceiver.waitForBroadcast());
     }
 
-    // Toggle WiFi twice, leaving it in the state it started in
-    private void toggleWifi() {
-        if (mWifiManager.isWifiEnabled()) {
-            Network wifiNetwork = getWifiNetwork();
-            disconnectFromWifi(wifiNetwork);
-            connectToWifi();
-        } else {
-            connectToWifi();
-            Network wifiNetwork = getWifiNetwork();
-            disconnectFromWifi(wifiNetwork);
-        }
-    }
-
     private int sendOrderedBroadcastAndReturnResultCode(
             Intent intent, int timeoutMs) throws InterruptedException {
         final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
@@ -167,233 +129,4 @@
         return resultCode;
     }
 
-    private Network getWifiNetwork() {
-        TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network network = null;
-        try {
-            network = callback.waitForAvailable();
-        } catch (InterruptedException e) {
-            fail("NetworkCallback wait was interrupted.");
-        } finally {
-            mCm.unregisterNetworkCallback(callback);
-        }
-        assertNotNull("Cannot find Network for wifi. Is wifi connected?", network);
-        return network;
-    }
-
-    /** Disable WiFi and wait for it to become disconnected from the network. */
-    private void disconnectFromWifi(Network wifiNetworkToCheck) {
-        final TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network lostWifiNetwork = null;
-
-        ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
-                ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mContext.registerReceiver(receiver, filter);
-
-        // Assert that we can establish a TCP connection on wifi.
-        Socket wifiBoundSocket = null;
-        if (wifiNetworkToCheck != null) {
-            try {
-                wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT);
-                testHttpRequest(wifiBoundSocket);
-            } catch (IOException e) {
-                fail("HTTP request before wifi disconnected failed with: " + e);
-            }
-        }
-
-        boolean disconnected = false;
-        try {
-            assertTrue(mWifiManager.setWifiEnabled(false));
-            // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
-            lostWifiNetwork = callback.waitForLost();
-            assertNotNull(lostWifiNetwork);
-            disconnected = receiver.waitForState();
-        } catch (InterruptedException ex) {
-            fail("disconnectFromWifi was interrupted");
-        } finally {
-            mCm.unregisterNetworkCallback(callback);
-            mContext.unregisterReceiver(receiver);
-        }
-
-        assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
-
-        // Check that the socket is closed when wifi disconnects.
-        if (wifiBoundSocket != null) {
-            try {
-                testHttpRequest(wifiBoundSocket);
-                fail("HTTP request should not succeed after wifi disconnects");
-            } catch (IOException expected) {
-                assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage());
-            }
-        }
-    }
-
-    /** Enable WiFi and wait for it to become connected to a network. */
-    private Network connectToWifi() {
-        final TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network wifiNetwork = null;
-
-        ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
-                ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mContext.registerReceiver(receiver, filter);
-
-        boolean connected = false;
-        try {
-            assertTrue(mWifiManager.setWifiEnabled(true));
-            // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
-            wifiNetwork = callback.waitForAvailable();
-            assertNotNull(wifiNetwork);
-            connected = receiver.waitForState();
-        } catch (InterruptedException ex) {
-            fail("connectToWifi was interrupted");
-        } finally {
-            mCm.unregisterNetworkCallback(callback);
-            mContext.unregisterReceiver(receiver);
-        }
-
-        assertTrue("Wifi must be configured to connect to an access point for this test.",
-                connected);
-        return wifiNetwork;
-    }
-
-    private NetworkRequest makeWifiNetworkRequest() {
-        return new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                .build();
-    }
-
-    private void testHttpRequest(Socket s) throws IOException {
-        OutputStream out = s.getOutputStream();
-        InputStream in = s.getInputStream();
-
-        final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
-        byte[] responseBytes = new byte[4096];
-        out.write(requestBytes);
-        in.read(responseBytes);
-        assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n"));
-    }
-
-    private Socket getBoundSocket(Network network, String host, int port) throws IOException {
-        InetSocketAddress addr = new InetSocketAddress(host, port);
-        Socket s = network.getSocketFactory().createSocket();
-        try {
-            s.setSoTimeout(SOCKET_TIMEOUT_MS);
-            s.connect(addr, SOCKET_TIMEOUT_MS);
-        } catch (IOException e) {
-            s.close();
-            throw e;
-        }
-        return s;
-    }
-
-    /**
-     * Receiver that captures the last connectivity change's network type and state. Recognizes
-     * both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
-     */
-    private class ConnectivityActionReceiver extends BroadcastReceiver {
-
-        private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
-
-        private final int mNetworkType;
-        private final NetworkInfo.State mNetState;
-
-        ConnectivityActionReceiver(int networkType, NetworkInfo.State netState) {
-            mNetworkType = networkType;
-            mNetState = netState;
-        }
-
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            NetworkInfo networkInfo = null;
-
-            // When receiving ConnectivityManager.CONNECTIVITY_ACTION, the NetworkInfo parcelable
-            // is stored in EXTRA_NETWORK_INFO. With a NETWORK_CALLBACK_ACTION, the Network is
-            // sent in EXTRA_NETWORK and we need to ask the ConnectivityManager for the NetworkInfo.
-            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
-                networkInfo = intent.getExtras()
-                        .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
-                assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK_INFO", networkInfo);
-            } else if (NETWORK_CALLBACK_ACTION.equals(action)) {
-                Network network = intent.getExtras()
-                        .getParcelable(ConnectivityManager.EXTRA_NETWORK);
-                assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK", network);
-                networkInfo = mCm.getNetworkInfo(network);
-                if (networkInfo == null) {
-                    // When disconnecting, it seems like we get an intent sent with an invalid
-                    // Network; that is, by the time we call ConnectivityManager.getNetworkInfo(),
-                    // it is invalid. Ignore these.
-                    Log.i(TAG, "ConnectivityActionReceiver NETWORK_CALLBACK_ACTION ignoring "
-                            + "invalid network");
-                    return;
-                }
-            } else {
-                fail("ConnectivityActionReceiver received unxpected intent action: " + action);
-            }
-
-            assertNotNull("ConnectivityActionReceiver didn't find NetworkInfo", networkInfo);
-            int networkType = networkInfo.getType();
-            State networkState = networkInfo.getState();
-            Log.i(TAG, "Network type: " + networkType + " state: " + networkState);
-            if (networkType == mNetworkType && networkInfo.getState() == mNetState) {
-                mReceiveLatch.countDown();
-            }
-        }
-
-        public boolean waitForState() throws InterruptedException {
-            return mReceiveLatch.await(30, TimeUnit.SECONDS);
-        }
-    }
-
-    /**
-     * Callback used in testRegisterNetworkCallback that allows caller to block on
-     * {@code onAvailable}.
-     */
-    private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
-        private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
-        private final CountDownLatch mLostLatch = new CountDownLatch(1);
-        private final CountDownLatch mUnavailableLatch = new CountDownLatch(1);
-
-        public Network currentNetwork;
-        public Network lastLostNetwork;
-
-        public Network waitForAvailable() throws InterruptedException {
-            return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null;
-        }
-
-        public Network waitForLost() throws InterruptedException {
-            return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
-        }
-
-        public boolean waitForUnavailable() throws InterruptedException {
-            return mUnavailableLatch.await(2, TimeUnit.SECONDS);
-        }
-
-
-        @Override
-        public void onAvailable(Network network) {
-            currentNetwork = network;
-            mAvailableLatch.countDown();
-        }
-
-        @Override
-        public void onLost(Network network) {
-            lastLostNetwork = network;
-            if (network.equals(currentNetwork)) {
-                currentNetwork = null;
-            }
-            mLostLatch.countDown();
-        }
-
-        @Override
-        public void onUnavailable() {
-            mUnavailableLatch.countDown();
-        }
-    }
 }
\ No newline at end of file
diff --git a/tests/tests/content/DirectBootAwareTestApp/Android.bp b/tests/tests/net/appForApi23/Android.bp
similarity index 70%
copy from tests/tests/content/DirectBootAwareTestApp/Android.bp
copy to tests/tests/net/appForApi23/Android.bp
index 17ccda1..82e2a08 100644
--- a/tests/tests/content/DirectBootAwareTestApp/Android.bp
+++ b/tests/tests/net/appForApi23/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2019 The Android Open Source Project
+// Copyright (C) 2016 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.
@@ -12,14 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-android_test_helper_app {
-    name: "CtsContentDirectBootAwareTestApp",
+android_test {
+    name: "CtsNetTestAppForApi23",
     defaults: ["cts_defaults"],
-    sdk_version: "current",
-    // tag this module as a cts test artifact
+
+    // Include both the 32 and 64 bit versions
+    compile_multilib: "both",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "23",
+
+    // Tag this module as a cts test artifact
     test_suites: [
         "cts",
         "vts",
         "general-tests",
     ],
+
 }
diff --git a/tests/tests/net/appForApi23/Android.mk b/tests/tests/net/appForApi23/Android.mk
deleted file mode 100644
index 54b60a0..0000000
--- a/tests/tests/net/appForApi23/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Include both the 32 and 64 bit versions
-LOCAL_MULTILIB := both
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsNetTestAppForApi23
-
-LOCAL_SDK_VERSION := 23
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/net/jni/Android.bp b/tests/tests/net/jni/Android.bp
new file mode 100644
index 0000000..baed48d
--- /dev/null
+++ b/tests/tests/net/jni/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2013 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_library_shared {
+    name: "libnativedns_jni",
+
+    srcs: ["NativeDnsJni.c"],
+
+    shared_libs: [
+        "libnativehelper_compat_libc++",
+        "liblog",
+    ],
+    stl: "libc++_static",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+
+}
+
+cc_library_shared {
+    name: "libnativemultinetwork_jni",
+
+    srcs: ["NativeMultinetworkJni.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-format",
+    ],
+    shared_libs: [
+        "libandroid",
+        "libnativehelper_compat_libc++",
+        "liblog",
+    ],
+    stl: "libc++_static",
+}
diff --git a/tests/tests/net/jni/Android.mk b/tests/tests/net/jni/Android.mk
deleted file mode 100644
index ccb1278..0000000
--- a/tests/tests/net/jni/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2013 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libnativedns_jni
-
-# Don't include this package in any configuration by default.
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := NativeDnsJni.c
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-
-LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
-LOCAL_CXX_STL := libc++_static
-
-LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libnativemultinetwork_jni
-# Don't include this package in any configuration by default.
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := NativeMultinetworkJni.cpp
-LOCAL_CFLAGS := -Wall -Werror -Wno-format
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
-LOCAL_CXX_STL := libc++_static
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/net/native/Android.mk b/tests/tests/net/native/Android.mk
deleted file mode 100644
index b798d87..0000000
--- a/tests/tests/net/native/Android.mk
+++ /dev/null
@@ -1,15 +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.
-
-include $(call all-subdir-makefiles)
diff --git a/tests/tests/net/native/qtaguid/Android.bp b/tests/tests/net/native/qtaguid/Android.bp
new file mode 100644
index 0000000..c0f0613
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/Android.bp
@@ -0,0 +1,53 @@
+// 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.
+
+// Build the unit tests.
+
+cc_test {
+    name: "CtsNativeNetTestCases",
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    srcs: ["src/NativeQtaguidTest.cpp"],
+
+    shared_libs: [
+        "libutils",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libgtest",
+        "libqtaguid",
+    ],
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+}
diff --git a/tests/tests/net/native/qtaguid/Android.mk b/tests/tests/net/native/qtaguid/Android.mk
deleted file mode 100644
index bf89e5f..0000000
--- a/tests/tests/net/native/qtaguid/Android.mk
+++ /dev/null
@@ -1,43 +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.
-
-# Build the unit tests.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CtsNativeNetTestCases
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_SRC_FILES := \
-    src/NativeQtaguidTest.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libutils \
-    liblog \
-
-LOCAL_STATIC_LIBRARIES := \
-    libgtest \
-    libqtaguid \
-
-LOCAL_CTS_TEST_PACKAGE := android.net.native
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-
-LOCAL_CFLAGS := -Werror -Wall
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index c444445..8c9bf6e 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -21,8 +21,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
+import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
+import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
+import static android.net.cts.util.CtsNetUtils.TEST_HOST;
+import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
 import static android.system.OsConstants.AF_INET;
@@ -34,7 +38,6 @@
 import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.UiAutomation;
-import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -53,23 +56,23 @@
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
 import android.net.SocketKeepalive;
+import android.net.cts.util.CtsNetUtils;
 import android.net.util.KeepaliveUtils;
 import android.net.wifi.WifiManager;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
-import android.system.Os;
-import android.system.OsConstants;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.compatibility.common.util.SystemUtil;
 import com.android.internal.R;
 import com.android.internal.telephony.PhoneConstants;
 
@@ -107,8 +110,6 @@
     public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
 
     private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
-    private static final String TEST_HOST = "connectivitycheck.gstatic.com";
-    private static final int SOCKET_TIMEOUT_MS = 2000;
     private static final int CONNECT_TIMEOUT_MS = 2000;
     private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000;
     private static final int KEEPALIVE_SOCKET_TIMEOUT_MS = 5000;
@@ -116,16 +117,6 @@
     private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
     private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
-    private static final int HTTP_PORT = 80;
-    private static final String HTTP_REQUEST =
-            "GET /generate_204 HTTP/1.0\r\n" +
-            "Host: " + TEST_HOST + "\r\n" +
-            "Connection: keep-alive\r\n\r\n";
-
-    // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
-    private static final String NETWORK_CALLBACK_ACTION =
-            "ConnectivityManagerTest.NetworkCallbackAction";
-
     // device could have only one interface: data, wifi.
     private static final int MIN_NUM_NETWORK_TYPES = 1;
 
@@ -137,8 +128,8 @@
     private final HashMap<Integer, NetworkConfig> mNetworks =
             new HashMap<Integer, NetworkConfig>();
     boolean mWifiConnectAttempted;
-    private TestNetworkCallback mCellNetworkCallback;
     private UiAutomation mUiAutomation;
+    private CtsNetUtils mCtsNetUtils;
     private boolean mShellPermissionIdentityAdopted;
 
     @Override
@@ -150,6 +141,7 @@
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mPackageManager = mContext.getPackageManager();
+        mCtsNetUtils = new CtsNetUtils(mContext);
         mWifiConnectAttempted = false;
 
         // Get com.android.internal.R.array.networkAttributes
@@ -174,10 +166,10 @@
     protected void tearDown() throws Exception {
         // Return WiFi to its original disabled state after tests that explicitly connect.
         if (mWifiConnectAttempted) {
-            disconnectFromWifi(null);
+            mCtsNetUtils.disconnectFromWifi(null);
         }
-        if (cellConnectAttempted()) {
-            disconnectFromCell();
+        if (mCtsNetUtils.cellConnectAttempted()) {
+            mCtsNetUtils.disconnectFromCell();
         }
         dropShellPermissionIdentity();
         super.tearDown();
@@ -190,10 +182,10 @@
      */
     private Network ensureWifiConnected() {
         if (mWifiManager.isWifiEnabled()) {
-            return getWifiNetwork();
+            return mCtsNetUtils.getWifiNetwork();
         }
         mWifiConnectAttempted = true;
-        return connectToWifi();
+        return mCtsNetUtils.connectToWifi();
     }
 
     public void testIsNetworkTypeValid() {
@@ -301,8 +293,8 @@
             return;
         }
 
-        Network wifiNetwork = connectToWifi();
-        Network cellNetwork = connectToCell();
+        Network wifiNetwork = mCtsNetUtils.connectToWifi();
+        Network cellNetwork = mCtsNetUtils.connectToCell();
         // This server returns the requestor's IP address as the response body.
         URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
         String wifiAddressString = httpGet(wifiNetwork, url);
@@ -320,33 +312,6 @@
         assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork));
     }
 
-    private Network connectToCell() throws InterruptedException {
-        if (cellConnectAttempted()) {
-            throw new IllegalStateException("Already connected");
-        }
-        NetworkRequest cellRequest = new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR)
-                .addCapability(NET_CAPABILITY_INTERNET)
-                .build();
-        mCellNetworkCallback = new TestNetworkCallback();
-        mCm.requestNetwork(cellRequest, mCellNetworkCallback);
-        final Network cellNetwork = mCellNetworkCallback.waitForAvailable();
-        assertNotNull("Cell network not available within timeout", cellNetwork);
-        return cellNetwork;
-    }
-
-    private boolean cellConnectAttempted() {
-        return mCellNetworkCallback != null;
-    }
-
-    private void disconnectFromCell() {
-        if (!cellConnectAttempted()) {
-            throw new IllegalStateException("Cell connection not attempted");
-        }
-        mCm.unregisterNetworkCallback(mCellNetworkCallback);
-        mCellNetworkCallback = null;
-    }
-
     /**
      * Performs a HTTP GET to the specified URL on the specified Network, and returns
      * the response body decoded as UTF-8.
@@ -508,7 +473,7 @@
         filter.addAction(NETWORK_CALLBACK_ACTION);
 
         ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
-                ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
+                mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
         mContext.registerReceiver(receiver, filter);
 
         // Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION.
@@ -567,7 +532,7 @@
     public void testRequestNetworkCallback_onUnavailable() {
         final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
         if (previousWifiEnabledState) {
-            disconnectFromWifi(null);
+            mCtsNetUtils.disconnectFromWifi(null);
         }
 
         final TestNetworkCallback callback = new TestNetworkCallback();
@@ -584,42 +549,11 @@
         } finally {
             mCm.unregisterNetworkCallback(callback);
             if (previousWifiEnabledState) {
-                connectToWifi();
+                mCtsNetUtils.connectToWifi();
             }
         }
     }
 
-    /** Enable WiFi and wait for it to become connected to a network. */
-    private Network connectToWifi() {
-        final TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network wifiNetwork = null;
-
-        ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
-                ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mContext.registerReceiver(receiver, filter);
-
-        boolean connected = false;
-        try {
-            SystemUtil.runShellCommand("svc wifi enable");
-            // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
-            wifiNetwork = callback.waitForAvailable();
-            assertNotNull(wifiNetwork);
-            connected = receiver.waitForState();
-        } catch (InterruptedException ex) {
-            fail("connectToWifi was interrupted");
-        } finally {
-            mCm.unregisterNetworkCallback(callback);
-            mContext.unregisterReceiver(receiver);
-        }
-
-        assertTrue("Wifi must be configured to connect to an access point for this test.",
-                connected);
-        return wifiNetwork;
-    }
-
     private InetAddress getFirstV4Address(Network network) {
         LinkProperties linkProperties = mCm.getLinkProperties(network);
         for (InetAddress address : linkProperties.getAddresses()) {
@@ -630,199 +564,6 @@
         return null;
     }
 
-    private Socket getBoundSocket(Network network, String host, int port) throws IOException {
-        InetSocketAddress addr = new InetSocketAddress(host, port);
-        Socket s = network.getSocketFactory().createSocket();
-        try {
-            s.setSoTimeout(SOCKET_TIMEOUT_MS);
-            s.connect(addr, SOCKET_TIMEOUT_MS);
-        } catch (IOException e) {
-            s.close();
-            throw e;
-        }
-        return s;
-    }
-
-    private void testHttpRequest(Socket s) throws IOException {
-        OutputStream out = s.getOutputStream();
-        InputStream in = s.getInputStream();
-
-        final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
-        byte[] responseBytes = new byte[4096];
-        out.write(requestBytes);
-        in.read(responseBytes);
-        assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n"));
-    }
-
-    /** Disable WiFi and wait for it to become disconnected from the network. */
-    private void disconnectFromWifi(Network wifiNetworkToCheck) {
-        final TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network lostWifiNetwork = null;
-
-        ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
-                ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mContext.registerReceiver(receiver, filter);
-
-        // Assert that we can establish a TCP connection on wifi.
-        Socket wifiBoundSocket = null;
-        if (wifiNetworkToCheck != null) {
-            try {
-                wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT);
-                testHttpRequest(wifiBoundSocket);
-            } catch (IOException e) {
-                fail("HTTP request before wifi disconnected failed with: " + e);
-            }
-        }
-
-        boolean disconnected = false;
-        try {
-            SystemUtil.runShellCommand("svc wifi disable");
-            // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
-            lostWifiNetwork = callback.waitForLost();
-            assertNotNull(lostWifiNetwork);
-            disconnected = receiver.waitForState();
-        } catch (InterruptedException ex) {
-            fail("disconnectFromWifi was interrupted");
-        } finally {
-            mCm.unregisterNetworkCallback(callback);
-            mContext.unregisterReceiver(receiver);
-        }
-
-        assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
-
-        // Check that the socket is closed when wifi disconnects.
-        if (wifiBoundSocket != null) {
-            try {
-                testHttpRequest(wifiBoundSocket);
-                fail("HTTP request should not succeed after wifi disconnects");
-            } catch (IOException expected) {
-                assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage());
-            }
-        }
-    }
-
-    /**
-     * Receiver that captures the last connectivity change's network type and state. Recognizes
-     * both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
-     */
-    private class ConnectivityActionReceiver extends BroadcastReceiver {
-
-        private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
-
-        private final int mNetworkType;
-        private final NetworkInfo.State mNetState;
-
-        ConnectivityActionReceiver(int networkType, NetworkInfo.State netState) {
-            mNetworkType = networkType;
-            mNetState = netState;
-        }
-
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            NetworkInfo networkInfo = null;
-
-            // When receiving ConnectivityManager.CONNECTIVITY_ACTION, the NetworkInfo parcelable
-            // is stored in EXTRA_NETWORK_INFO. With a NETWORK_CALLBACK_ACTION, the Network is
-            // sent in EXTRA_NETWORK and we need to ask the ConnectivityManager for the NetworkInfo.
-            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
-                networkInfo = intent.getExtras()
-                        .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
-                assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK_INFO", networkInfo);
-            } else if (NETWORK_CALLBACK_ACTION.equals(action)) {
-                Network network = intent.getExtras()
-                        .getParcelable(ConnectivityManager.EXTRA_NETWORK);
-                assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK", network);
-                networkInfo = mCm.getNetworkInfo(network);
-                if (networkInfo == null) {
-                    // When disconnecting, it seems like we get an intent sent with an invalid
-                    // Network; that is, by the time we call ConnectivityManager.getNetworkInfo(),
-                    // it is invalid. Ignore these.
-                    Log.i(TAG, "ConnectivityActionReceiver NETWORK_CALLBACK_ACTION ignoring "
-                            + "invalid network");
-                    return;
-                }
-            } else {
-                fail("ConnectivityActionReceiver received unxpected intent action: " + action);
-            }
-
-            assertNotNull("ConnectivityActionReceiver didn't find NetworkInfo", networkInfo);
-            int networkType = networkInfo.getType();
-            State networkState = networkInfo.getState();
-            Log.i(TAG, "Network type: " + networkType + " state: " + networkState);
-            if (networkType == mNetworkType && networkInfo.getState() == mNetState) {
-                mReceiveLatch.countDown();
-            }
-        }
-
-        public boolean waitForState() throws InterruptedException {
-            return mReceiveLatch.await(30, TimeUnit.SECONDS);
-        }
-    }
-
-    /**
-     * Callback used in testRegisterNetworkCallback that allows caller to block on
-     * {@code onAvailable}.
-     */
-    private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
-        private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
-        private final CountDownLatch mLostLatch = new CountDownLatch(1);
-        private final CountDownLatch mUnavailableLatch = new CountDownLatch(1);
-
-        public Network currentNetwork;
-        public Network lastLostNetwork;
-
-        public Network waitForAvailable() throws InterruptedException {
-            return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null;
-        }
-
-        public Network waitForLost() throws InterruptedException {
-            return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
-        }
-
-        public boolean waitForUnavailable() throws InterruptedException {
-            return mUnavailableLatch.await(2, TimeUnit.SECONDS);
-        }
-
-
-        @Override
-        public void onAvailable(Network network) {
-            currentNetwork = network;
-            mAvailableLatch.countDown();
-        }
-
-        @Override
-        public void onLost(Network network) {
-            lastLostNetwork = network;
-            if (network.equals(currentNetwork)) {
-                currentNetwork = null;
-            }
-            mLostLatch.countDown();
-        }
-
-        @Override
-        public void onUnavailable() {
-            mUnavailableLatch.countDown();
-        }
-    }
-
-    private Network getWifiNetwork() {
-        TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network network = null;
-        try {
-            network = callback.waitForAvailable();
-        } catch (InterruptedException e) {
-            fail("NetworkCallback wait was interrupted.");
-        } finally {
-            mCm.unregisterNetworkCallback(callback);
-        }
-        assertNotNull("Cannot find Network for wifi. Is wifi connected?", network);
-        return network;
-    }
-
     /** Verify restricted networks cannot be requested. */
     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
     public void testRestrictedNetworks() {
@@ -1108,30 +849,6 @@
                 keepalivesPerTransport, nc);
     }
 
-    private boolean isKeepaliveSupported() throws Exception {
-        final Network network = ensureWifiConnected();
-        final Executor executor = mContext.getMainExecutor();
-        final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
-        try (Socket s = getConnectedSocket(network, TEST_HOST,
-                HTTP_PORT, KEEPALIVE_SOCKET_TIMEOUT_MS, AF_INET);
-                SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
-            sk.start(MIN_KEEPALIVE_INTERVAL);
-            final TestSocketKeepaliveCallback.CallbackValue result = callback.pollCallback();
-            switch (result.callbackType) {
-                case ON_STARTED:
-                    sk.stop();
-                    callback.expectStopped();
-                    return true;
-                case ON_ERROR:
-                    if (result.error == SocketKeepalive.ERROR_UNSUPPORTED) return false;
-                    // else fallthrough.
-                default:
-                    fail("Got unexpected callback: " + result);
-                    return false;
-            }
-        }
-    }
-
     private void adoptShellPermissionIdentity() {
         mUiAutomation.adoptShellPermissionIdentity();
         mShellPermissionIdentityAdopted = true;
@@ -1144,14 +861,88 @@
         }
     }
 
+    private static boolean isTcpKeepaliveSupportedByKernel() {
+        final String kVersionString = VintfRuntimeInfo.getKernelVersion();
+        return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
+    }
+
+    private static Pair<Integer, Integer> getVersionFromString(String version) {
+        // Only gets major and minor number of the version string.
+        final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*");
+        final Matcher m = versionPattern.matcher(version);
+        if (m.matches()) {
+            final int major = Integer.parseInt(m.group(1));
+            final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3));
+            return new Pair<>(major, minor);
+        } else {
+            return new Pair<>(0, 0);
+        }
+    }
+
+    // TODO: Move to util class.
+    private static int compareMajorMinorVersion(final String s1, final String s2) {
+        final Pair<Integer, Integer> v1 = getVersionFromString(s1);
+        final Pair<Integer, Integer> v2 = getVersionFromString(s2);
+
+        if (v1.first == v2.first) {
+            return Integer.compare(v1.second, v2.second);
+        } else {
+            return Integer.compare(v1.first, v2.first);
+        }
+    }
+
+    /**
+     * Verifies that version string compare logic returns expected result for various cases.
+     * Note that only major and minor number are compared.
+     */
+    public void testMajorMinorVersionCompare() {
+        assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
+        assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
+        assertEquals(1, compareMajorMinorVersion("5.0", "4.8"));
+        assertEquals(1, compareMajorMinorVersion("5", "4.8"));
+        assertEquals(0, compareMajorMinorVersion("5", "5.0"));
+        assertEquals(1, compareMajorMinorVersion("5-beta1", "4.8"));
+        assertEquals(0, compareMajorMinorVersion("4.8.0.0", "4.8"));
+        assertEquals(0, compareMajorMinorVersion("4.8-RC1", "4.8"));
+        assertEquals(0, compareMajorMinorVersion("4.8", "4.8"));
+        assertEquals(-1, compareMajorMinorVersion("3.10", "4.8.0"));
+        assertEquals(-1, compareMajorMinorVersion("4.7.10.10", "4.8"));
+    }
+
+    /**
+     * Verifies that the keepalive API cannot create any keepalive when the maximum number of
+     * keepalives is set to 0.
+     */
+    @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    public void testKeepaliveUnsupported() throws Exception {
+        if (getSupportedKeepalivesFromRes() != 0) return;
+
+        adoptShellPermissionIdentity();
+
+        assertEquals(0, createConcurrentSocketKeepalives(1, 0));
+        assertEquals(0, createConcurrentSocketKeepalives(0, 1));
+
+        dropShellPermissionIdentity();
+    }
+
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     public void testCreateTcpKeepalive() throws Exception {
         adoptShellPermissionIdentity();
 
-        if (!isKeepaliveSupported()) return;
+        if (getSupportedKeepalivesFromRes() == 0) return;
+        // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
+        // NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive
+        // needs to be supported except if the kernel doesn't support it.
+        if (!isTcpKeepaliveSupportedByKernel()) {
+            // Sanity check to ensure the callback result is expected.
+            assertEquals(0, createConcurrentSocketKeepalives(0, 1));
+            Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel "
+                    + VintfRuntimeInfo.getKernelVersion());
+            return;
+        }
 
         final Network network = ensureWifiConnected();
-        final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
+        final byte[] requestBytes = CtsNetUtils.HTTP_REQUEST.getBytes("UTF-8");
         // So far only ipv4 tcp keepalive offload is supported.
         // TODO: add test case for ipv6 tcp keepalive offload when it is supported.
         try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
@@ -1213,14 +1004,16 @@
                 sk.start(MIN_KEEPALIVE_INTERVAL);
                 callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
             }
-
         }
     }
 
+    /**
+     * Creates concurrent keepalives until the specified counts of each type of keepalives are
+     * reached or the expected error callbacks are received for each type of keepalives.
+     *
+     * @return the total number of keepalives created.
+     */
     private int createConcurrentSocketKeepalives(int nattCount, int tcpCount) throws Exception {
-        // Use customization value in resource to prevent the need of privilege.
-        if (getSupportedKeepalivesFromRes() == 0) return 0;
-
         final Network network = ensureWifiConnected();
 
         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
@@ -1239,10 +1032,14 @@
                 ka.start(MIN_KEEPALIVE_INTERVAL);
                 TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback();
                 assertNotNull(cv);
-                if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR
-                        && cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
-                    // Limit reached.
-                    break;
+                if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR) {
+                    if (i == 0 && cv.error == SocketKeepalive.ERROR_UNSUPPORTED) {
+                        // Unsupported.
+                        break;
+                    } else if (i != 0 && cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
+                        // Limit reached.
+                        break;
+                    }
                 }
                 if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) {
                     kalist.add(ka);
@@ -1268,10 +1065,14 @@
             ka.start(MIN_KEEPALIVE_INTERVAL);
             TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback();
             assertNotNull(cv);
-            if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR
-                    && cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
-                // Limit reached.
-                break;
+            if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR) {
+                if (i == 0 && cv.error == SocketKeepalive.ERROR_UNSUPPORTED) {
+                    // Unsupported.
+                    break;
+                } else if (i != 0 && cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
+                    // Limit reached.
+                    break;
+                }
             }
             if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) {
                 kalist.add(ka);
@@ -1299,32 +1100,35 @@
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     public void testSocketKeepaliveLimit() throws Exception {
-        adoptShellPermissionIdentity();
-
         final int supported = getSupportedKeepalivesFromRes();
-
-        if (!isKeepaliveSupported()) {
-            // Sanity check.
-            assertEquals(0, supported);
+        if (supported == 0) {
             return;
         }
 
+        adoptShellPermissionIdentity();
+
         // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
         assertGreaterOrEqual(supported, KeepaliveUtils.MIN_SUPPORTED_KEEPALIVE_COUNT);
 
-        // Verifies that different types of keepalives can be established.
+        // Verifies that Nat-T keepalives can be established.
         assertEquals(supported, createConcurrentSocketKeepalives(supported + 1, 0));
-        assertEquals(supported, createConcurrentSocketKeepalives(0, supported + 1));
-
-        // Verifies that different types can be established at the same time.
-        assertEquals(supported, createConcurrentSocketKeepalives(
-                supported / 2, supported - supported / 2));
-
         // Verifies that keepalives don't get leaked in second round.
         assertEquals(supported, createConcurrentSocketKeepalives(supported + 1, 0));
-        assertEquals(supported, createConcurrentSocketKeepalives(0, supported + 1));
-        assertEquals(supported, createConcurrentSocketKeepalives(
-                supported / 2, supported - supported / 2));
+
+        // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
+        // NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel.
+        if (isTcpKeepaliveSupportedByKernel()) {
+            assertEquals(supported, createConcurrentSocketKeepalives(0, supported + 1));
+
+            // Verifies that different types can be established at the same time.
+            assertEquals(supported, createConcurrentSocketKeepalives(
+                    supported / 2, supported - supported / 2));
+
+            // Verifies that keepalives don't get leaked in second round.
+            assertEquals(supported, createConcurrentSocketKeepalives(0, supported + 1));
+            assertEquals(supported, createConcurrentSocketKeepalives(
+                    supported / 2, supported - supported / 2));
+        }
 
         dropShellPermissionIdentity();
     }
@@ -1335,14 +1139,9 @@
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     public void testSocketKeepaliveUnprivileged() throws Exception {
         final int supported = getSupportedKeepalivesFromRes();
-
-        adoptShellPermissionIdentity();
-        if (!isKeepaliveSupported()) {
-            // Sanity check.
-            assertEquals(0, supported);
+        if (supported == 0) {
             return;
         }
-        dropShellPermissionIdentity();
 
         final int allowedUnprivilegedPerUid = mContext.getResources().getInteger(
                 R.integer.config_allowedUnprivilegedKeepalivePerUid);
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index 40d64cf..e16fce0 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -21,20 +21,25 @@
 import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP;
 import static android.net.DnsResolver.TYPE_A;
 import static android.net.DnsResolver.TYPE_AAAA;
-import static android.system.OsConstants.EBADF;
+import static android.system.OsConstants.ETIMEDOUT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.ContentResolver;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.DnsPacket;
 import android.net.DnsResolver;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.ParseException;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
+import android.provider.Settings;
 import android.system.ErrnoException;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -53,20 +58,63 @@
     private static final char[] HEX_CHARS = {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
     };
+
+    static final String TEST_DOMAIN = "www.google.com";
+    static final String INVALID_PRIVATE_DNS_SERVER = "invalid.google";
+    static final byte[] TEST_BLOB = new byte[]{
+            /* Header */
+            0x55, 0x66, /* Transaction ID */
+            0x01, 0x00, /* Flags */
+            0x00, 0x01, /* Questions */
+            0x00, 0x00, /* Answer RRs */
+            0x00, 0x00, /* Authority RRs */
+            0x00, 0x00, /* Additional RRs */
+            /* Queries */
+            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+            0x00, 0x01, /* Type */
+            0x00, 0x01  /* Class */
+    };
     static final int TIMEOUT_MS = 12_000;
     static final int CANCEL_TIMEOUT_MS = 3_000;
     static final int CANCEL_RETRY_TIMES = 5;
     static final int NXDOMAIN = 3;
+    static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
 
+    private ContentResolver mCR;
     private ConnectivityManager mCM;
     private Executor mExecutor;
     private DnsResolver mDns;
 
+    private String mOldMode;
+    private String mOldDnsSpecifier;
+
+    @Override
     protected void setUp() throws Exception {
         super.setUp();
         mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         mDns = DnsResolver.getInstance();
         mExecutor = new Handler(Looper.getMainLooper())::post;
+        mCR = getContext().getContentResolver();
+        storePrivateDnsSetting();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        restorePrivateDnsSetting();
+        super.tearDown();
+    }
+
+    private void storePrivateDnsSetting() {
+        // Store private DNS setting
+        mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
+        mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
+    }
+
+    private void restorePrivateDnsSetting() {
+        // restore private DNS setting
+        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
+        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
     }
 
     private static String byteArrayToHexString(byte[] bytes) {
@@ -94,6 +142,9 @@
                 "This test requires that at least one network be connected. " +
                         "Please ensure that the device is connected to a network.",
                 testableNetworks.size() >= 1);
+        // In order to test query with null network, add null as an element.
+        // Test cases which query with null network will go on default network.
+        testableNetworks.add(null);
         return testableNetworks.toArray(new Network[0]);
     }
 
@@ -105,15 +156,12 @@
         public DnsParseException(String msg) {
             super(msg);
         }
-
-        public DnsParseException(String msg, Throwable cause) {
-            super(msg, cause);
-        }
     }
 
     private static class DnsAnswer extends DnsPacket {
         DnsAnswer(@NonNull byte[] data) throws DnsParseException {
             super(data);
+
             // Check QR field.(query (0), or a response (1)).
             if ((mHeader.flags & (1 << 15)) == 0) {
                 throw new DnsParseException("Not an answer packet");
@@ -123,10 +171,12 @@
         int getRcode() {
             return mHeader.rcode;
         }
-        int getANCount(){
+
+        int getANCount() {
             return mHeader.getRecordCount(ANSECTION);
         }
-        int getQDCount(){
+
+        int getQDCount() {
             return mHeader.getRecordCount(QDSECTION);
         }
     }
@@ -144,9 +194,8 @@
         private DnsAnswer mDnsAnswer;
 
         VerifyCancelCallback(@NonNull String msg, @Nullable CancellationSignal cancel) {
-            this.mMsg = msg;
-            this.mCancelSignal = cancel;
-            this.mDnsAnswer = null;
+            mMsg = msg;
+            mCancelSignal = cancel;
         }
 
         VerifyCancelCallback(@NonNull String msg) {
@@ -174,7 +223,7 @@
             mRcode = rcode;
             try {
                 mDnsAnswer = new DnsAnswer(answer);
-            } catch (DnsParseException e) {
+            } catch (ParseException | DnsParseException e) {
                 fail(mMsg + e.getMessage());
             }
             Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer));
@@ -187,15 +236,15 @@
         }
 
         private void assertValidAnswer() {
-            assertTrue(mMsg + "No valid answer", mDnsAnswer != null);
-            assertTrue(mMsg + " Unexpected error: reported rcode" + mRcode +
-                    " blob's rcode " + mDnsAnswer.getRcode(), mRcode == mDnsAnswer.getRcode());
+            assertNotNull(mMsg + " No valid answer", mDnsAnswer);
+            assertEquals(mMsg + " Unexpected error: reported rcode" + mRcode +
+                    " blob's rcode " + mDnsAnswer.getRcode(), mRcode, mDnsAnswer.getRcode());
         }
 
         public void assertHasAnswer() {
             assertValidAnswer();
             // Check rcode field.(0, No error condition).
-            assertTrue(mMsg + " Response error, rcode: " + mRcode, mRcode == 0);
+            assertEquals(mMsg + " Response error, rcode: " + mRcode, mRcode, 0);
             // Check answer counts.
             assertGreaterThan(mMsg + " No answer found", mDnsAnswer.getANCount(), 0);
             // Check question counts.
@@ -205,9 +254,9 @@
         public void assertNXDomain() {
             assertValidAnswer();
             // Check rcode field.(3, NXDomain).
-            assertTrue(mMsg + " Unexpected rcode: " + mRcode, mRcode == NXDOMAIN);
+            assertEquals(mMsg + " Unexpected rcode: " + mRcode, mRcode, NXDOMAIN);
             // Check answer counts. Expect 0 answer.
-            assertTrue(mMsg + " Not an empty answer", mDnsAnswer.getANCount() == 0);
+            assertEquals(mMsg + " Not an empty answer", mDnsAnswer.getANCount(), 0);
             // Check question counts.
             assertGreaterThan(mMsg + " No question found", mDnsAnswer.getQDCount(), 0);
         }
@@ -215,98 +264,84 @@
         public void assertEmptyAnswer() {
             assertValidAnswer();
             // Check rcode field.(0, No error condition).
-            assertTrue(mMsg + " Response error, rcode: " + mRcode, mRcode == 0);
+            assertEquals(mMsg + " Response error, rcode: " + mRcode, mRcode, 0);
             // Check answer counts. Expect 0 answer.
-            assertTrue(mMsg + " Not an empty answer", mDnsAnswer.getANCount() == 0);
+            assertEquals(mMsg + " Not an empty answer", mDnsAnswer.getANCount(), 0);
             // Check question counts.
             assertGreaterThan(mMsg + " No question found", mDnsAnswer.getQDCount(), 0);
         }
     }
 
-    public void testRawQuery() {
-        final String dname = "www.google.com";
-        final String msg = "RawQuery " + dname;
+    public void testRawQuery() throws InterruptedException {
+        final String msg = "RawQuery " + TEST_DOMAIN;
         for (Network network : getTestableNetworks()) {
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
-            mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+            mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
                     mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                callback.assertHasAnswer();
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
-            }
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            callback.assertHasAnswer();
         }
     }
 
-    public void testRawQueryBlob() {
+    public void testRawQueryBlob() throws InterruptedException {
         final byte[] blob = new byte[]{
-            /* Header */
-            0x55, 0x66, /* Transaction ID */
-            0x01, 0x00, /* Flags */
-            0x00, 0x01, /* Questions */
-            0x00, 0x00, /* Answer RRs */
-            0x00, 0x00, /* Authority RRs */
-            0x00, 0x00, /* Additional RRs */
-            /* Queries */
-            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
-            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
-            0x00, 0x01, /* Type */
-            0x00, 0x01  /* Class */
+                /* Header */
+                0x55, 0x66, /* Transaction ID */
+                0x01, 0x00, /* Flags */
+                0x00, 0x01, /* Questions */
+                0x00, 0x00, /* Answer RRs */
+                0x00, 0x00, /* Authority RRs */
+                0x00, 0x00, /* Additional RRs */
+                /* Queries */
+                0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+                0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+                0x00, 0x01, /* Type */
+                0x00, 0x01  /* Class */
         };
         final String msg = "RawQuery blob " + byteArrayToHexString(blob);
         for (Network network : getTestableNetworks()) {
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
             mDns.rawQuery(network, blob, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                callback.assertHasAnswer();
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
-            }
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            callback.assertHasAnswer();
         }
     }
 
-    public void testRawQueryRoot() {
+    public void testRawQueryRoot() throws InterruptedException {
         final String dname = "";
         final String msg = "RawQuery empty dname(ROOT) ";
         for (Network network : getTestableNetworks()) {
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
             mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
                     mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                // Except no answer record because of querying with empty dname(ROOT)
-                callback.assertEmptyAnswer();
-            } catch (InterruptedException e) {
-                fail(msg + "Waiting for DNS lookup was interrupted");
-            }
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            // Except no answer record because the root does not have AAAA records.
+            callback.assertEmptyAnswer();
         }
     }
 
-    public void testRawQueryNXDomain() {
+    public void testRawQueryNXDomain() throws InterruptedException {
         final String dname = "test1-nx.metric.gstatic.com";
         final String msg = "RawQuery " + dname;
         for (Network network : getTestableNetworks()) {
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
             mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
                     mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                callback.assertNXDomain();
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
-            }
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            callback.assertNXDomain();
         }
     }
 
-    public void testRawQueryCancel() throws ErrnoException {
-        final String dname = "www.google.com";
-        final String msg = "Test cancel RawQuery " + dname;
+    public void testRawQueryCancel() throws InterruptedException {
+        final String msg = "Test cancel RawQuery " + TEST_DOMAIN;
         // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
         // that the query is cancelled before it succeeds. If it is not cancelled before it
         // succeeds, retry the test until it is.
@@ -320,39 +355,22 @@
                 final CountDownLatch latch = new CountDownLatch(1);
                 final CancellationSignal cancelSignal = new CancellationSignal();
                 final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal);
-                mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
+                mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
                         mExecutor, cancelSignal, callback);
                 mExecutor.execute(() -> {
                     cancelSignal.cancel();
                     latch.countDown();
                 });
-                try {
-                    retry = callback.needRetry();
-                    assertTrue(msg + " query was not cancelled",
-                            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-                } catch (InterruptedException e) {
-                    fail(msg + "Waiting for DNS lookup was interrupted");
-                }
+
+                retry = callback.needRetry();
+                assertTrue(msg + " query was not cancelled",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             } while (retry);
         }
     }
 
-    public void testRawQueryBlobCancel() throws ErrnoException {
-        final byte[] blob = new byte[]{
-            /* Header */
-            0x55, 0x66, /* Transaction ID */
-            0x01, 0x00, /* Flags */
-            0x00, 0x01, /* Questions */
-            0x00, 0x00, /* Answer RRs */
-            0x00, 0x00, /* Authority RRs */
-            0x00, 0x00, /* Additional RRs */
-            /* Queries */
-            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
-            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
-            0x00, 0x01, /* Type */
-            0x00, 0x01  /* Class */
-        };
-        final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(blob);
+    public void testRawQueryBlobCancel() throws InterruptedException {
+        final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(TEST_BLOB);
         // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
         // that the query is cancelled before it succeeds. If it is not cancelled before it
         // succeeds, retry the test until it is.
@@ -366,37 +384,30 @@
                 final CountDownLatch latch = new CountDownLatch(1);
                 final CancellationSignal cancelSignal = new CancellationSignal();
                 final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal);
-                mDns.rawQuery(network, blob, FLAG_EMPTY, mExecutor, cancelSignal, callback);
+                mDns.rawQuery(network, TEST_BLOB, FLAG_EMPTY, mExecutor, cancelSignal, callback);
                 mExecutor.execute(() -> {
                     cancelSignal.cancel();
                     latch.countDown();
                 });
-                try {
-                    retry = callback.needRetry();
-                    assertTrue(msg + " cancel is not done",
-                            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-                } catch (InterruptedException e) {
-                    fail(msg + " Waiting for DNS lookup was interrupted");
-                }
+
+                retry = callback.needRetry();
+                assertTrue(msg + " cancel is not done",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             } while (retry);
         }
     }
 
-    public void testCancelBeforeQuery() throws ErrnoException {
-        final String dname = "www.google.com";
-        final String msg = "Test cancelled RawQuery " + dname;
+    public void testCancelBeforeQuery() throws InterruptedException {
+        final String msg = "Test cancelled RawQuery " + TEST_DOMAIN;
         for (Network network : getTestableNetworks()) {
             final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
             final CancellationSignal cancelSignal = new CancellationSignal();
             cancelSignal.cancel();
-            mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
+            mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
                     mExecutor, cancelSignal, callback);
-            try {
-                assertTrue(msg + " should not return any answers",
-                        !callback.waitForAnswer(CANCEL_TIMEOUT_MS));
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
-            }
+
+            assertTrue(msg + " should not return any answers",
+                    !callback.waitForAnswer(CANCEL_TIMEOUT_MS));
         }
     }
 
@@ -463,27 +474,21 @@
         }
     }
 
-    public void testQueryForInetAddress() {
-        final String dname = "www.google.com";
-        final String msg = "Test query for InetAddress " + dname;
+    public void testQueryForInetAddress() throws InterruptedException {
+        final String msg = "Test query for InetAddress " + TEST_DOMAIN;
         for (Network network : getTestableNetworks()) {
             final VerifyCancelInetAddressCallback callback =
                     new VerifyCancelInetAddressCallback(msg, null);
-            mDns.query(network, dname, FLAG_NO_CACHE_LOOKUP,
-                    mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
-            }
+            mDns.query(network, TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
         }
     }
 
-    public void testQueryCancelForInetAddress() throws ErrnoException {
-        final String dname = "www.google.com";
-        final String msg = "Test cancel query for InetAddress " + dname;
+    public void testQueryCancelForInetAddress() throws InterruptedException {
+        final String msg = "Test cancel query for InetAddress " + TEST_DOMAIN;
         // Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to
         // expect that the query is cancelled before it succeeds. If it is not cancelled before it
         // succeeds, retry the test until it is.
@@ -498,57 +503,131 @@
                 final CancellationSignal cancelSignal = new CancellationSignal();
                 final VerifyCancelInetAddressCallback callback =
                         new VerifyCancelInetAddressCallback(msg, cancelSignal);
-                mDns.query(network, dname, FLAG_EMPTY, mExecutor, cancelSignal, callback);
+                mDns.query(network, TEST_DOMAIN, FLAG_EMPTY, mExecutor, cancelSignal, callback);
                 mExecutor.execute(() -> {
                     cancelSignal.cancel();
                     latch.countDown();
                 });
-                try {
-                    retry = callback.needRetry();
-                    assertTrue(msg + " query was not cancelled",
-                            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-                } catch (InterruptedException e) {
-                    fail(msg + "Waiting for DNS lookup was interrupted");
-                }
+
+                retry = callback.needRetry();
+                assertTrue(msg + " query was not cancelled",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             } while (retry);
         }
     }
 
-    public void testQueryForInetAddressIpv4() {
-        final String dname = "www.google.com";
-        final String msg = "Test query for IPv4 InetAddress " + dname;
+    public void testQueryForInetAddressIpv4() throws InterruptedException {
+        final String msg = "Test query for IPv4 InetAddress " + TEST_DOMAIN;
         for (Network network : getTestableNetworks()) {
             final VerifyCancelInetAddressCallback callback =
                     new VerifyCancelInetAddressCallback(msg, null);
-            mDns.query(network, dname, TYPE_A, FLAG_NO_CACHE_LOOKUP,
+            mDns.query(network, TEST_DOMAIN, TYPE_A, FLAG_NO_CACHE_LOOKUP,
                     mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
-                assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer());
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
-            }
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
+            assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer());
         }
     }
 
-    public void testQueryForInetAddressIpv6() {
-        final String dname = "www.google.com";
-        final String msg = "Test query for IPv6 InetAddress " + dname;
+    public void testQueryForInetAddressIpv6() throws InterruptedException {
+        final String msg = "Test query for IPv6 InetAddress " + TEST_DOMAIN;
         for (Network network : getTestableNetworks()) {
             final VerifyCancelInetAddressCallback callback =
                     new VerifyCancelInetAddressCallback(msg, null);
-            mDns.query(network, dname, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+            mDns.query(network, TEST_DOMAIN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
                     mExecutor, null, callback);
-            try {
-                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
-                        callback.waitForAnswer());
-                assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
-                assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer());
-            } catch (InterruptedException e) {
-                fail(msg + " Waiting for DNS lookup was interrupted");
+
+            assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
+            assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer());
+        }
+    }
+
+    private void awaitPrivateDnsSetting(@NonNull String msg,
+            @NonNull Network network, @NonNull String server) 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 (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
+                    latch.countDown();
+                }
             }
+        };
+        mCM.registerNetworkCallback(request, callback);
+        assertTrue(msg, latch.await(PRIVATE_DNS_SETTING_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        mCM.unregisterNetworkCallback(callback);
+    }
+
+    public void testPrivateDnsBypass() throws InterruptedException {
+        final Network[] testNetworks = getTestableNetworks();
+
+        // Set an invalid private DNS server
+        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
+            // private DNS bypassable network or bind one.
+            if (network == null) continue;
+
+            // wait for private DNS setting propagating
+            awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
+                    network, INVALID_PRIVATE_DNS_SERVER);
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            final DnsResolver.Callback<List<InetAddress>> errorCallback =
+                    new DnsResolver.Callback<List<InetAddress>>() {
+                        @Override
+                        public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) {
+                            fail(msg + " should not get valid answer");
+                        }
+
+                        @Override
+                        public void onError(@NonNull DnsResolver.DnsException error) {
+                            assertEquals(DnsResolver.ERROR_SYSTEM, error.code);
+                            assertEquals(ETIMEDOUT, ((ErrnoException) error.getCause()).errno);
+                            latch.countDown();
+                        }
+                    };
+            // Private DNS strict mode with invalid DNS server is set
+            // Expect no valid answer returned but ErrnoException with ETIMEDOUT
+            mDns.query(network, TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, errorCallback);
+
+            assertTrue(msg + " invalid server round. No response after " + TIMEOUT_MS + "ms.",
+                    latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+            final VerifyCancelInetAddressCallback callback =
+                    new VerifyCancelInetAddressCallback(msg, null);
+            // Bypass privateDns, expect query works fine
+            mDns.query(network.getPrivateDnsBypassingCopy(),
+                    TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
+
+            assertTrue(msg + " bypass private DNS round. No answer after " + TIMEOUT_MS + "ms.",
+                    callback.waitForAnswer());
+            assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
+
+            // To ensure private DNS bypass still work even if passing null network.
+            // Bind process network with a private DNS bypassable network.
+            mCM.bindProcessToNetwork(network.getPrivateDnsBypassingCopy());
+            final VerifyCancelInetAddressCallback callbackWithNullNetwork =
+                    new VerifyCancelInetAddressCallback(msg + " with null network ", null);
+            mDns.query(null,
+                    TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callbackWithNullNetwork);
+
+            assertTrue(msg + " with null network bypass private DNS round. No answer after " +
+                    TIMEOUT_MS + "ms.", callbackWithNullNetwork.waitForAnswer());
+            assertTrue(msg + " with null network returned 0 results",
+                    !callbackWithNullNetwork.isAnswerEmpty());
+
+            // Reset process network to default.
+            mCM.bindProcessToNetwork(null);
         }
     }
 }
diff --git a/tests/tests/net/src/android/net/cts/IpSecBaseTest.java b/tests/tests/net/src/android/net/cts/IpSecBaseTest.java
index 26049cc..10e43e7 100644
--- a/tests/tests/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/tests/net/src/android/net/cts/IpSecBaseTest.java
@@ -76,11 +76,9 @@
 
     protected ConnectivityManager mCM;
     protected IpSecManager mISM;
-    protected Context mContext;
 
     @Before
     public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getContext();
         mISM =
                 (IpSecManager)
                         InstrumentationRegistry.getContext()
@@ -475,7 +473,7 @@
     private IpSecTransform buildDefaultTransform(InetAddress localAddr) throws Exception {
         try (IpSecManager.SecurityParameterIndex spi =
                 mISM.allocateSecurityParameterIndex(localAddr)) {
-            return buildIpSecTransform(mContext, spi, null, localAddr);
+            return buildIpSecTransform(InstrumentationRegistry.getContext(), spi, null, localAddr);
         }
     }
 
diff --git a/tests/tests/net/src/android/net/cts/IpSecManagerTest.java b/tests/tests/net/src/android/net/cts/IpSecManagerTest.java
index 1241785..355b496 100644
--- a/tests/tests/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/IpSecManagerTest.java
@@ -41,6 +41,7 @@
 import android.system.Os;
 import android.system.OsConstants;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import java.io.FileDescriptor;
@@ -73,12 +74,6 @@
 
     private static final byte[] AEAD_KEY = getKey(288);
 
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-    }
-
     /*
      * Allocate a random SPI
      * Allocate a specific SPI using previous randomly created SPI value
@@ -244,7 +239,7 @@
                 mISM.allocateSecurityParameterIndex(localAddr);
 
         IpSecTransform transform =
-                new IpSecTransform.Builder(mContext)
+                new IpSecTransform.Builder(InstrumentationRegistry.getContext())
                         .setEncryption(new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY))
                         .setAuthentication(
                                 new IpSecAlgorithm(
@@ -462,7 +457,8 @@
                 IpSecManager.SecurityParameterIndex spi =
                         mISM.allocateSecurityParameterIndex(local)) {
 
-            IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(mContext);
+            IpSecTransform.Builder transformBuilder =
+                    new IpSecTransform.Builder(InstrumentationRegistry.getContext());
             if (crypt != null) {
                 transformBuilder.setEncryption(crypt);
             }
@@ -623,7 +619,7 @@
             try (IpSecManager.SecurityParameterIndex spi =
                             mISM.allocateSecurityParameterIndex(local);
                     IpSecTransform transform =
-                            new IpSecTransform.Builder(mContext)
+                            new IpSecTransform.Builder(InstrumentationRegistry.getContext())
                                     .setEncryption(crypt)
                                     .setAuthentication(auth)
                                     .setIpv4Encapsulation(encapSocket, encapSocket.getPort())
diff --git a/tests/tests/net/src/android/net/cts/IpSecManagerTunnelTest.java b/tests/tests/net/src/android/net/cts/IpSecManagerTunnelTest.java
index 93638ac..999d2f1 100644
--- a/tests/tests/net/src/android/net/cts/IpSecManagerTunnelTest.java
+++ b/tests/tests/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -59,6 +59,7 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
+import android.platform.test.annotations.AppModeFull;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -79,6 +80,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
 public class IpSecManagerTunnelTest extends IpSecBaseTest {
     private static final String TAG = IpSecManagerTunnelTest.class.getSimpleName();
 
@@ -127,15 +129,15 @@
         // right appop permissions.
         setAppop(OP_MANAGE_IPSEC_TUNNELS, true);
 
-        TestNetworkInterface testIntf =
+        TestNetworkInterface testIface =
                 sTNM.createTunInterface(
                         new LinkAddress[] {
                             new LinkAddress(LOCAL_OUTER_4, IP4_PREFIX_LEN),
                             new LinkAddress(LOCAL_OUTER_6, IP6_PREFIX_LEN)
                         });
 
-        sTunFd = testIntf.getFileDescriptor();
-        sTunNetworkCallback = setupAndGetTestNetwork(testIntf.getInterfaceName());
+        sTunFd = testIface.getFileDescriptor();
+        sTunNetworkCallback = setupAndGetTestNetwork(testIface.getInterfaceName());
         sTunNetwork = sTunNetworkCallback.getNetworkBlocking();
 
         sTunUtils = new TunUtils(sTunFd);
@@ -240,8 +242,16 @@
     }
 
     /* Test runnables for callbacks after IPsec tunnels are set up. */
-    private interface TestRunnable {
-        void run(Network ipsecNetwork) throws Exception;
+    private abstract class IpSecTunnelTestRunnable {
+        /**
+         * Runs the test code, and returns the inner socket port, if any.
+         *
+         * @param ipsecNetwork The IPsec Interface based Network for binding sockets on
+         * @return the integer port of the inner socket if outbound, or 0 if inbound
+         *     IpSecTunnelTestRunnable
+         * @throws Exception if any part of the test failed.
+         */
+        public abstract int run(Network ipsecNetwork) throws Exception;
     }
 
     private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
@@ -288,8 +298,8 @@
         return expectedPacketSize;
     }
 
-    private interface TestRunnableFactory {
-        TestRunnable getTestRunnable(
+    private interface IpSecTunnelTestRunnableFactory {
+        IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
                 boolean transportInTunnelMode,
                 int spi,
                 InetAddress localInner,
@@ -299,12 +309,13 @@
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
+                int innerSocketPort,
                 int expectedPacketSize)
                 throws Exception;
     }
 
-    private class OutputTestRunnableFactory implements TestRunnableFactory {
-        public TestRunnable getTestRunnable(
+    private class OutputIpSecTunnelTestRunnableFactory implements IpSecTunnelTestRunnableFactory {
+        public IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
                 boolean transportInTunnelMode,
                 int spi,
                 InetAddress localInner,
@@ -314,13 +325,15 @@
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
+                int unusedInnerSocketPort,
                 int expectedPacketSize) {
-            return new TestRunnable() {
+            return new IpSecTunnelTestRunnable() {
                 @Override
-                public void run(Network ipsecNetwork) throws Exception {
+                public int run(Network ipsecNetwork) throws Exception {
                     // Build a socket and send traffic
                     JavaUdpSocket socket = new JavaUdpSocket(localInner);
                     ipsecNetwork.bindSocket(socket.mSocket);
+                    int innerSocketPort = socket.getPort();
 
                     // For Transport-In-Tunnel mode, apply transform to socket
                     if (transportInTunnelMode) {
@@ -333,19 +346,22 @@
                     socket.sendTo(TEST_DATA, remoteInner, socket.getPort());
 
                     // Verify that an encrypted packet is sent. As of right now, checking encrypted
-                    // body is not possible, due to our not knowing some of the fields of the
+                    // body is not possible, due to the test not knowing some of the fields of the
                     // inner IP header (flow label, flags, etc)
                     sTunUtils.awaitEspPacketNoPlaintext(
                             spi, TEST_DATA, encapPort != 0, expectedPacketSize);
 
                     socket.close();
+
+                    return innerSocketPort;
                 }
             };
         }
     }
 
-    private class InputPacketGeneratorTestRunnableFactory implements TestRunnableFactory {
-        public TestRunnable getTestRunnable(
+    private class InputReflectedIpSecTunnelTestRunnableFactory
+            implements IpSecTunnelTestRunnableFactory {
+        public IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
                 boolean transportInTunnelMode,
                 int spi,
                 InetAddress localInner,
@@ -355,14 +371,57 @@
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
+                int innerSocketPort,
                 int expectedPacketSize)
                 throws Exception {
-            return new TestRunnable() {
+            return new IpSecTunnelTestRunnable() {
                 @Override
-                public void run(Network ipsecNetwork) throws Exception {
+                public int run(Network ipsecNetwork) throws Exception {
+                    // Build a socket and receive traffic
+                    JavaUdpSocket socket = new JavaUdpSocket(localInner, innerSocketPort);
+                    ipsecNetwork.bindSocket(socket.mSocket);
+
+                    // For Transport-In-Tunnel mode, apply transform to socket
+                    if (transportInTunnelMode) {
+                        mISM.applyTransportModeTransform(
+                                socket.mSocket, IpSecManager.DIRECTION_IN, outTransportTransform);
+                        mISM.applyTransportModeTransform(
+                                socket.mSocket, IpSecManager.DIRECTION_OUT, inTransportTransform);
+                    }
+
+                    sTunUtils.reflectPackets();
+
+                    // Receive packet from socket, and validate that the payload is correct
+                    receiveAndValidatePacket(socket);
+
+                    socket.close();
+
+                    return 0;
+                }
+            };
+        }
+    }
+
+    private class InputPacketGeneratorIpSecTunnelTestRunnableFactory
+            implements IpSecTunnelTestRunnableFactory {
+        public IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
+                boolean transportInTunnelMode,
+                int spi,
+                InetAddress localInner,
+                InetAddress remoteInner,
+                InetAddress localOuter,
+                InetAddress remoteOuter,
+                IpSecTransform inTransportTransform,
+                IpSecTransform outTransportTransform,
+                int encapPort,
+                int innerSocketPort,
+                int expectedPacketSize)
+                throws Exception {
+            return new IpSecTunnelTestRunnable() {
+                @Override
+                public int run(Network ipsecNetwork) throws Exception {
                     // Build a socket and receive traffic
                     JavaUdpSocket socket = new JavaUdpSocket(localInner);
-                    // JavaUdpSocket socket = new JavaUdpSocket(localInner, socketPort.get());
                     ipsecNetwork.bindSocket(socket.mSocket);
 
                     // For Transport-In-Tunnel mode, apply transform to socket
@@ -402,6 +461,8 @@
                     receiveAndValidatePacket(socket);
 
                     socket.close();
+
+                    return 0;
                 }
             };
         }
@@ -415,7 +476,7 @@
                 outerFamily,
                 useEncap,
                 transportInTunnelMode,
-                new OutputTestRunnableFactory());
+                new OutputIpSecTunnelTestRunnableFactory());
     }
 
     private void checkTunnelInput(
@@ -426,7 +487,91 @@
                 outerFamily,
                 useEncap,
                 transportInTunnelMode,
-                new InputPacketGeneratorTestRunnableFactory());
+                new InputPacketGeneratorIpSecTunnelTestRunnableFactory());
+    }
+
+    /**
+     * Validates that the kernel can talk to itself.
+     *
+     * <p>This test takes an outbound IPsec packet, reflects it (by flipping IP src/dst), and
+     * injects it back into the TUN. This test then verifies that a packet with the correct payload
+     * is found on the specified socket/port.
+     */
+    public void checkTunnelReflected(
+            int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+            throws Exception {
+        if (!hasTunnelsFeature()) return;
+
+        InetAddress localInner = innerFamily == AF_INET ? LOCAL_INNER_4 : LOCAL_INNER_6;
+        InetAddress remoteInner = innerFamily == AF_INET ? REMOTE_INNER_4 : REMOTE_INNER_6;
+
+        InetAddress localOuter = outerFamily == AF_INET ? LOCAL_OUTER_4 : LOCAL_OUTER_6;
+        InetAddress remoteOuter = outerFamily == AF_INET ? REMOTE_OUTER_4 : REMOTE_OUTER_6;
+
+        // Preselect both SPI and encap port, to be used for both inbound and outbound tunnels.
+        int spi = getRandomSpi(localOuter, remoteOuter);
+        int expectedPacketSize =
+                getPacketSize(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+
+        try (IpSecManager.SecurityParameterIndex inTransportSpi =
+                        mISM.allocateSecurityParameterIndex(localInner, spi);
+                IpSecManager.SecurityParameterIndex outTransportSpi =
+                        mISM.allocateSecurityParameterIndex(remoteInner, spi);
+                IpSecTransform inTransportTransform =
+                        buildIpSecTransform(sContext, inTransportSpi, null, remoteInner);
+                IpSecTransform outTransportTransform =
+                        buildIpSecTransform(sContext, outTransportSpi, null, localInner);
+                UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
+
+            // Run output direction tests
+            IpSecTunnelTestRunnable outputIpSecTunnelTestRunnable =
+                    new OutputIpSecTunnelTestRunnableFactory()
+                            .getIpSecTunnelTestRunnable(
+                                    transportInTunnelMode,
+                                    spi,
+                                    localInner,
+                                    remoteInner,
+                                    localOuter,
+                                    remoteOuter,
+                                    inTransportTransform,
+                                    outTransportTransform,
+                                    useEncap ? encapSocket.getPort() : 0,
+                                    0,
+                                    expectedPacketSize);
+            int innerSocketPort =
+                    buildTunnelNetworkAndRunTests(
+                    localInner,
+                    remoteInner,
+                    localOuter,
+                    remoteOuter,
+                    spi,
+                    useEncap ? encapSocket : null,
+                    outputIpSecTunnelTestRunnable);
+
+            // Input direction tests, with matching inner socket ports.
+            IpSecTunnelTestRunnable inputIpSecTunnelTestRunnable =
+                    new InputReflectedIpSecTunnelTestRunnableFactory()
+                            .getIpSecTunnelTestRunnable(
+                                    transportInTunnelMode,
+                                    spi,
+                                    remoteInner,
+                                    localInner,
+                                    localOuter,
+                                    remoteOuter,
+                                    inTransportTransform,
+                                    outTransportTransform,
+                                    useEncap ? encapSocket.getPort() : 0,
+                                    innerSocketPort,
+                                    expectedPacketSize);
+            buildTunnelNetworkAndRunTests(
+                    remoteInner,
+                    localInner,
+                    localOuter,
+                    remoteOuter,
+                    spi,
+                    useEncap ? encapSocket : null,
+                    inputIpSecTunnelTestRunnable);
+        }
     }
 
     public void checkTunnel(
@@ -434,7 +579,7 @@
             int outerFamily,
             boolean useEncap,
             boolean transportInTunnelMode,
-            TestRunnableFactory factory)
+            IpSecTunnelTestRunnableFactory factory)
             throws Exception {
         if (!hasTunnelsFeature()) return;
 
@@ -461,14 +606,14 @@
                         buildIpSecTransform(sContext, outTransportSpi, null, localInner);
                 UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
 
-            buildTunnelAndNetwork(
+            buildTunnelNetworkAndRunTests(
                     localInner,
                     remoteInner,
                     localOuter,
                     remoteOuter,
                     spi,
                     useEncap ? encapSocket : null,
-                    factory.getTestRunnable(
+                    factory.getIpSecTunnelTestRunnable(
                             transportInTunnelMode,
                             spi,
                             localInner,
@@ -478,41 +623,42 @@
                             inTransportTransform,
                             outTransportTransform,
                             useEncap ? encapSocket.getPort() : 0,
+                            0,
                             expectedPacketSize));
         }
     }
 
-    private void buildTunnelAndNetwork(
+    private int buildTunnelNetworkAndRunTests(
             InetAddress localInner,
             InetAddress remoteInner,
             InetAddress localOuter,
             InetAddress remoteOuter,
             int spi,
             UdpEncapsulationSocket encapSocket,
-            TestRunnable test)
+            IpSecTunnelTestRunnable test)
             throws Exception {
         int innerPrefixLen = localInner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
         TestNetworkCallback testNetworkCb = null;
+        int innerSocketPort;
 
         try (IpSecManager.SecurityParameterIndex inSpi =
                         mISM.allocateSecurityParameterIndex(localOuter, spi);
                 IpSecManager.SecurityParameterIndex outSpi =
                         mISM.allocateSecurityParameterIndex(remoteOuter, spi);
-                IpSecManager.IpSecTunnelInterface tunnelIntf =
+                IpSecManager.IpSecTunnelInterface tunnelIface =
                         mISM.createIpSecTunnelInterface(localOuter, remoteOuter, sTunNetwork)) {
             // Build the test network
-            tunnelIntf.addAddress(localInner, innerPrefixLen);
-            testNetworkCb = setupAndGetTestNetwork(tunnelIntf.getInterfaceName());
+            tunnelIface.addAddress(localInner, innerPrefixLen);
+            testNetworkCb = setupAndGetTestNetwork(tunnelIface.getInterfaceName());
             Network testNetwork = testNetworkCb.getNetworkBlocking();
 
             // Check interface was created
-            NetworkInterface netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
-            assertNotNull(netIntf);
+            assertNotNull(NetworkInterface.getByName(tunnelIface.getInterfaceName()));
 
             // Verify address was added
-            netIntf = NetworkInterface.getByInetAddress(localInner);
-            assertNotNull(netIntf);
-            assertEquals(tunnelIntf.getInterfaceName(), netIntf.getDisplayName());
+            final NetworkInterface netIface = NetworkInterface.getByInetAddress(localInner);
+            assertNotNull(netIface);
+            assertEquals(tunnelIface.getInterfaceName(), netIface.getDisplayName());
 
             // Configure Transform parameters
             IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
@@ -531,28 +677,31 @@
                             transformBuilder.buildTunnelModeTransform(remoteOuter, inSpi);
                     IpSecTransform outTransform =
                             transformBuilder.buildTunnelModeTransform(localOuter, outSpi)) {
-                mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_IN, inTransform);
-                mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_OUT, outTransform);
+                mISM.applyTunnelModeTransform(tunnelIface, IpSecManager.DIRECTION_IN, inTransform);
+                mISM.applyTunnelModeTransform(
+                        tunnelIface, IpSecManager.DIRECTION_OUT, outTransform);
 
-                test.run(testNetwork);
+                innerSocketPort = test.run(testNetwork);
             }
 
             // Teardown the test network
             sTNM.teardownTestNetwork(testNetwork);
 
             // Remove addresses and check that interface is still present, but fails lookup-by-addr
-            tunnelIntf.removeAddress(localInner, innerPrefixLen);
-            assertNotNull(NetworkInterface.getByName(tunnelIntf.getInterfaceName()));
+            tunnelIface.removeAddress(localInner, innerPrefixLen);
+            assertNotNull(NetworkInterface.getByName(tunnelIface.getInterfaceName()));
             assertNull(NetworkInterface.getByInetAddress(localInner));
 
             // Check interface was cleaned up
-            tunnelIntf.close();
-            assertNull(NetworkInterface.getByName(tunnelIntf.getInterfaceName()));
+            tunnelIface.close();
+            assertNull(NetworkInterface.getByName(tunnelIface.getInterfaceName()));
         } finally {
             if (testNetworkCb != null) {
                 sCM.unregisterNetworkCallback(testNetworkCb);
             }
         }
+
+        return innerSocketPort;
     }
 
     private static void receiveAndValidatePacket(JavaUdpSocket socket) throws Exception {
@@ -676,35 +825,65 @@
     }
 
     @Test
+    public void testTransportInTunnelModeV4InV4Reflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, true);
+    }
+
+    @Test
     public void testTransportInTunnelModeV4InV4UdpEncap() throws Exception {
         checkTunnelOutput(AF_INET, AF_INET, true, true);
         checkTunnelInput(AF_INET, AF_INET, true, true);
     }
 
     @Test
+    public void testTransportInTunnelModeV4InV4UdpEncapReflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, true);
+    }
+
+    @Test
     public void testTransportInTunnelModeV4InV6() throws Exception {
         checkTunnelOutput(AF_INET, AF_INET6, false, true);
         checkTunnelInput(AF_INET, AF_INET6, false, true);
     }
 
     @Test
+    public void testTransportInTunnelModeV4InV6Reflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, true);
+    }
+
+    @Test
     public void testTransportInTunnelModeV6InV4() throws Exception {
         checkTunnelOutput(AF_INET6, AF_INET, false, true);
         checkTunnelInput(AF_INET6, AF_INET, false, true);
     }
 
     @Test
+    public void testTransportInTunnelModeV6InV4Reflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, true);
+    }
+
+    @Test
     public void testTransportInTunnelModeV6InV4UdpEncap() throws Exception {
         checkTunnelOutput(AF_INET6, AF_INET, true, true);
         checkTunnelInput(AF_INET6, AF_INET, true, true);
     }
 
     @Test
+    public void testTransportInTunnelModeV6InV4UdpEncapReflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, true);
+    }
+
+    @Test
     public void testTransportInTunnelModeV6InV6() throws Exception {
         checkTunnelOutput(AF_INET, AF_INET6, false, true);
         checkTunnelInput(AF_INET, AF_INET6, false, true);
     }
 
+    @Test
+    public void testTransportInTunnelModeV6InV6Reflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, true);
+    }
+
     // Tunnel mode tests
     @Test
     public void testTunnelV4InV4() throws Exception {
@@ -713,32 +892,62 @@
     }
 
     @Test
+    public void testTunnelV4InV4Reflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, false, false);
+    }
+
+    @Test
     public void testTunnelV4InV4UdpEncap() throws Exception {
         checkTunnelOutput(AF_INET, AF_INET, true, false);
         checkTunnelInput(AF_INET, AF_INET, true, false);
     }
 
     @Test
+    public void testTunnelV4InV4UdpEncapReflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET, true, false);
+    }
+
+    @Test
     public void testTunnelV4InV6() throws Exception {
         checkTunnelOutput(AF_INET, AF_INET6, false, false);
         checkTunnelInput(AF_INET, AF_INET6, false, false);
     }
 
     @Test
+    public void testTunnelV4InV6Reflected() throws Exception {
+        checkTunnelReflected(AF_INET, AF_INET6, false, false);
+    }
+
+    @Test
     public void testTunnelV6InV4() throws Exception {
         checkTunnelOutput(AF_INET6, AF_INET, false, false);
         checkTunnelInput(AF_INET6, AF_INET, false, false);
     }
 
     @Test
+    public void testTunnelV6InV4Reflected() throws Exception {
+        checkTunnelReflected(AF_INET6, AF_INET, false, false);
+    }
+
+    @Test
     public void testTunnelV6InV4UdpEncap() throws Exception {
         checkTunnelOutput(AF_INET6, AF_INET, true, false);
         checkTunnelInput(AF_INET6, AF_INET, true, false);
     }
 
     @Test
+    public void testTunnelV6InV4UdpEncapReflected() throws Exception {
+        checkTunnelReflected(AF_INET6, AF_INET, true, false);
+    }
+
+    @Test
     public void testTunnelV6InV6() throws Exception {
         checkTunnelOutput(AF_INET6, AF_INET6, false, false);
         checkTunnelInput(AF_INET6, AF_INET6, false, false);
     }
+
+    @Test
+    public void testTunnelV6InV6Reflected() throws Exception {
+        checkTunnelReflected(AF_INET6, AF_INET6, false, false);
+    }
 }
diff --git a/tests/tests/net/src/android/net/cts/NetworkWatchlistTest.java b/tests/tests/net/src/android/net/cts/NetworkWatchlistTest.java
index cb0cda8..e4e350c 100644
--- a/tests/tests/net/src/android/net/cts/NetworkWatchlistTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkWatchlistTest.java
@@ -25,6 +25,7 @@
 import android.net.ConnectivityManager;
 import android.platform.test.annotations.AppModeFull;
 import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -39,7 +40,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Formatter;
@@ -51,8 +52,6 @@
     private static final String TEST_WATCHLIST_XML = "assets/network_watchlist_config_for_test.xml";
     private static final String TEST_EMPTY_WATCHLIST_XML =
             "assets/network_watchlist_config_empty_for_test.xml";
-    private static final String SDCARD_CONFIG_PATH =
-            "/sdcard/network_watchlist_config_for_test.xml";
     private static final String TMP_CONFIG_PATH =
             "/data/local/tmp/network_watchlist_config_for_test.xml";
     // Generated from sha256sum network_watchlist_config_for_test.xml
@@ -84,8 +83,7 @@
         }
     }
 
-    private void cleanup() throws Exception {
-        runCommand("rm " + SDCARD_CONFIG_PATH);
+    private void cleanup() throws IOException {
         runCommand("rm " + TMP_CONFIG_PATH);
     }
 
@@ -118,22 +116,43 @@
     }
 
     private void saveResourceToFile(String res, String filePath) throws IOException {
-        InputStream in = getClass().getClassLoader().getResourceAsStream(res);
-        FileUtils.copyToFileOrThrow(in, new File(filePath));
+        // App can't access /data/local/tmp directly, so we pipe resource to file through stdin.
+        ParcelFileDescriptor stdin = pipeFromStdin(filePath);
+        pipeResourceToFileDescriptor(res, stdin);
+    }
+
+    /* Pipe stdin to a file in filePath. Returns PFD for stdin. */
+    private ParcelFileDescriptor pipeFromStdin(String filePath) {
+        // Not all devices have symlink for /dev/stdin, so use /proc/self/fd/0 directly.
+        // /dev/stdin maps to /proc/self/fd/0.
+        return runRwCommand("cp /proc/self/fd/0 " + filePath)[1];
+    }
+
+    private void pipeResourceToFileDescriptor(String res, ParcelFileDescriptor pfd)
+            throws IOException {
+        InputStream resStream = getClass().getClassLoader().getResourceAsStream(res);
+        FileOutputStream fdStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
+
+        FileUtils.copy(resStream, fdStream);
+
+        try {
+            fdStream.close();
+        } catch (IOException e) {
+        }
     }
 
     private static String runCommand(String command) throws IOException {
         return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
     }
 
+    private static ParcelFileDescriptor[] runRwCommand(String command) {
+        return InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation().executeShellCommandRw(command);
+    }
+
     private void setWatchlistConfig(String watchlistConfigFile) throws Exception {
         cleanup();
-        // Save test watchlist config to sdcard as app can't access /data/local/tmp
-        saveResourceToFile(watchlistConfigFile, SDCARD_CONFIG_PATH);
-        // Copy test watchlist config from sdcard to /data/local/tmp as system service
-        // can't access /sdcard
-        runCommand("cp " + SDCARD_CONFIG_PATH + " " + TMP_CONFIG_PATH);
-        // Set test watchlist config to system
+        saveResourceToFile(watchlistConfigFile, TMP_CONFIG_PATH);
         final String cmdResult = runCommand(
                 "cmd network_watchlist set-test-config " + TMP_CONFIG_PATH).trim();
         assertThat(cmdResult).contains("Success");
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 7576450..d8c7dc8 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -40,6 +40,7 @@
 import android.provider.Settings;
 import android.support.test.uiautomator.UiDevice;
 import android.test.AndroidTestCase;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -798,6 +799,8 @@
      * functionality.  The permission is intended to be granted to only the device setup wizard.
      */
     public void testNetworkSetupWizardPermission() {
+        final ArraySet<String> allowedPackages = new ArraySet();
+
         final PackageManager pm = getContext().getPackageManager();
 
         final Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -805,16 +808,39 @@
         final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
         String validPkg = "";
         if (ri != null) {
+            allowedPackages.add(ri.activityInfo.packageName);
             validPkg = ri.activityInfo.packageName;
         }
 
-        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
-                android.Manifest.permission.NETWORK_SETUP_WIZARD
+        final Intent preIntent = new Intent("com.android.setupwizard.OEM_PRE_SETUP");
+        preIntent.addCategory(Intent.CATEGORY_DEFAULT);
+        final ResolveInfo preRi = pm
+            .resolveActivity(preIntent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        String prePackageName = "";
+        if (null != preRi) {
+            prePackageName = preRi.activityInfo.packageName;
+        }
+
+        final Intent postIntent = new Intent("com.android.setupwizard.OEM_POST_SETUP");
+        postIntent.addCategory(Intent.CATEGORY_DEFAULT);
+        final ResolveInfo postRi = pm
+            .resolveActivity(postIntent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        String postPackageName = "";
+        if (null != postRi) {
+            postPackageName = postRi.activityInfo.packageName;
+        }
+        if (!TextUtils.isEmpty(prePackageName) && !TextUtils.isEmpty(postPackageName)
+            && prePackageName.equals(postPackageName)) {
+            allowedPackages.add(prePackageName);
+        }
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[]{
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
         }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
         for (PackageInfo pi : holding) {
-            if (!Objects.equals(pi.packageName, validPkg)) {
+            if (!allowedPackages.contains(pi.packageName)) {
                 fail("The NETWORK_SETUP_WIZARD permission must not be held by " + pi.packageName
-                    + " and must be revoked for security reasons [" + validPkg +"]");
+                    + " and must be revoked for security reasons [" + validPkg + "]");
             }
         }
     }
diff --git a/tests/tests/content/DirectBootAwareTestApp/Android.bp b/tests/tests/net/util/Android.bp
similarity index 70%
rename from tests/tests/content/DirectBootAwareTestApp/Android.bp
rename to tests/tests/net/util/Android.bp
index 17ccda1..1f94613 100644
--- a/tests/tests/content/DirectBootAwareTestApp/Android.bp
+++ b/tests/tests/net/util/Android.bp
@@ -1,3 +1,4 @@
+//
 // Copyright (C) 2019 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,15 +12,14 @@
 // 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: "CtsContentDirectBootAwareTestApp",
-    defaults: ["cts_defaults"],
-    sdk_version: "current",
-    // tag this module as a cts test artifact
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
+// Common utilities for cts net tests.
+java_library {
+    name: "cts-net-utils",
+    srcs: ["java/**/*.java", "java/**/*.kt"],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "junit",
     ],
-}
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..e19d2ba
--- /dev/null
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public final class CtsNetUtils {
+    private static final String TAG = CtsNetUtils.class.getSimpleName();
+    private static final int DURATION = 10000;
+    private static final int SOCKET_TIMEOUT_MS = 2000;
+
+    public static final int HTTP_PORT = 80;
+    public static final String TEST_HOST = "connectivitycheck.gstatic.com";
+    public static final String HTTP_REQUEST =
+            "GET /generate_204 HTTP/1.0\r\n" +
+                    "Host: " + TEST_HOST + "\r\n" +
+                    "Connection: keep-alive\r\n\r\n";
+    // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
+    public static final String NETWORK_CALLBACK_ACTION =
+            "ConnectivityManagerTest.NetworkCallbackAction";
+
+    private Context mContext;
+    private ConnectivityManager mCm;
+    private WifiManager mWifiManager;
+    private TestNetworkCallback mCellNetworkCallback;
+
+    public CtsNetUtils(Context context) {
+        mContext = context;
+        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+    }
+
+    // Toggle WiFi twice, leaving it in the state it started in
+    public void toggleWifi() {
+        if (mWifiManager.isWifiEnabled()) {
+            Network wifiNetwork = getWifiNetwork();
+            disconnectFromWifi(wifiNetwork);
+            connectToWifi();
+        } else {
+            connectToWifi();
+            Network wifiNetwork = getWifiNetwork();
+            disconnectFromWifi(wifiNetwork);
+        }
+    }
+
+    /** Enable WiFi and wait for it to become connected to a network. */
+    public Network connectToWifi() {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+        Network wifiNetwork = null;
+
+        ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
+                mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiver(receiver, filter);
+
+        boolean connected = false;
+        try {
+            SystemUtil.runShellCommand("svc wifi enable");
+            // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
+            wifiNetwork = callback.waitForAvailable();
+            assertNotNull(wifiNetwork);
+            connected = receiver.waitForState();
+        } catch (InterruptedException ex) {
+            fail("connectToWifi was interrupted");
+        } finally {
+            mCm.unregisterNetworkCallback(callback);
+            mContext.unregisterReceiver(receiver);
+        }
+
+        assertTrue("Wifi must be configured to connect to an access point for this test.",
+                connected);
+        return wifiNetwork;
+    }
+
+    /** Disable WiFi and wait for it to become disconnected from the network. */
+    public void disconnectFromWifi(Network wifiNetworkToCheck) {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+        Network lostWifiNetwork = null;
+
+        ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
+                mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiver(receiver, filter);
+
+        // Assert that we can establish a TCP connection on wifi.
+        Socket wifiBoundSocket = null;
+        if (wifiNetworkToCheck != null) {
+            try {
+                wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT);
+                testHttpRequest(wifiBoundSocket);
+            } catch (IOException e) {
+                fail("HTTP request before wifi disconnected failed with: " + e);
+            }
+        }
+
+        boolean disconnected = false;
+        try {
+            SystemUtil.runShellCommand("svc wifi disable");
+            // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
+            lostWifiNetwork = callback.waitForLost();
+            assertNotNull(lostWifiNetwork);
+            disconnected = receiver.waitForState();
+        } catch (InterruptedException ex) {
+            fail("disconnectFromWifi was interrupted");
+        } finally {
+            mCm.unregisterNetworkCallback(callback);
+            mContext.unregisterReceiver(receiver);
+        }
+
+        assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
+
+        // Check that the socket is closed when wifi disconnects.
+        if (wifiBoundSocket != null) {
+            try {
+                testHttpRequest(wifiBoundSocket);
+                fail("HTTP request should not succeed after wifi disconnects");
+            } catch (IOException expected) {
+                assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage());
+            }
+        }
+    }
+
+    public Network getWifiNetwork() {
+        TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+        Network network = null;
+        try {
+            network = callback.waitForAvailable();
+        } catch (InterruptedException e) {
+            fail("NetworkCallback wait was interrupted.");
+        } finally {
+            mCm.unregisterNetworkCallback(callback);
+        }
+        assertNotNull("Cannot find Network for wifi. Is wifi connected?", network);
+        return network;
+    }
+
+    public Network connectToCell() throws InterruptedException {
+        if (cellConnectAttempted()) {
+            throw new IllegalStateException("Already connected");
+        }
+        NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCellNetworkCallback = new TestNetworkCallback();
+        mCm.requestNetwork(cellRequest, mCellNetworkCallback);
+        final Network cellNetwork = mCellNetworkCallback.waitForAvailable();
+        assertNotNull("Cell network not available. " +
+                "Please ensure the device has working mobile data.", cellNetwork);
+        return cellNetwork;
+    }
+
+    public void disconnectFromCell() {
+        if (!cellConnectAttempted()) {
+            throw new IllegalStateException("Cell connection not attempted");
+        }
+        mCm.unregisterNetworkCallback(mCellNetworkCallback);
+        mCellNetworkCallback = null;
+    }
+
+    public boolean cellConnectAttempted() {
+        return mCellNetworkCallback != null;
+    }
+
+    private NetworkRequest makeWifiNetworkRequest() {
+        return new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .build();
+    }
+
+    private void testHttpRequest(Socket s) throws IOException {
+        OutputStream out = s.getOutputStream();
+        InputStream in = s.getInputStream();
+
+        final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
+        byte[] responseBytes = new byte[4096];
+        out.write(requestBytes);
+        in.read(responseBytes);
+        assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n"));
+    }
+
+    private Socket getBoundSocket(Network network, String host, int port) throws IOException {
+        InetSocketAddress addr = new InetSocketAddress(host, port);
+        Socket s = network.getSocketFactory().createSocket();
+        try {
+            s.setSoTimeout(SOCKET_TIMEOUT_MS);
+            s.connect(addr, SOCKET_TIMEOUT_MS);
+        } catch (IOException e) {
+            s.close();
+            throw e;
+        }
+        return s;
+    }
+
+    /**
+     * Receiver that captures the last connectivity change's network type and state. Recognizes
+     * both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
+     */
+    public static class ConnectivityActionReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
+
+        private final int mNetworkType;
+        private final NetworkInfo.State mNetState;
+        private final ConnectivityManager mCm;
+
+        public ConnectivityActionReceiver(ConnectivityManager cm, int networkType,
+                NetworkInfo.State netState) {
+            this.mCm = cm;
+            mNetworkType = networkType;
+            mNetState = netState;
+        }
+
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            NetworkInfo networkInfo = null;
+
+            // When receiving ConnectivityManager.CONNECTIVITY_ACTION, the NetworkInfo parcelable
+            // is stored in EXTRA_NETWORK_INFO. With a NETWORK_CALLBACK_ACTION, the Network is
+            // sent in EXTRA_NETWORK and we need to ask the ConnectivityManager for the NetworkInfo.
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
+                networkInfo = intent.getExtras()
+                        .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
+                assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK_INFO",
+                        networkInfo);
+            } else if (NETWORK_CALLBACK_ACTION.equals(action)) {
+                Network network = intent.getExtras()
+                        .getParcelable(ConnectivityManager.EXTRA_NETWORK);
+                assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK", network);
+                networkInfo = this.mCm.getNetworkInfo(network);
+                if (networkInfo == null) {
+                    // When disconnecting, it seems like we get an intent sent with an invalid
+                    // Network; that is, by the time we call ConnectivityManager.getNetworkInfo(),
+                    // it is invalid. Ignore these.
+                    Log.i(TAG, "ConnectivityActionReceiver NETWORK_CALLBACK_ACTION ignoring "
+                            + "invalid network");
+                    return;
+                }
+            } else {
+                fail("ConnectivityActionReceiver received unxpected intent action: " + action);
+            }
+
+            assertNotNull("ConnectivityActionReceiver didn't find NetworkInfo", networkInfo);
+            int networkType = networkInfo.getType();
+            State networkState = networkInfo.getState();
+            Log.i(TAG, "Network type: " + networkType + " state: " + networkState);
+            if (networkType == mNetworkType && networkInfo.getState() == mNetState) {
+                mReceiveLatch.countDown();
+            }
+        }
+
+        public boolean waitForState() throws InterruptedException {
+            return mReceiveLatch.await(30, TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * Callback used in testRegisterNetworkCallback that allows caller to block on
+     * {@code onAvailable}.
+     */
+    public static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+        private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
+        private final CountDownLatch mLostLatch = new CountDownLatch(1);
+        private final CountDownLatch mUnavailableLatch = new CountDownLatch(1);
+
+        public Network currentNetwork;
+        public Network lastLostNetwork;
+
+        public Network waitForAvailable() throws InterruptedException {
+            return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null;
+        }
+
+        public Network waitForLost() throws InterruptedException {
+            return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
+        }
+
+        public boolean waitForUnavailable() throws InterruptedException {
+            return mUnavailableLatch.await(2, TimeUnit.SECONDS);
+        }
+
+
+        @Override
+        public void onAvailable(Network network) {
+            currentNetwork = network;
+            mAvailableLatch.countDown();
+        }
+
+        @Override
+        public void onLost(Network network) {
+            lastLostNetwork = network;
+            if (network.equals(currentNetwork)) {
+                currentNetwork = null;
+            }
+            mLostLatch.countDown();
+        }
+
+        @Override
+        public void onUnavailable() {
+            mUnavailableLatch.countDown();
+        }
+    }
+}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index aca23e3..8494d5e 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -41,7 +41,8 @@
     src/android/os/cts/IEmptyService.aidl \
     src/android/os/cts/ISeccompIsolatedService.aidl \
     src/android/os/cts/ISecondary.aidl \
-    src/android/os/cts/ISharedMemoryService.aidl
+    src/android/os/cts/ISharedMemoryService.aidl \
+    src/android/os/cts/IParcelExceptionService.aidl
 
 LOCAL_PACKAGE_NAME := CtsOsTestCases
 
@@ -51,7 +52,7 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 4092538..d706470 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -84,6 +84,10 @@
             android:name="android.os.cts.SharedMemoryService"
             android:process=":sharedmem"
             android:exported="false" />
+        <service
+            android:name="android.os.cts.ParcelExceptionService"
+            android:process=":remote"
+            android:exported="true" />
 
         <service android:name="android.os.cts.LocalService">
             <intent-filter>
diff --git a/tests/tests/os/assets/minijail/isolated-common.policy b/tests/tests/os/assets/minijail/isolated-common.policy
index a8f4e25..11205ae 100644
--- a/tests/tests/os/assets/minijail/isolated-common.policy
+++ b/tests/tests/os/assets/minijail/isolated-common.policy
@@ -75,6 +75,7 @@
 # madvise: advice==MADV_DONTNEED
 madvise: arg2 == 4; return EPERM
 
+membarrier: 1
 memfd_create: return EPERM
 mkdirat: return EPERM
 mknodat: return EPERM
diff --git a/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
new file mode 100644
index 0000000..7d09693
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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;
+
+parcelable ExceptionalParcelable;
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java
new file mode 100644
index 0000000..333cf57
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 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.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class ExceptionalParcelable implements Parcelable {
+    private final IBinder mBinder;
+
+    ExceptionalParcelable(IBinder binder) {
+        mBinder = binder;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Write a binder to the Parcel and then throw an exception
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        // Write a binder for the exception to overwrite
+        out.writeStrongBinder(mBinder);
+
+        // Throw an exception
+        throw new IllegalArgumentException("A truly exceptional message");
+    }
+
+    public static final Creator<ExceptionalParcelable> CREATOR =
+            new Creator<ExceptionalParcelable>() {
+                @Override
+                public ExceptionalParcelable createFromParcel(Parcel source) {
+                    return new ExceptionalParcelable(source.readStrongBinder());
+                }
+
+                @Override
+                public ExceptionalParcelable[] newArray(int size) {
+                    return new ExceptionalParcelable[size];
+                }
+            };
+}
diff --git a/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl b/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
new file mode 100644
index 0000000..ce7af6d
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 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.os.cts.ExceptionalParcelable;
+
+interface IParcelExceptionService {
+//parcelable android.os.cts.ExceptionalParcelable;
+    ExceptionalParcelable writeBinderThrowException();
+}
diff --git a/tests/tests/os/src/android/os/cts/ParcelExceptionService.java b/tests/tests/os/src/android/os/cts/ParcelExceptionService.java
new file mode 100644
index 0000000..d8387e3
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ParcelExceptionService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class ParcelExceptionService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new ParcelExceptionServiceImpl();
+    }
+
+    private static class ParcelExceptionServiceImpl extends IParcelExceptionService.Stub {
+        private final IBinder mBinder = new Binder();
+
+
+        @Override
+        public ExceptionalParcelable writeBinderThrowException() throws RemoteException {
+            return new ExceptionalParcelable(mBinder);
+        }
+    }
+}
diff --git a/tests/tests/os/src/android/os/cts/ParcelTest.java b/tests/tests/os/src/android/os/cts/ParcelTest.java
index 3715850..77d325c 100644
--- a/tests/tests/os/src/android/os/cts/ParcelTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelTest.java
@@ -24,7 +24,14 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.Signature;
 import android.os.BadParcelableException;
 import android.os.Binder;
@@ -39,6 +46,8 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
+import com.google.common.util.concurrent.AbstractFuture;
+
 public class ParcelTest extends AndroidTestCase {
 
     public void testObtain() {
@@ -3308,4 +3317,55 @@
             // good
         }
     }
+
+    public static class ParcelExceptionConnection extends AbstractFuture<IParcelExceptionService>
+            implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            set(IParcelExceptionService.Stub.asInterface(service));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+        }
+
+        @Override
+        public IParcelExceptionService get() throws InterruptedException, ExecutionException {
+            try {
+                return get(5, TimeUnit.SECONDS);
+            } catch (TimeoutException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void testExceptionOverwritesObject() throws Exception {
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(
+                "android.os.cts", "android.os.cts.ParcelExceptionService"));
+
+        final ParcelExceptionConnection connection = new ParcelExceptionConnection();
+
+        mContext.startService(intent);
+        assertTrue(mContext.bindService(intent, connection,
+                Context.BIND_ABOVE_CLIENT | Context.BIND_EXTERNAL_SERVICE));
+
+
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken("android.os.cts.IParcelExceptionService");
+        IParcelExceptionService service = connection.get();
+        try {
+            assertTrue("Transaction failed", service.asBinder().transact(
+                    IParcelExceptionService.Stub.TRANSACTION_writeBinderThrowException, data, reply,
+                    0));
+        } catch (Exception e) {
+            fail("Exception caught from transaction: " + e);
+        }
+        reply.setDataPosition(0);
+        assertTrue("Exception should have occurred on service-side",
+                reply.readExceptionCode() != 0);
+        assertNull("Binder should have been overwritten by the exception",
+                reply.readStrongBinder());
+    }
 }
diff --git a/tests/tests/packageinstaller/atomicinstall/Android.bp b/tests/tests/packageinstaller/atomicinstall/Android.bp
index 667a32b..2eccbdd 100644
--- a/tests/tests/packageinstaller/atomicinstall/Android.bp
+++ b/tests/tests/packageinstaller/atomicinstall/Android.bp
@@ -20,6 +20,7 @@
     java_resources:  [
         ":AtomicInstallTestAppAv1",
         ":AtomicInstallTestAppBv1",
+        ":AtomicInstallCorrupt"
     ],
     static_libs: [
         "androidx.test.runner",
@@ -33,6 +34,12 @@
     ],
 }
 
+filegroup {
+    name: "AtomicInstallCorrupt",
+    srcs: ["testdata/apk/prebuilt/corrupt.apk"],
+    path: "testdata/apk/prebuilt"
+}
+
 android_test_helper_app {
     name: "AtomicInstallTestAppAv1",
 
diff --git a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java b/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java
index ba49b04..f9b03fc 100644
--- a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java
+++ b/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java
@@ -26,7 +26,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.os.RemoteException;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -50,6 +49,7 @@
     private static final String TEST_APP_B = "com.android.tests.atomicinstall.testapp.B";
     public static final String TEST_APP_A_FILENAME = "AtomicInstallTestAppAv1.apk";
     public static final String TEST_APP_B_FILENAME = "AtomicInstallTestAppBv1.apk";
+    public static final String TEST_APP_CORRUPT_FILENAME = "corrupt.apk";
 
     private void adoptShellPermissions() {
         InstrumentationRegistry
@@ -154,6 +154,30 @@
     }
 
     @Test
+    public void testEarlyFailureFailsAll() throws Exception {
+        final PackageInstaller.SessionParams parentSessionParams =
+                createSessionParams(/*staged*/false, /*multipackage*/true,
+                        /*enableRollback*/ false, /*inherit*/false);
+        final int parentSessionId = createSessionId(/*apkFileName*/null, parentSessionParams);
+        final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
+        for (String apkFile : new String[]{
+                TEST_APP_A_FILENAME, TEST_APP_B_FILENAME, TEST_APP_CORRUPT_FILENAME}) {
+            final PackageInstaller.SessionParams childSessionParams =
+                    createSessionParams(/*staged*/false, /*multipackage*/false,
+                            /*enableRollback*/ false, /*inherit*/false);
+            final int childSessionId =
+                    createSessionId(apkFile, childSessionParams);
+            parentSession.addChildSessionId(childSessionId);
+        }
+        parentSession.commit(LocalIntentSender.getIntentSender());
+
+        final Intent intent = LocalIntentSender.getIntentSenderResult();
+        assertStatusFailure(intent, "Failed to parse");
+        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(-1);
+    }
+
+    @Test
     public void testInvalidStateScenarios() throws Exception {
         final PackageInstaller.SessionParams parentSessionParams =
                 createSessionParams(/*staged*/false, /*multipackage*/true,
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/prebuilt/corrupt.apk b/tests/tests/packageinstaller/atomicinstall/testdata/apk/prebuilt/corrupt.apk
new file mode 100644
index 0000000..6e9af35
--- /dev/null
+++ b/tests/tests/packageinstaller/atomicinstall/testdata/apk/prebuilt/corrupt.apk
@@ -0,0 +1 @@
+This is not a valid APK
diff --git a/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt b/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt
index e99476a..10c2efa 100644
--- a/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt
+++ b/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt
@@ -183,9 +183,4 @@
     fun uninstallTestPackage() {
         uiDevice.executeShellCommand("pm uninstall $TEST_APK_PACKAGE_NAME")
     }
-
-    @After
-    fun resetAppOps() {
-        AppOpsUtils.reset(packageName)
-    }
 }
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index 2dba4fd..c4ee088 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -45,14 +45,11 @@
         <option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission29.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationAndBackgroundPermission29.apk" />
         <option name="push" value="CtsAppThatAccessesLocationOnCommand.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk" />
         <option name="push" value="AppThatDoesNotHaveBgLocationAccess.apk->/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk" />
-        <option name="push" value="CtsAppThatAccessesStorageOnCommand22.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesStorageOnCommand22.apk" />
-        <option name="push" value="CtsAppThatAccessesStorageOnCommand28.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesStorageOnCommand28.apk" />
-        <option name="push" value="CtsAppThatAccessesStorageOnCommand29.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesStorageOnCommand29.apk" />
-        <option name="push" value="CtsAppThatAccessesStorageOnCommand28v3.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesStorageOnCommand28v3.apk" />
         <option name="push" value="CtsAppWithSharedUidThatRequestsPermissions.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsPermissions.apk" />
         <option name="push" value="CtsAppWithSharedUidThatRequestsNoPermissions.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsNoPermissions.apk" />
         <option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission28.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsLocationPermission28.apk" />
         <option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission29.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsLocationPermission29.apk" />
+        <option name="push" value="CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk" />
     </target_preparer>
 
     <!-- Remove additional apps if installed -->
diff --git a/tests/tests/permission/AppThatAccessesStorage28/Android.mk b/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.mk
similarity index 85%
rename from tests/tests/permission/AppThatAccessesStorage28/Android.mk
rename to tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.mk
index 2edf5b5..a7d0652 100644
--- a/tests/tests/permission/AppThatAccessesStorage28/Android.mk
+++ b/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.mk
@@ -23,8 +23,8 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 # Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_PACKAGE_NAME := CtsAppThatAccessesStorageOnCommand28
+LOCAL_PACKAGE_NAME := CtsAppThatRequestsCalendarContactsBodySensorCustomPermission
 
-include $(BUILD_CTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml b/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
new file mode 100644
index 0000000..ef4d82d
--- /dev/null
+++ b/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.permission.cts.appthatrequestcustompermission"
+          android:versionCode="1">
+
+    <permission-group
+        android:name="android.permission.cts.appthatrequestcustompermission.TEST_GROUP"
+        android:label="test permission group"
+        android:protectionLevel="dangerous" />
+
+    <permission
+        android:name="android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION"
+        android:label="test permission"
+        android:permissionGroup="android.permission.cts.appthatrequestcustompermission.TEST_GROUP"
+        android:protectionLevel="dangerous" />
+
+    <uses-permission android:name="android.permission.READ_CALENDAR" />
+    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.BODY_SENSORS" />
+    <uses-permission android:name="android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION" />
+
+    <application />
+</manifest>
diff --git a/tests/tests/permission/AppThatAccessesStorage22/Android.mk b/tests/tests/permission/AppThatAccessesStorage22/Android.mk
deleted file mode 100644
index db662e0..0000000
--- a/tests/tests/permission/AppThatAccessesStorage22/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
-
-LOCAL_PACKAGE_NAME := CtsAppThatAccessesStorageOnCommand22
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatAccessesStorage22/AndroidManifest.xml b/tests/tests/permission/AppThatAccessesStorage22/AndroidManifest.xml
deleted file mode 100644
index 152ed4c..0000000
--- a/tests/tests/permission/AppThatAccessesStorage22/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 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.permission.cts.appthataccessesstorage"
-    android:versionCode="1">
-
-    <uses-sdk android:targetSdkVersion="22" />
-
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
-    <application android:label="CtsStorageAccess" />
-</manifest>
-
diff --git a/tests/tests/permission/AppThatAccessesStorage28/AndroidManifest.xml b/tests/tests/permission/AppThatAccessesStorage28/AndroidManifest.xml
deleted file mode 100644
index 55cc4d4..0000000
--- a/tests/tests/permission/AppThatAccessesStorage28/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 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.permission.cts.appthataccessesstorage"
-    android:versionCode="1">
-
-    <uses-sdk android:targetSdkVersion="28" />
-
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
-    <application android:label="CtsStorageAccess" />
-</manifest>
-
diff --git a/tests/tests/permission/AppThatAccessesStorage28v3/Android.mk b/tests/tests/permission/AppThatAccessesStorage28v3/Android.mk
deleted file mode 100644
index 64aedd1..0000000
--- a/tests/tests/permission/AppThatAccessesStorage28v3/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
-
-LOCAL_PACKAGE_NAME := CtsAppThatAccessesStorageOnCommand28v3
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatAccessesStorage28v3/AndroidManifest.xml b/tests/tests/permission/AppThatAccessesStorage28v3/AndroidManifest.xml
deleted file mode 100644
index af8311e..0000000
--- a/tests/tests/permission/AppThatAccessesStorage28v3/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 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.permission.cts.appthataccessesstorage"
-    android:versionCode="3">
-
-    <uses-sdk android:targetSdkVersion="28" />
-
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
-    <application android:label="CtsStorageAccess" />
-</manifest>
-
diff --git a/tests/tests/permission/AppThatAccessesStorage29/Android.mk b/tests/tests/permission/AppThatAccessesStorage29/Android.mk
deleted file mode 100644
index 3c3571e..0000000
--- a/tests/tests/permission/AppThatAccessesStorage29/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
-
-LOCAL_PACKAGE_NAME := CtsAppThatAccessesStorageOnCommand29
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatAccessesStorage29/AndroidManifest.xml b/tests/tests/permission/AppThatAccessesStorage29/AndroidManifest.xml
deleted file mode 100644
index d4ec8d7..0000000
--- a/tests/tests/permission/AppThatAccessesStorage29/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 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.permission.cts.appthataccessesstorage"
-    android:versionCode="2">
-
-    <!-- STOPSHIP: Set to apk level that shipped the location tristate -->
-    <!-- <uses-sdk android:targetSdkVersion="29" /> -->
-
-    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
-
-    <application android:label="CtsStorageAccess" />
-</manifest>
-
diff --git a/tests/tests/permission/AppThatRequestLocationPermission29/AndroidManifest.xml b/tests/tests/permission/AppThatRequestLocationPermission29/AndroidManifest.xml
index 76e76ac..fee75ae 100644
--- a/tests/tests/permission/AppThatRequestLocationPermission29/AndroidManifest.xml
+++ b/tests/tests/permission/AppThatRequestLocationPermission29/AndroidManifest.xml
@@ -19,8 +19,7 @@
     package="android.permission.cts.appthatrequestpermission"
     android:versionCode="1">
 
-    <!-- STOPSHIP: Set to apk level that shipped the location tristate -->
-    <!-- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> -->
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
diff --git a/tests/tests/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml b/tests/tests/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml
index b382dcd..3d3e312 100644
--- a/tests/tests/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml
+++ b/tests/tests/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml
@@ -19,8 +19,7 @@
     package="android.permission.cts.appthatrequestpermission"
     android:versionCode="4">
 
-    <!-- STOPSHIP: Set to apk level that shipped the location tristate -->
-    <!-- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> -->
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionControllerTest.java b/tests/tests/permission/src/android/permission/cts/PermissionControllerTest.java
index b0dd59a..000b50f 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionControllerTest.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionControllerTest.java
@@ -19,7 +19,9 @@
 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.BODY_SENSORS;
 import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.WRITE_CALENDAR;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.permissionToOp;
@@ -27,13 +29,18 @@
 import static android.permission.PermissionControllerManager.REASON_INSTALLER_POLICY_VIOLATION;
 import static android.permission.PermissionControllerManager.REASON_MALWARE;
 
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.AppOpsManager;
 import android.app.UiAutomation;
 import android.content.Context;
+import android.content.pm.PermissionGroupInfo;
 import android.permission.PermissionControllerManager;
+import android.permission.RuntimePermissionPresentationInfo;
 import android.platform.test.annotations.AppModeFull;
 
 import androidx.annotation.NonNull;
@@ -47,9 +54,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -63,6 +72,12 @@
     private static final String APK =
             "/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk";
     private static final String APP = "android.permission.cts.appthataccesseslocation";
+    private static final String APK2 =
+            "/data/local/tmp/cts/permissions/"
+                    + "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk";
+    private static final String APP2 = "android.permission.cts.appthatrequestcustompermission";
+    private static final String CUSTOM_PERMISSION =
+            "android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION";
 
     private static final UiAutomation sUiAutomation =
             InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -79,17 +94,18 @@
     @BeforeClass
     public static void installApp() {
         runShellCommand("pm install -r -g " + APK);
+        runShellCommand("pm install -r " + APK2);
     }
 
     @AfterClass
     public static void uninstallApp() {
         runShellCommand("pm uninstall " + APP);
+        runShellCommand("pm uninstall " + APP2);
     }
 
     @Before
     public void setup() throws Exception {
-        sUiAutomation.adoptShellPermissionIdentity();
-        resetAppState();
+        runWithShellPermissionIdentity(() -> resetAppState());
     }
 
     private @NonNull Map<String, List<String>> revoke(@NonNull Map<String, List<String>> request,
@@ -118,10 +134,21 @@
     }
 
     private @NonNull Map<String, List<String>> revoke(@NonNull Map<String, List<String>> request,
-            boolean doDryRun) throws Exception {
+            boolean doDryRun, boolean adoptShell) throws Exception {
+        if (adoptShell) {
+            Map<String, List<String>> revokeRet =
+                    callWithShellPermissionIdentity(() -> revoke(
+                            request, doDryRun, REASON_MALWARE, sContext.getMainExecutor()));
+            return revokeRet;
+        }
         return revoke(request, doDryRun, REASON_MALWARE, sContext.getMainExecutor());
     }
 
+    private @NonNull Map<String, List<String>> revoke(@NonNull Map<String, List<String>> request,
+            boolean doDryRun) throws Exception {
+        return revoke(request, doDryRun, true);
+    }
+
     private void setAppOp(@NonNull String pkg, @NonNull String perm, int mode) throws Exception {
         sContext.getSystemService(AppOpsManager.class).setUidMode(permissionToOp(perm),
                 sContext.getPackageManager().getPackageUid(pkg, 0), mode);
@@ -156,11 +183,12 @@
     @Test
     public void doNotRevokeAlreadyRevokedPermission() throws Exception {
         // Properly revoke the permission
-        sUiAutomation.revokeRuntimePermission(APP, ACCESS_BACKGROUND_LOCATION);
-        setAppOp(APP, ACCESS_FINE_LOCATION, MODE_FOREGROUND);
+        runWithShellPermissionIdentity(() -> {
+            sUiAutomation.revokeRuntimePermission(APP, ACCESS_BACKGROUND_LOCATION);
+            setAppOp(APP, ACCESS_FINE_LOCATION, MODE_FOREGROUND);
+        });
 
         Map<String, List<String>> request = buildRequest(APP, ACCESS_BACKGROUND_LOCATION);
-
         Map<String, List<String>> result = revoke(request, false);
 
         assertThat(result).isEmpty();
@@ -208,10 +236,8 @@
     @Test
     public void revokePolicyViolationFromWrongPackage() throws Exception {
         Map<String, List<String>> request = buildRequest(APP, ACCESS_FINE_LOCATION);
-
-        Map<String, List<String>> result = revoke(request, false,
-                REASON_INSTALLER_POLICY_VIOLATION, sContext.getMainExecutor());
-
+        Map<String, List<String>> result = callWithShellPermissionIdentity(() -> revoke(request,
+                false, REASON_INSTALLER_POLICY_VIOLATION, sContext.getMainExecutor()));
         assertThat(result).isEmpty();
     }
 
@@ -220,10 +246,11 @@
         Map<String, List<String>> request = buildRequest(APP, ACCESS_BACKGROUND_LOCATION);
 
         AtomicBoolean wasRunOnExecutor = new AtomicBoolean();
-        revoke(request, true, REASON_MALWARE, command -> {
-            wasRunOnExecutor.set(true);
-            command.run();
-        });
+        runWithShellPermissionIdentity(() ->
+                revoke(request, true, REASON_MALWARE, command -> {
+                    wasRunOnExecutor.set(true);
+                    command.run();
+                }));
 
         assertThat(wasRunOnExecutor.get()).isTrue();
     }
@@ -285,20 +312,140 @@
 
     @Test(expected = SecurityException.class)
     public void tryToRevokeWithoutPermission() throws Exception {
-        sUiAutomation.dropShellPermissionIdentity();
-        try {
-            Map<String, List<String>> request = buildRequest(APP, ACCESS_BACKGROUND_LOCATION);
+        Map<String, List<String>> request = buildRequest(APP, ACCESS_BACKGROUND_LOCATION);
 
-            // This will fail as the test-app does not have the required permission
-            revoke(request, true);
+        // This will fail as the test-app does not have the required permission
+        revoke(request, true, false);
+    }
+
+    @Test
+    public void runtimePermissionPresentationInfoLocationApp() throws java.lang.Exception {
+        CompletableFuture<List<RuntimePermissionPresentationInfo>> futurePermissionInfos =
+                new CompletableFuture<>();
+
+        List<String> runtimePermissions;
+        List<RuntimePermissionPresentationInfo> permissionInfos;
+
+        sUiAutomation.adoptShellPermissionIdentity();
+        try {
+            sController.getAppPermissions(APP, futurePermissionInfos::complete, null);
+            runtimePermissions = PermissionUtils.getRuntimePermissions(APP);
+            assertThat(runtimePermissions).isNotEmpty();
+            permissionInfos = futurePermissionInfos.get();
         } finally {
-            sUiAutomation.adoptShellPermissionIdentity();
+            sUiAutomation.dropShellPermissionIdentity();
         }
+
+        assertRuntimePermissionLabelsAreValid(runtimePermissions, permissionInfos, 3, APP);
+    }
+
+    private void assertRuntimePermissionLabelsAreValid(List<String> runtimePermissions,
+            List<RuntimePermissionPresentationInfo> permissionInfos, int expectedRuntimeGranted,
+            String app) throws Exception {
+        int numRuntimeGranted = 0;
+        for (String permission : runtimePermissions) {
+            if (PermissionUtils.isPermissionGranted(app, permission)) {
+                numRuntimeGranted++;
+            }
+        }
+        assertThat(numRuntimeGranted).isEqualTo(expectedRuntimeGranted);
+
+        ArrayList<CharSequence> maybeStandardPermissionLabels = new ArrayList<>();
+        ArrayList<CharSequence> nonStandardPermissionLabels = new ArrayList<>();
+        for (PermissionGroupInfo permGroup : sContext.getPackageManager().getAllPermissionGroups(
+                0)) {
+            CharSequence permissionGroupLabel = permGroup.loadLabel(sContext.getPackageManager());
+            if (permGroup.packageName.equals("android")) {
+                maybeStandardPermissionLabels.add(permissionGroupLabel);
+            } else {
+                nonStandardPermissionLabels.add(permissionGroupLabel);
+            }
+        }
+
+        int numInfosGranted = 0;
+
+        for (RuntimePermissionPresentationInfo permissionInfo : permissionInfos) {
+            CharSequence permissionGroupLabel = permissionInfo.getLabel();
+
+            // PermissionInfo should be included in exactly one of existing (possibly) standard
+            // or nonstandard permission groups
+            if (permissionInfo.isStandard()) {
+                assertThat(maybeStandardPermissionLabels).contains(permissionGroupLabel);
+            } else {
+                assertThat(nonStandardPermissionLabels).contains(permissionGroupLabel);
+            }
+            if (permissionInfo.isGranted()) {
+                numInfosGranted++;
+            }
+        }
+
+        // Each permissionInfo represents one or more runtime permissions, but we don't have a
+        // mapping, so we check that we have at least as many runtimePermissions as permissionInfos
+        assertThat(numRuntimeGranted).isAtLeast(numInfosGranted);
+    }
+
+    @Test
+    public void runtimePermissionPresentationInfoCustomApp() throws java.lang.Exception {
+        CompletableFuture<List<RuntimePermissionPresentationInfo>> futurePermissionInfos =
+                new CompletableFuture<>();
+
+        // Grant all requested permissions except READ_CALENDAR
+        sUiAutomation.grantRuntimePermission(APP2, CUSTOM_PERMISSION);
+        PermissionUtils.grantPermission(APP2, BODY_SENSORS);
+        PermissionUtils.grantPermission(APP2, READ_CONTACTS);
+        PermissionUtils.grantPermission(APP2, WRITE_CALENDAR);
+
+        List<String> runtimePermissions;
+        List<RuntimePermissionPresentationInfo> permissionInfos;
+        sUiAutomation.adoptShellPermissionIdentity();
+        try {
+            sController.getAppPermissions(APP2, futurePermissionInfos::complete, null);
+            runtimePermissions = PermissionUtils.getRuntimePermissions(APP2);
+
+            permissionInfos = futurePermissionInfos.get();
+        } finally {
+            sUiAutomation.dropShellPermissionIdentity();
+        }
+
+        assertThat(permissionInfos).isNotEmpty();
+        assertThat(runtimePermissions.size()).isEqualTo(5);
+        assertRuntimePermissionLabelsAreValid(runtimePermissions, permissionInfos, 4, APP2);
+    }
+
+    @Test
+    public void runtimePermissionLabelSet() {
+        assertThat(new RuntimePermissionPresentationInfo("test", true,
+                true).getLabel()).isEqualTo("test");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void runtimePermissionLabelSetNull() {
+        RuntimePermissionPresentationInfo info = new RuntimePermissionPresentationInfo(null, true,
+                true);
+    }
+
+    @Test
+    public void runtimePermissionGrantedCanBeTrue() {
+        assertThat(new RuntimePermissionPresentationInfo("", true, true).isGranted()).isTrue();
+    }
+
+    @Test
+    public void runtimePermissionGrantedCanBeFalse() {
+        assertThat(new RuntimePermissionPresentationInfo("", false, true).isGranted()).isFalse();
+    }
+
+    @Test
+    public void runtimePermissionStandardCanBeTrue() {
+        assertThat(new RuntimePermissionPresentationInfo("", true, true).isStandard()).isTrue();
+    }
+
+    @Test
+    public void runtimePermissionStandardCanBeFalse() {
+        assertThat(new RuntimePermissionPresentationInfo("", true, false).isStandard()).isFalse();
     }
 
     @After
     public void dropShellPermissions() throws Exception {
-        resetAppState();
-        sUiAutomation.dropShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> resetAppState());
     }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
index 95c274c..78e82ff 100644
--- a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -237,8 +237,8 @@
     public void testWriteMediaStorage() throws Exception {
         final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         final PackageManager pm = getContext().getPackageManager();
-        final List<PackageInfo> pkgs = pm
-                .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        final List<PackageInfo> pkgs = pm.getInstalledPackages(
+                PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS);
         for (PackageInfo pkg : pkgs) {
             final boolean isSystem = pkg.applicationInfo.uid == android.os.Process.SYSTEM_UID;
             final boolean hasFrontDoor = pm.getLaunchIntentForPackage(pkg.packageName) != null;
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 227ef5b..32e4296 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
@@ -275,6 +275,30 @@
         }
     }
 
+    /**
+     * Verify that getNetworkType and getDataNetworkType requires Permission.
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE}.
+     */
+    @Test
+    public void testGetNetworkType() {
+        if (!mHasTelephony) {
+            return;
+        }
+
+        if (mTelephonyManager.getNetworkType() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+            fail("getNetworkType should return UNKNOWN");
+        }
+
+        try {
+            mTelephonyManager.getDataNetworkType();
+            fail("getDataNetworkType did not throw a SecurityException");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index cc96cdc..49c28b4 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -911,6 +911,7 @@
     <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
         android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_accessBackgroundLocation"
+        android:permissionFlags="hardRestricted"
         android:description="@string/permdesc_accessBackgroundLocation"
         android:protectionLevel="dangerous|instant" />
 
@@ -4432,7 +4433,7 @@
     <!-- Allows an application to watch changes and/or active state of app ops.
          @hide <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WATCH_APPOPS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|privileged" />
 
     <!-- Allows an application to directly open the "Open by default" page inside a package's
          Details screen.
diff --git a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
index 6092066..586c74c 100644
--- a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
@@ -16,16 +16,18 @@
 
 package android.permission2.cts;
 
+import static android.permission2.cts.Utils.eventually;
+
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-import static org.junit.Assert.fail;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
+
 import android.Manifest;
 import android.Manifest.permission;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
-
-
 import android.content.Context;
 import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
@@ -34,18 +36,19 @@
 import android.platform.test.annotations.AppModeFull;
 import android.util.ArraySet;
 
-import java.util.Collections;
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.util.Collections;
 import java.util.Set;
 
 import javax.annotation.Nullable;
 
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
 /**
  * Tests for restricted permission behaviors.
  */
@@ -69,19 +72,6 @@
 
     private static @NonNull BroadcastReceiver sCommandReceiver;
 
-    public interface ThrowingRunnable extends Runnable {
-        void runOrThrow() throws Exception;
-
-        @Override
-        default void run() {
-            try {
-                runOrThrow();
-            } catch (Exception ex) {
-                throw new RuntimeException(ex);
-            }
-        }
-    }
-
     @BeforeClass
     public static void setUpOnce() {
         sCommandReceiver = new CommandBroadcastReceiver();
@@ -99,77 +89,77 @@
     @Test
     @AppModeFull
     public void testDefaultAllRestrictedPermissionsWhitelistedAtInstall() throws Exception {
-        try {
-            // Install with no changes to whitelisted permissions, not attempting to grant.
-            installRestrictedPermissionUserApp(null /*whitelistedPermissions*/,
-                    null /*grantedPermissions*/);
+        // Install with no changes to whitelisted permissions, not attempting to grant.
+        installRestrictedPermissionUserApp(null /*whitelistedPermissions*/,
+                null /*grantedPermissions*/);
 
-            // All restricted permission should be whitelisted.
-            assertAllRestrictedPermissionWhitelisted();
+        // All restricted permission should be whitelisted.
+        assertAllRestrictedPermissionWhitelisted();
 
-            // No restricted permission should be granted.
-            assertNoRestrictedPermissionGranted();
-        } finally {
-            uninstallApp();
-        }
+        // No restricted permission should be granted.
+        assertNoRestrictedPermissionGranted();
     }
 
     @Test
     @AppModeFull
     public void testSomeRestrictedPermissionsWhitelistedAtInstall() throws Exception {
-        try {
-            // Whitelist only these permissions.
-            final Set<String> whitelistedPermissions = new ArraySet<>(2);
-            whitelistedPermissions.add(Manifest.permission.SEND_SMS);
-            whitelistedPermissions.add(Manifest.permission.READ_CALL_LOG);
+        // Whitelist only these permissions.
+        final Set<String> whitelistedPermissions = new ArraySet<>(2);
+        whitelistedPermissions.add(Manifest.permission.SEND_SMS);
+        whitelistedPermissions.add(Manifest.permission.READ_CALL_LOG);
 
-            // Install with some whitelisted permissions, not attempting to grant.
-            installRestrictedPermissionUserApp(whitelistedPermissions, null /*grantedPermissions*/);
+        // Install with some whitelisted permissions, not attempting to grant.
+        installRestrictedPermissionUserApp(whitelistedPermissions, null /*grantedPermissions*/);
 
-            // Some restricted permission should be whitelisted.
-            assertRestrictedPermissionWhitelisted(whitelistedPermissions);
+        // Some restricted permission should be whitelisted.
+        eventually(() -> assertRestrictedPermissionWhitelisted(whitelistedPermissions));
 
-            // No restricted permission should be granted.
-            assertNoRestrictedPermissionGranted();
-        } finally {
-            uninstallApp();
-        }
+        // No restricted permission should be granted.
+        assertNoRestrictedPermissionGranted();
     }
 
     @Test
     @AppModeFull
     public void testNoneRestrictedPermissionWhitelistedAtInstall() throws Exception {
-        try {
-            // Install with all whitelisted permissions, not attempting to grant.
-            installRestrictedPermissionUserApp(Collections.emptySet(),
-                    null /*grantedPermissions*/);
+        // Install with all whitelisted permissions, not attempting to grant.
+        installRestrictedPermissionUserApp(Collections.emptySet(),
+                null /*grantedPermissions*/);
 
-            // No restricted permission should be whitelisted.
-            assertNoRestrictedPermissionWhitelisted();
+        // No restricted permission should be whitelisted.
+        assertNoRestrictedPermissionWhitelisted();
 
-            // No restricted permission should be granted.
-            assertNoRestrictedPermissionGranted();
-        } finally {
-            uninstallApp();
-        }
+        // No restricted permission should be granted.
+        assertNoRestrictedPermissionGranted();
     }
 
     @Test
     @AppModeFull
     public void testSomeRestrictedPermissionsGrantedAtInstall() throws Exception {
+        // Grant only these permissions.
+        final Set<String> grantedPermissions = new ArraySet<>(1);
+        grantedPermissions.add(Manifest.permission.SEND_SMS);
+        grantedPermissions.add(Manifest.permission.READ_CALL_LOG);
+
+        // Install with no whitelisted permissions attempting to grant.
+        installRestrictedPermissionUserApp(null /*whitelistedPermissions*/, grantedPermissions);
+
+        // All restricted permission should be whitelisted.
+        assertAllRestrictedPermissionWhitelisted();
+
+        // Some restricted permission should be granted.
+        assertRestrictedPermissionGranted(grantedPermissions);
+    }
+
+    @Test
+    @AppModeFull
+    public void testCanGrantSoftRestrictedNotWhitelistedPermissions() throws Exception {
         try {
-            // Grant only these permissions.
-            final Set<String> grantedPermissions = new ArraySet<>(1);
-            grantedPermissions.add(Manifest.permission.SEND_SMS);
-            grantedPermissions.add(Manifest.permission.READ_CALL_LOG);
+            final Set<String> grantedPermissions = new ArraySet<>();
+            grantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+            grantedPermissions.add(permission.WRITE_EXTERNAL_STORAGE);
 
-            // Install with no whitelisted permissions attempting to grant.
-            installRestrictedPermissionUserApp(null /*whitelistedPermissions*/, grantedPermissions);
+            installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet(), grantedPermissions);
 
-            // All restricted permission should be whitelisted.
-            assertAllRestrictedPermissionWhitelisted();
-
-            // Some restricted permission should be granted.
             assertRestrictedPermissionGranted(grantedPermissions);
         } finally {
             uninstallApp();
@@ -179,206 +169,204 @@
     @Test
     @AppModeFull
     public void testAllRestrictedPermissionsGrantedAtInstall() throws Exception {
-        try {
-            // Install with whitelisted permissions attempting to grant.
-            installRestrictedPermissionUserApp(null /*whitelistedPermissions*/,
-                    Collections.emptySet());
+        // Install with whitelisted permissions attempting to grant.
+        installRestrictedPermissionUserApp(null /*whitelistedPermissions*/,
+                Collections.emptySet());
 
-            // All restricted permission should be whitelisted.
-            assertAllRestrictedPermissionWhitelisted();
+        // All restricted permission should be whitelisted.
+        assertAllRestrictedPermissionWhitelisted();
 
-            // Some restricted permission should be granted.
-            assertAllRestrictedPermissionGranted();
-        } finally {
-            uninstallApp();
-        }
+        // Some restricted permission should be granted.
+        assertAllRestrictedPermissionGranted();
     }
 
     @Test
     @AppModeFull
     public void testWhitelistAccessControl() throws Exception {
-        try {
-            // Install with no whitelisted permissions not attempting to grant.
-            installRestrictedPermissionUserApp(Collections.emptySet(),
-                    Collections.emptySet());
+        // Install with no whitelisted permissions not attempting to grant.
+        installRestrictedPermissionUserApp(Collections.emptySet(),
+                Collections.emptySet());
 
-            assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
-                    PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
+        assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
+                PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
 
-            assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
-                    PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
+        assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
+                PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
 
-            assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
-                    PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
-        } finally {
-            uninstallApp();
-        }
+        assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
+                PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk28DefaultWhitelistedHasFullAccess() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
+        // Install with whitelisted permissions.
+        installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
 
-            // Check expected storage mode
-            assertHasFullStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasFullStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk28DefaultNotWhitelistedHasIsolatedAccess() throws Exception {
-        try {
-            // Install with no whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
+        // Install with no whitelisted permissions.
+        installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
 
-            // Check expected storage mode
-            assertHasIsolatedStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasIsolatedStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk28OptInWhitelistedHasIsolatedAccess() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
+        // Install with whitelisted permissions.
+        installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
 
-            // Check expected storage mode
-            assertHasIsolatedStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasIsolatedStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk28OptInNotWhitelistedHasIsolatedAccess() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
+        // Install with whitelisted permissions.
+        installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
 
-            // Check expected storage mode
-            assertHasIsolatedStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasIsolatedStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk29DefaultWhitelistedHasIsolatedAccess() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
+        // Install with whitelisted permissions.
+        installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
 
-            // Check expected storage mode
-            assertHasIsolatedStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasIsolatedStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk29DefaultNotWhitelistedHasIsolatedAccess() throws Exception {
-        try {
-            // Install with no whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_29, null /*whitelistedPermissions*/);
+        // Install with no whitelisted permissions.
+        installApp(APK_USES_STORAGE_DEFAULT_29, null /*whitelistedPermissions*/);
 
-            // Check expected storage mode
-            assertHasIsolatedStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasIsolatedStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk29OptOutWhitelistedHasFullAccess() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_OPT_OUT_29, null /*whitelistedPermissions*/);
+        // Install with whitelisted permissions.
+        installApp(APK_USES_STORAGE_OPT_OUT_29, null /*whitelistedPermissions*/);
 
-            // Check expected storage mode
-            assertHasFullStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasFullStorageAccess();
     }
 
     @Test
     @AppModeFull
     public void testStorageTargetingSdk29OptOutNotWhitelistedHasIsolatedAccess() throws Exception {
-        try {
-            // Install with no whitelisted permissions.
-            installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet() );
+        // Install with no whitelisted permissions.
+        installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
 
-            // Check expected storage mode
-            assertHasIsolatedStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected storage mode
+        assertHasIsolatedStorageAccess();
     }
 
     @Test
     @AppModeFull
-    public void testStorageDoesNotChangeOnUpdate() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
+    public void testStorageTargetingSdk29CanOptOutViaUpdate() throws Exception {
+        installApp(APK_USES_STORAGE_DEFAULT_29, null);
+        installApp(APK_USES_STORAGE_OPT_OUT_29, null);
 
-            // Check expected storage mode
-            assertHasFullStorageAccess();
+        eventually(this::assertHasFullStorageAccess);
+    }
 
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk29CanOptOutViaDowngradeTo28() throws Exception {
+        installApp(APK_USES_STORAGE_DEFAULT_29, null);
+        installApp(APK_USES_STORAGE_DEFAULT_28, null);
 
-            // Check expected storage mode
-            assertHasFullStorageAccess();
+        eventually(this::assertHasFullStorageAccess);
+    }
 
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_29, null /*whitelistedPermissions*/);
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk28CanRemoveOptInViaUpdate() throws Exception {
+        installApp(APK_USES_STORAGE_OPT_IN_28, null);
+        installApp(APK_USES_STORAGE_DEFAULT_28, null);
 
-            // Check expected storage mode
-            assertHasFullStorageAccess();
-        } finally {
-            uninstallApp();
-        }
+        eventually(this::assertHasFullStorageAccess);
+    }
+
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk28CanRemoveOptInByOptingOut() throws Exception {
+        installApp(APK_USES_STORAGE_OPT_IN_28, null);
+        installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+
+        eventually(this::assertHasFullStorageAccess);
+    }
+
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk28DoesNotLooseAccessWhenOptingIn() throws Exception {
+        installApp(APK_USES_STORAGE_DEFAULT_28, null);
+        installApp(APK_USES_STORAGE_OPT_IN_28, null);
+
+        eventually(this::assertHasFullStorageAccess);
+    }
+
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk28DoesNotLooseAccessViaUpdate() throws Exception {
+        installApp(APK_USES_STORAGE_DEFAULT_28, null);
+        installApp(APK_USES_STORAGE_DEFAULT_29, null);
+
+        eventually(this::assertHasFullStorageAccess);
+    }
+
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk29DoesNotLooseAccessViaUpdate() throws Exception {
+        installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+        installApp(APK_USES_STORAGE_DEFAULT_29, null);
+
+        eventually(this::assertHasFullStorageAccess);
+    }
+
+    @Test
+    @AppModeFull
+    public void testStorageTargetingSdk29DoesNotLooseAccessWhenOptingIn() throws Exception {
+        installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+        installApp(APK_USES_STORAGE_OPT_IN_28, null);
+
+        eventually(this::assertHasFullStorageAccess);
     }
 
     @Test
     @AppModeFull
     public void testCannotControlStorageWhitelistPostInstall1() throws Exception {
-        try {
-            // Install with whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
+        // Install with whitelisted permissions.
+        installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
 
-            // Check expected state of restricted permissions.
-            assertCannotUnWhitelistStorage();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected state of restricted permissions.
+        assertCannotUnWhitelistStorage();
     }
 
     @Test
     @AppModeFull
     public void testCannotControlStorageWhitelistPostInstall2() throws Exception {
-        try {
-            // Install with no whitelisted permissions.
-            installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
+        // Install with no whitelisted permissions.
+        installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
 
-            // Check expected state of restricted permissions.
-            assertCannotWhitelistStorage();
-        } finally {
-            uninstallApp();
-        }
+        // Check expected state of restricted permissions.
+        assertCannotWhitelistStorage();
     }
 
     private void assertHasFullStorageAccess() throws Exception {
@@ -593,17 +581,17 @@
                     PackageManager.GET_PERMISSIONS);
 
             for (String permission : packageInfo.requestedPermissions) {
+                String op = AppOpsManager.permissionToOp(permission);
+
                 if (expectedWhitelistedPermissions != null
                         && expectedWhitelistedPermissions.contains(permission)) {
-                    assertThat(appOpsManager.unsafeCheckOpNoThrow(
-                            AppOpsManager.permissionToOp(permission),
-                            packageInfo.applicationInfo.uid, PKG))
-                            .isEqualTo(AppOpsManager.MODE_ALLOWED);
+                    assertThat(
+                            appOpsManager.unsafeCheckOpNoThrow(op, packageInfo.applicationInfo.uid,
+                                    PKG)).named(op).isEqualTo(AppOpsManager.MODE_ALLOWED);
                 } else {
-                    assertThat(appOpsManager.unsafeCheckOpNoThrow(
-                            AppOpsManager.permissionToOp(permission),
-                            packageInfo.applicationInfo.uid, PKG))
-                            .isEqualTo(AppOpsManager.MODE_DEFAULT);
+                    assertThat(
+                            appOpsManager.unsafeCheckOpNoThrow(op, packageInfo.applicationInfo.uid,
+                                    PKG)).named(op).isEqualTo(AppOpsManager.MODE_DEFAULT);
                 }
             }
         });
@@ -661,7 +649,8 @@
                 final String permission = packageInfo.requestedPermissions[i];
                 final PermissionInfo permissionInfo = packageManager.getPermissionInfo(
                         permission, 0);
-                if ((permissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0) {
+                if ((permissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+                        || (permissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
                     if (expectedGrantedPermissions != null
                             && expectedGrantedPermissions.contains(permission)) {
                         assertThat((packageInfo.requestedPermissionsFlags[i]
@@ -688,7 +677,7 @@
     private void installApp(@NonNull String app, @Nullable Set<String> whitelistedPermissions,
             @Nullable Set<String> grantedPermissions) throws Exception {
         // Install the app and whitelist/grant all permission if requested.
-        final StringBuilder command = new StringBuilder("pm install ");
+        final StringBuilder command = new StringBuilder("pm install -r ");
         if (whitelistedPermissions != null) {
             command.append("--restrict-permissions ");
         }
@@ -696,7 +685,8 @@
             command.append("-g ");
         }
         command.append(app);
-        runShellCommand(command.toString());
+        String installResult = runShellCommand(command.toString());
+        assertThat(installResult.trim()).isEqualTo("Success");
 
         // Whitelist subset of permissions if requested
         if (whitelistedPermissions != null && !whitelistedPermissions.isEmpty()) {
@@ -721,7 +711,8 @@
         }
     }
 
-    private void uninstallApp() {
+    @After
+    public void uninstallApp() {
         runShellCommand("pm uninstall " + PKG);
     }
 
@@ -729,7 +720,7 @@
         return InstrumentationRegistry.getInstrumentation().getContext();
     }
 
-    private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
+    private static void runWithShellPermissionIdentity(@NonNull Utils.ThrowingRunnable command)
             throws Exception {
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity();
diff --git a/tests/tests/permission2/src/android/permission2/cts/Utils.java b/tests/tests/permission2/src/android/permission2/cts/Utils.java
new file mode 100644
index 0000000..7cf2f4d
--- /dev/null
+++ b/tests/tests/permission2/src/android/permission2/cts/Utils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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 andf
+ * limitations under the License.
+ */
+
+package android.permission2.cts;
+
+import androidx.annotation.NonNull;
+
+public class Utils {
+    private static final long TIMEOUT_MILLIS = 30000;
+
+    public interface ThrowingRunnable extends Runnable {
+        void runOrThrow() throws Exception;
+
+        @Override
+        default void run() {
+            try {
+                runOrThrow();
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    /**
+     * Make sure that a {@link ThrowingRunnable} eventually finishes without throwing a {@link
+     * Exception}.
+     *
+     * @param r The {@link ThrowingRunnable} to run.
+     */
+    public static void eventually(@NonNull ThrowingRunnable r) throws Exception {
+        long start = System.currentTimeMillis();
+
+        while (true) {
+            try {
+                r.runOrThrow();
+                return;
+            } catch (Throwable e) {
+                if (System.currentTimeMillis() - start < TIMEOUT_MILLIS) {
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException ignored) {
+                        throw e;
+                    }
+                } else {
+                    throw e;
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index c31491d..9e71c69 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -300,6 +300,30 @@
         assertTrue(queryLong(uri, MediaColumns.DATE_ADDED) >= startTime);
     }
 
+    @Test
+    public void testInPlaceUpdate_mediaFileWithInvalidRelativePath() throws Exception {
+        final File file = new File(ProviderTestUtils.stageDownloadDir(mVolumeName),
+                "test" + System.nanoTime() + ".jpg");
+        ProviderTestUtils.stageFile(R.raw.scenery, file);
+        Log.d(TAG, "Staged image file at " + file.getAbsolutePath());
+
+        final ContentValues insertValues = new ContentValues();
+        insertValues.put(MediaColumns.DATA, file.getAbsolutePath());
+        insertValues.put(MediaStore.Images.ImageColumns.DESCRIPTION, "Not a cat photo");
+        final Uri uri = mResolver.insert(mExternalImages, insertValues);
+        assertEquals(0, queryLong(uri, MediaStore.Images.ImageColumns.IS_PRIVATE));
+        assertStringColumn(uri, MediaStore.Images.ImageColumns.DESCRIPTION, "Not a cat photo");
+
+        final ContentValues updateValues = new ContentValues();
+        updateValues.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_IMAGE);
+        updateValues.put(FileColumns.MIME_TYPE, "image/jpeg");
+        updateValues.put(MediaStore.Images.ImageColumns.IS_PRIVATE, 1);
+        int updateRows = mResolver.update(uri, updateValues, null, null);
+        assertEquals(1, updateRows);
+        // Only interested in update not throwing exception. No need in checking whenever values
+        // were actually updates, as it is not in the scope of this test.
+    }
+
     private long queryLong(Uri uri, String columnName) {
         try (Cursor c = mResolver.query(uri, new String[] { columnName }, null, null, null)) {
             assertTrue(c.moveToFirst());
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index 0150f2f..177fddb 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -19,6 +19,7 @@
 import static android.provider.cts.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -313,7 +314,7 @@
         final Uri pendingUri = MediaStoreUtils.createPending(mContext, params);
         final Uri publishUri;
         try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
-            try (InputStream in = mContext.getResources().openRawResource(R.raw.volantis);
+            try (InputStream in = mContext.getResources().openRawResource(R.raw.lg_g4_iso_800_jpg);
                  OutputStream out = session.openOutputStream()) {
                 android.os.FileUtils.copy(in, out);
             }
@@ -328,14 +329,19 @@
             final ExifInterface exif = new ExifInterface(is);
             final float[] latLong = new float[2];
             exif.getLatLong(latLong);
-            assertEquals(37.42303, latLong[0], 0.001);
-            assertEquals(-122.162025, latLong[1], 0.001);
+            assertEquals(53.83451, latLong[0], 0.001);
+            assertEquals(10.69585, latLong[1], 0.001);
+
+            String xmp = exif.getAttribute(ExifInterface.TAG_XMP);
+            assertTrue("Failed to read XMP longitude", xmp.contains("53,50.070500N"));
+            assertTrue("Failed to read XMP latitude", xmp.contains("10,41.751000E"));
+            assertTrue("Failed to read non-location XMP", xmp.contains("LensDefaults"));
         }
         // As owner, we should be able to request the original bytes
         try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
         }
 
-        // Now remove ownership, which means that Exif should be redacted
+        // Now remove ownership, which means that Exif/XMP location data should be redacted
         ProviderTestUtils.executeShellCommand(
                 "content update --uri " + publishUri + " --bind owner_package_name:n:",
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
@@ -345,6 +351,11 @@
             exif.getLatLong(latLong);
             assertEquals(0, latLong[0], 0.001);
             assertEquals(0, latLong[1], 0.001);
+
+            String xmp = exif.getAttribute(ExifInterface.TAG_XMP);
+            assertFalse("Failed to redact XMP longitude", xmp.contains("53,50.070500N"));
+            assertFalse("Failed to redact XMP latitude", xmp.contains("10,41.751000E"));
+            assertTrue("Redacted non-location XMP", xmp.contains("LensDefaults"));
         }
         // We can't request original bytes unless we have permission
         try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index a1695ba..06f0222 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -191,7 +191,7 @@
         Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery,opts);
         String stringUrl = null;
         try{
-            stringUrl = Media.insertImage(mContentResolver, src, null, null);
+            stringUrl = Media.insertImage(mContentResolver, src, "cts" + System.nanoTime(), null);
         } catch (UnsupportedOperationException e) {
             // the tests will be aborted because the image will be put in sdcard
             fail("There is no sdcard attached! " + e.getMessage());
@@ -223,7 +223,7 @@
         assertNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MICRO_KIND, null));
 
         // insert image, then delete it via the files table
-        stringUrl = Media.insertImage(mContentResolver, src, null, null);
+        stringUrl = Media.insertImage(mContentResolver, src, "cts" + System.nanoTime(), null);
         c = mContentResolver.query(Uri.parse(stringUrl),
                 new String[]{ Media._ID, Media.DATA}, null, null, null);
         c.moveToFirst();
@@ -300,7 +300,8 @@
 
         // insert an image
         Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery);
-        Uri uri = Uri.parse(Media.insertImage(mContentResolver, src, "test", "test description"));
+        Uri uri = Uri.parse(Media.insertImage(mContentResolver, src, "cts" + System.nanoTime(),
+                "test description"));
         long imageId = ContentUris.parseId(uri);
 
         assertNotNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MINI_KIND, null));
@@ -313,7 +314,8 @@
         assertNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MICRO_KIND, null));
 
         // insert again
-        uri = Uri.parse(Media.insertImage(mContentResolver, src, "test", "test description"));
+        uri = Uri.parse(Media.insertImage(mContentResolver, src, "cts" + System.nanoTime(),
+                "test description"));
         imageId = ContentUris.parseId(uri);
 
         // query its thumbnail again
@@ -326,6 +328,8 @@
         assertEquals("unexpected number of updated rows",
                 1, mContentResolver.update(uri, values, null /* where */, null /* where args */));
 
+        SystemClock.sleep(1000);
+
         // image was marked as regular file in the database, which should have deleted its thumbnail
         assertNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MINI_KIND, null));
         assertNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MICRO_KIND, null));
@@ -359,7 +363,9 @@
         Uri url[] = new Uri[3];
         try{
             for (int i = 0; i < url.length; i++) {
-                url[i] = Uri.parse(Media.insertImage(mContentResolver, src, null, null));
+                url[i] = Uri.parse(
+                        Media.insertImage(mContentResolver, src, "cts" + System.nanoTime(), null));
+                mRowsAdded.add(url[i]);
                 long origId = Long.parseLong(url[i].getLastPathSegment());
                 Bitmap foo = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver,
                         origId, Thumbnails.MICRO_KIND, null);
@@ -367,7 +373,10 @@
             }
 
             // Remove one of the images, which will also delete any thumbnails
-            mContentResolver.delete(url[1], null, null);
+            // If the image was deleted, we don't want to delete it again
+            if (mContentResolver.delete(url[1], null, null) > 0) {
+                mRowsAdded.remove(url[1]);
+            }
 
             long removedId = Long.parseLong(url[1].getLastPathSegment());
             long remainingId1 = Long.parseLong(url[0].getLastPathSegment());
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index 65c5154..fdf46ce3 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -21,6 +21,7 @@
 import static android.provider.cts.ProviderTestUtils.assertNotExists;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -54,10 +55,12 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
 
 @Presubmit
 @RunWith(Parameterized.class)
@@ -200,7 +203,7 @@
 
     /**
      * This test doesn't hold
-     * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}, so Exif
+     * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}, so Exif and XMP
      * location information should be redacted.
      */
     @Test
@@ -232,6 +235,14 @@
             assertEquals("+37.4217-122.0834/",
                     mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
         }
+        try (InputStream in = mContentResolver.openInputStream(publishUri)) {
+            byte[] bytes = FileUtils.readInputStreamFully(in);
+            byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
+            String xmp = new String(xmpBytes);
+            assertTrue("Failed to read XMP longitude", xmp.contains("10,41.751000E"));
+            assertTrue("Failed to read XMP latitude", xmp.contains("53,50.070500N"));
+            assertTrue("Failed to read non-location XMP", xmp.contains("13166/7763"));
+        }
         // As owner, we should be able to request the original bytes
         try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
         }
@@ -246,6 +257,14 @@
             assertEquals(null,
                     mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
         }
+        try (InputStream in = mContentResolver.openInputStream(publishUri)) {
+            byte[] bytes = FileUtils.readInputStreamFully(in);
+            byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
+            String xmp = new String(xmpBytes);
+            assertFalse("Failed to redact XMP longitude", xmp.contains("10,41.751000E"));
+            assertFalse("Failed to redact XMP latitude", xmp.contains("53,50.070500N"));
+            assertTrue("Redacted non-location XMP", xmp.contains("13166/7763"));
+        }
         // We can't request original bytes unless we have permission
         try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
             fail("Able to read original content without ACCESS_MEDIA_LOCATION");
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index 6d9a4bd..32ea780 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -164,6 +164,8 @@
         assertEquals("unexpected number of updated rows",
                 1, mResolver.update(uri, values, null /* where */, null /* where args */));
 
+        SystemClock.sleep(1000);
+
         // video was marked as regular file in the database, which should have deleted its thumbnail
         assertNull(Thumbnails.getThumbnail(mResolver, Long.valueOf(uri.getLastPathSegment()),
                 Thumbnails.MINI_KIND, null /* options */));
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index 220ef47..e748360 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -183,6 +183,14 @@
                 "android.provider.cts");
     }
 
+    static File stageDownloadDir(String volumeName) throws IOException {
+        if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
+            volumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
+        }
+        return Environment.buildPath(MediaStore.getVolumePath(volumeName),
+                Environment.DIRECTORY_DOWNLOADS, "android.provider.cts");
+    }
+
     static File stageFile(int resId, File file) throws IOException {
         // The caller may be trying to stage into a location only available to
         // the shell user, so we need to perform the entire copy as the shell
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index a62b12c..736e501 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -107,6 +107,7 @@
             new ActivityTestRule<>(WaitForResultActivity.class);
 
     private String mRoleHolder;
+    private int mCurrentUserId;
 
     @Before
     public void saveRoleHolder() throws Exception {
@@ -132,6 +133,7 @@
 
     @Before
     public void installApp() throws Exception {
+        mCurrentUserId = Process.myUserHandle().getIdentifier();
         installPackage(APP_APK_PATH);
     }
 
@@ -351,16 +353,16 @@
         return mActivityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS);
     }
 
-    private static void clearPackageData(@NonNull String packageName) {
-        runShellCommand("pm clear " + packageName);
+    private void clearPackageData(@NonNull String packageName) {
+        runShellCommand("pm clear --user " + mCurrentUserId + " " + packageName);
     }
 
     private void installPackage(@NonNull String apkPath) {
-        runShellCommand("pm install -r " + apkPath);
+        runShellCommand("pm install -r --user " + mCurrentUserId + " " + apkPath);
     }
 
     private void uninstallPackage(@NonNull String packageName) {
-        runShellCommand("pm uninstall " + packageName);
+        runShellCommand("pm uninstall --user " + mCurrentUserId + " " + packageName);
     }
 
     @Test
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
index ef992e6..8a78d0c 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
@@ -28,7 +28,7 @@
 	ctstestrunner-axt \
 	compatibility-device-util-axt
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 29
 LOCAL_JAVA_LIBRARIES += android.test.runner
 LOCAL_JAVA_LIBRARIES += android.test.base
 # Tag this module as a cts test artifact
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/OWNERS b/tests/tests/secure_element/access_control/AccessControlApp1/OWNERS
deleted file mode 100644
index 9af5f4d..0000000
--- a/tests/tests/secure_element/access_control/AccessControlApp1/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 456592
-kandoiruchi@google.com
\ No newline at end of file
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
index 28cb203..d82007a 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
index 38f12ee..494125f 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
@@ -28,7 +28,7 @@
        ctstestrunner-axt \
        compatibility-device-util-axt
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 29
 LOCAL_JAVA_LIBRARIES += android.test.runner
 LOCAL_JAVA_LIBRARIES += android.test.base
 # Tag this module as a cts test artifact
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/OWNERS b/tests/tests/secure_element/access_control/AccessControlApp2/OWNERS
deleted file mode 100644
index 9af5f4d..0000000
--- a/tests/tests/secure_element/access_control/AccessControlApp2/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 456592
-kandoiruchi@google.com
\ No newline at end of file
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
index 1769767..6c8cb70 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
index 335c08a..11f0200 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
@@ -28,7 +28,7 @@
 	ctstestrunner-axt \
 	compatibility-device-util-axt
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 29
 LOCAL_JAVA_LIBRARIES += android.test.runner
 LOCAL_JAVA_LIBRARIES += android.test.base
 # Tag this module as a cts test artifact
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/OWNERS b/tests/tests/secure_element/access_control/AccessControlApp3/OWNERS
deleted file mode 100644
index 9af5f4d..0000000
--- a/tests/tests/secure_element/access_control/AccessControlApp3/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 456592
-kandoiruchi@google.com
\ No newline at end of file
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
index ab07811..4d8f5e3 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/OWNERS b/tests/tests/secure_element/access_control/OWNERS
new file mode 100644
index 0000000..853b7c3
--- /dev/null
+++ b/tests/tests/secure_element/access_control/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 456592
+zachoverflow@google.com
+jackcwyu@google.com
+tokuda@google.com
+georgekgchang@google.com
+jimmychchang@google.com
diff --git a/tests/tests/security/res/raw/bug_73552574_avc.mp4 b/tests/tests/security/res/raw/bug_73552574_avc.mp4
new file mode 100644
index 0000000..1cca70c
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_73552574_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_73552574_framelen.mp4 b/tests/tests/security/res/raw/bug_73552574_framelen.mp4
new file mode 100644
index 0000000..36728cc
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_73552574_framelen.mp4
@@ -0,0 +1,93 @@
+48
+4
+28
+208
+0
+10
+39
+386
+8
+70
+6
+32
+31
+4
+8
+24
+10
+22
+12
+108
+9
+229
+38
+12
+10
+166
+39
+250
+43
+8
+70
+6
+29
+12
+4
+8
+33
+12
+0
+10
+156
+10
+39
+94
+10
+39
+386
+8
+70
+6
+10
+31
+4
+8
+24
+10
+22
+12
+70
+9
+420
+0
+8
+36
+6
+12
+20
+31
+102
+229
+38
+12
+10
+156
+10
+39
+197
+251
+38
+12
+10
+156
+10
+180
+10
+39
+386
+8
+70
+6
+32
+31
+6441
diff --git a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
index 71e4c4f..9ce81a8 100644
--- a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -98,8 +98,9 @@
             // APK for an activity that collects information printed in the CTS report header
             "android.tests.devicesetup",
 
-            // Wifi test utility used by Tradefed...
+            // Test utilities used by Tradefed harness
             "com.android.tradefed.utils.wifi",
+            "android.tradefed.contentprovider",
 
             // Game used for CTS testing...
             "com.replica.replicaisland",
diff --git a/tests/tests/security/src/android/security/cts/RenderTarget.java b/tests/tests/security/src/android/security/cts/RenderTarget.java
new file mode 100644
index 0000000..3c0d1c4
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/RenderTarget.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import static android.opengl.EGL14.EGL_ALPHA_SIZE;
+import static android.opengl.EGL14.EGL_BLUE_SIZE;
+import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
+import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
+import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
+import static android.opengl.EGL14.EGL_DEPTH_SIZE;
+import static android.opengl.EGL14.EGL_GREEN_SIZE;
+import static android.opengl.EGL14.EGL_HEIGHT;
+import static android.opengl.EGL14.EGL_NONE;
+import static android.opengl.EGL14.EGL_NO_CONTEXT;
+import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
+import static android.opengl.EGL14.EGL_RED_SIZE;
+import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
+import static android.opengl.EGL14.EGL_SURFACE_TYPE;
+import static android.opengl.EGL14.EGL_WIDTH;
+import static android.opengl.EGL14.EGL_WINDOW_BIT;
+import static android.opengl.EGL14.eglChooseConfig;
+import static android.opengl.EGL14.eglCreateContext;
+import static android.opengl.EGL14.eglCreatePbufferSurface;
+import static android.opengl.EGL14.eglGetDisplay;
+import static android.opengl.EGL14.eglInitialize;
+import static android.opengl.EGL14.eglMakeCurrent;
+import static android.opengl.GLES20.glDeleteTextures;
+import static android.opengl.GLES20.glGenTextures;
+
+import android.graphics.SurfaceTexture;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+
+public final class RenderTarget {
+    static final String TAG = "RenderTarget";
+
+    private static final int SETUP_THREAD = 1;
+    private static final int CREATE_SINK = 2;
+    private static final int DESTROY_SINK = 3;
+    private static final int UPDATE_TEX_IMAGE = 4;
+
+    private static final Handler sHandler;
+    static {
+        HandlerThread thread = new HandlerThread("RenderTarget-GL");
+        thread.start();
+        sHandler = new Handler(thread.getLooper(), new RenderTargetThread());
+        sHandler.sendEmptyMessage(SETUP_THREAD);
+    }
+
+    public static RenderTarget create() {
+        GenericFuture<RenderTarget> future = new GenericFuture<>();
+        Message.obtain(sHandler, CREATE_SINK, future).sendToTarget();
+        try {
+            return future.get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Failed to createSink()", e);
+        }
+    }
+
+    private final SurfaceTexture mSurfaceTexture;
+    private final int mGlTexId;
+    private Surface mSurface;
+
+    private RenderTarget(SurfaceTexture surfaceTexture, int glTexId) {
+        mSurfaceTexture = surfaceTexture;
+        mGlTexId = glTexId;
+        mSurface = new Surface(mSurfaceTexture);
+    }
+
+    public Surface getSurface() {
+        return mSurface;
+    }
+
+    public void setDefaultSize(int width, int height) {
+        mSurfaceTexture.setDefaultBufferSize(width, height);
+    }
+
+    public void destroy() {
+        mSurface = null;
+        Message.obtain(sHandler, DESTROY_SINK, this).sendToTarget();
+    }
+
+    private static class RenderTargetThread implements Handler.Callback,
+            SurfaceTexture.OnFrameAvailableListener {
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case SETUP_THREAD:
+                    setupThread();
+                    return true;
+                case CREATE_SINK:
+                    createSink((GenericFuture<RenderTarget>) msg.obj);
+                    return true;
+                case DESTROY_SINK:
+                    destroySink((RenderTarget) msg.obj);
+                    return true;
+                case UPDATE_TEX_IMAGE:
+                    updateTexImage((SurfaceTexture) msg.obj);
+                default:
+                    return false;
+            }
+        }
+
+        private void createSink(GenericFuture<RenderTarget> sinkFuture) {
+            int[] tex = new int[1];
+            glGenTextures(1, tex, 0);
+            SurfaceTexture texture = new SurfaceTexture(tex[0]);
+            texture.setOnFrameAvailableListener(this);
+            sinkFuture.setResult(new RenderTarget(texture, tex[0]));
+        }
+
+        private void destroySink(RenderTarget sink) {
+            sHandler.removeMessages(UPDATE_TEX_IMAGE, sink.mSurfaceTexture);
+            sink.mSurfaceTexture.setOnFrameAvailableListener(null);
+            sink.mSurfaceTexture.release();
+            glDeleteTextures(1, new int[] { sink.mGlTexId }, 0);
+        }
+
+        private void updateTexImage(SurfaceTexture texture) {
+            texture.updateTexImage();
+        }
+
+        private void setupThread() {
+            EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+            if (display == null) {
+                throw new IllegalStateException("eglGetDisplay failed");
+            }
+            int[] version = new int[2];
+            if (!eglInitialize(display, version, 0, version, 1)) {
+                throw new IllegalStateException("eglInitialize failed");
+            }
+            final int[] egl_attribs = new int[] {
+                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL_RED_SIZE, 8,
+                    EGL_GREEN_SIZE, 8,
+                    EGL_BLUE_SIZE, 8,
+                    EGL_ALPHA_SIZE, 8,
+                    EGL_DEPTH_SIZE, 0,
+                    EGL_CONFIG_CAVEAT, EGL_NONE,
+                    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+                    EGL_NONE
+            };
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] num_configs = new int[1];
+            if (!eglChooseConfig(display, egl_attribs, 0, configs, 0, 1, num_configs, 0)
+                    || num_configs[0] <= 0 || configs[0] == null) {
+                throw new IllegalStateException("eglChooseConfig failed");
+            }
+            EGLConfig config = configs[0];
+            final int[] gl_attribs = new int[] {
+                    EGL_CONTEXT_CLIENT_VERSION, 2,
+                    EGL_NONE
+            };
+            EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, gl_attribs, 0);
+            if (context == null) {
+                throw new IllegalStateException("eglCreateContext failed");
+            }
+            final int[] pbuffer_attribs = new int[] { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+            EGLSurface pbuffer = eglCreatePbufferSurface(display, config, pbuffer_attribs, 0);
+            if (pbuffer == null) {
+                throw new IllegalStateException("create pbuffer surface failed");
+            }
+            if (!eglMakeCurrent(display, pbuffer, pbuffer, context)) {
+                throw new IllegalStateException("Failed to make current");
+            }
+        }
+
+        @Override
+        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+            Log.i(TAG, "new frame available");
+            Message.obtain(sHandler, UPDATE_TEX_IMAGE, surfaceTexture).sendToTarget();
+        }
+    }
+
+    private static class GenericFuture<T> {
+        private boolean mHasResult = false;
+        private T mResult;
+        public void setResult(T result) {
+            synchronized (this) {
+                if (mHasResult) {
+                    throw new IllegalStateException("Result already set");
+                }
+                mHasResult = true;
+                mResult = result;
+                notifyAll();
+            }
+        }
+
+        public T get() throws InterruptedException {
+            synchronized (this) {
+                while (!mHasResult) {
+                    wait();
+                }
+                return mResult;
+            }
+        }
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 7b9cef0..a39941c 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -804,6 +804,12 @@
      before any existing test methods
      ***********************************************************/
 
+    @SecurityTest(minPatchLevel = "2018-06")
+    public void testBug_73552574() throws Exception {
+        int[] frameSizes = getFrameSizes(R.raw.bug_73552574_framelen);
+        doStagefrightTestRawBlob(R.raw.bug_73552574_avc, "video/avc", 320, 240, frameSizes);
+    }
+
     @SecurityTest(minPatchLevel = "2018-02")
     public void testStagefright_bug_68342866() throws Exception {
         Thread server = new Thread() {
@@ -845,7 +851,8 @@
                 mp.setOnErrorListener(mpcl);
                 mp.setOnPreparedListener(mpcl);
                 mp.setOnCompletionListener(mpcl);
-                Surface surface = getDummySurface();
+                RenderTarget renderTarget = RenderTarget.create();
+                Surface surface = renderTarget.getSurface();
                 mp.setSurface(surface);
                 AssetFileDescriptor fd = null;
                 try {
@@ -858,6 +865,7 @@
                 }
                 Looper.loop();
                 mp.release();
+                renderTarget.destroy();
             }
         });
         t.start();
@@ -1109,32 +1117,6 @@
         doStagefrightTestMediaPlayerANR(rid, null);
     }
 
-    private Surface getDummySurface() {
-        int[] textures = new int[1];
-        GLES20.glGenTextures(1, textures, 0);
-        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
-        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_MIN_FILTER,
-                GLES20.GL_NEAREST);
-        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_MAG_FILTER,
-                GLES20.GL_LINEAR);
-        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_WRAP_S,
-                GLES20.GL_CLAMP_TO_EDGE);
-        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_WRAP_T,
-                GLES20.GL_CLAMP_TO_EDGE);
-        SurfaceTexture surfaceTex = new SurfaceTexture(textures[0]);
-        surfaceTex.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
-            @Override
-            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
-                Log.i(TAG, "new frame available");
-            }
-        });
-        return new Surface(surfaceTex);
-    }
-
     public JSONArray getCrashReport(String testname, long timeout)
         throws InterruptedException {
         Log.i(TAG, CrashUtils.UPLOAD_REQUEST);
@@ -1297,7 +1279,8 @@
                 mp.setOnErrorListener(mpcl);
                 mp.setOnPreparedListener(mpcl);
                 mp.setOnCompletionListener(mpcl);
-                Surface surface = getDummySurface();
+                RenderTarget renderTarget = RenderTarget.create();
+                Surface surface = renderTarget.getSurface();
                 mp.setSurface(surface);
                 AssetFileDescriptor fd = null;
                 try {
@@ -1320,6 +1303,7 @@
 
                 Looper.loop();
                 mp.release();
+                renderTarget.destroy();
             }
         });
 
@@ -1440,9 +1424,10 @@
                 Log.i(TAG, "Decoding track " + t + " using codec " + codecName);
                 ex.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
                 MediaCodec codec = MediaCodec.createByCodecName(codecName);
+                RenderTarget renderTarget = RenderTarget.create();
                 Surface surface = null;
                 if (mime.startsWith("video/")) {
-                    surface = getDummySurface();
+                    surface = renderTarget.getSurface();
                 }
                 try {
                     codec.configure(format, surface, null, 0);
@@ -1484,6 +1469,7 @@
                     // local exceptions ignored, not security issues
                 } finally {
                     codec.release();
+                    renderTarget.destroy();
                 }
             }
             ex.unselectTrack(t);
@@ -1921,7 +1907,8 @@
                 mp.setOnErrorListener(mpl);
                 mp.setOnPreparedListener(mpl);
                 mp.setOnCompletionListener(mpl);
-                Surface surface = getDummySurface();
+                RenderTarget renderTarget = RenderTarget.create();
+                Surface surface = renderTarget.getSurface();
                 mp.setSurface(surface);
                 AssetFileDescriptor fd = null;
                 try {
@@ -1943,6 +1930,7 @@
 
                 Looper.loop();
                 mp.release();
+                renderTarget.destroy();
             }
         });
 
diff --git a/tests/tests/systemui/OWNERS b/tests/tests/systemui/OWNERS
index 958a89a..583baa0 100644
--- a/tests/tests/systemui/OWNERS
+++ b/tests/tests/systemui/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 136515
 evanlaird@google.com
+felkachang@google.com
+
diff --git a/tests/tests/systemui/src/android/systemui/cts/TouchHelper.java b/tests/tests/systemui/src/android/systemui/cts/TouchHelper.java
new file mode 100644
index 0000000..1ad74b9
--- /dev/null
+++ b/tests/tests/systemui/src/android/systemui/cts/TouchHelper.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.systemui.cts;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+public class TouchHelper {
+
+    private static final long REGULAR_CLICK_LENGTH = 100;
+    private final UiAutomation mUiAutomation;
+    private long mDownTime = 0;
+
+    public TouchHelper(Instrumentation instrumentation) {
+        mUiAutomation = instrumentation.getUiAutomation();
+    }
+
+    /**
+     * To click (x, y) without checking the boundary.
+     *
+     * @param x the x position on the screen
+     * @param y the y position on the screen
+     * @return true if the click event inject success, otherwise false
+     */
+    public boolean click(int x, int y) {
+        if (touchDown(x, y)) {
+            SystemClock.sleep(REGULAR_CLICK_LENGTH);
+            if (touchUp(x, y)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean touchDown(int x, int y) {
+        mDownTime = SystemClock.uptimeMillis();
+        MotionEvent event = getMotionEvent(mDownTime, mDownTime, MotionEvent.ACTION_DOWN, x, y);
+        return injectEventSync(event);
+    }
+
+    private boolean touchUp(int x, int y) {
+        final long eventTime = SystemClock.uptimeMillis();
+        MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_UP, x, y);
+        mDownTime = 0;
+        return injectEventSync(event);
+    }
+
+    private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
+            float x, float y) {
+
+        MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
+        properties.id = 0;
+        properties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+
+        MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+        coords.pressure = 1;
+        coords.size = 1;
+        coords.x = x;
+        coords.y = y;
+
+        return MotionEvent.obtain(downTime, eventTime, action, 1,
+                new MotionEvent.PointerProperties[] { properties },
+                new MotionEvent.PointerCoords[] { coords },
+                0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
+    }
+
+    private boolean injectEventSync(InputEvent event) {
+        return mUiAutomation.injectInputEvent(event, true);
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java
index 30ee9b8..7543c20 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java
@@ -16,11 +16,16 @@
 
 package android.systemui.cts;
 
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+
 import android.annotation.MainThread;
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,6 +40,7 @@
 
 public class WindowInsetsActivity extends LightBarBaseActivity implements View.OnClickListener,
         View.OnApplyWindowInsetsListener {
+    private static final int DISPLAY_CUTOUT_SLACK_DP = 20;
 
     private TextView mContent;
     private boolean mIsSetViewBound;
@@ -46,6 +52,7 @@
     private Consumer<Boolean> mInitialFinishCallBack;
     private int mClickCount;
     private Consumer<View> mClickConsumer;
+    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
 
 
     /**
@@ -71,12 +78,21 @@
         mContent.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
         mContent.getRootView().setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
         getWindow().getDecorView().setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
+        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        getWindow().getAttributes().layoutInDisplayCutoutMode
+                = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setStatusBarColor(0x22ff0000);
+        getWindow().setNavigationBarColor(0x22ff0000);
         mContent.setOnClickListener(this);
         mContent.requestApplyInsets();
         setContentView(mContent);
 
         mContentWindowInsets = getWindow().getDecorView().getRootWindowInsets();
         mInitialFinishCallBack = null;
+
+        getDisplay().getRealMetrics(mDisplayMetrics);
     }
 
     @Override
@@ -198,6 +214,21 @@
             sb.append("content boundary = ").append(mContentBound).append("\n");
         }
 
+        Display display = getDisplay();
+        if (display != null) {
+            sb.append("------------------------").append("\n");
+            DisplayCutout displayCutout = display.getCutout();
+            if (displayCutout != null) {
+                sb.append("displayCutout = ").append(displayCutout.toString()).append("\n");
+            } else {
+                sb.append("Display cut out = null\n");
+            }
+
+            sb.append("real size = (").append(mDisplayMetrics.widthPixels).append(",")
+                    .append(mDisplayMetrics.heightPixels).append(")\n");
+        }
+
+
         mContent.setText(sb.toString());
     }
 
@@ -217,30 +248,30 @@
     /**
      * To count the draggable boundary that has consume the related insets.
      **/
-
     @MainThread
-    public Rect getOperationArea(boolean insetMandatorySystemGesture,
-            boolean insetTappableElements, WindowInsets windowInsets) {
-
-        Insets insets;
-        if (insetMandatorySystemGesture) {
-            insets = windowInsets.getMandatorySystemGestureInsets();
-        } else if (insetTappableElements) {
-            insets = windowInsets.getTappableElementInsets();
-        } else {
-            insets = windowInsets.getSystemGestureInsets();
-        }
-
+    public Rect getOperationArea(Insets insets, WindowInsets windowInsets) {
         int left = insets.left;
-        int top = 0;
-        if (windowInsets.getStableInsetTop() >= insets.top) {
-            top = windowInsets.getStableInsetTop() - insets.top;
-        } else {
-            top = insets.top;
-        }
+        int top = insets.top;
         int right = insets.right;
         int bottom = insets.bottom;
 
+        final DisplayCutout cutout = windowInsets.getDisplayCutout();
+        if (cutout != null) {
+            int slack = (int) (DISPLAY_CUTOUT_SLACK_DP * mDisplayMetrics.density);
+            if (cutout.getSafeInsetLeft() > 0) {
+                left = Math.max(left, cutout.getSafeInsetLeft() + slack);
+            }
+            if (cutout.getSafeInsetTop() > 0) {
+                top = Math.max(top, cutout.getSafeInsetTop() + slack);
+            }
+            if (cutout.getSafeInsetRight() > 0) {
+                right = Math.max(right, cutout.getSafeInsetRight() + slack);
+            }
+            if (cutout.getSafeInsetBottom() > 0) {
+                bottom = Math.max(bottom, cutout.getSafeInsetBottom() + slack);
+            }
+        }
+
         Rect windowBoundary = getViewBound(getContentView());
         Rect rect = new Rect(windowBoundary);
         rect.left += left;
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
index 11d40a0..2ca4156 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
@@ -23,6 +23,7 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -30,6 +31,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
+import android.os.Bundle;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
@@ -63,9 +65,11 @@
     private static final String DEF_SCREENSHOT_BASE_PATH =
             "/sdcard/WindowInsetsBehaviorTests";
     private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
-    public static final int STEPS = 10;
-    public static final int DIP_INTERVAL = 40;
+    private static final String ARGUMENT_KEY_FORCE_ENABLE = "force_enable_gesture_navigation";
+    private static final int STEPS = 10;
+    private static final int DIP_INTERVAL = 40;
 
+    private final boolean mForceEnableGestureNavigation;
     private final Map<String, Boolean> mSystemGestureOptionsMap;
     private float mPixelsPerDp;
     private UiDevice mDevice;
@@ -73,6 +77,7 @@
     private String mEdgeToEdgeNavigationTitle;
     private String mSystemNavigationTitle;
     private String mGesturePreferenceTitle;
+    private TouchHelper mTouchHelper;
     private boolean mConfiguredInSettings;
 
     private static String getSettingsString(Resources res, String strResName) {
@@ -88,8 +93,16 @@
      * To initial all of options in System Gesture.
      */
     public WindowInsetsBehaviorTests() {
+        Bundle bundle = InstrumentationRegistry.getArguments();
+        mForceEnableGestureNavigation = (bundle != null)
+                && "true".equalsIgnoreCase(bundle.getString(ARGUMENT_KEY_FORCE_ENABLE));
+
         mSystemGestureOptionsMap = new ArrayMap();
 
+        if (!mForceEnableGestureNavigation) {
+            return;
+        }
+
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         PackageManager packageManager = context.getPackageManager();
         Resources res = null;
@@ -173,14 +186,17 @@
         return mDevice.findObject(targetSelector);
     }
 
-
     private boolean launchToSettingsSystemGesture() {
+        if (!mForceEnableGestureNavigation) {
+            return false;
+        }
+
         /* launch to the close to the system gesture fragment */
-        Intent intent = new Intent();
-        intent.setClassName("com.android.settings",
-                "com.android.settings.Settings$SystemDashboardActivity");
-        intent.putExtra(":settings:show_fragment",
-                "com.android.settings.gestures.GestureSettings");
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        ComponentName settingComponent = new ComponentName(SETTINGS_PACKAGE_NAME,
+                String.format("%s.%s$%s", SETTINGS_PACKAGE_NAME, "Settings",
+                        "SystemDashboardActivity"));
+        intent.setComponent(settingComponent);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         mTargetContext.startActivity(intent);
 
@@ -230,6 +246,7 @@
     @Before
     public void setUp() throws Exception {
         mDevice = UiDevice.getInstance(getInstrumentation());
+        mTouchHelper = new TouchHelper(getInstrumentation());
         mTargetContext = getInstrumentation().getTargetContext();
         if (!hasSystemGestureFeature()) {
             return;
@@ -303,7 +320,10 @@
         mActivity.setOnClickConsumer((view) -> {
             latch.countDown();
         });
-        mDevice.click(p.x, p.y);
+        // mDevice.click(p.x, p.y) has the limitation without consideration of the cutout
+        if (!mTouchHelper.click(p.x, p.y)) {
+            fail("Can't inject event at" + p);
+        }
 
         /* wait until the OnClickListener triggered, and then click the next point */
         try {
@@ -313,7 +333,7 @@
         }
 
         if (latch.getCount() > 0) {
-            fail("Doesn't receive onClickEvent");
+            fail("Doesn't receive onClickEvent at " + p);
         }
     }
 
@@ -514,8 +534,9 @@
 
         mainThreadRun(() -> mActivity.setSystemGestureExclusion(true));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(true,
-                    false, mContentViewWindowInsets));
+        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+                mContentViewWindowInsets.getMandatorySystemGestureInsets(),
+                mContentViewWindowInsets));
 
         int dragCount = dragInViewBoundary(mDragBound);
 
@@ -532,35 +553,13 @@
     }
 
     @Test
-    public void mandatorySystemGesture_tapSamplePoints_excludeViewRects_withoutAnyCancel() {
-        assumeTrue(hasSystemGestureFeature());
-
-        mainThreadRun(() -> mActivity.setSystemGestureExclusion(true));
-        mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(false,
-                true, mContentViewWindowInsets));
-
-        int count = clickAllOfSamplePoints(mDragBound, this::clickAndWaitByUiDevice);
-
-        mainThreadRun(() -> {
-            mClickCount = mActivity.getClickCount();
-            mActionCancelPoints = mActivity.getActionCancelPoints();
-        });
-        mScreenshotTestRule.capture();
-
-        assertEquals("The number of click not match", count, mClickCount);
-        assertEquals("The Number of the canceled points not match", 0,
-                mActionCancelPoints.size());
-    }
-
-    @Test
     public void systemGesture_notExcludeViewRects_withoutAnyCancel() {
         assumeTrue(hasSystemGestureFeature());
 
         mainThreadRun(() -> mActivity.setSystemGestureExclusion(false));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(false,
-                false, mContentViewWindowInsets));
+        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+                mContentViewWindowInsets.getSystemGestureInsets(), mContentViewWindowInsets));
 
         int dragCount = dragInViewBoundary(mDragBound);
 
@@ -577,13 +576,13 @@
     }
 
     @Test
-    public void systemGesture_tapSamplePoints_notExcludeViewRects_withoutAnyCancel() {
+    public void tappableElements_tapSamplePoints_excludeViewRects_withoutAnyCancel() {
         assumeTrue(hasSystemGestureFeature());
 
-        mainThreadRun(() -> mActivity.setSystemGestureExclusion(false));
+        mainThreadRun(() -> mActivity.setSystemGestureExclusion(true));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(false,
-                true, mContentViewWindowInsets));
+        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+                mContentViewWindowInsets.getTappableElementInsets(), mContentViewWindowInsets));
 
         int count = clickAllOfSamplePoints(mDragBound, this::clickAndWaitByUiDevice);
 
@@ -598,19 +597,14 @@
                 mActionCancelPoints.size());
     }
 
-    /**
-     * To tap all possible points except the system gesture area and tappable elements area.
-     * Caution: don't touch system gesture area because it still exists and will trigger cancel
-     * event.
-     */
     @Test
     public void tappableElements_tapSamplePoints_notExcludeViewRects_withoutAnyCancel() {
         assumeTrue(hasSystemGestureFeature());
 
         mainThreadRun(() -> mActivity.setSystemGestureExclusion(false));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(false,
-                true, mContentViewWindowInsets));
+        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+                mContentViewWindowInsets.getTappableElementInsets(), mContentViewWindowInsets));
 
         int count = clickAllOfSamplePoints(mDragBound, this::clickAndWaitByUiDevice);
 
diff --git a/tests/tests/telephony/current/AndroidManifest.xml b/tests/tests/telephony/current/AndroidManifest.xml
index ec800087..49829ee 100644
--- a/tests/tests/telephony/current/AndroidManifest.xml
+++ b/tests/tests/telephony/current/AndroidManifest.xml
@@ -20,6 +20,7 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
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 96c4129..e8b734d 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -24,6 +24,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.Manifest;
+import android.Manifest.permission;
+import android.app.UiAutomation;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Parcel;
@@ -54,6 +57,8 @@
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -214,6 +219,7 @@
         mPm = getContext().getPackageManager();
         Pair<Integer, Integer> verPair = mTm.getRadioHalVersion();
         mRadioHalVersion = makeRadioVersion(verPair.first, verPair.second);
+        TelephonyManagerTest.grantLocationPermissions();
     }
 
     /**
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellLocationTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellLocationTest.java
index 145d909..fc97d48 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellLocationTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellLocationTest.java
@@ -66,6 +66,8 @@
             return;
         }
 
+        TelephonyManagerTest.grantLocationPermissions();
+
         // getCellLocation should never return null,
         // but that is allowed if the cell network type
         // is LTE (since there is no LteCellLocation class)
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/NetworkServiceTest.java b/tests/tests/telephony/current/src/android/telephony/cts/NetworkServiceTest.java
new file mode 100644
index 0000000..07a71bf
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/NetworkServiceTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.telephony.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.NetworkService;
+import android.telephony.NetworkService.NetworkServiceProvider;
+
+import org.junit.Test;
+
+public class NetworkServiceTest {
+
+    private static final int SLOT_INDEX_CTS = 1;
+
+    private static class CtsNetworkService extends NetworkService {
+        private class CtsNetworkServiceProvider extends NetworkServiceProvider {
+            CtsNetworkServiceProvider(int slotId) {
+                super(slotId);
+            }
+
+            @Override
+            public void close() {}
+        }
+
+        @Override
+        public NetworkServiceProvider onCreateNetworkServiceProvider(int slotIndex) {
+            return new CtsNetworkServiceProvider(slotIndex);
+        }
+    }
+
+    @Test
+    public void testNetworkService() {
+        CtsNetworkService networkService = new CtsNetworkService();
+        NetworkServiceProvider networkServiceProvider =
+                networkService.onCreateNetworkServiceProvider(SLOT_INDEX_CTS);
+        assertEquals(SLOT_INDEX_CTS, networkServiceProvider.getSlotIndex());
+
+        networkServiceProvider.notifyNetworkRegistrationInfoChanged();
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
index 8c29609..7ffcd3c 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -722,6 +722,8 @@
             return;
         }
 
+        TelephonyManagerTest.grantLocationPermissions();
+
         TestThread t = new TestThread(new Runnable() {
             public void run() {
                 Looper.prepare();
@@ -807,14 +809,20 @@
                     public void onDataConnectionStateChanged(int state) {
                         synchronized(mLock) {
                             mOnDataConnectionStateChangedCalled = true;
-                            mLock.notify();
+                            if (mOnDataConnectionStateChangedCalled
+                                    && mOnDataConnectionStateChangedWithNetworkTypeCalled) {
+                                mLock.notify();
+                            }
                         }
                     }
                     @Override
                     public void onDataConnectionStateChanged(int state, int networkType) {
                         synchronized(mLock) {
                             mOnDataConnectionStateChangedWithNetworkTypeCalled = true;
-                            mLock.notify();
+                            if (mOnDataConnectionStateChangedCalled
+                                    && mOnDataConnectionStateChangedWithNetworkTypeCalled) {
+                                mLock.notify();
+                            }
                         }
                     }
                 };
@@ -885,6 +893,8 @@
             return;
         }
 
+        TelephonyManagerTest.grantLocationPermissions();
+
         TestThread t = new TestThread(new Runnable() {
             public void run() {
                 Looper.prepare();
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 24c39b7..5e4abf4 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -94,6 +94,8 @@
 
     @BeforeClass
     public static void setUpClass() throws Exception {
+        if (!isSupported()) return;
+
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand("svc wifi disable");
 
@@ -116,12 +118,16 @@
 
     @AfterClass
     public static void tearDownClass() throws Exception {
+        if (!isSupported()) return;
+
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand("svc wifi enable");
     }
 
     @Before
     public void setUp() throws Exception {
+        if (!isSupported()) return;
+
         mSm = InstrumentationRegistry.getContext().getSystemService(SubscriptionManager.class);
         mSubId = SubscriptionManager.getDefaultDataSubscriptionId();
         mPackageName = InstrumentationRegistry.getContext().getPackageName();
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 57394d7..1536053 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -26,6 +26,8 @@
 import static org.junit.Assert.fail;
 
 import android.Manifest;
+import android.Manifest.permission;
+import android.app.UiAutomation;
 import android.bluetooth.BluetoothAdapter;
 import android.content.ComponentName;
 import android.content.Context;
@@ -40,7 +42,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -73,6 +74,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -131,6 +133,30 @@
             TelephonyManager.NETWORK_TYPE_LTE_CA,
             TelephonyManager.NETWORK_TYPE_NR);
 
+    private static final int EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST = 0;
+    private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+    static {
+        EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
+        EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
+    }
+
+    private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
+    static {
+        EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
+    }
+
     @Before
     public void setUp() throws Exception {
         mTelephonyManager =
@@ -149,6 +175,14 @@
         }
     }
 
+    public static void grantLocationPermissions() {
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        String packageName = getContext().getPackageName();
+        uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_COARSE_LOCATION);
+        uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_FINE_LOCATION);
+        uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_BACKGROUND_LOCATION);
+    }
+
     @Test
     public void testListen() throws Throwable {
         if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
@@ -161,6 +195,8 @@
             return;
         }
 
+        grantLocationPermissions();
+
         TestThread t = new TestThread(new Runnable() {
             public void run() {
                 Looper.prepare();
@@ -1245,8 +1281,10 @@
 
         assertFalse(emergencyNumberList == null);
 
+        checkEmergencyNumberFormat(emergencyNumberList);
+
         int defaultSubId = mSubscriptionManager.getDefaultSubscriptionId();
-      
+
         // 112 and 911 should always be available
         // Reference: 3gpp 22.101, Section 10 - Emergency Calls
         assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
@@ -1422,6 +1460,48 @@
         return false;
     }
 
+    private static void checkEmergencyNumberFormat(
+            Map<Integer, List<EmergencyNumber>> emergencyNumberLists) {
+        for (List<EmergencyNumber> emergencyNumberList : emergencyNumberLists.values()) {
+            for (EmergencyNumber emergencyNumber : emergencyNumberList) {
+
+                // Validate Emergency number address
+                assertTrue(validateEmergencyNumberAddress(emergencyNumber.getNumber()));
+
+                // Validate Emergency number country Iso
+                assertTrue(validateEmergencyNumberCountryIso(emergencyNumber.getCountryIso()));
+
+                // Validate Emergency number mnc
+                assertTrue(validateEmergencyNumberMnc(emergencyNumber.getMnc()));
+
+                // Validate Emergency service category list
+                assertTrue(validateEmergencyServiceCategoryList(
+                        emergencyNumber.getEmergencyServiceCategories()));
+
+                // Validate Emergency number source list
+                assertTrue(validateEmergencyNumberSourceList(
+                        emergencyNumber.getEmergencyNumberSources()));
+
+                // Validate Emergency URN list
+                // (just verify it is not null, because the support of this field is optional)
+                assertTrue(emergencyNumber.getEmergencyUrns() != null);
+
+                // Validat Emergency call routing
+                assertTrue(validateEmergencyCallRouting(
+                        emergencyNumber.getEmergencyCallRouting()));
+
+                // Valid the emergency number should be at least in a valid source.
+                assertTrue(validateEmergencyNumberFromAnySource(emergencyNumber));
+
+                // Valid the emergency number should be at least in a valid category.
+                assertTrue(validateEmergencyNumberInAnyCategory(emergencyNumber));
+            }
+
+            // Validate compareTo
+            assertTrue(validateEmergencyNumberCompareTo(emergencyNumberList));
+        }
+    }
+
     /**
      * Tests {@link TelephonyManager#updateAvailableNetworks}
      */
@@ -1557,6 +1637,169 @@
         }
     }
 
+    /**
+     * Validate Emergency Number address that only contains the dialable character.
+     *
+     * @param address Emergency number address to validate
+     * @return {@code true} if the address is valid; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyNumberAddress(String address) {
+        if (address == null) {
+            return false;
+        }
+        for (char c : address.toCharArray()) {
+            if (!isDialable(c)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Validate Emergency Number country Iso
+     *
+     * @param countryIso Emergency number country iso to validate
+     * @return {@code true} if the country iso is valid; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyNumberCountryIso(String countryIso) {
+        if (countryIso == null) {
+            return false;
+        }
+        int length = countryIso.length();
+        return length >= 0 && length <= 2;
+    }
+
+    /**
+     * Validate Emergency Number MNC
+     *
+     * @param mnc Emergency number MNC to validate
+     * @return {@code true} if the MNC is valid; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyNumberMnc(String mnc) {
+        if (mnc == null) {
+            return false;
+        }
+        int length = mnc.length();
+        return length >= 0 && length <= 3;
+    }
+
+    /**
+     * Validate Emergency service category list
+     *
+     * @param categories Emergency service category list to validate
+     * @return {@code true} if the category list is valid; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyServiceCategoryList(List<Integer> categories) {
+        if (categories == null) {
+            return false;
+        }
+        if (categories.contains(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) {
+            return categories.size() == 1;
+        }
+        for (int category : categories) {
+            if (!EMERGENCY_SERVICE_CATEGORY_SET.contains(category)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Validate Emergency number source list
+     *
+     * @param categories Emergency number source list to validate
+     * @return {@code true} if the source list is valid; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyNumberSourceList(List<Integer> sources) {
+        if (sources == null) {
+            return false;
+        }
+        for (int source : sources) {
+            if (!EMERGENCY_NUMBER_SOURCE_SET.contains(source)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Validate Emergency call routing.
+     *
+     * @param routing Emergency call routing to validate
+     * @return {@code true} if the emergency call routing is valid; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyCallRouting(int routing) {
+        return routing >= EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN
+                && routing <= (EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY
+                | EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+    }
+
+    /**
+     * Valid the emergency number should be at least from a valid source.
+     *
+     * @param emergencyNumber Emergency number to verify
+     * @return {@code true} if the emergency number is from any source; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyNumberFromAnySource(EmergencyNumber emergencyNumber) {
+        boolean isFromAnySource = false;
+        for (int possibleSourceValue = EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST;
+                possibleSourceValue <= (EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
+                possibleSourceValue++) {
+            if (emergencyNumber.isFromSources(possibleSourceValue)) {
+                isFromAnySource = true;
+                break;
+            }
+        }
+        return isFromAnySource;
+    }
+
+    /**
+     * Valid the emergency number should be at least in a valid category.
+     *
+     * @param emergencyNumber Emergency number to verify
+     * @return {@code true} if it is in any category; {@code false} otherwise.
+     */
+    private static boolean validateEmergencyNumberInAnyCategory(EmergencyNumber emergencyNumber) {
+        boolean isInAnyCategory = false;
+        for (int possibleCategoryValue = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+                possibleCategoryValue <= (EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
+                possibleCategoryValue++) {
+            if (emergencyNumber.isInEmergencyServiceCategories(possibleCategoryValue)) {
+                isInAnyCategory = true;
+                break;
+            }
+        }
+        return isInAnyCategory;
+    }
+
+    private static boolean validateEmergencyNumberCompareTo(
+            List<EmergencyNumber> emergencyNumberList) {
+        if (emergencyNumberList == null) {
+            return false;
+        }
+        if (emergencyNumberList.size() > 0) {
+            EmergencyNumber emergencyNumber = emergencyNumberList.get(0);
+            if (emergencyNumber.compareTo(emergencyNumber) != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isDialable(char c) {
+        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N';
+    }
+
     private int getValidSlotIndex() {
         return ShellIdentityUtils.invokeMethodWithShellPermissions(
                 mTelephonyManager, (tm) -> {
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png b/tests/tests/uirendering/res/drawable-nodpi/vector_drawable_scale_golden.png
similarity index 100%
rename from tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
rename to tests/tests/uirendering/res/drawable-nodpi/vector_drawable_scale_golden.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable/vector_icon_create.xml b/tests/tests/uirendering/res/drawable/vector_icon_create.xml
new file mode 100644
index 0000000..55113f3
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable/vector_icon_create.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:opticalInsetLeft="1px"
+        android:opticalInsetTop="2px"
+        android:opticalInsetRight="3px"
+        android:opticalInsetBottom="4px"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75-3.75L3.0,17.25zM20.707,7.0429993c0.391-0.391 0.391-1.023 0.0-1.414l-2.336-2.336c-0.391-0.391-1.023-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/layout/vector_drawable_scale_layout.xml b/tests/tests/uirendering/res/layout/vector_drawable_scale_layout.xml
similarity index 100%
rename from tests/tests/graphics/res/layout/vector_drawable_scale_layout.xml
rename to tests/tests/uirendering/res/layout/vector_drawable_scale_layout.xml
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
index e65ff2d..c8a1ebd 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
@@ -40,6 +40,17 @@
         mThreshold = threshold;
     }
 
+    /**
+     * Compute the size of the window. The window defaults to WINDOW_SIZE, but
+     * must be contained within dimension.
+     */
+    private int computeWindowSize(int coordinateStart, int dimension) {
+        if (coordinateStart + WINDOW_SIZE <= dimension) {
+            return WINDOW_SIZE;
+        }
+        return dimension - coordinateStart;
+    }
+
     @Override
     public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
             int height) {
@@ -47,16 +58,20 @@
         int windows = 0;
 
         for (int currentWindowY = 0 ; currentWindowY < height ; currentWindowY += WINDOW_SIZE) {
+            int windowHeight = computeWindowSize(currentWindowY, height);
             for (int currentWindowX = 0 ; currentWindowX < width ; currentWindowX += WINDOW_SIZE) {
+                int windowWidth = computeWindowSize(currentWindowX, width);
                 int start = indexFromXAndY(currentWindowX, currentWindowY, stride, offset);
-                if (isWindowWhite(ideal, start, stride) && isWindowWhite(given, start, stride)) {
+                if (isWindowWhite(ideal, start, stride, windowWidth, windowHeight)
+                        && isWindowWhite(given, start, stride, windowWidth, windowHeight)) {
                     continue;
                 }
                 windows++;
-                double[] means = getMeans(ideal, given, start, stride);
+                double[] means = getMeans(ideal, given, start, stride, windowWidth, windowHeight);
                 double meanX = means[0];
                 double meanY = means[1];
-                double[] variances = getVariances(ideal, given, meanX, meanY, start, stride);
+                double[] variances = getVariances(ideal, given, meanX, meanY, start, stride,
+                        windowWidth, windowHeight);
                 double varX = variances[0];
                 double varY = variances[1];
                 double stdBoth = variances[2];
@@ -76,9 +91,10 @@
         return (SSIMTotal >= mThreshold);
     }
 
-    private boolean isWindowWhite(int[] colors, int start, int stride) {
-        for (int y = 0 ; y < WINDOW_SIZE ; y++) {
-            for (int x = 0 ; x < WINDOW_SIZE ; x++) {
+    private boolean isWindowWhite(int[] colors, int start, int stride,
+            int windowWidth, int windowHeight) {
+        for (int y = 0; y < windowHeight; y++) {
+            for (int x = 0; x < windowWidth; x++) {
                 if (colors[indexFromXAndY(x, y, stride, start)] != Color.WHITE) {
                     return false;
                 }
@@ -101,18 +117,19 @@
      * where the first double is the mean of the first set and the second double is the mean of the
      * second set.
      */
-    private double[] getMeans(int[] pixels0, int[] pixels1, int start, int stride) {
+    private double[] getMeans(int[] pixels0, int[] pixels1, int start, int stride,
+            int windowWidth, int windowHeight) {
         double avg0 = 0;
         double avg1 = 0;
-        for (int y = 0 ; y < WINDOW_SIZE ; y++) {
-            for (int x = 0 ; x < WINDOW_SIZE ; x++) {
+        for (int y = 0; y < windowHeight; y++) {
+            for (int x = 0; x < windowWidth; x++) {
                 int index = indexFromXAndY(x, y, stride, start);
                 avg0 += getIntensity(pixels0[index]);
                 avg1 += getIntensity(pixels1[index]);
             }
         }
-        avg0 /= WINDOW_SIZE * WINDOW_SIZE;
-        avg1 /= WINDOW_SIZE * WINDOW_SIZE;
+        avg0 /= windowWidth * windowHeight;
+        avg1 /= windowWidth * windowHeight;
         return new double[] {avg0, avg1};
     }
 
@@ -122,12 +139,12 @@
      * the second is the variance of the second set of pixels, and the third is the covariance.
      */
     private double[] getVariances(int[] pixels0, int[] pixels1, double mean0, double mean1,
-            int start, int stride) {
+            int start, int stride, int windowWidth, int windowHeight) {
         double var0 = 0;
         double var1 = 0;
         double varBoth = 0;
-        for (int y = 0 ; y < WINDOW_SIZE ; y++) {
-            for (int x = 0 ; x < WINDOW_SIZE ; x++) {
+        for (int y = 0; y < windowHeight; y++) {
+            for (int x = 0; x < windowWidth; x++) {
                 int index = indexFromXAndY(x, y, stride, start);
                 double v0 = getIntensity(pixels0[index]) - mean0;
                 double v1 = getIntensity(pixels1[index]) - mean1;
@@ -136,9 +153,9 @@
                 varBoth += v0 * v1;
             }
         }
-        var0 /= (WINDOW_SIZE * WINDOW_SIZE) - 1;
-        var1 /= (WINDOW_SIZE * WINDOW_SIZE) - 1;
-        varBoth /= (WINDOW_SIZE * WINDOW_SIZE) - 1;
+        var0 /= (windowWidth * windowHeight) - 1;
+        var1 /= (windowWidth * windowHeight) - 1;
+        varBoth /= (windowWidth * windowHeight) - 1;
         return new double[] {var0, var1, varBoth};
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index b9816db..ac1fa0e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -20,16 +20,18 @@
 import android.graphics.BitmapFactory;
 import android.uirendering.cts.bitmapcomparers.BitmapComparer;
 import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
-import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 
 public class GoldenImageVerifier extends BitmapVerifier {
-    private BitmapComparer mBitmapComparer;
-    private int[] mGoldenBitmapArray;
+    private final BitmapComparer mBitmapComparer;
+    private final int[] mGoldenBitmapArray;
+    private final int mWidth;
+    private final int mHeight;
 
     public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
-        mGoldenBitmapArray = new int[ActivityTestBase.TEST_WIDTH * ActivityTestBase.TEST_HEIGHT];
-        goldenBitmap.getPixels(mGoldenBitmapArray, 0, ActivityTestBase.TEST_WIDTH, 0, 0,
-                ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        mWidth = goldenBitmap.getWidth();
+        mHeight = goldenBitmap.getHeight();
+        mGoldenBitmapArray = new int[mWidth * mHeight];
+        goldenBitmap.getPixels(mGoldenBitmapArray, 0, mWidth, 0, 0, mWidth, mHeight);
         mBitmapComparer = bitmapComparer;
     }
 
@@ -38,6 +40,15 @@
     }
 
     @Override
+    public boolean verify(Bitmap bitmap) {
+        // Clip to the size of the golden image.
+        if (bitmap.getWidth() > mWidth || bitmap.getHeight() > mHeight) {
+            bitmap = Bitmap.createBitmap(bitmap, 0, 0, mWidth, mHeight);
+        }
+        return super.verify(bitmap);
+    }
+
+    @Override
     public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
         boolean success = mBitmapComparer.verifySame(mGoldenBitmapArray, bitmap, offset, stride,
                 width, height);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/blendmode/BitmapBlendModeCanvasClient.java b/tests/tests/uirendering/src/android/uirendering/cts/blendmode/BitmapBlendModeCanvasClient.java
new file mode 100644
index 0000000..5402169
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/blendmode/BitmapBlendModeCanvasClient.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.uirendering.cts.blendmode;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+
+/**
+ * CanvasClient used to draw bitmaps with the clear and dstover blend modes to verify
+ * that backward compatible behavior is respected on API levels 27 and older where clear is treated
+ * as dst out for drawing bitmaps. However, on newer API levels, the clear blend mode is
+ * properly respected
+ */
+public class BitmapBlendModeCanvasClient implements CanvasClient {
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    /**
+     * Color used to render the circle in the mask bitmap
+     */
+    private final int mBitmapColor;
+
+    /**
+     * Color used to draw tint the area overlapping the content in the mask
+     */
+    private final int mMaskColor;
+
+    /**
+     * PorterDuff mode used to mask out the content in the bitmap mask
+     */
+    private final PorterDuff.Mode mBitmapMode;
+
+    /**
+     * PorterDuff moe used to render over the content in the bitmap mask
+     */
+    private final PorterDuff.Mode mMaskMode;
+
+    public BitmapBlendModeCanvasClient(PorterDuff.Mode bitmapMode, PorterDuff.Mode maskMode,
+            int bitmapColor, int maskColor) {
+        mBitmapMode = bitmapMode;
+        mMaskMode = maskMode;
+        mBitmapColor = bitmapColor;
+        mMaskColor = maskColor;
+    }
+
+    @Override
+    public void draw(Canvas canvas, int width, int height) {
+        Bitmap bitmap = createBitmap(width, height);
+
+        mPaint.setXfermode(new PorterDuffXfermode(mBitmapMode));
+        canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
+
+        mPaint.setXfermode(new PorterDuffXfermode(mMaskMode));
+        mPaint.setColor(mMaskColor);
+        canvas.drawRect(0, 0, width, height, mPaint);
+    }
+
+    private Bitmap createBitmap(int width, int height) {
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        paint.setColor(mBitmapColor);
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        canvas.drawCircle(
+                width / 2.0f,
+                height / 2.0f,
+                Math.min(width, height) / 2.0f,
+                paint);
+        return bitmap;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapBlendModeTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapBlendModeTest.java
new file mode 100644
index 0000000..e5cf2d1
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapBlendModeTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.uirendering.cts.testclasses;
+
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.blendmode.BitmapBlendModeCanvasClient;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+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 BitmapBlendModeTest extends ActivityTestBase {
+
+    private static final Point[] SAMPLE_POINTS = {
+            new Point(0, 0),
+            new Point(TEST_WIDTH - 1, 0),
+            new Point(0, TEST_HEIGHT - 1),
+            new Point(TEST_WIDTH - 1, TEST_HEIGHT - 1),
+            new Point(TEST_WIDTH / 2, TEST_HEIGHT / 2)
+    };
+
+    @Test
+    public void testClearBlendMode() {
+        // Verify that using CLEAR for rendering a bitmap actually clears all the pixels within
+        // the rectangular bounds of the bitmap itself.
+        // The output image here should be entirely red even though the original mask bitmap
+        // is drawn with a blue circle
+        int[] colors = {Color.RED, Color.RED, Color.RED, Color.RED, Color.RED};
+        BitmapBlendModeCanvasClient client = new BitmapBlendModeCanvasClient(
+                PorterDuff.Mode.CLEAR, PorterDuff.Mode.DST_OVER,
+                Color.BLUE, Color.RED);
+        createTest().addCanvasClientWithoutUsingPicture(client, true)
+                .runWithVerifier(new SamplePointVerifier(SAMPLE_POINTS, colors));
+    }
+
+    @Test
+    public void testDstOutBlendMode() {
+        // Verify that using DST_OUT for rendering a bitmap only clears the overlapping
+        // pixels of the bitmap from the destination.
+        // The output image here should be a red circle with a white background even though
+        // the original mask bitmap is drawn with a blue circle
+        int[] colors = {Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.RED};
+        BitmapBlendModeCanvasClient client = new BitmapBlendModeCanvasClient(
+                PorterDuff.Mode.DST_OUT, PorterDuff.Mode.DST_OVER,
+                Color.BLUE, Color.RED);
+        createTest().addCanvasClientWithoutUsingPicture(client, true)
+                .runWithVerifier(new SamplePointVerifier(SAMPLE_POINTS, colors));
+    }
+}
+
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 20f1fda..f1acc16 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -165,7 +165,7 @@
                     view.setScaleX(2);
                     view.setScaleY(2);
                 })
-                .runWithComparer(new MSSIMComparer(0.90));
+                .runWithComparer(new MSSIMComparer(0.87));
     }
 
     @Test
@@ -220,6 +220,6 @@
                         initBlueWebView(hwFence), true, hwFence)
                 .addLayout(R.layout.circle_clipped_webview,
                         initBlueWebView(swFence), false, swFence)
-                .runWithComparer(new MSSIMComparer(0.95));
+                .runWithComparer(new MSSIMComparer(0.94));
     }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableScaleTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableScaleTest.java
new file mode 100644
index 0000000..2362a78
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableScaleTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.content.Context;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+@MediumTest
+public class VectorDrawableScaleTest extends ActivityTestBase {
+    @Test
+    public void testVectorDrawableInImageView() {
+        final Context context = getInstrumentation().getTargetContext();
+        createTest()
+                .addLayout(R.layout.vector_drawable_scale_layout, view-> {
+                    setupImageViews(view);
+                }, false /* not HW only*/)
+                .runWithVerifier(new GoldenImageVerifier(context,
+                      R.drawable.vector_drawable_scale_golden,
+                      new MSSIMComparer(.87f)));
+    }
+
+    // Setup 2 imageviews, one big and one small. The purpose of this test is to make sure that the
+    // imageview with smaller scale will not affect the appearance in the imageview with larger
+    // scale.
+    private static void setupImageViews(View view) {
+        ImageView imageView = (ImageView) view.findViewById(R.id.imageview1);
+        imageView.setImageResource(R.drawable.vector_icon_create);
+        imageView = (ImageView) view.findViewById(R.id.imageview2);
+        imageView.setImageResource(R.drawable.vector_icon_create);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index 14fbc0f..6c29c18 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -110,7 +110,8 @@
 
     @Test
     public void testOvalOutlineClip() {
-        // In hw this works because clipping to a non-round rect isn't supported, and is no-op'd.
+        // In hw this works because clipping to an arbitrary path (i.e. not a rectangle, round rect
+        // or circle) isn't supported, and is no-op'd.
         // In sw this works because Outline clipping isn't supported.
         createTest()
                 .addLayout(R.layout.blue_padded_layout, view -> {
@@ -122,10 +123,35 @@
                             mPath.addOval(0, 0, view.getWidth(), view.getHeight(),
                                     Path.Direction.CW);
                             outline.setConvexPath(mPath);
-                            assertFalse(outline.canClip()); // NOTE: non-round-rect, so can't clip
+                            assertFalse(outline.canClip());
                         }
                     });
-                    view.setClipToOutline(true); // should do nothing, since non-rect clip
+                    view.setClipToOutline(true); // should do nothing
+                })
+                .runWithVerifier(makeClipVerifier(FULL_RECT));
+    }
+
+    @Test
+    public void testConcaveOutlineClip() {
+        // As of Q, Outline#setConvexPath no longer throws on a concave path, but as above, it does
+        // not result in clipping. (hw no-op's the arbitrary path, and sw doesn't support Outline
+        // clipping.)
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, view -> {
+                    view.setOutlineProvider(new ViewOutlineProvider() {
+                        Path mPath = new Path();
+                        @Override
+                        public void getOutline(View view, Outline outline) {
+                            mPath.reset();
+                            mPath.addRect(0, 0, 10, 100, Path.Direction.CW);
+                            mPath.addRect(0, 0, 100, 10, Path.Direction.CW);
+                            assertFalse(mPath.isConvex());
+
+                            outline.setConvexPath(mPath);
+                            assertFalse(outline.canClip());
+                        }
+                    });
+                    view.setClipToOutline(true); // should do nothing
                 })
                 .runWithVerifier(makeClipVerifier(FULL_RECT));
     }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewFadingEdgeTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewFadingEdgeTests.java
new file mode 100644
index 0000000..81b1586
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewFadingEdgeTests.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 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.uirendering.cts.testclasses;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewFadingEdgeTests extends ActivityTestBase {
+    class FadingView extends View {
+        public boolean mEnableFadingLeftEdge = false;
+        public boolean mEnableFadingRightEdge = false;
+        public boolean mEnableFadingTopEdge = false;
+        public boolean mEnableFadingBottomEdge = false;
+
+        FadingView(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            canvas.drawColor(Color.RED);
+        }
+
+        @Override
+        protected float getLeftFadingEdgeStrength() {
+            return mEnableFadingLeftEdge ? 1.0f : 0.0f;
+        }
+
+        @Override
+        protected float getRightFadingEdgeStrength() {
+            return mEnableFadingRightEdge ? 1.0f : 0.0f;
+        }
+
+        @Override
+        protected float getTopFadingEdgeStrength() {
+            return mEnableFadingTopEdge ? 1.0f : 0.0f;
+        }
+
+        @Override
+        protected float getBottomFadingEdgeStrength() {
+            return mEnableFadingBottomEdge ? 1.0f : 0.0f;
+        }
+    }
+
+    private FadingView attachFadingView(View parentView) {
+        final FadingView child = new FadingView(parentView.getContext());
+        child.setLayoutParams(new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+
+        FrameLayout root = (FrameLayout) parentView.findViewById(R.id.frame_layout);
+        root.addView(child);
+        return child;
+    }
+
+    @Test
+    public void testCreateLeftFade() {
+        createTest()
+                .addLayout(R.layout.frame_layout, (ViewInitializer) view -> {
+                    final FadingView child = attachFadingView(view);
+                    child.mEnableFadingLeftEdge = true;
+                    child.setFadingEdgeLength(TEST_WIDTH / 2);
+                    child.setHorizontalFadingEdgeEnabled(true);
+                    Assert.assertEquals(0, child.getSolidColor());
+                })
+                .runWithVerifier(new SamplePointVerifier(new Point[] {
+                        new Point(0, 0),
+                        new Point(TEST_WIDTH / 4, 0),
+                        new Point(TEST_WIDTH / 2, 0),
+                        new Point(TEST_WIDTH - 1, 0)
+                }, new int[] {
+                        Color.WHITE,
+                        0xFFFF8080, // gradient halfway between red and white
+                        Color.RED,
+                        Color.RED,
+
+                }, 20)); // Tolerance set to account for interpolation
+    }
+
+    @Test
+    public void testCreateRightFade() {
+        createTest()
+                .addLayout(R.layout.frame_layout, (ViewInitializer) view -> {
+                    final FadingView child = attachFadingView(view);
+                    child.mEnableFadingRightEdge = true;
+                    child.setFadingEdgeLength(TEST_WIDTH / 2);
+                    child.setHorizontalFadingEdgeEnabled(true);
+                    Assert.assertEquals(0, child.getSolidColor());
+                })
+                .runWithVerifier(new SamplePointVerifier(new Point[] {
+                        new Point(0, 0),
+                        new Point(TEST_WIDTH / 2, 0),
+                        new Point((TEST_WIDTH / 4) * 3, 0),
+                        new Point(TEST_WIDTH - 1, 0)
+                }, new int[] {
+                        Color.RED,
+                        Color.RED,
+                        0xFFFF8080, // gradient halfway between red and white
+                        Color.WHITE
+                }, 20)); // Tolerance set to account for interpolation
+    }
+
+    @Test
+    public void testCreateTopFade() {
+        createTest()
+                .addLayout(R.layout.frame_layout, (ViewInitializer) view -> {
+                    final FadingView child = attachFadingView(view);
+                    child.mEnableFadingTopEdge = true;
+                    child.setFadingEdgeLength(TEST_HEIGHT / 2);
+                    child.setVerticalFadingEdgeEnabled(true);
+                    Assert.assertEquals(0, child.getSolidColor());
+                })
+                .runWithVerifier(new SamplePointVerifier(new Point[] {
+                        new Point(0, 0),
+                        new Point(0, TEST_HEIGHT / 4),
+                        new Point(0, TEST_HEIGHT / 2),
+                        new Point(0, TEST_HEIGHT - 1)
+                }, new int[] {
+                        Color.WHITE,
+                        0xFFFF8080, // gradient halfway between red and white
+                        Color.RED,
+                        Color.RED,
+                }, 20)); // Tolerance set to account for interpolation
+    }
+
+    @Test
+    public void testCreateBottomFade() {
+        createTest()
+                .addLayout(R.layout.frame_layout, (ViewInitializer) view -> {
+                    final FadingView child = attachFadingView(view);
+                    child.mEnableFadingBottomEdge = true;
+                    child.setFadingEdgeLength(TEST_HEIGHT / 2);
+                    child.setVerticalFadingEdgeEnabled(true);
+                    Assert.assertEquals(0, child.getSolidColor());
+                })
+                .runWithVerifier(new SamplePointVerifier(new Point[] {
+                        new Point(0, 0),
+                        new Point(0, TEST_HEIGHT / 2),
+                        new Point(0, (TEST_HEIGHT / 4) * 3),
+                        new Point(0, TEST_HEIGHT - 1)
+                }, new int[] {
+                        Color.RED,
+                        Color.RED,
+                        0xFFFF8080, // gradient halfway between red and white
+                        Color.WHITE
+                }, 20)); // Tolerance set to account for interpolation
+    }
+
+    @Test
+    public void testCreateLeftAndRightFade() {
+        createTest()
+                .addLayout(R.layout.frame_layout, (ViewInitializer) view -> {
+                    final FadingView child = attachFadingView(view);
+                    child.mEnableFadingLeftEdge = true;
+                    child.mEnableFadingRightEdge = true;
+                    child.setFadingEdgeLength(TEST_WIDTH / 2);
+                    child.setHorizontalFadingEdgeEnabled(true);
+                    Assert.assertEquals(0, child.getSolidColor());
+                })
+                .runWithVerifier(new SamplePointVerifier(new Point[] {
+                        new Point(0, 0),
+                        new Point(TEST_WIDTH / 4, 0),
+                        new Point(TEST_WIDTH / 2, 0),
+                        new Point((TEST_WIDTH / 4) * 3, 0),
+                        new Point(TEST_WIDTH - 1, 0)
+                }, new int[] {
+                        Color.WHITE,
+                        0xFFFF8080, // gradient halfway between red and white
+                        Color.RED,
+                        0xFFFF8080, // gradient halfway between red and white
+                        Color.WHITE
+                }, 20)); // Tolerance set to account for interpolation
+    }
+}
diff --git a/tests/tests/uirendering27/Android.bp b/tests/tests/uirendering27/Android.bp
new file mode 100644
index 0000000..c0e84f9
--- /dev/null
+++ b/tests/tests/uirendering27/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "CtsUiRenderingTestCases27",
+    sdk_version: "test_current",
+    target_sdk_version: "27",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctsdeviceutillegacy-axt",
+        "mockito-target-minus-junit4",
+        "androidx.test.rules",
+        "kotlin-test",
+    ],
+
+    libs: ["android.test.runner.stubs"],
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
diff --git a/tests/tests/uirendering27/AndroidManifest.xml b/tests/tests/uirendering27/AndroidManifest.xml
new file mode 100644
index 0000000..b828857
--- /dev/null
+++ b/tests/tests/uirendering27/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.uirendering.cts27"
+          android:targetSandboxVersion="2">
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application>
+        <!--
+         * Some tests (e.g. ShadowTests#testShadowLayout) may have different results depending on
+         * the position on screen, so current implementation of verifier assumes that rendered image
+         * is centered. Therefore activity made non-resizable to work correctly on devices that
+         * start up in multi-window mode.
+         -->
+        <activity android:name="android.uirendering.cts.testinfrastructure.DrawActivity"
+                  android:theme="@style/DefaultTheme"
+                  android:screenOrientation="locked"
+                  android:resizeableActivity="false"
+                  android:configChanges="uiMode" />
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.uirendering.cts.runner.UiRenderingRunner"
+                     android:targetPackage="android.uirendering.cts27">
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/uirendering27/AndroidTest.xml b/tests/tests/uirendering27/AndroidTest.xml
new file mode 100644
index 0000000..ecd5727
--- /dev/null
+++ b/tests/tests/uirendering27/AndroidTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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 UI Rendering test cases">
+    <option name="test-suite-tag" value="cts" />
+    <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="multi_abi" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsUiRenderingTestCases27.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.uirendering.cts27" />
+        <option name="runtime-hint" value="11m55s" />
+        <option name="runner" value="android.uirendering.cts.runner.UiRenderingRunner" />
+        <option name="isolated-storage" value="false" />
+    </test>
+
+    <!-- Collect the files in the dump directory for debugging -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/UiRenderingCaptures" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/tests/tests/uirendering27/OWNERS b/tests/tests/uirendering27/OWNERS
new file mode 100644
index 0000000..fe5d8ba
--- /dev/null
+++ b/tests/tests/uirendering27/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 59572
+jreck@google.com
+njawad@google.com
+djsollen@google.com
+stani@google.com
+scroggo@google.com
diff --git a/tests/tests/uirendering27/TEST_MAPPING b/tests/tests/uirendering27/TEST_MAPPING
new file mode 100644
index 0000000..fa55ba8
--- /dev/null
+++ b/tests/tests/uirendering27/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsUiRenderingTestCases27"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/tests/uirendering27/res/layout/test_container.xml b/tests/tests/uirendering27/res/layout/test_container.xml
new file mode 100644
index 0000000..fd7b4e2
--- /dev/null
+++ b/tests/tests/uirendering27/res/layout/test_container.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:layout_gravity="center"
+        android:id="@+id/test_content_wrapper"
+        android:layout_width="@dimen/test_width"
+        android:layout_height="@dimen/test_height">
+    </FrameLayout>
+</FrameLayout>
diff --git a/tests/tests/uirendering27/res/values/dimens.xml b/tests/tests/uirendering27/res/values/dimens.xml
new file mode 100644
index 0000000..4228611
--- /dev/null
+++ b/tests/tests/uirendering27/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!-- 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.
+  -->
+<resources>
+    <dimen name="test_width">90px</dimen>
+    <dimen name="test_height">90px</dimen>
+
+    <item type="dimen" format="float" name="expected_ambient_shadow_alpha">0.039</item>
+    <item type="dimen" format="float" name="expected_spot_shadow_alpha">0.19</item>
+</resources>
diff --git a/tests/tests/uirendering27/res/values/themes.xml b/tests/tests/uirendering27/res/values/themes.xml
new file mode 100644
index 0000000..e75376b
--- /dev/null
+++ b/tests/tests/uirendering27/res/values/themes.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <style name="DefaultTheme" parent="@android:style/Theme.Material.NoActionBar.Fullscreen">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:fadingEdge">none</item>
+        <item name="android:windowBackground">@android:color/white</item>
+        <item name="android:windowContentTransitions">false</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:ambientShadowAlpha">0.039</item>
+        <item name="android:spotShadowAlpha">0.19</item>
+        <item name="android:forceDarkAllowed">false</item>
+    </style>
+    <style name="AutoDarkTheme" parent="@style/DefaultTheme">
+        <item name="android:forceDarkAllowed">true</item>
+        <item name="android:isLightTheme">true</item>
+    </style>
+</resources>
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/bitmapcomparers/BitmapComparer.java b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapcomparers/BitmapComparer.java
new file mode 100644
index 0000000..c995e43
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapcomparers/BitmapComparer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.bitmapcomparers;
+
+/**
+ * This abstract class can be used by the tester to implement their own comparison methods
+ */
+public abstract class BitmapComparer {
+    /**
+     * Compares the two bitmaps given using Java.
+     * @param offset where in the bitmaps to start
+     * @param stride how much to skip between two different rows
+     * @param width the width of the subsection being tested
+     * @param height the height of the subsection being tested
+     * @return
+     */
+    public abstract boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height);
+
+    /**
+     * This calculates the position in an array that would represent a bitmap given the parameters.
+     */
+    protected static int indexFromXAndY(int x, int y, int stride, int offset) {
+        return x + (y * stride) + offset;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/BitmapVerifier.java b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/BitmapVerifier.java
new file mode 100644
index 0000000..61767b3
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/BitmapVerifier.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+/**
+ * Checks to see if a Bitmap follows the algorithm provided by the verifier
+ */
+public abstract class BitmapVerifier {
+    protected static final int PASS_COLOR = Color.WHITE;
+    protected static final int FAIL_COLOR = Color.RED;
+
+    protected Bitmap mDifferenceBitmap;
+
+    public boolean verify(Bitmap bitmap) {
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+        int[] pixels = new int[width * height];
+        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+        return verify(pixels, 0, width, width, height);
+    }
+
+    /**
+     * This will test if the bitmap is good or not.
+     */
+    public abstract boolean verify(int[] bitmap, int offset, int stride, int width, int height);
+
+    /**
+     * This calculates the position in an array that would represent a bitmap given the parameters.
+     */
+    protected static int indexFromXAndY(int x, int y, int stride, int offset) {
+        return x + (y * stride) + offset;
+    }
+
+    public Bitmap getDifferenceBitmap() {
+        return mDifferenceBitmap;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
new file mode 100644
index 0000000..26b8890
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.bitmapverifiers;
+
+import androidx.annotation.ColorInt;
+
+/**
+ * Checks to see if a bitmap is entirely a single color
+ */
+public class ColorVerifier extends PerPixelBitmapVerifier {
+    @ColorInt
+    private int mColor;
+
+    public ColorVerifier(@ColorInt int color) {
+        this(color, DEFAULT_THRESHOLD);
+    }
+
+    public ColorVerifier(@ColorInt int color, int colorTolerance) {
+        super(colorTolerance);
+        mColor = color;
+    }
+
+    public ColorVerifier(@ColorInt int color, int colorThreshold, float spatialTolerance) {
+        super(colorThreshold, spatialTolerance);
+        mColor = color;
+    }
+
+    @Override
+    @ColorInt
+    protected int getExpectedColor(int x, int y) {
+        return mColor;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
new file mode 100644
index 0000000..f71f78d
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import androidx.annotation.ColorInt;
+import android.uirendering.cts.util.CompareUtils;
+import android.util.Log;
+
+/**
+ * This class looks at every pixel in a given bitmap and verifies that it is correct.
+ */
+public abstract class PerPixelBitmapVerifier extends BitmapVerifier {
+    private static final String TAG = "PerPixelBitmapVerifer";
+    public static final int DEFAULT_THRESHOLD = 48;
+
+    // total color difference tolerated without the pixel failing
+    private int mColorTolerance;
+
+    // portion of bitmap allowed to fail pixel check
+    private float mSpatialTolerance;
+
+    public PerPixelBitmapVerifier() {
+        this(DEFAULT_THRESHOLD, 0);
+    }
+
+    public PerPixelBitmapVerifier(int colorTolerance) {
+        this(colorTolerance, 0);
+    }
+
+    public PerPixelBitmapVerifier(int colorTolerance, float spatialTolerance) {
+        mColorTolerance = colorTolerance;
+        mSpatialTolerance = spatialTolerance;
+    }
+
+    @ColorInt
+    protected int getExpectedColor(int x, int y) {
+        return Color.WHITE;
+    }
+
+    public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+        int failures = 0;
+        int[] differenceMap = new int[bitmap.length];
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                if (!verifyPixel(x, y, bitmap[index])) {
+                    if (failures < 50) {
+                        Log.d(TAG, "Expected : " + Integer.toHexString(getExpectedColor(x, y))
+                                + " received : " + Integer.toHexString(bitmap[index])
+                                + " at position (" + x + "," + y + ")");
+                    }
+                    failures++;
+                    differenceMap[index] = FAIL_COLOR;
+                } else {
+                    differenceMap[index] = PASS_COLOR;
+                }
+            }
+        }
+        int toleratedFailures = (int) (mSpatialTolerance * width * height);
+        boolean success = failures <= toleratedFailures;
+        Log.d(TAG, failures + " failures observed out of "
+                + toleratedFailures + " tolerated failures");
+        if (!success) {
+            mDifferenceBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            mDifferenceBitmap.setPixels(differenceMap, offset, stride, 0, 0, width, height);
+        }
+        return success;
+    }
+
+
+    protected boolean verifyPixel(int x, int y, int observedColor) {
+        int expectedColor = getExpectedColor(x, y);
+        return CompareUtils.verifyPixelWithThreshold(observedColor, expectedColor, mColorTolerance);
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
new file mode 100644
index 0000000..f4bece1
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Rect;
+
+/**
+ * Tests to see if there is rectangle of a certain color, with a background given
+ */
+public class RectVerifier extends PerPixelBitmapVerifier {
+    private int mOuterColor;
+    private int mInnerColor;
+    private Rect mInnerRect;
+
+    public RectVerifier(int outerColor, int innerColor, Rect innerRect) {
+        this(outerColor, innerColor, innerRect, DEFAULT_THRESHOLD);
+    }
+
+    public RectVerifier(int outerColor, int innerColor, Rect innerRect, int tolerance) {
+        super(tolerance);
+        mOuterColor = outerColor;
+        mInnerColor = innerColor;
+        mInnerRect = innerRect;
+    }
+
+    @Override
+    protected int getExpectedColor(int x, int y) {
+        return mInnerRect.contains(x, y) ? mInnerColor : mOuterColor;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
new file mode 100644
index 0000000..31cd282
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.util.CompareUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * This class will test specific points, and ensure that they match up perfectly with the input colors
+ */
+public class SamplePointVerifier extends BitmapVerifier {
+    private static final String TAG = "SamplePoint";
+    public static final int DEFAULT_TOLERANCE = 20;
+    private final Point[] mTestPoints;
+    private final int[] mExpectedColors;
+    protected final int mTolerance;
+
+    public SamplePointVerifier(Point[] testPoints, int[] expectedColors) {
+        this(testPoints, expectedColors, DEFAULT_TOLERANCE);
+    }
+
+    public SamplePointVerifier(Point[] testPoints, int[] expectedColors, int tolerance) {
+        mTestPoints = testPoints;
+        mExpectedColors = expectedColors;
+        mTolerance = tolerance;
+    }
+
+    @Override
+    public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+        boolean success = true;
+        int[] differenceMap = new int[bitmap.length];
+        Arrays.fill(differenceMap, PASS_COLOR);
+        for (int i = 0 ; i < mTestPoints.length ; i++) {
+            int x = mTestPoints[i].x;
+            int y = mTestPoints[i].y;
+            int index = indexFromXAndY(x, y, stride, offset);
+            if (!verifyPixel(bitmap[index], mExpectedColors[i])) {
+                Log.d(TAG, "Expected : " + Integer.toHexString(mExpectedColors[i]) +
+                        " at position x = " + x + " y = " + y + " , tested color : " +
+                        Integer.toHexString(bitmap[index]));
+                differenceMap[index] = FAIL_COLOR;
+                success = false;
+            } else {
+                differenceMap[index] = PASS_COLOR;
+            }
+        }
+        if (!success) {
+            mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
+                    ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+            mDifferenceBitmap.setPixels(differenceMap, offset, stride, 0, 0,
+                    ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        }
+        return success;
+    }
+
+    protected boolean verifyPixel(int color, int expectedColor) {
+        return CompareUtils.verifyPixelWithThreshold(color, expectedColor, mTolerance);
+    }
+
+    public interface VerifyPixelColor {
+        boolean verifyPixel(int color);
+    }
+
+    public static SamplePointVerifier create(Point[] testPoints, int[] expectedColors,
+            int tolerance, VerifyPixelColor verifier) {
+        return new SamplePointVerifier(testPoints, expectedColors, tolerance) {
+            @Override
+            protected boolean verifyPixel(int color, int expectedColor) {
+                return super.verifyPixel(color, expectedColor) && verifier.verifyPixel(color);
+            }
+        };
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/blendmode/BitmapBlendModeCanvasClient.java b/tests/tests/uirendering27/src/android/uirendering/cts/blendmode/BitmapBlendModeCanvasClient.java
new file mode 100644
index 0000000..696205e
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/blendmode/BitmapBlendModeCanvasClient.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.uirendering.cts.blendmode;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+
+/**
+ * CanvasClient used to draw bitmaps with the clear and dstover blend modes to verify
+ * that backward compatible behavior is respected on API levels 27 and older where clear is treated
+ * as dst out for drawing bitmaps. However, on newer API levels, the clear blend mode is
+ * properly respected
+ */
+public class BitmapBlendModeCanvasClient implements CanvasClient {
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    /**
+     * Color used to render the circle in the mask bitmap
+     */
+    private final int mBitmapColor;
+
+    /**
+     * Color used to draw tint the area overlapping the content in the mask
+     */
+    private final int mMaskColor;
+
+    /**
+     * PorterDuff mode used to mask out the content in the bitmap mask
+     */
+    private final PorterDuff.Mode mBitmapMode;
+
+    /**
+     * PorterDuff moe used to render over the content in the bitmap mask
+     */
+    private final PorterDuff.Mode mMaskMode;
+
+    public BitmapBlendModeCanvasClient(PorterDuff.Mode bitmapMode, PorterDuff.Mode maskMode,
+            int bitmapColor, int maskColor) {
+        mBitmapMode = bitmapMode;
+        mMaskMode = maskMode;
+        mBitmapColor = bitmapColor;
+        mMaskColor = maskColor;
+    }
+
+    @Override
+    public void draw(Canvas canvas, int width, int height) {
+        Bitmap bitmap = createBitmap(width, height);
+
+        mPaint.setXfermode(new PorterDuffXfermode(mBitmapMode));
+        canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
+
+        mPaint.setXfermode(new PorterDuffXfermode(mMaskMode));
+        mPaint.setColor(mMaskColor);
+        canvas.drawRect(0, 0, width, height, mPaint);
+    }
+
+    private Bitmap createBitmap(int width, int height) {
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        paint.setColor(mBitmapColor);
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        canvas.drawCircle(
+                width / 2.0f,
+                height / 2.0f,
+                Math.min(width, height) / 2.0f,
+                paint);
+        return bitmap;
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/differencevisualizers/DifferenceVisualizer.java b/tests/tests/uirendering27/src/android/uirendering/cts/differencevisualizers/DifferenceVisualizer.java
new file mode 100644
index 0000000..348a1c0
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/differencevisualizers/DifferenceVisualizer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.differencevisualizers;
+
+/**
+ * This class can be extended by the tester, to allow for various ways to debug.
+ */
+public abstract class DifferenceVisualizer {
+    public abstract int[] getDifferences(int[] ideal, int[] given);
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/differencevisualizers/PassFailVisualizer.java b/tests/tests/uirendering27/src/android/uirendering/cts/differencevisualizers/PassFailVisualizer.java
new file mode 100644
index 0000000..7641519
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/differencevisualizers/PassFailVisualizer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.differencevisualizers;
+
+import android.graphics.Color;
+
+/**
+ * This class creates difference maps that show which pixels were correct, and which weren't
+ */
+public class PassFailVisualizer extends DifferenceVisualizer {
+    /**
+     * This method will return a bitmap where white is same red is different
+     * @param ideal the desired result
+     * @param given the produced result
+     */
+    @Override
+    public int[] getDifferences(int[] ideal, int[] given) {
+        int[] output = new int[ideal.length];
+        for (int y = 0; y < output.length; y++) {
+            if (ideal[y] == given[y]) {
+                output[y] = Color.WHITE;
+            } else {
+                output[y] = Color.RED;
+            }
+        }
+        return output;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/runner/UiRenderingRunner.java b/tests/tests/uirendering27/src/android/uirendering/cts/runner/UiRenderingRunner.java
new file mode 100644
index 0000000..a238699
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/runner/UiRenderingRunner.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.runner;
+
+import android.os.Bundle;
+import android.support.test.uiautomator.UiDevice;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnitRunner;
+
+/**
+ * TODO: Do some cool stuff we also want like sharing DrawActivity cross-class.
+ */
+public class UiRenderingRunner extends AndroidJUnitRunner {
+
+    @Override
+    protected void waitForActivitiesToComplete() {
+        // No.
+    }
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+
+        final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        try {
+            device.wakeUp();
+            device.executeShellCommand("wm dismiss-keyguard");
+        } catch (Exception e) {
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        // Ok now wait if necessary
+        super.waitForActivitiesToComplete();
+
+        super.onDestroy();
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testclasses/BitmapBlendModeTest.java b/tests/tests/uirendering27/src/android/uirendering/cts/testclasses/BitmapBlendModeTest.java
new file mode 100644
index 0000000..08db303
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testclasses/BitmapBlendModeTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.uirendering.cts.testclasses;
+
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.blendmode.BitmapBlendModeCanvasClient;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+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 BitmapBlendModeTest extends ActivityTestBase {
+
+    private static final Point[] SAMPLE_POINTS = {
+            new Point(0, 0),
+            new Point(TEST_WIDTH - 1, 0),
+            new Point(0, TEST_HEIGHT - 1),
+            new Point(TEST_WIDTH - 1, TEST_HEIGHT - 1),
+            new Point (TEST_WIDTH / 2, TEST_HEIGHT / 2)
+    };
+
+    @Test
+    public void testClearBlendMode() {
+        // Verify that using CLEAR for rendering a bitmap has the same behavior as
+        // DST_OUT. This is to verify the backward compatible behavior of using clear ends up
+        // utilizing dst_out while rendering a bitmap on API levels 27 and below
+        // The output image here should be a red circle with a white background even though
+        // the original mask bitmap is drawn with a blue circle
+        int[] colors = {Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.RED};
+        BitmapBlendModeCanvasClient client = new BitmapBlendModeCanvasClient(
+                PorterDuff.Mode.CLEAR, PorterDuff.Mode.DST_OVER,
+                Color.BLUE, Color.RED);
+        createTest().addCanvasClientWithoutUsingPicture(client, true)
+                .runWithVerifier(new SamplePointVerifier(SAMPLE_POINTS, colors));
+    }
+
+    @Test
+    public void testDstOutBlendMode() {
+        // Verify that using DST_OUT for rendering a bitmap only clears the overlapping
+        // pixels of the bitmap from the destination.
+        // The output image here should be a red circle with a white background even though
+        // the original mask bitmap is drawn with a blue circle
+        int[] colors = {Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.RED};
+        BitmapBlendModeCanvasClient client = new BitmapBlendModeCanvasClient(
+                PorterDuff.Mode.DST_OUT, PorterDuff.Mode.DST_OVER,
+                Color.BLUE, Color.RED);
+        createTest().addCanvasClientWithoutUsingPicture(client, true)
+                .runWithVerifier(new SamplePointVerifier(SAMPLE_POINTS, colors));
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
new file mode 100644
index 0000000..ab5af62
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.util.BitmapAsserter;
+import android.util.Log;
+import android.view.PixelCopy;
+
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SynchronousPixelCopy;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Rule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class contains the basis for the graphics hardware test classes. Contained within this class
+ * are several methods that help with the execution of tests, and should be extended to gain the
+ * functionality built in.
+ */
+public abstract class ActivityTestBase {
+    public static final String TAG = "ActivityTestBase";
+    public static final boolean DEBUG = false;
+
+    //The minimum height and width of a device
+    public static final int TEST_WIDTH = 90;
+    public static final int TEST_HEIGHT = 90;
+
+    private TestCaseBuilder mTestCaseBuilder;
+    private Screenshotter mScreenshotter;
+
+    private static DrawActivity sActivity;
+
+    @Rule
+    public Tracer name = new Tracer();
+
+    private BitmapAsserter mBitmapAsserter = new BitmapAsserter(this.getClass().getSimpleName(),
+            name.getMethodName());
+
+    protected String getName() {
+        return name.getMethodName();
+    }
+
+    protected Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+
+    protected DrawActivity getActivity() {
+        if (sActivity == null) {
+            Instrumentation instrumentation = getInstrumentation();
+            instrumentation.setInTouchMode(true);
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClass(instrumentation.getTargetContext(), DrawActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.putExtra(DrawActivity.EXTRA_WIDE_COLOR_GAMUT, isWideColorGamut());
+            intent.putExtra(DrawActivity.EXTRA_USE_FORCE_DARK, useForceDark());
+            sActivity = (DrawActivity) instrumentation.startActivitySync(intent);
+        }
+        return sActivity;
+    }
+
+    protected boolean isWideColorGamut() {
+        return false;
+    }
+
+    protected boolean useForceDark() {
+        return false;
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        if (sActivity != null) {
+            // All tests are finished, tear down the activity
+            sActivity.allTestsFinished();
+            sActivity = null;
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if (mTestCaseBuilder != null) {
+            List<TestCase> testCases = mTestCaseBuilder.getTestCases();
+
+            if (testCases.size() == 0) {
+                throw new IllegalStateException("Must have at least one test case");
+            }
+
+            for (TestCase testCase : testCases) {
+                if (!testCase.wasTestRan) {
+                    Log.w(TAG, getName() + " not all of the tests ran");
+                    break;
+                }
+            }
+            mTestCaseBuilder = null;
+        }
+    }
+
+    private Bitmap takeScreenshot(TestPositionInfo testPositionInfo) {
+        if (mScreenshotter == null) {
+            SynchronousPixelCopy copy = new SynchronousPixelCopy();
+            Bitmap dest = Bitmap.createBitmap(
+                    TEST_WIDTH, TEST_HEIGHT,
+                    getActivity().getWindow().isWideColorGamut()
+                            ? Config.RGBA_F16 : Config.ARGB_8888);
+            Rect srcRect = new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT);
+            srcRect.offset(testPositionInfo.surfaceOffset.x, testPositionInfo.surfaceOffset.y);
+            Log.d(TAG, "capturing screenshot of " + srcRect.toShortString());
+            int copyResult = copy.request(getActivity().getWindow(), srcRect, dest);
+            Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
+            return dest;
+        } else {
+            return mScreenshotter.takeScreenshot(testPositionInfo);
+        }
+    }
+
+    private TestPositionInfo runRenderSpec(TestCase testCase) {
+        TestPositionInfo testPositionInfo = getActivity().enqueueRenderSpecAndWait(
+                testCase.layoutID, testCase.canvasClient,
+                testCase.viewInitializer, testCase.useHardware, testCase.usePicture);
+        testCase.wasTestRan = true;
+        if (testCase.readyFence != null) {
+            try {
+                testCase.readyFence.await(5, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException("readyFence didn't signal within 5 seconds");
+            }
+            // The fence setup may have (and probably did) changed things that we need to wait
+            // have been drawn. So force an invalidate() and wait for it to finish
+            getActivity().waitForRedraw();
+        }
+        return testPositionInfo;
+    }
+
+    /**
+     * Used to execute a specific part of a test and get the resultant bitmap
+     */
+    private Bitmap captureRenderSpec(TestCase testCase) {
+        return takeScreenshot(runRenderSpec(testCase));
+    }
+
+    protected TestCaseBuilder createTest() {
+        mTestCaseBuilder = new TestCaseBuilder();
+        mScreenshotter = null;
+        return mTestCaseBuilder;
+    }
+
+    public static class TestPositionInfo {
+        /**
+         * Position of capture area in surface space - use this offset for e.g.
+         * PixelCopy from a window's surface.
+         */
+        public final Point surfaceOffset;
+
+        /**
+         * Position of capture area in screen space - use this offset for e.g.
+         * {@code getInstrumentation().getUiAutomation().takeScreenshot()},
+         * since those screenshots are captured in screen space.
+         */
+        public final Point screenOffset;
+
+        public TestPositionInfo(Point surfaceOffset, Point screenOffset) {
+            this.surfaceOffset = surfaceOffset;
+            this.screenOffset = screenOffset;
+        }
+    }
+
+    public interface Screenshotter {
+        Bitmap takeScreenshot(TestPositionInfo params);
+    }
+
+    /**
+     * Defines a group of CanvasClients, XML layouts, and WebView html files for testing.
+     */
+    protected class TestCaseBuilder {
+        private List<TestCase> mTestCases;
+
+        private TestCaseBuilder() {
+            mTestCases = new ArrayList<>();
+        }
+
+        /**
+         * Runs a test where the first test case is considered the "ideal" image and from there,
+         * every test case is tested against it.
+         */
+        public void runWithComparer(BitmapComparer bitmapComparer) {
+            if (mTestCases.size() == 0) {
+                throw new IllegalStateException("Need at least one test to run");
+            }
+
+            Bitmap idealBitmap = captureRenderSpec(mTestCases.remove(0));
+
+            for (TestCase testCase : mTestCases) {
+                Bitmap testCaseBitmap = captureRenderSpec(testCase);
+                mBitmapAsserter.assertBitmapsAreSimilar(idealBitmap, testCaseBitmap, bitmapComparer,
+                        getName(), testCase.getDebugString());
+            }
+        }
+
+        /**
+         * Runs a test where each testcase is independent of the others and each is checked against
+         * the verifier given.
+         */
+        public void runWithVerifier(BitmapVerifier bitmapVerifier) {
+            if (mTestCases.size() == 0) {
+                throw new IllegalStateException("Need at least one test to run");
+            }
+
+            for (TestCase testCase : mTestCases) {
+                Bitmap testCaseBitmap = captureRenderSpec(testCase);
+                mBitmapAsserter.assertBitmapIsVerified(testCaseBitmap, bitmapVerifier,
+                        getName(), testCase.getDebugString());
+            }
+            getActivity().reset();
+        }
+
+        private static final int VERIFY_ANIMATION_LOOP_COUNT = 20;
+        private static final int VERIFY_ANIMATION_SLEEP_MS = 100;
+
+        /**
+         * Runs a test where each testcase is independent of the others and each is checked against
+         * the verifier given in a loop.
+         *
+         * A screenshot is captured several times in a loop, to ensure that valid output is produced
+         * at many different times during the animation.
+         */
+        public void runWithAnimationVerifier(BitmapVerifier bitmapVerifier) {
+            if (mTestCases.size() == 0) {
+                throw new IllegalStateException("Need at least one test to run");
+            }
+
+            for (TestCase testCase : mTestCases) {
+                TestPositionInfo testPositionInfo = runRenderSpec(testCase);
+
+                for (int i = 0; i < VERIFY_ANIMATION_LOOP_COUNT; i++) {
+                    try {
+                        Thread.sleep(VERIFY_ANIMATION_SLEEP_MS);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                    Bitmap testCaseBitmap = takeScreenshot(testPositionInfo);
+                    mBitmapAsserter.assertBitmapIsVerified(testCaseBitmap, bitmapVerifier,
+                            getName(), testCase.getDebugString());
+                }
+            }
+        }
+
+        /**
+         * Runs a test where each testcase is run without verification. Should only be used
+         * where custom CanvasClients, Views, or ViewInitializers do their own internal
+         * test assertions.
+         */
+        public void runWithoutVerification() {
+            runWithVerifier(new BitmapVerifier() {
+                @Override
+                public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+                    return true;
+                }
+            });
+        }
+
+        public TestCaseBuilder withScreenshotter(Screenshotter screenshotter) {
+            Assert.assertNull("Screenshotter is already set!", mScreenshotter);
+            mScreenshotter = screenshotter;
+            return this;
+        }
+
+        public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer) {
+            return addLayout(layoutId, viewInitializer, false)
+                    .addLayout(layoutId, viewInitializer, true);
+        }
+
+        public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer,
+                boolean useHardware) {
+            mTestCases.add(new TestCase(layoutId, viewInitializer, useHardware));
+            return this;
+        }
+
+        public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer,
+                boolean useHardware, CountDownLatch readyFence) {
+            TestCase test = new TestCase(layoutId, viewInitializer, useHardware);
+            test.readyFence = readyFence;
+            mTestCases.add(test);
+            return this;
+        }
+
+        public TestCaseBuilder addCanvasClient(CanvasClient canvasClient) {
+            return addCanvasClient(null, canvasClient);
+        }
+
+        public TestCaseBuilder addCanvasClient(CanvasClient canvasClient, boolean useHardware) {
+            return addCanvasClient(null, canvasClient, useHardware);
+        }
+
+        public TestCaseBuilder addCanvasClient(String debugString, CanvasClient canvasClient) {
+            return addCanvasClient(debugString, canvasClient, false)
+                    .addCanvasClient(debugString, canvasClient, true);
+        }
+
+        public TestCaseBuilder addCanvasClient(String debugString,
+                CanvasClient canvasClient, boolean useHardware) {
+            return addCanvasClientInternal(debugString, canvasClient, useHardware, false)
+                    .addCanvasClientInternal(debugString, canvasClient, useHardware, true);
+        }
+
+        public TestCaseBuilder addCanvasClientWithoutUsingPicture(CanvasClient canvasClient) {
+            return addCanvasClientWithoutUsingPicture(null, canvasClient);
+        }
+
+        public TestCaseBuilder addCanvasClientWithoutUsingPicture(String debugString,
+                CanvasClient canvasClient) {
+            return addCanvasClientInternal(debugString, canvasClient, false, false)
+                    .addCanvasClientInternal(debugString, canvasClient, true, false);
+        }
+
+        public TestCaseBuilder addCanvasClientWithoutUsingPicture(CanvasClient canvasClient,
+                boolean useHardware) {
+            return addCanvasClientInternal(null, canvasClient, useHardware, false);
+        }
+
+        private TestCaseBuilder addCanvasClientInternal(String debugString,
+                CanvasClient canvasClient, boolean useHardware, boolean usePicture) {
+            mTestCases.add(new TestCase(canvasClient, debugString, useHardware, usePicture));
+            return this;
+        }
+
+        private List<TestCase> getTestCases() {
+            return mTestCases;
+        }
+    }
+
+    private class TestCase {
+        public int layoutID;
+        public ViewInitializer viewInitializer;
+        /**
+         * After launching the test case this fence is used to signal when
+         * to proceed with capture & verification. If this is null the test
+         * proceeds immediately to verification
+         */
+        @Nullable
+        public CountDownLatch readyFence;
+
+        public CanvasClient canvasClient;
+        public String canvasClientDebugString;
+
+        public boolean useHardware;
+        public boolean usePicture = false;
+        public boolean wasTestRan = false;
+
+        public TestCase(int layoutId, ViewInitializer viewInitializer, boolean useHardware) {
+            this.layoutID = layoutId;
+            this.viewInitializer = viewInitializer;
+            this.useHardware = useHardware;
+        }
+
+        public TestCase(CanvasClient client, String debugString, boolean useHardware,
+                boolean usePicture) {
+            this.canvasClient = client;
+            this.canvasClientDebugString = debugString;
+            this.useHardware = useHardware;
+            this.usePicture = usePicture;
+        }
+
+        public String getDebugString() {
+            String debug = "";
+            if (canvasClient != null) {
+                debug += "CanvasClient : ";
+                if (canvasClientDebugString != null) {
+                    debug += canvasClientDebugString;
+                } else {
+                    debug += "no debug string given";
+                }
+            } else {
+                debug += "Layout resource : " +
+                        getActivity().getResources().getResourceName(layoutID);
+            }
+            debug += "\nTest ran in " + (useHardware ? "hardware" : "software") +
+                    (usePicture ? " with picture" : " without picture") + "\n";
+            return debug;
+        }
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClient.java b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClient.java
new file mode 100644
index 0000000..13db944
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClient.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure;
+
+import android.graphics.Canvas;
+
+/**
+ * An interface for specifying canvas commands.
+ *
+ * Implementations of the interface are not required to save/restore canvas state -
+ * callers of draw() will handle saving/restoring as necessary.
+ */
+public interface CanvasClient {
+    void draw(Canvas canvas, int width, int height);
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClientDrawable.java b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClientDrawable.java
new file mode 100644
index 0000000..d796e6c
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClientDrawable.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.drawable.Drawable;
+
+public class CanvasClientDrawable extends Drawable {
+    private final CanvasClient mCanvasClient;
+
+    public CanvasClientDrawable(CanvasClient canvasClient) {
+        mCanvasClient = canvasClient;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        int saveCount = canvas.save();
+        canvas.clipRect(0, 0, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        mCanvasClient.draw(canvas, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        canvas.restoreToCount(saveCount);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {}
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {}
+
+    @Override
+    public int getOpacity() {
+        return 0;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
new file mode 100644
index 0000000..8012c82
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A simple View that uses a CanvasClient to draw its contents
+ */
+public class CanvasClientView extends View {
+    private boolean mUsePicture = false;
+    private CanvasClient mCanvasClient;
+
+    public CanvasClientView(Context context) {
+        super(context);
+    }
+
+    public CanvasClientView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CanvasClientView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setUsePicture(boolean usePicture) {
+        mUsePicture = usePicture;
+    }
+
+    public void setCanvasClient(CanvasClient canvasClient) {
+        mCanvasClient = canvasClient;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (ActivityTestBase.DEBUG) {
+            String s = canvas.isHardwareAccelerated() ? "HARDWARE" : "SOFTWARE";
+            Paint paint = new Paint();
+            paint.setColor(Color.BLACK);
+            paint.setTextSize(20);
+            canvas.drawText(s, 200, 200, paint);
+        }
+        if (mCanvasClient == null) throw new IllegalStateException("Canvas client missing");
+
+        int saveCount = canvas.save();
+        canvas.clipRect(0, 0, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        if (mUsePicture) {
+            Picture picture = new Picture();
+            Canvas pictureCanvas = picture.beginRecording(ActivityTestBase.TEST_WIDTH,
+                    ActivityTestBase.TEST_HEIGHT);
+            mCanvasClient.draw(pictureCanvas, ActivityTestBase.TEST_WIDTH,
+                    ActivityTestBase.TEST_HEIGHT);
+            picture.endRecording();
+            picture.draw(canvas);
+        } else {
+            mCanvasClient.draw(canvas, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        }
+        canvas.restoreToCount(saveCount);
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt
new file mode 100644
index 0000000..d4735ad
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure
+
+import android.app.Activity
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Point
+import android.os.Bundle
+import android.os.Handler
+import android.os.Message
+import android.uirendering.cts27.R
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.Nullable
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+class Offset(val dx: Float, val dy: Float) {
+    override fun equals(other: Any?): Boolean {
+        return this === other || (other as? Offset)?.let {
+            dx == other.dx && dy == other.dy
+        } ?: false
+    }
+
+    override fun hashCode(): Int = dx.hashCode() xor dy.hashCode()
+}
+
+/**
+ * A generic activity that uses a view specified by the user.
+ */
+class DrawActivity : Activity() {
+    companion object {
+        internal const val EXTRA_WIDE_COLOR_GAMUT = "DrawActivity.WIDE_COLOR_GAMUT"
+        internal const val EXTRA_USE_FORCE_DARK = "DrawActivity.USE_FORCE_DARK"
+
+        private const val TIME_OUT_MS: Long = 10000
+
+        private const val LAYOUT_MSG = 1
+        private const val CANVAS_MSG = 2
+    }
+
+    private val mLock = java.lang.Object()
+    private var mPositionInfo: ActivityTestBase.TestPositionInfo? = null
+
+    private lateinit var mHandler: Handler
+    private lateinit var mTestContainer: ViewGroup
+
+    private var mView: View? = null
+
+    private var mViewInitializer: ViewInitializer? = null
+
+    public override fun onCreate(bundle: Bundle?) {
+        super.onCreate(bundle)
+
+        if (intent.getBooleanExtra(EXTRA_USE_FORCE_DARK, false)) {
+            forceUiMode(Configuration.UI_MODE_NIGHT_YES)
+            setTheme(R.style.AutoDarkTheme)
+        } else {
+            forceUiMode(Configuration.UI_MODE_NIGHT_NO)
+        }
+
+        if (intent.getBooleanExtra(EXTRA_WIDE_COLOR_GAMUT, false)) {
+            window.colorMode = ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
+        }
+
+        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
+                View.SYSTEM_UI_FLAG_FULLSCREEN
+        mHandler = RenderSpecHandler()
+
+        setContentView(R.layout.test_container)
+        mTestContainer = findViewById(R.id.test_content_wrapper)
+    }
+
+    private fun forceUiMode(mode: Int) {
+        if (resources.configuration.uiMode != mode) {
+            val newConfig = Configuration(resources.configuration)
+            newConfig.uiMode = mode
+            resources.updateConfiguration(newConfig, resources.displayMetrics)
+            onConfigurationChanged(newConfig)
+        }
+    }
+
+    fun enqueueRenderSpecAndWait(
+        layoutId: Int,
+        canvasClient: CanvasClient?,
+        @Nullable viewInitializer: ViewInitializer?,
+        useHardware: Boolean,
+        usePicture: Boolean
+    ): ActivityTestBase.TestPositionInfo {
+        (mHandler as RenderSpecHandler).setViewInitializer(viewInitializer)
+        val arg2 = if (useHardware) View.LAYER_TYPE_NONE else View.LAYER_TYPE_SOFTWARE
+        synchronized(mLock) {
+            if (canvasClient != null) {
+                mHandler.obtainMessage(CANVAS_MSG, if (usePicture) 1 else 0,
+                    arg2, canvasClient
+                ).sendToTarget()
+            } else {
+                mHandler.obtainMessage(LAYOUT_MSG, layoutId, arg2)
+                    .sendToTarget()
+            }
+
+            try {
+                mLock.wait(TIME_OUT_MS)
+            } catch (e: InterruptedException) {
+                throw AssertionError(e)
+            }
+        }
+        assertNotNull("Timeout waiting for draw", mPositionInfo)
+        return mPositionInfo!!
+    }
+
+    fun waitForRedraw() {
+        synchronized(mLock) {
+            mHandler.post {
+                mTestContainer.invalidate()
+                notifyOnDrawCompleted()
+            }
+            try {
+                mLock.wait(TIME_OUT_MS)
+            } catch (e: InterruptedException) {
+                throw AssertionError(e)
+            }
+        }
+    }
+
+    private fun notifyOnDrawCompleted() {
+        mTestContainer.viewTreeObserver.registerFrameCommitCallback {
+            val location = IntArray(2)
+            mTestContainer.getLocationInWindow(location)
+            val surfaceOffset = Point(location[0], location[1])
+            mTestContainer.getLocationOnScreen(location)
+            val screenOffset = Point(location[0], location[1])
+            synchronized(mLock) {
+                mPositionInfo = ActivityTestBase.TestPositionInfo(
+                    surfaceOffset, screenOffset
+                )
+                mLock.notify()
+            }
+        }
+    }
+
+    fun reset() {
+        val fence = CountDownLatch(1)
+        mHandler.post {
+            mViewInitializer?.teardownView()
+            mViewInitializer = null
+            mView = null
+            mTestContainer.removeAllViews()
+            fence.countDown()
+        }
+        assertTrue(fence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS))
+    }
+
+    private inner class RenderSpecHandler : Handler() {
+
+        fun setViewInitializer(viewInitializer: ViewInitializer?) {
+            mViewInitializer = viewInitializer
+        }
+
+        override fun handleMessage(message: Message) {
+            mTestContainer.removeAllViews()
+            when (message.what) {
+                LAYOUT_MSG -> {
+                    mView = LayoutInflater.from(this@DrawActivity).inflate(
+                        message.arg1, mTestContainer, false
+                    )
+                }
+
+                CANVAS_MSG -> {
+                    val canvasClientView = CanvasClientView(this@DrawActivity)
+                    canvasClientView.setCanvasClient(message.obj as CanvasClient)
+                    if (message.arg1 != 0) {
+                        canvasClientView.setUsePicture(true)
+                    }
+                    mView = canvasClientView
+                }
+            }
+
+            if (mView == null) {
+                throw IllegalStateException("failed to inflate test content")
+            }
+
+            mTestContainer.addView(
+                mView, ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT
+            )
+
+            if (mViewInitializer != null) {
+                mViewInitializer!!.initializeView(mView)
+            }
+
+            // set layer on wrapper parent of view, so view initializer
+            // can control layer type of View under test.
+            mTestContainer.setLayerType(message.arg2, null)
+
+            notifyOnDrawCompleted()
+        }
+    }
+
+    override fun onPause() {
+        super.onPause()
+        mViewInitializer?.run {
+            teardownView()
+        }
+    }
+
+    override fun finish() {
+        // Ignore
+    }
+
+    /** Call this when all the tests that use this activity have completed.
+     * This will then clean up any internal state and finish the activity.  */
+    fun allTestsFinished() {
+        super.finish()
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/Tracer.java b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/Tracer.java
new file mode 100644
index 0000000..8ea72fc
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/Tracer.java
@@ -0,0 +1,47 @@
+/*
+  * Copyright (C) 2018 The Android Open Source Project
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+
+package android.uirendering.cts.testinfrastructure;
+
+import android.os.Trace;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class Tracer implements TestRule {
+    private String mName;
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                mName = description.getMethodName();
+                Trace.beginSection(mName);
+                try {
+                    base.evaluate();
+                } finally {
+                    Trace.endSection();
+                }
+            }
+        };
+    }
+
+    public String getMethodName() {
+        return mName;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
new file mode 100644
index 0000000..cd573d1
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure;
+
+import android.view.View;
+
+/**
+ * Called after a view is created to set various properties on the view
+ */
+public interface ViewInitializer {
+    void initializeView(View view);
+
+    default void teardownView() {};
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/util/BitmapAsserter.java b/tests/tests/uirendering27/src/android/uirendering/cts/util/BitmapAsserter.java
new file mode 100644
index 0000000..47f4c89
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/util/BitmapAsserter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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.uirendering.cts.util;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
+import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+
+public class BitmapAsserter {
+    private DifferenceVisualizer mDifferenceVisualizer;
+    private String mClassName;
+
+    public BitmapAsserter(String className, String name) {
+        mClassName = className;
+        mDifferenceVisualizer = new PassFailVisualizer();
+
+        // Create a location for the files to be held, if it doesn't exist already
+        BitmapDumper.createSubDirectory(mClassName);
+
+        // If we have a test currently, let's remove the older files if they exist
+        if (name != null) {
+            BitmapDumper.deleteFileInClassFolder(mClassName, name);
+        }
+    }
+
+    /**
+     * Compares the two bitmaps saved using the given test. If they fail, the files are saved using
+     * the test name.
+     */
+    public void assertBitmapsAreSimilar(Bitmap bitmap1, Bitmap bitmap2, BitmapComparer comparer,
+            String testName, String debugMessage) {
+        boolean success;
+        int width = bitmap1.getWidth();
+        int height = bitmap1.getHeight();
+
+        if (width != bitmap2.getWidth() || height != bitmap2.getHeight()) {
+            fail("Can't compare bitmaps of different sizes");
+        }
+
+        int[] pixels1 = new int[width * height];
+        int[] pixels2 = new int[width * height];
+        bitmap1.getPixels(pixels1, 0, width, 0, 0, width, height);
+        bitmap2.getPixels(pixels2, 0, width, 0, 0, width, height);
+        success = comparer.verifySame(pixels1, pixels2, 0, width, width, height);
+
+        if (!success) {
+            BitmapDumper.dumpBitmaps(bitmap1, bitmap2, testName, mClassName, mDifferenceVisualizer);
+        }
+
+        assertTrue(debugMessage, success);
+    }
+
+    /**
+     * Tests to see if a bitmap passes a verifier's test. If it doesn't the bitmap is saved to the
+     * sdcard.
+     */
+    public void assertBitmapIsVerified(Bitmap bitmap, BitmapVerifier bitmapVerifier,
+            String testName, String debugMessage) {
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+        boolean success = bitmapVerifier.verify(bitmap);
+        if (!success) {
+            Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
+            BitmapDumper.dumpBitmap(croppedBitmap, testName, mClassName);
+            BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), testName + "_verifier",
+                    mClassName);
+        }
+        assertTrue(debugMessage, success);
+    }
+
+
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/util/BitmapDumper.java b/tests/tests/uirendering27/src/android/uirendering/cts/util/BitmapDumper.java
new file mode 100644
index 0000000..5c59c46
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/util/BitmapDumper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.util;
+
+import android.graphics.Bitmap;
+import android.os.Environment;
+import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BitmapUtils;
+
+import java.io.File;
+
+/**
+ * A utility class that will allow the user to save bitmaps to the sdcard on the device.
+ */
+public final class BitmapDumper {
+    private final static String TAG = "BitmapDumper";
+    private final static String IDEAL_RENDERING_FILE_NAME = "idealCapture.png";
+    private final static String TESTED_RENDERING_FILE_NAME = "testedCapture.png";
+    private final static String VISUALIZER_RENDERING_FILE_NAME = "visualizer.png";
+    private final static String SINGULAR_FILE_NAME = "capture.png";
+    private final static String CAPTURE_SUB_DIRECTORY = Environment.getExternalStorageDirectory()
+            + "/UiRenderingCaptures/";
+
+    private BitmapDumper() {}
+
+    /**
+     * Deletes the specific files for the given test in a given class.
+     */
+    public static void deleteFileInClassFolder(String className, String testName) {
+        File directory = new File(CAPTURE_SUB_DIRECTORY + className);
+
+        String[] children = directory.list();
+        if (children == null) {
+            return;
+        }
+        for (String file : children) {
+            if (file.startsWith(testName)) {
+                new File(directory, file).delete();
+            }
+        }
+    }
+
+    public static void createSubDirectory(String className) {
+        File saveDirectory = new File(CAPTURE_SUB_DIRECTORY + className);
+        if (saveDirectory.exists()) {
+            return;
+        }
+        // Create the directory if it isn't already created.
+        saveDirectory.mkdirs();
+    }
+
+    /**
+     * Saves two files, one the capture of an ideal drawing, and one the capture of the tested
+     * drawing. The third file saved is a bitmap that is returned from the given visualizer's
+     * method.
+     * The files are saved to the sdcard directory
+     */
+    public static void dumpBitmaps(Bitmap idealBitmap, Bitmap testedBitmap, String testName,
+            String className, DifferenceVisualizer differenceVisualizer) {
+        Bitmap visualizerBitmap;
+
+        int width = idealBitmap.getWidth();
+        int height = idealBitmap.getHeight();
+        int[] testedArray = new int[width * height];
+        int[] idealArray = new int[width * height];
+        idealBitmap.getPixels(testedArray, 0, width, 0, 0, width, height);
+        testedBitmap.getPixels(idealArray, 0, width, 0, 0, width, height);
+        int[] visualizerArray = differenceVisualizer.getDifferences(idealArray, testedArray);
+        visualizerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        visualizerBitmap.setPixels(visualizerArray, 0, width, 0, 0, width, height);
+        Bitmap croppedBitmap = Bitmap.createBitmap(testedBitmap, 0, 0, width, height);
+
+        saveFile(className, testName, IDEAL_RENDERING_FILE_NAME, idealBitmap);
+        saveFile(className, testName, TESTED_RENDERING_FILE_NAME, croppedBitmap);
+        saveFile(className, testName, VISUALIZER_RENDERING_FILE_NAME, visualizerBitmap);
+    }
+
+    public static void dumpBitmap(Bitmap bitmap, String testName, String className) {
+        if (bitmap == null) {
+            Log.d(TAG, "File not saved, bitmap was null for test : " + testName);
+            return;
+        }
+        saveFile(className, testName, SINGULAR_FILE_NAME, bitmap);
+    }
+
+    private static void saveFile(String className, String testName, String fileName, Bitmap bitmap) {
+        BitmapUtils.saveBitmap(bitmap, CAPTURE_SUB_DIRECTORY + className,
+                testName + "_" + fileName);
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/util/CompareUtils.java b/tests/tests/uirendering27/src/android/uirendering/cts/util/CompareUtils.java
new file mode 100644
index 0000000..c80a778
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/util/CompareUtils.java
@@ -0,0 +1,28 @@
+package android.uirendering.cts.util;
+
+import android.graphics.Color;
+
+public class CompareUtils {
+    /**
+     * @return True if close enough
+     */
+    public static boolean verifyPixelWithThreshold(int color, int expectedColor, int threshold) {
+        int diff = Math.abs(Color.red(color) - Color.red(expectedColor))
+                + Math.abs(Color.green(color) - Color.green(expectedColor))
+                + Math.abs(Color.blue(color) - Color.blue(expectedColor));
+        return diff <= threshold;
+    }
+
+    /**
+     * @param threshold Per channel differences for R / G / B channel against the average of these 3
+     *                  channels. Should be less than 2 normally.
+     * @return True if the color is close enough to be a gray scale color.
+     */
+    public static boolean verifyPixelGrayScale(int color, int threshold) {
+        int average =  Color.red(color) + Color.green(color) + Color.blue(color);
+        average /= 3;
+        return Math.abs(Color.red(color) - average) <= threshold
+                && Math.abs(Color.green(color) - average) <= threshold
+                && Math.abs(Color.blue(color) - average) <= threshold;
+    }
+}
diff --git a/tests/tests/uirendering27/src/android/uirendering/cts/util/DrawCountDown.java b/tests/tests/uirendering27/src/android/uirendering/cts/util/DrawCountDown.java
new file mode 100644
index 0000000..cc260d7
--- /dev/null
+++ b/tests/tests/uirendering27/src/android/uirendering/cts/util/DrawCountDown.java
@@ -0,0 +1,56 @@
+package android.uirendering.cts.util;
+
+import android.view.View;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DrawCountDown implements OnPreDrawListener {
+    private static Set<DrawCountDown> sPendingCallbacks = new HashSet<>();
+
+    private int mDrawCount;
+    private View mTargetView;
+    private Runnable mRunnable;
+
+    private DrawCountDown(View targetView, int countFrames, Runnable countReachedListener) {
+        mTargetView = targetView;
+        mDrawCount = countFrames;
+        mRunnable = countReachedListener;
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        if (mDrawCount <= 0) {
+            synchronized (sPendingCallbacks) {
+                sPendingCallbacks.remove(this);
+            }
+            mTargetView.getViewTreeObserver().removeOnPreDrawListener(this);
+            mRunnable.run();
+        } else {
+            mDrawCount--;
+            mTargetView.postInvalidate();
+        }
+        return true;
+ 
+    }
+
+    public static void countDownDraws(View targetView, int countFrames,
+            Runnable onDrawCountReachedListener) {
+        DrawCountDown counter = new DrawCountDown(targetView, countFrames,
+                onDrawCountReachedListener);
+        synchronized (sPendingCallbacks) {
+            sPendingCallbacks.add(counter);
+        }
+        targetView.getViewTreeObserver().addOnPreDrawListener(counter);
+    }
+
+    public static void cancelPending() {
+        synchronized (sPendingCallbacks) {
+            for (DrawCountDown counter : sPendingCallbacks) {
+                counter.mTargetView.getViewTreeObserver().removeOnPreDrawListener(counter);
+            }
+            sPendingCallbacks.clear();
+        }
+    }
+}
diff --git a/tests/tests/view/res/layout/view_attribute_layout.xml b/tests/tests/view/res/layout/view_attribute_layout.xml
index d3c3200..5dbfe6b 100644
--- a/tests/tests/view/res/layout/view_attribute_layout.xml
+++ b/tests/tests/view/res/layout/view_attribute_layout.xml
@@ -18,7 +18,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:orientation="vertical"
               android:layout_width="match_parent"
-              android:layout_height="match_parent">
+              android:layout_height="match_parent"
+              android:theme="@style/Theme_OverrideTextAppearance">
     <View android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:id="@+id/view1"
diff --git a/tests/tests/view/res/values/themes.xml b/tests/tests/view/res/values/themes.xml
new file mode 100644
index 0000000..e44b58a
--- /dev/null
+++ b/tests/tests/view/res/values/themes.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="Theme_OverrideTextAppearance" parent="@android:style/Theme.Material">
+        <item name="android:textAppearanceLarge">@android:style/TextAppearance.Material.Large</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 945e3f9..c7297f1 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -1814,6 +1814,33 @@
         assertTrue(view.isClickable());
     }
 
+    @Test
+    public void testSetOnGenericMotionListener() {
+        View view = new View(mActivity);
+        MotionEvent event =
+                EventUtils.generateMouseEvent(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        assertFalse(view.dispatchGenericMotionEvent(event));
+        event.recycle();
+
+        View.OnGenericMotionListener listener = mock(View.OnGenericMotionListener.class);
+        doReturn(true).when(listener).onGenericMotion(any(), any());
+        view.setOnGenericMotionListener(listener);
+
+        MotionEvent event2 =
+                EventUtils.generateMouseEvent(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        assertTrue(view.dispatchGenericMotionEvent(event2));
+        event2.recycle();
+
+        view.setOnGenericMotionListener(null);
+        MotionEvent event3 =
+                EventUtils.generateMouseEvent(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        assertFalse(view.dispatchGenericMotionEvent(event3));
+        event3.recycle();
+    }
+
     @Test(expected=NullPointerException.class)
     public void testPerformLongClickNullParent() {
         MockView view = new MockView(mActivity);
diff --git a/tests/tests/voiceinteraction/AndroidManifest.xml b/tests/tests/voiceinteraction/AndroidManifest.xml
index 5d7c00f..393d7a8 100644
--- a/tests/tests/voiceinteraction/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/AndroidManifest.xml
@@ -36,7 +36,6 @@
                 android:label="Local Interaction Activity">
           <intent-filter>
               <action android:name="android.intent.action.TEST_LOCAL_INTERACTION_ACTIVITY" />
-              <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
       </activity>
       <receiver android:name="VoiceInteractionTestReceiver"
diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml
index eb7a7ad..bbf478d 100644
--- a/tests/tests/voiceinteraction/AndroidTest.xml
+++ b/tests/tests/voiceinteraction/AndroidTest.xml
@@ -19,15 +19,19 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="not-shardable" value="true" />
+
+    <!-- Force service to be installed as non-instant mode, always -->
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsVoiceInteractionService.apk" />
-        <option name="test-file-name" value="CtsVoiceInteractionApp.apk" />
-        <option name="test-file-name" value="CtsVoiceInteractionTestCases.apk" />
+        <option name="instant-mode" value="false"/>
+        <option name="force-install-mode" value="FULL"/>
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="CtsVoiceInteractionService.apk"/>
     </target_preparer>
 
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="settings put secure voice_interaction_service android.voiceinteraction.service/.MainInteractionService" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsVoiceInteractionTestCases.apk"/>
+        <option name="test-file-name" value="CtsVoiceInteractionApp.apk"/>
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index 94bef39..d949326 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -18,8 +18,13 @@
 import android.app.VoiceInteractor.PickOptionRequest.Option;
 import android.content.LocusId;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
 
 public class Utils {
     public enum TestCaseType {
@@ -36,6 +41,8 @@
         SUPPORTS_COMMANDS_TEST
     }
 
+    private static final String TAG = Utils.class.getSimpleName();
+
     public static final long OPERATION_TIMEOUT_MS = 5000;
 
     public static final String TESTCASE_TYPE = "testcase_type";
@@ -99,7 +106,6 @@
     public static final String DIRECT_ACTIONS_RESULT_CANCELLED = "cancelled";
     public static final String DIRECT_ACTIONS_RESULT_EXECUTING = "executing";
 
-
     public static final String DIRECT_ACTIONS_ACTION_ID = "actionId";
     public static final Bundle DIRECT_ACTIONS_ACTION_EXTRAS = new Bundle();
     static {
@@ -108,22 +114,37 @@
     }
     public static final LocusId DIRECT_ACTIONS_LOCUS_ID = new LocusId("locusId");
 
+    public static final String SERVICE_NAME =
+            "android.voiceinteraction.service/.MainInteractionService";
+
     public static final String toBundleString(Bundle bundle) {
         if (bundle == null) {
-            return "*** Bundle is null ****";
+            return "null_bundle";
         }
-        StringBuffer buf = new StringBuffer("Bundle is: ");
+        StringBuffer buf = new StringBuffer("Bundle[ ");
         String testType = bundle.getString(TESTCASE_TYPE);
+        boolean empty = true;
         if (testType != null) {
+            empty = false;
             buf.append("testcase type = " + testType);
         }
         ArrayList<String> info = bundle.getStringArrayList(TESTINFO);
         if (info != null) {
             for (String s : info) {
+                empty = false;
                 buf.append(s + "\n\t\t");
             }
+        } else {
+            for (String key : bundle.keySet()) {
+                empty = false;
+                Object value = bundle.get(key);
+                if (value instanceof Bundle) {
+                    value = toBundleString((Bundle) value);
+                }
+                buf.append(key).append('=').append(value).append(' ');
+            }
         }
-        return buf.toString();
+        return empty ? "empty_bundle" : buf.append(']').toString();
     }
 
     public static final String toOptionsString(Option[] options) {
@@ -143,4 +164,26 @@
         testinfo.getStringArrayList(testinfo.getString(Utils.TESTCASE_TYPE))
             .add(TEST_ERROR + " " + msg);
     }
+
+    public static boolean await(CountDownLatch latch) {
+        try {
+            if (latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) return true;
+            Log.e(TAG, "latch timed out");
+        } catch (InterruptedException e) {
+            /* ignore */
+            Log.e(TAG, "Interrupted", e);
+        }
+        return false;
+    }
+
+    public static boolean await(Condition condition) {
+        try {
+            if (condition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) return true;
+            Log.e(TAG, "condition timed out");
+        } catch (InterruptedException e) {
+            /* ignore */
+            Log.e(TAG, "Interrupted", e);
+        }
+        return false;
+    }
 }
diff --git a/tests/tests/voiceinteraction/service/AndroidManifest.xml b/tests/tests/voiceinteraction/service/AndroidManifest.xml
index faf3d76..b01653c 100644
--- a/tests/tests/voiceinteraction/service/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/service/AndroidManifest.xml
@@ -24,21 +24,26 @@
               android:label="CTS test voice interaction service"
               android:permission="android.permission.BIND_VOICE_INTERACTION"
               android:process=":interactor"
-              android:exported="true">
+              android:exported="true"
+              android:visibleToInstantApps="true">
           <meta-data android:name="android.voice_interaction"
                      android:resource="@xml/interaction_service" />
           <intent-filter>
               <action android:name="android.service.voice.VoiceInteractionService" />
           </intent-filter>
       </service>
-      <activity android:name=".VoiceInteractionMain" >
+      <activity android:name=".VoiceInteractionMain"
+                android:exported="true"
+                android:visibleToInstantApps="true">
           <intent-filter>
               <action android:name="android.intent.action.START_TEST" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
       </activity>
       <activity android:name=".SettingsActivity"
-                android:label="Voice Interaction Settings">
+                android:label="Voice Interaction Settings"
+                android:exported="true"
+                android:visibleToInstantApps="true">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.DEFAULT" />
@@ -46,10 +51,14 @@
       </activity>
       <service android:name=".MainInteractionSessionService"
               android:permission="android.permission.BIND_VOICE_INTERACTION"
-              android:process=":session">
+              android:process=":session"
+              android:exported="true"
+              android:visibleToInstantApps="true">
       </service>
       <service android:name=".MainRecognitionService"
-              android:label="CTS Voice Recognition Service">
+              android:label="CTS Voice Recognition Service"
+              android:exported="true"
+              android:visibleToInstantApps="true">
           <intent-filter>
               <action android:name="android.speech.RecognitionService" />
               <category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
index cf635a8..d20ad80 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/DirectActionsSession.java
@@ -117,11 +117,7 @@
         mLock.lock();
         try {
             if (mActivityId == null) {
-                try {
-                    mCondition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-                } catch (InterruptedException e) {
-                    /* ignore */
-                }
+                Utils.await(mCondition);
             }
             final Bundle result = new Bundle();
             if (mActivityId != null) {
@@ -142,18 +138,14 @@
 
         mLock.lock();
         try {
-            requestDirectActions(mActivityId,null, AsyncTask.THREAD_POOL_EXECUTOR, (b) -> {
+            requestDirectActions(mActivityId, null, AsyncTask.THREAD_POOL_EXECUTOR, (b) -> {
                 actions.addAll(b);
                 latch.countDown();
             });
         } finally {
             mLock.unlock();
         }
-        try {
-            latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            /* ignore */
-        }
+        Utils.await(latch);
 
         outResult.putParcelableArrayList(Utils.DIRECT_ACTIONS_KEY_RESULT, actions);
     }
@@ -168,11 +160,7 @@
             result.putAll(b);
             latch.countDown();
         });
-        try {
-            latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            /* ignore */
-        }
+        Utils.await(latch);
 
         outResult.putBundle(Utils.DIRECT_ACTIONS_KEY_RESULT, result);
     }
@@ -199,19 +187,11 @@
             resultLatch.countDown()
         );
 
-        try {
-            resultLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            /* ignore */
-        }
+        Utils.await(resultLatch);
 
         cancellationSignal.cancel();
 
-        try {
-            cancelLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            /* ignore */
-        }
+        Utils.await(cancelLatch);
 
         outResult.putBundle(Utils.DIRECT_ACTIONS_KEY_RESULT, result);
     }
@@ -220,11 +200,7 @@
         mLock.lock();
         try {
             if (!mActionsInvalidated) {
-                try {
-                    mCondition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-                } catch (InterruptedException e) {
-                    /* ignore */
-                }
+                Utils.await(mCondition);
             }
             outResult.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, mActionsInvalidated);
             mActionsInvalidated = false;
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
index ca92286..ccd20e5 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionSession;
@@ -60,10 +61,14 @@
                     // Call to verify onGetSupportedVoiceActions is available.
                     onGetSupportedVoiceActions(Collections.emptySet());
                     args = new Bundle();
-                    Intent intent = new Intent();
-                    intent.setComponent(new ComponentName("android.voiceinteraction.testapp",
-                            "android.voiceinteraction.testapp.TestApp"));
+                    Intent intent = new Intent()
+                            .setAction(Intent.ACTION_VIEW)
+                            .addCategory(Intent.CATEGORY_VOICE)
+                            .addCategory(Intent.CATEGORY_BROWSABLE)
+                            .setData(Uri.parse("https://android.voiceinteraction.testapp"
+                                    + "/TestApp"));
                     args.putParcelable("intent", intent);
+                    Log.v(TAG, "showSession(): " + args);
                     showSession(args, 0);
                 } else {
                     Log.wtf(TAG, "**** Not starting MainInteractionService because" +
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
index 6f82d6f..e53a93f 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -29,7 +29,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 public class MainInteractionSession extends VoiceInteractionSession {
     static final String TAG = "MainInteractionSession";
 
@@ -50,6 +49,7 @@
         }
         sessionStarted.setClassName("android.voiceinteraction.cts",
                 "android.voiceinteraction.cts.VoiceInteractionTestReceiver");
+        Log.i(TAG, "onCreate(): broadcast intent=" + sessionStarted);
         getContext().sendBroadcast(sessionStarted);
     }
 
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/AbstractVoiceInteractionTestCase.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/AbstractVoiceInteractionTestCase.java
new file mode 100644
index 0000000..a5cb7d4
--- /dev/null
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/AbstractVoiceInteractionTestCase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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.voiceinteraction.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.voiceinteraction.common.Utils;
+
+import com.android.compatibility.common.util.RequiredFeatureRule;
+import com.android.compatibility.common.util.SettingsStateChangerRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+/**
+ * Base class for all test cases
+ */
+@RunWith(AndroidJUnit4.class)
+abstract class AbstractVoiceInteractionTestCase {
+
+    // TODO: use PackageManager's / make it @TestApi
+    protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
+
+    protected final Context mContext = getInstrumentation().getTargetContext();
+
+    @Rule
+    public final RequiredFeatureRule mRequiredFeatureRule = new RequiredFeatureRule(
+            FEATURE_VOICE_RECOGNIZERS);
+
+    @Rule
+    public final SettingsStateChangerRule mServiceSetterRule = new SettingsStateChangerRule(
+            mContext, Settings.Secure.VOICE_INTERACTION_SERVICE, Utils.SERVICE_NAME);
+
+    @Before
+    public void prepareDevice() throws Exception {
+        // Unlock screen.
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+        // Dismiss keyguard, in case it's set as "Swipe to unlock".
+        runShellCommand("wm dismiss-keyguard");
+    }
+}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
index 55b367f..338c361 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
@@ -17,21 +17,20 @@
 package android.voiceinteraction.cts;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.app.DirectAction;
-import android.content.Context;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteCallback;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
 import android.voiceinteraction.common.Utils;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.ThrowingRunnable;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -39,11 +38,14 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * Tests for the direction action related functions.
  */
-@RunWith(AndroidJUnit4.class)
-public class DirectActionsTest {
+public class DirectActionsTest extends AbstractVoiceInteractionTestCase {
+    private static final String TAG = DirectActionsTest.class.getSimpleName();
     private static final long OPERATION_TIMEOUT_MS = 5000;
 
     private final @NonNull SessionControl mSessionControl = new SessionControl();
@@ -56,6 +58,7 @@
         try {
             // Get the actions.
             final List<DirectAction> actions = mSessionControl.getDirectActions();
+            Log.v(TAG, "actions: " + actions);
 
             // Only the expected action should be reported
             final DirectAction action = getExpectedDirectActionAssertively(actions);
@@ -72,6 +75,7 @@
         }
     }
 
+    @AppModeFull(reason = "testPerformDirectAction() is enough")
     @Test
     public void testCancelPerformedDirectAction() throws Exception {
         mActivityControl.startActivity();
@@ -79,6 +83,7 @@
         try {
             // Get the actions.
             final List<DirectAction> actions = mSessionControl.getDirectActions();
+            Log.v(TAG, "actions: " + actions);
 
             // Only the expected action should be reported
             final DirectAction action = getExpectedDirectActionAssertively(actions);
@@ -95,6 +100,7 @@
         }
     }
 
+    @AppModeFull(reason = "testPerformDirectAction() is enough")
     @Test
     public void testVoiceInteractorDestroy() throws Exception {
         mActivityControl.startActivity();
@@ -103,19 +109,16 @@
             // Get the actions to set up the VoiceInteractor
             mSessionControl.getDirectActions();
 
-            assertThat(mActivityControl.detectInteractorDestroyed(() -> {
-                try {
-                    mSessionControl.stopVoiceInteractionSession();
-                } catch (TimeoutException e) {
-                    /* ignore */
-                }
-            })).isTrue();
+            assertThat(mActivityControl
+                    .detectInteractorDestroyed(() -> mSessionControl.stopVoiceInteractionSession()))
+                            .isTrue();
         } finally {
             mSessionControl.stopVoiceInteractionSession();
             mActivityControl.finishActivity();
         }
     }
 
+    @AppModeFull(reason = "testPerformDirectAction() is enough")
     @Test
     public void testNotifyDirectActionsChanged() throws Exception {
         mActivityControl.startActivity();
@@ -124,23 +127,18 @@
             // Get the actions to set up the VoiceInteractor
             mSessionControl.getDirectActions();
 
-            assertThat(mSessionControl.detectDirectActionsInvalidated(() -> {
-                try {
-                    mActivityControl.invalidateDirectActions();
-                } catch (TimeoutException e) {
-                    /* ignore */
-                }
-            })).isTrue();
+            assertThat(mSessionControl.detectDirectActionsInvalidated(
+                    () -> mActivityControl.invalidateDirectActions())).isTrue();
         } finally {
             mSessionControl.stopVoiceInteractionSession();
             mActivityControl.finishActivity();
         }
     }
 
-    private class SessionControl {
+    private final class SessionControl {
         private @Nullable RemoteCallback mControl;
 
-        private void startVoiceInteractionSession() throws TimeoutException {
+        private void startVoiceInteractionSession() throws Exception {
             final CountDownLatch latch = new CountDownLatch(1);
 
             final RemoteCallback callback = new RemoteCallback((result) -> {
@@ -156,23 +154,21 @@
             intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-            getContext().startActivity(intent);
+            Log.v(TAG, "startVoiceInteractionSession(): " + intent);
+            mContext.startActivity(intent);
 
-            try {
-                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    throw new TimeoutException();
-                }
-            } catch (InterruptedException e) {
-                /* cannot happen */
+            if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                throw new TimeoutException("actitvity not started in " + OPERATION_TIMEOUT_MS
+                        + "ms");
             }
         }
 
-        private void stopVoiceInteractionSession() throws TimeoutException {
+        private void stopVoiceInteractionSession() throws Exception {
             executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_FINISH,
                     null /*directAction*/, null /*arguments*/, null /*postActionCommand*/);
         }
 
-        @Nullable List<DirectAction> getDirectActions() throws TimeoutException {
+        @Nullable List<DirectAction> getDirectActions() throws Exception {
             final ArrayList<DirectAction> actions = new ArrayList<>();
             final Bundle result = executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS,
                     null /*directAction*/, null /*arguments*/, null /*postActionCommand*/);
@@ -181,19 +177,20 @@
         }
 
         @Nullable Bundle performDirectAction(@NonNull DirectAction directAction,
-                @NonNull Bundle arguments) throws TimeoutException {
+                @NonNull Bundle arguments) throws Exception {
             return executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION,
                     directAction, arguments, null /*postActionCommand*/);
         }
 
         @Nullable Bundle performDirectActionAndCancel(@NonNull DirectAction directAction,
-                @NonNull Bundle arguments) throws TimeoutException {
+                @NonNull Bundle arguments) throws Exception {
             return executeCommand(Utils.DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL,
                     directAction, arguments, null /*postActionCommand*/);
         }
 
-        @Nullable boolean detectDirectActionsInvalidated(@NonNull Runnable postActionCommand)
-                throws TimeoutException {
+        @Nullable
+        boolean detectDirectActionsInvalidated(@NonNull ThrowingRunnable postActionCommand)
+                throws Exception {
             final Bundle result = executeCommand(
                     Utils.DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED,
                     null /*directAction*/, null /*arguments*/, postActionCommand);
@@ -201,8 +198,8 @@
         }
 
         @Nullable Bundle executeCommand(@NonNull String action, @Nullable DirectAction directAction,
-                @Nullable Bundle arguments, @Nullable Runnable postActionCommand)
-                throws TimeoutException {
+                @Nullable Bundle arguments, @Nullable ThrowingRunnable postActionCommand)
+                throws Exception {
             final CountDownLatch latch = new CountDownLatch(1);
 
             final Bundle result = new Bundle();
@@ -218,20 +215,21 @@
             command.putBundle(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS, arguments);
             command.putParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
 
+            Log.v(TAG, "executeCommand(): action=" + action + " command="
+                    + Utils.toBundleString(command));
             mControl.sendResult(command);
 
             if (postActionCommand != null) {
+                Log.v(TAG, "Executing post-action command for " + action);
                 postActionCommand.run();
             }
 
-            try {
-                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    throw new TimeoutException();
-                }
-            } catch (InterruptedException e) {
-                /* cannot happen */
+            if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                throw new TimeoutException("result not received in " + OPERATION_TIMEOUT_MS + "ms");
             }
 
+            Log.v(TAG, "returning " + Utils.toBundleString(result));
+
             return result;
         }
     }
@@ -239,55 +237,67 @@
     private final class ActivityControl {
         private @Nullable RemoteCallback mControl;
 
-        void startActivity() throws TimeoutException {
+        void startActivity() throws Exception {
             final CountDownLatch latch = new CountDownLatch(1);
 
             final RemoteCallback callback = new RemoteCallback((result) -> {
+                Log.v(TAG, "ActivityControl: testapp called the callback: "
+                        + Utils.toBundleString(result));
                 mControl = result.getParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL);
                 latch.countDown();
             });
 
-            final Intent intent = new Intent();
-            intent.setClassName("android.voiceinteraction.testapp",
-                    "android.voiceinteraction.testapp.DirectActionsActivity");
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
+            final Intent intent = new Intent()
+                    .setAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .setData(Uri.parse("https://android.voiceinteraction.testapp"
+                            + "/DirectActionsActivity"))
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .putExtra(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
 
-            getContext().startActivity(intent);
+            if (!mContext.getPackageManager().isInstantApp()) {
+                intent.setPackage("android.voiceinteraction.testapp");
+            }
 
-            try {
-                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    throw new TimeoutException();
-                }
-            } catch (InterruptedException e) {
-                /* cannot happen */
+            Log.v(TAG, "startActivity: " + intent);
+
+            mContext.startActivity(intent);
+
+            if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                throw new TimeoutException("actitvity not started in " + OPERATION_TIMEOUT_MS
+                        + "ms");
             }
         }
 
-        private boolean detectInteractorDestroyed(Runnable destroyTrigger) throws TimeoutException {
+        private boolean detectInteractorDestroyed(ThrowingRunnable destroyTrigger)
+                throws Exception {
             final Bundle result = executeRemoteCommand(
                     Utils.DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR,
                     destroyTrigger);
             return result.getBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT);
         }
 
-        void finishActivity() throws TimeoutException {
-            executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_FINISH,
-                    null /*postActionCommand*/);
+        void finishActivity() throws Exception {
+            executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_FINISH);
         }
 
-        void invalidateDirectActions() throws TimeoutException {
-            executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS,
-                    null /*postActionCommand*/);
+        void invalidateDirectActions() throws Exception {
+            executeRemoteCommand(Utils.DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS);
         }
 
-        Bundle executeRemoteCommand(@NonNull String action,
-                @Nullable Runnable postActionCommand) throws TimeoutException {
+        @NonNull Bundle executeRemoteCommand(@NonNull String action) throws Exception {
+            return executeRemoteCommand(action, /* postActionCommand= */ null);
+        }
+
+        @NonNull Bundle executeRemoteCommand(@NonNull String action,
+                @Nullable ThrowingRunnable postActionCommand) throws Exception {
             final Bundle result = new Bundle();
 
             final CountDownLatch latch = new CountDownLatch(1);
 
             final RemoteCallback callback = new RemoteCallback((b) -> {
+                Log.v(TAG, "executeRemoteCommand(): received result from '" + action + "': "
+                        + Utils.toBundleString(b));
                 if (b != null) {
                     result.putAll(b);
                 }
@@ -298,18 +308,21 @@
             command.putString(Utils.DIRECT_ACTIONS_KEY_COMMAND, action);
             command.putParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK, callback);
 
+            Log.v(TAG, "executeRemoteCommand(): sending command for '" + action + "'");
             mControl.sendResult(command);
 
             if (postActionCommand != null) {
-                postActionCommand.run();
+                try {
+                    postActionCommand.run();
+                } catch (TimeoutException e) {
+                    Log.e(TAG, "action '" + action + "' timed out" );
+                } catch (Exception e) {
+                    throw e;
+                }
             }
 
-            try {
-                if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                    throw new TimeoutException();
-                }
-            } catch (InterruptedException e) {
-                /* cannot happen */
+            if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                throw new TimeoutException("result not received in " + OPERATION_TIMEOUT_MS + "ms");
             }
             return result;
         }
@@ -317,6 +330,7 @@
 
     private @NonNull DirectAction getExpectedDirectActionAssertively(
             @Nullable List<DirectAction> actions) {
+        assertWithMessage("no actions").that(actions).isNotEmpty();
         final DirectAction action = actions.get(0);
         assertThat(action.getId()).isEqualTo(Utils.DIRECT_ACTIONS_ACTION_ID);
         assertThat(action.getExtras().getString(Utils.DIRECT_ACTION_EXTRA_KEY))
@@ -328,22 +342,21 @@
     private @NonNull Bundle createActionArguments() {
         final Bundle args = new Bundle();
         args.putString(Utils.DIRECT_ACTIONS_KEY_ARGUMENTS, Utils.DIRECT_ACTIONS_KEY_ARGUMENTS);
+        Log.v(TAG, "createActionArguments(): " + Utils.toBundleString(args));
         return args;
     }
 
     private void assertActionSucceeded(@NonNull Bundle result) {
         final Bundle bundle = result.getBundle(Utils.DIRECT_ACTIONS_KEY_RESULT);
         final String status = bundle.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
-        assertThat(Utils.DIRECT_ACTIONS_RESULT_PERFORMED).isEqualTo(status);
+        assertWithMessage("assertActionSucceeded(%s)", Utils.toBundleString(result))
+                .that(Utils.DIRECT_ACTIONS_RESULT_PERFORMED).isEqualTo(status);
     }
 
     private void assertActionCancelled(@NonNull Bundle result) {
         final Bundle bundle = result.getBundle(Utils.DIRECT_ACTIONS_KEY_RESULT);
         final String status = bundle.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
-        assertThat(Utils.DIRECT_ACTIONS_RESULT_CANCELLED).isEqualTo(status);
-    }
-
-    private static @NonNull Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getContext();
+        assertWithMessage("assertActionCancelled(%s)", Utils.toBundleString(result))
+                .that(Utils.DIRECT_ACTIONS_RESULT_CANCELLED).isEqualTo(status);
     }
 }
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
index 9bfc7c4..fdbeb35 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
@@ -16,17 +16,13 @@
 
 package android.voiceinteraction.cts;
 
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.fail;
 
-import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-
-import com.android.compatibility.common.util.RequiredFeatureRule;
+import android.net.Uri;
+import android.util.Log;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -40,15 +36,13 @@
 import androidx.test.rule.ActivityTestRule;
 
 @RunWith(AndroidJUnit4.class)
-public class LocalVoiceInteractionTest {
+public class LocalVoiceInteractionTest extends AbstractVoiceInteractionTestCase {
 
+    private static final String TAG = LocalVoiceInteractionTest.class.getSimpleName();
     private static final int TIMEOUT_MS = 20 * 1000;
 
-    // TODO: use PackageManager's / make it @TestApi
-    protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
 
     private TestLocalInteractionActivity mTestActivity;
-    private final Context mContext = getInstrumentation().getTargetContext();
     private final CountDownLatch mLatchStart = new CountDownLatch(1);
     private final CountDownLatch mLatchStop = new CountDownLatch(1);
 
@@ -56,19 +50,15 @@
     public final ActivityTestRule<TestLocalInteractionActivity> mActivityTestRule =
             new ActivityTestRule<>(TestLocalInteractionActivity.class, false, false);
 
-    @Rule
-    public final RequiredFeatureRule mRequiredFeatureRule = new RequiredFeatureRule(
-            FEATURE_VOICE_RECOGNIZERS);
-
     @Before
     public void setUp() throws Exception {
         startTestActivity();
     }
 
     private void startTestActivity() throws Exception {
-        Intent intent = new Intent();
-        intent.setAction("android.intent.action.TEST_LOCAL_INTERACTION_ACTIVITY");
-        intent.setComponent(new ComponentName(mContext, TestLocalInteractionActivity.class));
+        Intent intent = new Intent()
+                .setAction("android.intent.action.TEST_LOCAL_INTERACTION_ACTIVITY");
+        Log.i(TAG, "startTestActivity: " + intent);
         mTestActivity = mActivityTestRule.launchActivity(intent);
     }
 
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java
index b5d5938..efd131b 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java
@@ -32,12 +32,13 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Log.i(TAG, " in onCreate");
+        Log.i(TAG, " in onCreate: " + getIntent());
     }
 
     void startLocalInteraction(CountDownLatch counter) {
         Bundle privateOptions = new Bundle();
         privateOptions.putString(Utils.PRIVATE_OPTIONS_KEY, Utils.PRIVATE_OPTIONS_VALUE);
+        Log.i(TAG, "startLocalInteraction(): " + Utils.toBundleString(privateOptions));
         mStarted = counter;
         startLocalVoiceInteraction(privateOptions);
     }
@@ -57,7 +58,7 @@
 
     @Override
     public void onLocalVoiceInteractionStopped() {
-        Log.i(TAG, " in onLocalVoiceInteractionStarted");
+        Log.i(TAG, " in onLocalVoiceInteractionStopped");
         if (mStopped != null) {
             mStopped.countDown();
         }
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
index 920d828..b327f5d 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestStartActivity.java
@@ -17,8 +17,8 @@
 package android.voiceinteraction.cts;
 
 import android.app.Activity;
-import android.content.Intent;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -34,11 +34,11 @@
     @Override
     protected void onResume() {
         super.onResume();
-        Log.i(TAG, " in onResume");
         Intent intent = new Intent();
         intent.setAction("android.intent.action.START_TEST");
         intent.setComponent(new ComponentName("android.voiceinteraction.service",
                 "android.voiceinteraction.service.VoiceInteractionMain"));
+        Log.i(TAG, " in onResume: " + intent);
         startActivity(intent);
     }
 
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 21e4e6c..fbb4836 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -16,8 +16,6 @@
 
 package android.voiceinteraction.cts;
 
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
@@ -28,33 +26,28 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.voiceinteraction.common.Utils;
 
-import com.android.compatibility.common.util.RequiredFeatureRule;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.rule.ActivityTestRule;
 
-@RunWith(AndroidJUnit4.class)
-public class VoiceInteractionTest {
+// TODO: ideally we should split testAll() into multiple tests, and run at least one of them
+// on instant
+@AppModeFull(reason = "DirectActionsTest is enough")
+public class VoiceInteractionTest extends AbstractVoiceInteractionTestCase {
     static final String TAG = "VoiceInteractionTest";
     private static final int TIMEOUT_MS = 20 * 1000;
 
-    // TODO: use PackageManager's / make it @TestApi
-    protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
-
     private TestStartActivity mTestActivity;
-    private final Context mContext = getInstrumentation().getTargetContext();
     private TestResultsReceiver mReceiver;
     private Bundle mResults;
     private final CountDownLatch mLatch = new CountDownLatch(1);
@@ -63,10 +56,6 @@
     public final ActivityTestRule<TestStartActivity> mActivityTestRule =
             new ActivityTestRule<>(TestStartActivity.class, false, false);
 
-    @Rule
-    public final RequiredFeatureRule mRequiredFeatureRule = new RequiredFeatureRule(
-            FEATURE_VOICE_RECOGNIZERS);
-
     @Before
     public void setUp() throws Exception {
         mReceiver = new TestResultsReceiver();
@@ -92,6 +81,7 @@
         Intent intent = new Intent();
         intent.setAction("android.intent.action.TEST_START_ACTIVITY");
         intent.setComponent(new ComponentName(mContext, TestStartActivity.class));
+        Log.v(TAG, "startTestActivity:" + intent);
         mTestActivity = mActivityTestRule.launchActivity(intent);
     }
 
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
index 14ae14b..f0ac213 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
@@ -28,6 +28,8 @@
 
 public class VoiceInteractionTestReceiver extends BroadcastReceiver {
 
+    private static final String TAG = VoiceInteractionTestReceiver.class.getSimpleName();
+
     private static CountDownLatch sServiceStartedLatch = new CountDownLatch(1);
     private static Intent sReceivedIntent;
 
@@ -43,7 +45,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        Log.i("VoiceInteractionTestReceiver", "Got broadcast that MainInteractionService started");
+        Log.i(TAG, "Got broadcast that MainInteractionService started");
         sReceivedIntent = intent;
         sServiceStartedLatch.countDown();
     }
diff --git a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
index 4e7156b..c683c66 100644
--- a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
@@ -25,15 +25,28 @@
                 android:label="Voice Interaction Test App"
                 android:theme="@android:style/Theme.DeviceDefault">
           <intent-filter>
-              <action android:name="android.intent.action.TEST_APP" />
+              <action android:name="android.intent.action.VIEW"/>
               <category android:name="android.intent.category.DEFAULT" />
+              <category android:name="android.intent.category.BROWSABLE" />
               <category android:name="android.intent.category.VOICE" />
+              <data android:scheme="https" />
+              <data android:host="android.voiceinteraction.testapp" />
+              <data android:path="/TestApp" />
           </intent-filter>
       </activity>
 
        <activity android:name=".DirectActionsActivity"
                 android:label="Direct actions activity"
                 android:exported="true">
+          <intent-filter>
+              <action android:name="android.intent.action.VIEW"/>
+              <category android:name="android.intent.category.DEFAULT" />
+              <category android:name="android.intent.category.BROWSABLE" />
+              <data android:scheme="https" />
+              <data android:host="android.voiceinteraction.testapp" />
+              <data android:path="/DirectActionsActivity" />
+              <category android:name="android.intent.category.VOICE" />
+          </intent-filter>
         </activity>
 
     </application>
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
index 3457507..9a67f51 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
@@ -19,10 +19,12 @@
 import android.app.Activity;
 import android.app.DirectAction;
 import android.app.VoiceInteractor;
+import android.content.Intent;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.RemoteCallback;
+import android.util.Log;
 import android.voiceinteraction.common.Utils;
 
 import androidx.annotation.NonNull;
@@ -31,7 +33,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 /**
@@ -39,14 +40,19 @@
  */
 public final class DirectActionsActivity extends Activity {
 
+    private static final String TAG = DirectActionsActivity.class.getSimpleName();
+
     @Override
     protected void onResume() {
         super.onResume();
-        final Bundle args = getIntent().getExtras();
-        final RemoteCallback callback = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK);
+        final Intent intent = getIntent();
+        Log.v(TAG, "onResume: " + intent);
+        final Bundle args = intent.getExtras();
+        final RemoteCallback callBack = args.getParcelable(Utils.DIRECT_ACTIONS_KEY_CALLBACK);
 
         final RemoteCallback control = new RemoteCallback((cmdArgs) -> {
             final String command = cmdArgs.getString(Utils.DIRECT_ACTIONS_KEY_COMMAND);
+            Log.v(TAG, "on remote callback: command=" + command);
             switch (command) {
                 case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR: {
                     final RemoteCallback commandCallback = cmdArgs.getParcelable(
@@ -68,13 +74,15 @@
 
         final Bundle result = new Bundle();
         result.putParcelable(Utils.DIRECT_ACTIONS_KEY_CONTROL, control);
-        callback.sendResult(result);
+        Log.v(TAG, "onResume(): result=" + Utils.toBundleString(result));
+        callBack.sendResult(result);
     }
 
     @Override
     public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal,
             @NonNull Consumer<List<DirectAction>> callback) {
         if (getVoiceInteractor() == null) {
+            Log.e(TAG, "onGetDirectActions(): no voice interactor");
             callback.accept(Collections.emptyList());
             return;
         }
@@ -119,11 +127,7 @@
             latch.countDown();
         });
 
-        try {
-            latch.await(Utils.OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            /* ignore */
-        }
+        Utils.await(latch);
 
         callback.sendResult(result);
     }
@@ -132,6 +136,7 @@
         getVoiceInteractor().notifyDirectActionsChanged();
         final Bundle result = new Bundle();
         result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+        Log.v(TAG, "invalidateDirectActions(): " + Utils.toBundleString(result));
         callback.sendResult(result);
     }
 
@@ -139,6 +144,7 @@
         finish();
         final Bundle result = new Bundle();
         result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
+        Log.v(TAG, "doFinish(): " + Utils.toBundleString(result));
         callback.sendResult(result);
     }
 
@@ -146,6 +152,7 @@
         final Bundle result = new Bundle();
         result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
                 Utils.DIRECT_ACTIONS_RESULT_PERFORMED);
+        Log.v(TAG, "reportActionPerformed(): " + Utils.toBundleString(result));
         callback.accept(result);
     }
 
@@ -153,6 +160,7 @@
         final Bundle result = new Bundle();
         result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
                 Utils.DIRECT_ACTIONS_RESULT_CANCELLED);
+        Log.v(TAG, "reportActionCancelled(): " + Utils.toBundleString(result));
         callback.accept(result);
     }
 
@@ -160,10 +168,12 @@
         final Bundle result = new Bundle();
         result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT,
                 Utils.DIRECT_ACTIONS_RESULT_EXECUTING);
+        Log.v(TAG, "reportActionExecuting(): " + Utils.toBundleString(result));
         callback.accept(result);
     }
 
     private static void reportActionFailed(Consumer<Bundle> callback) {
+        Log.v(TAG, "reportActionFailed()");
         callback.accept( new Bundle());
     }
 }
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 321afb4..6f1b7f1 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -309,7 +309,8 @@
         </activity>
 
         <activity android:name="android.widget.cts.ListPopupWindowCtsActivity"
-                  android:label="ListPopupWindowCtsActivity">
+                  android:label="ListPopupWindowCtsActivity"
+                  android:windowSoftInputMode="stateAlwaysHidden">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index 123b9e7..fd9afe8 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <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" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWidgetTestCases.apk" />
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
index 3d05bf9..a31963c 100644
--- a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -36,6 +37,8 @@
 import android.util.DisplayMetrics;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 import android.view.View;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
@@ -343,6 +346,22 @@
                 magnifierPosition.y, PIXEL_COMPARISON_DELTA);
     }
 
+    @Test
+    public void testShow_whenPixelCopyFails() throws Throwable {
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
+            mActivity.setContentView(R.layout.magnifier_activity_centered_surfaceview_layout);
+        }, false /*forceLayout*/);
+        final View view = mActivity.findViewById(R.id.magnifier_centered_view);
+
+        runOnUiThreadAndWaitForCompletion(() -> mMagnifier = new Magnifier.Builder(view).build());
+        // The PixelCopy will fail as no draw has been done so far to the SurfaceView.
+        showMagnifier(0f, 0f);
+
+        assertNull(mMagnifier.getPosition());
+        assertNull(mMagnifier.getSourcePosition());
+        assertNull(mMagnifier.getContent());
+    }
+
     //***** Tests for #dismiss() *****//
 
     @Test
@@ -708,8 +727,15 @@
     public void testSourcePosition_respectsMaxInSurfaceBounds_forSurfaceView() throws Throwable {
         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
             mActivity.setContentView(R.layout.magnifier_activity_centered_surfaceview_layout);
-        }, false /*forceLayout*/);
+        }, false /* forceLayout */);
         final View view = mActivity.findViewById(R.id.magnifier_centered_view);
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, view, () -> {
+            // Draw something in the SurfaceView for the Magnifier to copy.
+            final SurfaceHolder surfaceHolder = ((SurfaceView) view).getHolder();
+            final Canvas canvas = surfaceHolder.lockHardwareCanvas();
+            canvas.drawColor(Color.BLUE);
+            surfaceHolder.unlockCanvasAndPost(canvas);
+        });
         final Magnifier.Builder builder = new Magnifier.Builder(view)
                 .setSize(100, 100)
                 .setInitialZoom(5f) /* 20x20 source size */
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index db78965..ade1f69 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -736,9 +736,21 @@
     @Test
     public void testGetMaxAvailableHeight() {
         mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP));
+
         Rect displayFrame = new Rect();
         View anchorView = mActivity.findViewById(R.id.anchor_upper);
-        anchorView.getWindowVisibleDisplayFrame(displayFrame);
+
+        // Move the top of the displayFrame to the anchor position.
+        int[] anchorPos = new int[2];
+        anchorView.getLocationOnScreen(anchorPos);
+        displayFrame.top = anchorPos[1];
+
+        // Move the bottom of the displayFrame to the bottom of the window.
+        Rect window = new Rect();
+        anchorView.getWindowDisplayFrame(window);
+        displayFrame.bottom = window.bottom;
+
+        // Display height is from the bottom of the action bar to the bottom of the screen.
         int displayHeight = displayFrame.height();
         int available = displayHeight - anchorView.getHeight();
         int maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
diff --git a/tools/cts-api-coverage/proto/cts_report.proto b/tools/cts-api-coverage/proto/cts_report.proto
index afb4338..5007c69 100644
--- a/tools/cts-api-coverage/proto/cts_report.proto
+++ b/tools/cts-api-coverage/proto/cts_report.proto
@@ -90,8 +90,12 @@
 // The numbers are in dpi and should match android.util.DisplayMetrics.DENSITY_*
 enum LogicalDensity {
   LDPI = 120;
+  DENSITY_140 = 140;
   MDPI = 160;
+  DENSITY_180 = 180;
+  DENSITY_200 = 200;
   TVDPI = 213;
+  DENSITY_220 = 220;
   HDPI = 240;
   DENSITY_260 = 260;
   DENSITY_280 = 280;
@@ -103,10 +107,12 @@
   // DENSITY_XXHIGH (480 dpi).
   DENSITY_400 = 400;
   DENSITY_420 = 420;
+  DENSITY_440 = 440;
   XXHDPI = 480;
   // Intermediate density for screens that sit somewhere between DENSITY_XXHIGH (480 dpi) and
   // DENSITY_XXXHIGH (640 dpi).
   DENSITY_560 = 560;
+  DENSITY_600 = 600;
   XXXHDPI = 640;
 }
 
@@ -349,4 +355,4 @@
     optional string abi = 6;
   }
   repeated TestPackage test_package = 8;
-}
\ No newline at end of file
+}